From ef4be960ca20fe5fc334f22442bd908adfb92442 Mon Sep 17 00:00:00 2001 From: Bozo The Builder Date: Sun, 23 Apr 2023 04:06:52 -0700 Subject: [PATCH] Sync changes from upstream repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alain Co-authored-by: Andrew Le Bihan Co-authored-by: croudyj Co-authored-by: Dale Fugier Co-authored-by: Dale Lear Co-authored-by: David Eränen Co-authored-by: Greg Arden Co-authored-by: Mikko Oksanen Co-authored-by: piac Co-authored-by: Steve Baer Co-authored-by: TimHemmelman Co-authored-by: Will Pearson --- CMakeLists.txt | 9 +- makefile | 3 + opennurbs.h | 1 + opennurbsRhino_iOS.xcodeproj/project.pbxproj | 10 + opennurbs_3dm_attributes.cpp | 261 ++- opennurbs_3dm_attributes.h | 44 +- opennurbs_3dm_settings.cpp | 782 +++++-- opennurbs_3dm_settings.h | 174 +- opennurbs_archive_manifest.cpp | 6 +- opennurbs_color.cpp | 9 + opennurbs_color.h | 6 + opennurbs_decals.cpp | 18 +- opennurbs_decals.h | 6 +- opennurbs_defines.h | 6 + opennurbs_dimension.cpp | 7 +- opennurbs_dithering.cpp | 34 +- opennurbs_dithering.h | 27 +- opennurbs_embedded_file.cpp | 5 + opennurbs_embedded_file.h | 27 +- opennurbs_extensions.cpp | 142 +- opennurbs_font.h | 2 +- opennurbs_ground_plane.cpp | 220 +- opennurbs_ground_plane.h | 79 +- opennurbs_internal_defines.h | 74 +- opennurbs_layer.cpp | 279 ++- opennurbs_layer.h | 43 +- opennurbs_line.cpp | 6 + opennurbs_line.h | 3 + opennurbs_linear_workflow.cpp | 137 +- opennurbs_linear_workflow.h | 82 +- opennurbs_linetype.cpp | 6 + opennurbs_linetype.h | 6 + opennurbs_math.cpp | 48 + opennurbs_math.h | 13 + opennurbs_md5.h | 6 +- opennurbs_mesh.h | 2 +- opennurbs_model_component.cpp | 22 +- opennurbs_model_component.h | 6 +- opennurbs_plane.h | 44 +- opennurbs_planesurface.cpp | 134 +- opennurbs_point.h | 6 +- opennurbs_post_effects.cpp | 758 +++++- opennurbs_post_effects.h | 197 +- opennurbs_public.vcxproj | 2 + opennurbs_public.xcodeproj/project.pbxproj | 16 + opennurbs_public_staticlib.vcxproj | 2 + opennurbs_public_version.h | 16 +- opennurbs_quaternion.h | 7 +- opennurbs_render_channels.cpp | 13 +- opennurbs_render_channels.h | 25 +- opennurbs_render_content.cpp | 29 +- opennurbs_render_content.h | 123 +- opennurbs_safe_frame.cpp | 29 +- opennurbs_safe_frame.h | 69 +- opennurbs_sectionstyle.cpp | 632 +++++ opennurbs_sectionstyle.h | 146 ++ opennurbs_sha1.h | 6 +- opennurbs_skylight.cpp | 80 +- opennurbs_skylight.h | 44 +- opennurbs_statics.cpp | 402 ++-- opennurbs_string.cpp | 4 +- opennurbs_string.h | 6 + opennurbs_subd.cpp | 2173 +++++++++++++++++- opennurbs_subd.h | 831 ++++++- opennurbs_subd_data.cpp | 10 +- opennurbs_subd_data.h | 387 +++- opennurbs_subd_eval.cpp | 12 +- opennurbs_subd_heap.cpp | 4 +- opennurbs_subd_limit.cpp | 4 +- opennurbs_subd_matrix.cpp | 27 +- opennurbs_subd_ring.cpp | 869 ++----- opennurbs_sun.cpp | 1003 ++++++-- opennurbs_sun.h | 297 ++- opennurbs_system_runtime.h | 17 +- opennurbs_unicode.cpp | 47 +- opennurbs_unicode.h | 42 +- opennurbs_wstring.cpp | 8 +- opennurbs_xform.h | 2 +- opennurbs_xml.cpp | 411 ++-- opennurbs_xml.h | 371 +-- 80 files changed, 8798 insertions(+), 3118 deletions(-) create mode 100644 opennurbs_sectionstyle.cpp create mode 100644 opennurbs_sectionstyle.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 33ecf776..5b8e8fb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,7 @@ set( OPENNURBS_PUBLIC_HEADERS opennurbs_revsurface.h opennurbs_rtree.h opennurbs_safe_frame.h + opennurbs_sectionstyle.h opennurbs_sha1.h opennurbs_skylight.h opennurbs_sleeplock.h @@ -310,6 +311,7 @@ set( OPENNURBS_PUBLIC_SOURCES opennurbs_revsurface.cpp opennurbs_rtree.cpp opennurbs_safe_frame.cpp + opennurbs_sectionstyle.cpp opennurbs_sha1.cpp opennurbs_skylight.cpp opennurbs_sleeplock.cpp @@ -429,6 +431,7 @@ set( OPENNURBS_PLUS_SOURCES opennurbs_plus_rectpack.cpp opennurbs_plus_rectpack1.cpp opennurbs_plus_rectpack2.cpp + opennurbs_plus_registry.cpp opennurbs_plus_sections.cpp opennurbs_plus_sil.cpp opennurbs_plus_sleeplock.cpp @@ -483,6 +486,7 @@ add_library( OpenNURBS SHARED ${OPENNURBS_PUBLIC_MEMORY} ${OPENNURBS_PLUS_HEADERS} ${OPENNURBS_PLUS_SOURCES} + opennurbs_dll.cpp ) if (MSVC) @@ -566,7 +570,10 @@ target_compile_definitions(opennurbsStatic PRIVATE ON_COMPILING_OPENNURBS Z_PREF target_compile_definitions(OpenNURBS PRIVATE OPENNURBS_EXPORTS Z_PREFIX MY_ZCALLOC) target_include_directories(opennurbsStatic PUBLIC .) -target_include_directories( OpenNURBS PUBLIC .) +target_include_directories(OpenNURBS PUBLIC .) + +target_precompile_headers(opennurbsStatic PRIVATE opennurbs.h) +target_precompile_headers(OpenNURBS PRIVATE opennurbs.h) install( TARGETS opennurbsStatic DESTINATION "lib") install( FILES ${OPENNURBS_PUBLIC_HEADERS} DESTINATION "include/opennurbsStatic") diff --git a/makefile b/makefile index aa43144c..651d3a83 100644 --- a/makefile +++ b/makefile @@ -194,6 +194,7 @@ ON_INC = opennurbs.h \ opennurbs_revsurface.h \ opennurbs_rtree.h \ opennurbs_safe_frame.h \ + opennurbs_sectionstyle.h \ opennurbs_sha1.h \ opennurbs_skylight.h \ opennurbs_sleeplock.h \ @@ -365,6 +366,7 @@ ON_SRC = opennurbs_3dm_attributes.cpp \ opennurbs_revsurface.cpp \ opennurbs_rtree.cpp \ opennurbs_safe_frame.cpp \ + opennurbs_sectionstyle.cpp \ opennurbs_sha1.cpp \ opennurbs_skylight.cpp \ opennurbs_sleeplock.cpp \ @@ -555,6 +557,7 @@ ON_OBJ = opennurbs_3dm_attributes.o \ opennurbs_revsurface.o \ opennurbs_rtree.o \ opennurbs_safe_frame.o \ + opennurbs_sectionstyle.o \ opennurbs_sha1.o \ opennurbs_skylight.o \ opennurbs_sleeplock.o \ diff --git a/opennurbs.h b/opennurbs.h index 0b41b712..2a213ca6 100644 --- a/opennurbs.h +++ b/opennurbs.h @@ -110,6 +110,7 @@ #include "opennurbs_texture_mapping.h" // texture coordinate evaluation #include "opennurbs_texture.h" // texture definition #include "opennurbs_material.h" // simple rendering material +#include "opennurbs_sectionstyle.h" // attributes for drawing sections #include "opennurbs_layer.h" // layer definition #include "opennurbs_linetype.h" // linetype definition #include "opennurbs_group.h" // group name and index diff --git a/opennurbsRhino_iOS.xcodeproj/project.pbxproj b/opennurbsRhino_iOS.xcodeproj/project.pbxproj index 737950eb..c333c08e 100644 --- a/opennurbsRhino_iOS.xcodeproj/project.pbxproj +++ b/opennurbsRhino_iOS.xcodeproj/project.pbxproj @@ -300,6 +300,9 @@ 992E4E2128217D9A00033A20 /* opennurbs_decals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 992E4E1D28217D9A00033A20 /* opennurbs_decals.cpp */; }; 992E4E2628217DB700033A20 /* opennurbs_decals.h in Headers */ = {isa = PBXBuildFile; fileRef = 992E4E2328217DB700033A20 /* opennurbs_decals.h */; }; 992E4E2728217DB700033A20 /* opennurbs_decals.h in Headers */ = {isa = PBXBuildFile; fileRef = 992E4E2328217DB700033A20 /* opennurbs_decals.h */; }; + 994E996629ED114B0057779D /* opennurbs_sectionstyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 994E996329ED114B0057779D /* opennurbs_sectionstyle.h */; }; + 994E996829ED11600057779D /* opennurbs_sectionstyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 994E996729ED11600057779D /* opennurbs_sectionstyle.cpp */; }; + 994E999729EDC1510057779D /* opennurbs_sectionstyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 994E996729ED11600057779D /* opennurbs_sectionstyle.cpp */; }; 997CC53228AEA75900E18BDB /* opennurbs_archivable_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 997CC52F28AEA75800E18BDB /* opennurbs_archivable_dictionary.h */; }; 997CC53428AEA76600E18BDB /* opennurbs_archivable_dictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 997CC53328AEA76600E18BDB /* opennurbs_archivable_dictionary.cpp */; }; 997CC53528AEA8AB00E18BDB /* opennurbs_archivable_dictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 997CC53328AEA76600E18BDB /* opennurbs_archivable_dictionary.cpp */; }; @@ -1005,6 +1008,8 @@ 99284D042800FD4B00CA9E82 /* opennurbs_render_channels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_render_channels.h; sourceTree = ""; }; 992E4E1D28217D9A00033A20 /* opennurbs_decals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_decals.cpp; sourceTree = ""; }; 992E4E2328217DB700033A20 /* opennurbs_decals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_decals.h; sourceTree = ""; }; + 994E996329ED114B0057779D /* opennurbs_sectionstyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_sectionstyle.h; sourceTree = ""; }; + 994E996729ED11600057779D /* opennurbs_sectionstyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sectionstyle.cpp; sourceTree = ""; }; 997CC52F28AEA75800E18BDB /* opennurbs_archivable_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_archivable_dictionary.h; sourceTree = ""; }; 997CC53328AEA76600E18BDB /* opennurbs_archivable_dictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_archivable_dictionary.cpp; sourceTree = ""; }; 99B39376284A90C4000FCE50 /* opennurbs_mesh_modifiers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_mesh_modifiers.h; sourceTree = ""; }; @@ -1391,6 +1396,7 @@ 10D7D0DB09E0523C0056FF9C /* opennurbs_revsurface.h */, 102A82A810684EC900781833 /* opennurbs_rtree.h */, A165DEEC27CF88B00006F184 /* opennurbs_safe_frame.h */, + 994E996329ED114B0057779D /* opennurbs_sectionstyle.h */, DF2447011BE96D2600FD193A /* opennurbs_sha1.h */, A1C9656F27FCBFB7006A1C3E /* opennurbs_skylight.h */, 1D741C1321B9C5DC00AA10E5 /* opennurbs_sleeplock.h */, @@ -1626,6 +1632,7 @@ 10D7D02709E04F820056FF9C /* opennurbs_revsurface.cpp */, 102A82A610684EB200781833 /* opennurbs_rtree.cpp */, A165DEE727CF88A90006F184 /* opennurbs_safe_frame.cpp */, + 994E996729ED11600057779D /* opennurbs_sectionstyle.cpp */, DF2447001BE96D2600FD193A /* opennurbs_sha1.cpp */, A1C9656527FCBF7F006A1C3E /* opennurbs_skylight.cpp */, 1D741C1721B9C5F700AA10E5 /* opennurbs_sleeplock.cpp */, @@ -1889,6 +1896,7 @@ 10D7D12409E052650056FF9C /* opennurbs_zlib.h in Headers */, 10D7D12509E052650056FF9C /* opennurbs.h in Headers */, 10A930EC0B2F16980022B2F6 /* opennurbs_plus.h in Headers */, + 994E996629ED114B0057779D /* opennurbs_sectionstyle.h in Headers */, 101624660E955C2500B0189B /* opennurbs_lookup.h in Headers */, D6184CC81B0F83560099E507 /* opennurbs_version_number.h in Headers */, 100A35D9100E1C8D008FB1F2 /* opennurbs_plus_registry.h in Headers */, @@ -2167,6 +2175,7 @@ 10D7CFC409E04EA60056FF9C /* opennurbs_curveonsurface.cpp in Sources */, 10D7CFC509E04EA60056FF9C /* opennurbs_curveproxy.cpp in Sources */, 10D7CFC709E04EA60056FF9C /* opennurbs_cylinder.cpp in Sources */, + 994E996829ED11600057779D /* opennurbs_sectionstyle.cpp in Sources */, 10D7CFEE09E04F0A0056FF9C /* opennurbs_defines.cpp in Sources */, 10D7CFEF09E04F0A0056FF9C /* opennurbs_detail.cpp in Sources */, 1D3212C11C48648300A5E542 /* opennurbs_model_geometry.cpp in Sources */, @@ -2414,6 +2423,7 @@ DF6D38A41F2A72DF00D997E4 /* opennurbs_curveonsurface.cpp in Sources */, DF6D38A51F2A72DF00D997E4 /* opennurbs_curveproxy.cpp in Sources */, DF6D38A61F2A72DF00D997E4 /* opennurbs_cylinder.cpp in Sources */, + 994E999729EDC1510057779D /* opennurbs_sectionstyle.cpp in Sources */, DF6D38A71F2A72DF00D997E4 /* opennurbs_defines.cpp in Sources */, DF6D38A81F2A72DF00D997E4 /* opennurbs_detail.cpp in Sources */, DF6D38A91F2A72DF00D997E4 /* opennurbs_model_geometry.cpp in Sources */, diff --git a/opennurbs_3dm_attributes.cpp b/opennurbs_3dm_attributes.cpp index 2fe719fe..5f2b0a2b 100644 --- a/opennurbs_3dm_attributes.cpp +++ b/opennurbs_3dm_attributes.cpp @@ -38,15 +38,12 @@ public: bool m_clipping_proof = false; ON::SectionAttributesSource m_section_attributes_source = ON::SectionAttributesSource::FromLayer; - ON::SectionFillRule m_section_fill_rule = ON::SectionFillRule::ClosedCurves; - 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; double m_linetype_scale = 1.0; ON_Color m_hatch_background_fill; bool m_hatch_boundary_visible = false; std::shared_ptr m_custom_linetype; + std::shared_ptr m_custom_section_style; ON_DecalCollection m_decals; ON_MeshModifiers m_mesh_modifiers; @@ -73,18 +70,6 @@ bool ON_3dmObjectAttributesPrivate::operator==(const ON_3dmObjectAttributesPriva if (m_section_attributes_source != other.m_section_attributes_source) return false; - if (m_section_fill_rule != other.m_section_fill_rule) - return false; - - if (m_section_hatch_index != other.m_section_hatch_index) - return false; - - if (m_section_hatch_scale != other.m_section_hatch_scale) - return false; - - if (m_section_hatch_rotation != other.m_section_hatch_rotation) - return false; - if (m_linetype_scale != other.m_linetype_scale) return false; @@ -94,6 +79,20 @@ bool ON_3dmObjectAttributesPrivate::operator==(const ON_3dmObjectAttributesPriva if (m_hatch_boundary_visible != other.m_hatch_boundary_visible) return false; + { + const ON_SectionStyle* customThis = m_custom_section_style.get(); + const ON_SectionStyle* customOther = other.m_custom_section_style.get(); + if (nullptr == customThis && customOther) + return false; + if (customThis && nullptr == customOther) + return false; + if (customThis && customOther) + { + if ((*customThis) != (*customOther)) + return false; + } + } + const ON_Linetype* customThis = m_custom_linetype.get(); const ON_Linetype* customOther = other.m_custom_linetype.get(); if (customThis != customOther) @@ -431,9 +430,12 @@ enum ON_3dmObjectAttributesTypeCodes : unsigned char // 30 Nov 2022 S. Baer // file version 2.10: custom linetype CustomLinetype = 38, + // 18 Apr 2023 S. Baer + // file version 2.11: custome section style + CustomSectionStyle = 39, // add items here - LastAttributeTypeCode = 38 + LastAttributeTypeCode = 39 }; bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) @@ -729,7 +731,10 @@ bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) int hatch_index = 0; rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern, &hatch_index); if (!rc) break; - SetSectionHatchIndex(hatch_index); + ON_SectionStyle ss; + CustomSectionStyle(&ss); + ss.SetHatchIndex(hatch_index); + SetCustomSectionStyle(ss); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -738,7 +743,10 @@ bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) double hatch_scale = 1; rc = file.ReadDouble(&hatch_scale); if (!rc) break; - SetSectionHatchScale(hatch_scale); + ON_SectionStyle ss; + CustomSectionStyle(&ss); + ss.SetHatchScale(hatch_scale); + SetCustomSectionStyle(ss); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -747,7 +755,10 @@ bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) double hatch_rotation = 0; rc = file.ReadDouble(&hatch_rotation); if (!rc) break; - SetSectionHatchRotation(hatch_rotation); + ON_SectionStyle ss; + CustomSectionStyle(&ss); + ss.SetHatchRotation(hatch_rotation); + SetCustomSectionStyle(ss); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -816,8 +827,9 @@ bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) unsigned char c = 0; rc = file.ReadChar(&c); if (!rc) break; - - SetSectionFillRule(ON::SectionFillRuleFromUnsigned(c)); + ON_SectionStyle ss; + CustomSectionStyle(&ss); + ss.SetSectionFillRule(ON::SectionFillRuleFromUnsigned(c)); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; @@ -841,6 +853,21 @@ bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) if (minor_version <= 10) break; + if (ON_3dmObjectAttributesTypeCodes::CustomSectionStyle == itemid) // 39 + { + ON_SectionStyle sectionStyle; + rc = sectionStyle.Read(file); + if (!rc) break; + + SetCustomSectionStyle(sectionStyle); + + rc = file.ReadChar(&itemid); + if (!rc || 0 == itemid) break; + } + + if (minor_version <= 11) + 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. @@ -1036,8 +1063,10 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const // 15 Jun 2022 S. Baer // Chunk version = 2.9 to support SectionFillRule // 30 Nov 2022 S. Baer - // Chunk version = 2.10 to support - bool rc = file.Write3dmChunkVersion(2,10); + // Chunk version = 2.10 to support custom linetype + // 18 Apr 2023 S. Baer + // Chunk version = 2.11 to support custom section style + bool rc = file.Write3dmChunkVersion(2,11); while(rc) { if (!rc) break; @@ -1238,6 +1267,7 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const if (!rc) break; } + const ON_SectionStyle* customSectionStyle = CustomSectionStyle(); // 12 Aug 2021 S. Baer // Items 23, 24, 25, 26 were in a version of Rhino 8 WIP for about 24 hours. // They were most likely never used by anyone @@ -1282,32 +1312,38 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const if (!rc) break; } - if (SectionHatchIndex() != ON_UNSET_INT_INDEX) - { - c = ON_3dmObjectAttributesTypeCodes::SectionHatchIndex; // 30 - rc = file.WriteChar(c); - if (!rc) break; - rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern, SectionHatchIndex()); - if (!rc) break; - } + // 23 April 2023 S. Baer + // Stop writing individual section attributes. All section attribute IO has been + // moved to writing an ON_SectionStyle instance + //if (customSectionStyle) + //{ + // if (customSectionStyle->HatchIndex() != ON_UNSET_INT_INDEX) + // { + // c = ON_3dmObjectAttributesTypeCodes::SectionHatchIndex; // 30 + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern, customSectionStyle->HatchIndex()); + // if (!rc) break; + // } - if (SectionHatchScale() != 1.0) - { - c = ON_3dmObjectAttributesTypeCodes::SectionHatchScale; // 31 - rc = file.WriteChar(c); - if (!rc) break; - rc = file.WriteDouble(SectionHatchScale()); - if (!rc) break; - } + // if (customSectionStyle->HatchScale() != 1.0) + // { + // c = ON_3dmObjectAttributesTypeCodes::SectionHatchScale; // 31 + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.WriteDouble(customSectionStyle->HatchScale()); + // if (!rc) break; + // } - if (SectionHatchRotation() != 0.0) - { - c = ON_3dmObjectAttributesTypeCodes::SectionHatchRotation; // 32 - rc = file.WriteChar(c); - if (!rc) break; - rc = file.WriteDouble(SectionHatchRotation()); - if (!rc) break; - } + // if (customSectionStyle->HatchRotation() != 0.0) + // { + // c = ON_3dmObjectAttributesTypeCodes::SectionHatchRotation; // 32 + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.WriteDouble(customSectionStyle->HatchRotation()); + // if (!rc) break; + // } + //} if (fabs(1.0 - LinetypePatternScale()) > ON_EPSILON) { @@ -1357,16 +1393,20 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const if (!rc) break; } - // 15 Jun 2022 S. Baer - // Write section fill rule - if (SectionFillRule() != ON::SectionFillRule::ClosedCurves) - { - c = ON_3dmObjectAttributesTypeCodes::SectionFillRule; // 37 - rc = file.WriteChar(c); - if (!rc) break; - rc = file.WriteChar((unsigned char)SectionFillRule()); - if (!rc) break; - } + // 23 April 2023 S. Baer + // Stop writing individual section attributes. All section attribute IO has been + // moved to writing an ON_SectionStyle instance + //if (customSectionStyle) + //{ + // if (customSectionStyle->SectionFillRule() != ON::SectionFillRule::ClosedCurves) + // { + // c = ON_3dmObjectAttributesTypeCodes::SectionFillRule; // 37 + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.WriteChar((unsigned char)(customSectionStyle->SectionFillRule())); + // if (!rc) break; + // } + //} // 30 Nov 2022 S. Baer // Write custom linetype @@ -1382,6 +1422,17 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const } } + // 18 Apr 2023 S. Baer + // Write custom section style + if (customSectionStyle && !customSectionStyle->SectionAttributesEqual(ON_SectionStyle::Unset)) + { + c = ON_3dmObjectAttributesTypeCodes::CustomSectionStyle; // 39 + rc = file.WriteChar(c); + if (!rc) break; + rc = customSectionStyle->Write(file); + if (!rc) break; + } + // 0 indicates end of attributes - this should be the last item written c = 0; rc = file.WriteChar(c); @@ -1887,17 +1938,12 @@ unsigned int ON_3dmObjectAttributes::ApplyParentalControl( if (ON::SectionAttributesSource::FromLayer == SectionAttributesSource() && parent_layer.Index() >= 0) { SetSectionAttributesSource(ON::SectionAttributesSource::FromLayer); - SetSectionFillRule(parent_layer.SectionFillRule()); - SetSectionHatchIndex(parent_layer.SectionHatchIndex()); - SetSectionHatchRotation(parent_layer.SectionHatchRotation()); - SetSectionHatchScale(parent_layer.SectionHatchScale()); } else { - SetSectionFillRule(parents_attributes.SectionFillRule()); - SetSectionHatchIndex(parents_attributes.SectionHatchIndex()); - SetSectionHatchRotation(parents_attributes.SectionHatchRotation()); - SetSectionHatchScale(parents_attributes.SectionHatchScale()); + ON_SectionStyle sectionStyle; + parents_attributes.CustomSectionStyle(§ionStyle); + SetCustomSectionStyle(sectionStyle); } } } @@ -2334,62 +2380,6 @@ void ON_3dmObjectAttributes::GetClipParticipation( } } -ON::SectionFillRule ON_3dmObjectAttributes::SectionFillRule() const -{ - return m_private ? m_private->m_section_fill_rule : DefaultAttributesPrivate.m_section_fill_rule; -} -void ON_3dmObjectAttributes::SetSectionFillRule(ON::SectionFillRule rule) -{ - if (SectionFillRule() == rule) - return; - - if (nullptr == m_private) - m_private = new ON_3dmObjectAttributesPrivate(this); - m_private->m_section_fill_rule = rule; -} - -int ON_3dmObjectAttributes::SectionHatchIndex() const -{ - return m_private ? m_private->m_section_hatch_index : DefaultAttributesPrivate.m_section_hatch_index; -} - -void ON_3dmObjectAttributes::SetSectionHatchIndex(int index) -{ - if (SectionHatchIndex() == index) - return; - - if (nullptr == m_private) - m_private = new ON_3dmObjectAttributesPrivate(this); - m_private->m_section_hatch_index = index; -} - -double ON_3dmObjectAttributes::SectionHatchScale() const -{ - return m_private ? m_private->m_section_hatch_scale : DefaultAttributesPrivate.m_section_hatch_scale; -} -void ON_3dmObjectAttributes::SetSectionHatchScale(double scale) -{ - if (SectionHatchScale() == scale) - return; - - if (nullptr == m_private) - m_private = new ON_3dmObjectAttributesPrivate(this); - m_private->m_section_hatch_scale = scale; -} - -double ON_3dmObjectAttributes::SectionHatchRotation() const -{ - return m_private ? m_private->m_section_hatch_rotation : DefaultAttributesPrivate.m_section_hatch_rotation; -} -void ON_3dmObjectAttributes::SetSectionHatchRotation(double rotation) -{ - if (SectionHatchRotation() == rotation) - return; - - if (nullptr == m_private) - m_private = new ON_3dmObjectAttributesPrivate(this); - m_private->m_section_hatch_rotation = rotation; -} ON::SectionAttributesSource ON_3dmObjectAttributes::SectionAttributesSource() const { @@ -2405,6 +2395,33 @@ void ON_3dmObjectAttributes::SetSectionAttributesSource(ON::SectionAttributesSou m_private->m_section_attributes_source = source; } +void ON_3dmObjectAttributes::SetCustomSectionStyle(const ON_SectionStyle& sectionStyle) +{ + if (nullptr == m_private) + m_private = new ON_3dmObjectAttributesPrivate(this); + + m_private->m_custom_section_style.reset(new ON_SectionStyle(sectionStyle)); +} +const ON_SectionStyle* ON_3dmObjectAttributes::CustomSectionStyle(ON_SectionStyle* sectionStyle) const +{ + const ON_SectionStyle* rc = nullptr; + if (m_private) + rc = m_private->m_custom_section_style.get(); + + if (sectionStyle && rc) + { + *sectionStyle = *rc; + } + + return rc; +} +void ON_3dmObjectAttributes::RemoveCustomSectionStyle() +{ + if (m_private) + m_private->m_custom_section_style.reset(); +} + + double ON_3dmObjectAttributes::LinetypePatternScale() const { return m_private ? m_private->m_linetype_scale : 1.0; diff --git a/opennurbs_3dm_attributes.h b/opennurbs_3dm_attributes.h index 27bb5208..55384a75 100644 --- a/opennurbs_3dm_attributes.h +++ b/opennurbs_3dm_attributes.h @@ -399,28 +399,32 @@ public: // this intersection can result in curves as well as hatches for the // closed curves generated - // When to fill/hatch the sections for an object can depend on the type of - // object being sectioned. See ON_SectionFillRule for the choices of - // when to generate hatches. - ON::SectionFillRule SectionFillRule() const; - void SetSectionFillRule(ON::SectionFillRule rule); - - // Hatch pattern index for hatch to use when drawing a closed section - // Default is ON_UNSET_INT_INDEX which means don't draw a hatch - int SectionHatchIndex() const; - void SetSectionHatchIndex(int index); - - // Scale applied to the hatch pattern for a section - double SectionHatchScale() const; - void SetSectionHatchScale(double scale); - - // Rotation angle in radians applied to hatch pattern for a section. - double SectionHatchRotation() const; - void SetSectionHatchRotation(double rotation); - // Source for all section related attributes ON::SectionAttributesSource SectionAttributesSource() const; void SetSectionAttributesSource(ON::SectionAttributesSource source); + + /* + Description: + Attributes can have optional custom section style associated with them. + This function adds a custom section style for this attribute. + */ + void SetCustomSectionStyle(const ON_SectionStyle& sectionStyle); + + /* + Description: + Attributes can have optional custom section styles associated with them. + This function returns the custom section style if one exists. + Parameters: + sectionStyle [out] - if not nullptr and a custom section style exists, + the data in the custom section style is copied to sectionStyle + */ + const ON_SectionStyle* CustomSectionStyle(ON_SectionStyle* sectionStyle = nullptr) const; + + /* + Description: + Remove any custom section style associated with this attribute + */ + void RemoveCustomSectionStyle(); #pragma endregion // Per object linetype scale @@ -440,7 +444,7 @@ public: 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 + not attached to this attribute, then nullptr is returned */ const ON_Linetype* CustomLinetype() const; diff --git a/opennurbs_3dm_settings.cpp b/opennurbs_3dm_settings.cpp index bd650a88..b4942074 100644 --- a/opennurbs_3dm_settings.cpp +++ b/opennurbs_3dm_settings.cpp @@ -704,7 +704,11 @@ bool ON_3dmUnitsAndTolerances::IsValid() const { for (;;) { - if (!(m_distance_display_precision >= 0 && m_distance_display_precision <= 7)) + // April 17, 2023 - Tim + // Changed upper limit to 8 so we can use the display precision + // stuff found in the annotation code for V8 + // Fixes https://mcneel.myjetbrains.com/youtrack/issue/RH-74242 + if (!(m_distance_display_precision >= 0 && m_distance_display_precision <= 8)) break; if (!((int)m_distance_display_mode >= 0 && (int)m_distance_display_mode <= 3)) @@ -768,7 +772,11 @@ int ON_3dmUnitsAndTolerances::DistanceDisplayPrecision() const void ON_3dmUnitsAndTolerances::SetDistanceDisplayPrecision(int distance_display_precision) { - if (distance_display_precision >= 0 && distance_display_precision <= 7) + // April 17, 2023 - Tim + // Changed upper limit to 8 so we can use the display precision + // stuff found in the annotation code for V8 + // Fixes https://mcneel.myjetbrains.com/youtrack/issue/RH-74242 + if (distance_display_precision >= 0 && distance_display_precision <= 8) m_distance_display_precision = distance_display_precision; } @@ -821,17 +829,6 @@ unsigned int ON_3dmUnitsAndTolerances::SetInvalidTolerancesToDefaultValues() // ON_3dmRenderSettings // -// This is inside the 'current content' section. -#define ON_RENDER_BACKGROUND_ENVIRONMENT L"environment" - -// These are inside the 'rendering' section. -#define ON_RENDER_CUSTOM_REFLECTIVE_ENVIRONMENT_ON L"custom-env-for-refl-and-refr-on" -#define ON_RENDER_CUSTOM_REFLECTIVE_ENVIRONMENT L"custom-env-for-refl-and-refr" - -// These are inside the 'sun' section. -#define ON_RENDER_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON L"skylight-custom-environment-on" -#define ON_RENDER_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT L"skylight-custom-environment" - static const wchar_t* XMLPathBack360(void) // Not used for 'override'. { return ON_RDK_DOCUMENT ON_RDK_SLASH ON_RDK_CURRENT_CONTENT; @@ -847,109 +844,115 @@ static const wchar_t* XMLPathSkylight(void) return ON_RDK_DOCUMENT ON_RDK_SLASH ON_RDK_SETTINGS ON_RDK_SLASH ON_RDK_SUN; } -ON_EnvironmentsPrivate::ON_EnvironmentsPrivate(const ON_EnvironmentsPrivate& ei) +ON_EnvironmentsImpl::ON_EnvironmentsImpl(const ON_EnvironmentsImpl& ei) { operator = (ei); } -ON_EnvironmentsPrivate& ON_EnvironmentsPrivate::operator = (const ON_EnvironmentsPrivate& ep) +ON_EnvironmentsImpl& ON_EnvironmentsImpl::operator = (const ON_EnvironmentsImpl& ep) { if (this != &ep) { - SetBackgroundRenderEnvironment (ep.BackgroundRenderEnvironment()); + SetBackgroundRenderEnvironmentId (ep.BackgroundRenderEnvironmentId()); SetSkylightingRenderEnvironmentOverride(ep.SkylightingRenderEnvironmentOverride()); - SetSkylightingRenderEnvironment (ep.SkylightingRenderEnvironment()); + SetSkylightingRenderEnvironmentId (ep.SkylightingRenderEnvironmentId()); SetReflectionRenderEnvironmentOverride (ep.ReflectionRenderEnvironmentOverride()); - SetReflectionRenderEnvironment (ep.ReflectionRenderEnvironment()); + SetReflectionRenderEnvironmentId (ep.ReflectionRenderEnvironmentId()); } return *this; } -bool ON_EnvironmentsPrivate::operator == (const ON_EnvironmentsPrivate& ep) +bool ON_EnvironmentsImpl::operator == (const ON_EnvironmentsImpl& ep) { - if (BackgroundRenderEnvironment() != ep.BackgroundRenderEnvironment()) return false; + if (BackgroundRenderEnvironmentId() != ep.BackgroundRenderEnvironmentId()) return false; if (SkylightingRenderEnvironmentOverride() != ep.SkylightingRenderEnvironmentOverride()) return false; - if (SkylightingRenderEnvironment() != ep.SkylightingRenderEnvironment()) return false; + if (SkylightingRenderEnvironmentId() != ep.SkylightingRenderEnvironmentId()) return false; if (ReflectionRenderEnvironmentOverride() != ep.ReflectionRenderEnvironmentOverride()) return false; - if (ReflectionRenderEnvironment() != ep.ReflectionRenderEnvironment()) return false; + if (ReflectionRenderEnvironmentId() != ep.ReflectionRenderEnvironmentId()) return false; return true; } -ON_UUID ON_EnvironmentsPrivate::BackgroundRenderEnvironment(void) const +ON_UUID ON_EnvironmentsImpl::BackgroundRenderEnvironmentId(void) const { - const wchar_t* s = ON_RENDER_BACKGROUND_ENVIRONMENT; - return GetParameter_NoType(XMLPathBack360(), s, L"uuid", ON_nil_uuid).AsUuid(); + return GetParameter_NoType(XMLPathBack360(), ON_RDK_BACKGROUND_ENVIRONMENT, L"uuid", ON_nil_uuid).AsUuid(); } -void ON_EnvironmentsPrivate::SetBackgroundRenderEnvironment(const ON_UUID& id) +void ON_EnvironmentsImpl::SetBackgroundRenderEnvironmentId(const ON_UUID& id) { - const wchar_t* s = ON_RENDER_BACKGROUND_ENVIRONMENT; - SetParameter_NoType(XMLPathBack360(), s, id); + SetParameter_NoType(XMLPathBack360(), ON_RDK_BACKGROUND_ENVIRONMENT, id); } -bool ON_EnvironmentsPrivate::SkylightingRenderEnvironmentOverride(void) const +bool ON_EnvironmentsImpl::SkylightingRenderEnvironmentOverride(void) const { - const wchar_t* s = ON_RENDER_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON; - return GetParameter(XMLPathSkylight(), s, false); + return GetParameter(XMLPathSkylight(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_OVERRIDE, false); } -void ON_EnvironmentsPrivate::SetSkylightingRenderEnvironmentOverride(bool on) +void ON_EnvironmentsImpl::SetSkylightingRenderEnvironmentOverride(bool on) { - const wchar_t* s = ON_RENDER_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON; - SetParameter(XMLPathSkylight(), s, on); + SetParameter(XMLPathSkylight(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_OVERRIDE, on); } -ON_UUID ON_EnvironmentsPrivate::SkylightingRenderEnvironment(void) const +ON_UUID ON_EnvironmentsImpl::SkylightingRenderEnvironmentId(void) const { - const wchar_t* s = ON_RENDER_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT; - return GetParameter_NoType(XMLPathSkylight(), s, L"uuid", ON_nil_uuid).AsUuid(); + return GetParameter_NoType(XMLPathSkylight(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_ID, L"uuid", ON_nil_uuid).AsUuid(); } -void ON_EnvironmentsPrivate::SetSkylightingRenderEnvironment(const ON_UUID& id) +void ON_EnvironmentsImpl::SetSkylightingRenderEnvironmentId(const ON_UUID& id) { - const wchar_t* s = ON_RENDER_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT; - SetParameter_NoType(XMLPathSkylight(), s, id); + SetParameter_NoType(XMLPathSkylight(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_ID, id); } -bool ON_EnvironmentsPrivate::ReflectionRenderEnvironmentOverride(void) const +bool ON_EnvironmentsImpl::ReflectionRenderEnvironmentOverride(void) const { - const wchar_t* s = ON_RENDER_CUSTOM_REFLECTIVE_ENVIRONMENT_ON; - return GetParameter(XMLPathReflRefr(), s, false); + return GetParameter(XMLPathReflRefr(), ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT_ON, false); } -void ON_EnvironmentsPrivate::SetReflectionRenderEnvironmentOverride(bool on) +void ON_EnvironmentsImpl::SetReflectionRenderEnvironmentOverride(bool on) { - const wchar_t* s = ON_RENDER_CUSTOM_REFLECTIVE_ENVIRONMENT_ON; - SetParameter(XMLPathReflRefr(), s, on); + SetParameter(XMLPathReflRefr(), ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT_ON, on); } -ON_UUID ON_EnvironmentsPrivate::ReflectionRenderEnvironment(void) const +ON_UUID ON_EnvironmentsImpl::ReflectionRenderEnvironmentId(void) const { - const wchar_t* s = ON_RENDER_CUSTOM_REFLECTIVE_ENVIRONMENT; - return GetParameter_NoType(XMLPathReflRefr(), s, L"uuid", ON_nil_uuid).AsUuid(); + return GetParameter_NoType(XMLPathReflRefr(), ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT, L"uuid", ON_nil_uuid).AsUuid(); } -void ON_EnvironmentsPrivate::SetReflectionRenderEnvironment(const ON_UUID& id) +void ON_EnvironmentsImpl::SetReflectionRenderEnvironmentId(const ON_UUID& id) { - const wchar_t* s = ON_RENDER_CUSTOM_REFLECTIVE_ENVIRONMENT; - SetParameter_NoType(XMLPathReflRefr(), s, id); + SetParameter_NoType(XMLPathReflRefr(), ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT, id); } ON_OBJECT_IMPLEMENT(ON_3dmRenderSettings, ON_Object, "58A5953A-57C5-4FD3-84F5-7D4240478D15"); -ON_3dmRenderSettingsPrivate::ON_3dmRenderSettingsPrivate() - : - _dithering (_rdk_document_data), - _ground_plane (_rdk_document_data), - _linear_workflow(_rdk_document_data), - _render_channels(_rdk_document_data), - _safe_frame (_rdk_document_data), - _skylight (_rdk_document_data), - _sun (_rdk_document_data), - _environments (_rdk_document_data) +ON_DECL ON_XMLNode& ON_GetRdkDocNode(const ON_3dmRenderSettings& rs) { + return ON_3dmRenderSettingsPrivate::Get(rs)._rdk_document_data; +} + +ON_DECL ON__UINT_PTR ON_GetDocumentObjectSpecializer(const ON_3dmRenderSettings& rs) +{ + return ON__UINT_PTR(&ON_3dmRenderSettingsPrivate::Get(rs)); +} + +ON_DECL void ON_SpecializeDocumentObjects(ON__UINT_PTR specializer, + ON_GroundPlane& gp, ON_LinearWorkflow& lw, ON_SunEx& sun) +{ + auto* priv = reinterpret_cast(specializer); + ON_ASSERT(nullptr != priv); + if (nullptr != priv) + { + priv->SpecializeGroundPlane(gp); + priv->SpecializeLinearWorkflow(lw); + priv->SpecializeSun(sun); + } +} + +ON_3dmRenderSettingsPrivate::ON_3dmRenderSettingsPrivate() +{ + CreateDocumentObjects(); + // 26th January 2023 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-71396 // Populate the RDK document data with defaults. The previous fix for this did it in RdkDocNode() // which was the wrong place. We have to do it even if the RDK isn't being used. @@ -958,35 +961,56 @@ ON_3dmRenderSettingsPrivate::ON_3dmRenderSettingsPrivate() } ON_3dmRenderSettingsPrivate::ON_3dmRenderSettingsPrivate(const ON_3dmRenderSettingsPrivate& p) - : - _dithering (_rdk_document_data), - _ground_plane (_rdk_document_data), - _linear_workflow(_rdk_document_data), - _render_channels(_rdk_document_data), - _safe_frame (_rdk_document_data), - _skylight (_rdk_document_data), - _sun (_rdk_document_data), - _environments (_rdk_document_data) { + CreateDocumentObjects(); operator = (p); } +ON_3dmRenderSettingsPrivate::~ON_3dmRenderSettingsPrivate() +{ + delete _ground_plane; + delete _dithering; + delete _safe_frame; + delete _skylight; + delete _linear_workflow; + delete _render_channels; + delete _sun; + delete _environments; + delete _post_effects; +} + const ON_3dmRenderSettingsPrivate& ON_3dmRenderSettingsPrivate::operator = (const ON_3dmRenderSettingsPrivate& p) { if (this != &p) { - // Copy the XML. + // Copy the entire document XML. _rdk_document_data = p._rdk_document_data; - // Check that all of the document objects now have matching properties. - ON_ASSERT(_dithering == p._dithering); - ON_ASSERT(_ground_plane == p._ground_plane); - ON_ASSERT(_linear_workflow == p._linear_workflow); - ON_ASSERT(_render_channels == p._render_channels); - ON_ASSERT(_safe_frame == p._safe_frame); - ON_ASSERT(_skylight == p._skylight); - ON_ASSERT(_sun == p._sun); - ON_ASSERT(_environments == p._environments); + // Invalidate any document object caches. The pointers to these objects are never null because + // they are created in every constructor. See CreateDocumentObjects(). + // It's critical that the document objects do not get deleted or recreated here because there can be + // clients storing temporary pointers to them around this call. What's more, this function can never + // change the class of the document objects. For example, if the ground plane is a specialized one, + // it will always be the _same_ specialized one. They never get deleted during the lifetime of this. + _ground_plane ->InvalidateCache(); + _dithering ->InvalidateCache(); + _safe_frame ->InvalidateCache(); + _skylight ->InvalidateCache(); + _linear_workflow->InvalidateCache(); + _render_channels->InvalidateCache(); + _sun ->InvalidateCache(); + _post_effects ->InvalidateCache(); + + // Check that all the document objects now have matching properties. + ON_ASSERT(*_ground_plane == *p._ground_plane); + ON_ASSERT(*_dithering == *p._dithering); + ON_ASSERT(*_safe_frame == *p._safe_frame); + ON_ASSERT(*_skylight == *p._skylight); + ON_ASSERT(*_linear_workflow == *p._linear_workflow); + ON_ASSERT(*_render_channels == *p._render_channels); + ON_ASSERT(*_sun == *p._sun); + ON_ASSERT(*_environments == *p._environments); + ON_ASSERT(*_post_effects == *p._post_effects); } return *this; @@ -1001,6 +1025,28 @@ ON_3dmRenderSettings::~ON_3dmRenderSettings() } } +void ON_3dmRenderSettingsPrivate::CreateDocumentObjects(void) +{ + // This function must be called from every constructor. + _ground_plane = new ON_GroundPlane (_rdk_document_data); + _dithering = new ON_Dithering (_rdk_document_data); + _safe_frame = new ON_SafeFrame (_rdk_document_data); + _skylight = new ON_Skylight (_rdk_document_data); + _linear_workflow = new ON_LinearWorkflow (_rdk_document_data); + _render_channels = new ON_RenderChannels (_rdk_document_data); + _sun = new ON_SunEx (_rdk_document_data); + _environments = new ON_EnvironmentsImpl(_rdk_document_data); + _post_effects = new ON_PostEffects (_rdk_document_data); +} + +ON_3dmRenderSettingsPrivate& ON_3dmRenderSettingsPrivate::Get(const ON_3dmRenderSettings& rs) +{ + if (nullptr == rs.m_private) + rs.m_private = new ON_3dmRenderSettingsPrivate; + + return *rs.m_private; +} + ON_3dmRenderSettings::ON_3dmRenderSettings(const ON_3dmRenderSettings& rs) { operator = (rs); @@ -1093,6 +1139,51 @@ void ON_3dmRenderSettingsPrivate::SetToDefaults(void) dd.CopyDefaultsTo(_rdk_document_data); } +void ON_3dmRenderSettingsPrivate::SpecializeGroundPlane(ON_GroundPlane& gp) +{ + // This is called from ON_SpecializeDocumentObjects() and is only called once during the lifetime of this. + ON_ASSERT(!_gp_specialized); + + // Make the incoming object use the document data of this. + gp.SetXMLNode(_rdk_document_data); + + // Replace the vanilla ground plane with the incoming object. This takes ownership of it. + delete _ground_plane; + _ground_plane = &gp; + + _gp_specialized = true; +} + +void ON_3dmRenderSettingsPrivate::SpecializeLinearWorkflow(ON_LinearWorkflow& lw) +{ + // This is called from ON_SpecializeDocumentObjects() and is only called once during the lifetime of this. + ON_ASSERT(!_lw_specialized); + + // Make the incoming object use the document data of this. + lw.SetXMLNode(_rdk_document_data); + + // Replace the vanilla linear workflow with the incoming object. This takes ownership of it. + delete _linear_workflow; + _linear_workflow = &lw; + + _lw_specialized = true; +} + +void ON_3dmRenderSettingsPrivate::SpecializeSun(ON_SunEx& sun) +{ + // This is called from ON_SpecializeDocumentObjects() and is only called once during the lifetime of this. + ON_ASSERT(!_sun_specialized); + + // Make the incoming object use the document data of this. + sun.SetXMLNode(_rdk_document_data); + + // Replace the vanilla sun with the incoming object. This takes ownership of it. + delete _sun; + _sun = &sun; + + _sun_specialized = true; +} + void ON_3dmRenderSettings::Dump( ON_TextLog& text_log ) const { text_log.Print("m_bCustomImageSize = %s\n",m_bCustomImageSize?"true":"false"); @@ -1128,6 +1219,15 @@ void ON_3dmRenderSettings::Dump( ON_TextLog& text_log ) const text_log.Print(L"m_snapshot = %s\n", (const wchar_t*)m_snapshot); text_log.Print("m_bForceViewportAspectRatio = %s\n", m_bForceViewportAspectRatio ? "true" : "false"); + + // 19th April 2023 John Croudy. I thought it would be handy to add the RDK XML for debugging. + // I didn't know that the state of the text log is checked with a hash by the OpenNURBS tests. + // Since the RDK XML can change textually (e.g., different property order), the string is never + // guaranteed to be the same for the same actual XML state. Therefore, this broke the OpenNURBS tests. + //const auto& priv = ON_3dmRenderSettingsPrivate::Get(*this); + //const auto s = priv._rdk_document_data.NodeForRead().String(); + //text_log.Print("RDK XML = "); + //text_log.PrintString(s); } bool ON_3dmRenderSettings::UseV5ReadWrite(const ON_BinaryArchive& file) @@ -1518,189 +1618,322 @@ void ON_3dmRenderSettings::SetScaleBackgroundToFit( bool bScaleBackgroundToFit ) m_bScaleBackgroundToFit = bScaleBackgroundToFit?true:false; } -ON_SafeFrame& ON_3dmRenderSettings::SafeFrame(void) const +ON_GroundPlane& ON_3dmRenderSettings::GroundPlane(void) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_safe_frame; + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_ground_plane); + + return *m_private->_ground_plane; } -ON_GroundPlane& ON_3dmRenderSettings::GroundPlane(void) const +const ON_GroundPlane& ON_3dmRenderSettings::GroundPlane(void) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - return m_private->_ground_plane; + return const_cast(this)->GroundPlane(); } -ON_LinearWorkflow& ON_3dmRenderSettings::LinearWorkflow(void) const +ON_Dithering& ON_3dmRenderSettings::Dithering(void) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_linear_workflow; + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_dithering); + + return *m_private->_dithering; } -ON_Skylight& ON_3dmRenderSettings::Skylight(void) const +const ON_Dithering& ON_3dmRenderSettings::Dithering(void) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - return m_private->_skylight; + return const_cast(this)->Dithering(); } -ON_Sun& ON_3dmRenderSettings::Sun(void) const +ON_SafeFrame& ON_3dmRenderSettings::SafeFrame(void) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_sun; + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_safe_frame); + + return *m_private->_safe_frame; } -ON_Dithering& ON_3dmRenderSettings::Dithering(void) const +const ON_SafeFrame& ON_3dmRenderSettings::SafeFrame(void) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - return m_private->_dithering; + return const_cast(this)->SafeFrame(); } -ON_RenderChannels& ON_3dmRenderSettings::RenderChannels(void) const +ON_Skylight& ON_3dmRenderSettings::Skylight(void) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_render_channels; + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_skylight); + + return *m_private->_skylight; } -ON_XMLNode& ON_3dmRenderSettings::RdkDocNode(void) const +const ON_Skylight& ON_3dmRenderSettings::Skylight(void) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - return m_private->_rdk_document_data; + return const_cast(this)->Skylight(); } -ON_UUID ON_3dmRenderSettings::BackgroundRenderEnvironment(void) const +ON_LinearWorkflow& ON_3dmRenderSettings::LinearWorkflow(void) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_environments.BackgroundRenderEnvironment(); + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_linear_workflow); + + return *m_private->_linear_workflow; } -void ON_3dmRenderSettings::SetBackgroundRenderEnvironment(const ON_UUID& id) +const ON_LinearWorkflow& ON_3dmRenderSettings::LinearWorkflow(void) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - m_private->_environments.SetBackgroundRenderEnvironment(id); + return const_cast(this)->LinearWorkflow(); } -bool ON_3dmRenderSettings::SkylightingRenderEnvironmentOverride(void) const +ON_RenderChannels& ON_3dmRenderSettings::RenderChannels(void) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_environments.SkylightingRenderEnvironmentOverride(); + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_render_channels); + + return *m_private->_render_channels; } -void ON_3dmRenderSettings::SetSkylightingRenderEnvironmentOverride(bool on) +const ON_RenderChannels& ON_3dmRenderSettings::RenderChannels(void) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - m_private->_environments.SetSkylightingRenderEnvironmentOverride(on); + return const_cast(this)->RenderChannels(); } -ON_UUID ON_3dmRenderSettings::SkylightingRenderEnvironment(void) const +ON_Sun& ON_3dmRenderSettings::Sun(void) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_environments.SkylightingRenderEnvironment(); + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_sun); + + return *m_private->_sun; } -void ON_3dmRenderSettings::SetSkylightingRenderEnvironment(const ON_UUID& id) +const ON_Sun& ON_3dmRenderSettings::Sun(void) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - m_private->_environments.SetSkylightingRenderEnvironment(id); + return const_cast(this)->Sun(); } -bool ON_3dmRenderSettings::ReflectionRenderEnvironmentOverride(void) const +bool ON_3dmRenderSettings::RenderEnvironmentOverride(EnvironmentUsage usage) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_environments.ReflectionRenderEnvironmentOverride(); + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_environments); + + const auto& e = *m_private->_environments; + switch (usage) + { + default: + case EnvironmentUsage::Background: return m_background_style == 3; + case EnvironmentUsage::Reflection: return e.ReflectionRenderEnvironmentOverride(); + case EnvironmentUsage::Skylighting: return e.SkylightingRenderEnvironmentOverride(); + } } -void ON_3dmRenderSettings::SetReflectionRenderEnvironmentOverride(bool on) +void ON_3dmRenderSettings::SetRenderEnvironmentOverride(EnvironmentUsage usage, bool on) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - m_private->_environments.SetReflectionRenderEnvironmentOverride(on); + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_environments); + + auto& e = *m_private->_environments; + switch (usage) + { + default: + case EnvironmentUsage::Background: ON_ASSERT(false); break; + case EnvironmentUsage::Reflection: e.SetReflectionRenderEnvironmentOverride(on); break; + case EnvironmentUsage::Skylighting: e.SetSkylightingRenderEnvironmentOverride(on); break; + } } -ON_UUID ON_3dmRenderSettings::ReflectionRenderEnvironment(void) const +ON_UUID ON_3dmRenderSettings::RenderEnvironmentId(EnvironmentUsage usage, EnvironmentPurpose purpose) const { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - return m_private->_environments.ReflectionRenderEnvironment(); + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_environments); + + const auto& e = *m_private->_environments; + + if (EnvironmentPurpose::Standard == purpose) + { + switch (usage) + { + default: + case EnvironmentUsage::Background: return e.BackgroundRenderEnvironmentId(); + case EnvironmentUsage::Reflection: return e.ReflectionRenderEnvironmentId(); + case EnvironmentUsage::Skylighting: return e.SkylightingRenderEnvironmentId(); + } + } + else + if (EnvironmentPurpose::ForRendering == purpose) + { + switch (usage) + { + default: + case EnvironmentUsage::Background: + { + if (m_background_style != 3) + return ON_nil_uuid; + + const auto uuid_env = e.BackgroundRenderEnvironmentId(); + if (ON_UuidIsNotNil(uuid_env)) + return uuid_env; + + return ON_UuidDefaultEnvironmentInstance; + } + + case EnvironmentUsage::Reflection: + if (e.ReflectionRenderEnvironmentOverride()) + return e.ReflectionRenderEnvironmentId(); + + return RenderEnvironmentId(EnvironmentUsage::Background, purpose); + + case EnvironmentUsage::Skylighting: + if (!Skylight().On()) + return ON_nil_uuid; + + if (e.SkylightingRenderEnvironmentOverride()) + return e.SkylightingRenderEnvironmentId(); + + return RenderEnvironmentId(EnvironmentUsage::Background, purpose); + } + } + + ON_ASSERT(false); + return ON_nil_uuid; } -void ON_3dmRenderSettings::SetReflectionRenderEnvironment(const ON_UUID& id) +void ON_3dmRenderSettings::SetRenderEnvironmentId(EnvironmentUsage usage, const ON_UUID& id) { - if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; + ON_3dmRenderSettingsPrivate::Get(*this); - m_private->_environments.SetReflectionRenderEnvironment(id); + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_environments); + + auto& e = *m_private->_environments; + switch (usage) + { + default: + case EnvironmentUsage::Background: e.SetBackgroundRenderEnvironmentId(id); break; + case EnvironmentUsage::Reflection: e.SetReflectionRenderEnvironmentId(id); break; + case EnvironmentUsage::Skylighting: e.SetSkylightingRenderEnvironmentId(id); break; + } } -extern ON_UUID uuidRenderSettingsPreset_Studio; -extern ON_UUID uuidRenderSettingsPreset_Custom; -extern ON_UUID uuidRenderSettingsPreset_Exterior; -extern ON_UUID uuidRenderSettingsPreset_Interior; +ON_PostEffects& ON_3dmRenderSettings::PostEffects(void) +{ + ON_3dmRenderSettingsPrivate::Get(*this); + + // The pointer is never null. + ON_ASSERT(nullptr != m_private->_post_effects); + + return *m_private->_post_effects; +} + +const ON_PostEffects& ON_3dmRenderSettings::PostEffects(void) const +{ + return const_cast(this)->PostEffects(); +} + +extern ON_UUID uuidRenderPreset_Studio; +extern ON_UUID uuidRenderPreset_Custom; +extern ON_UUID uuidRenderPreset_Exterior; +extern ON_UUID uuidRenderPreset_Interior; static const wchar_t* rendering = ON_RDK_DOCUMENT ON_RDK_SLASH ON_RDK_SETTINGS ON_RDK_SLASH ON_RDK_RENDERING; -ON_UUID ON_3dmRenderSettings::CurrentRenderingPreset(void) const +ON_UUID ON_3dmRenderSettings::CurrentRenderPreset(void) const { - const auto* node = RdkDocNode().GetNodeAtPath(rendering); + const auto* node = ON_GetRdkDocNode(*this).GetNodeAtPath(rendering); if (nullptr != node) { ON_XMLParameters p(*node); ON_XMLVariant value; - if (p.GetParam(ON_RDK_CURRENT_PRESET, value)) + if (p.GetParam(ON_RDK_CURRENT_RENDER_PRESET, value)) return value.AsUuid(); } return ON_nil_uuid; } -void ON_3dmRenderSettings::SetCurrentRenderingPreset(const ON_UUID& uuid) +void ON_3dmRenderSettings::SetCurrentRenderPreset(const ON_UUID& uuid) { - ON_ASSERT((uuidRenderSettingsPreset_Studio == uuid) || (uuidRenderSettingsPreset_Custom == uuid) || (uuidRenderSettingsPreset_Exterior == uuid) || (uuidRenderSettingsPreset_Interior == uuid)); + ON_ASSERT((uuidRenderPreset_Studio == uuid) || (uuidRenderPreset_Custom == uuid) || + (uuidRenderPreset_Exterior == uuid) || (uuidRenderPreset_Interior == uuid)); - auto* node = RdkDocNode().GetNodeAtPath(rendering); + auto* node = ON_GetRdkDocNode(*this).GetNodeAtPath(rendering); if (nullptr != node) { ON_XMLParameters p(*node); - p.SetParam(ON_RDK_CURRENT_PRESET, uuid); + p.SetParam(ON_RDK_CURRENT_RENDER_PRESET, uuid); } } -void ON_3dmRenderSettings::GetRenderingPresets(ON_SimpleArray& presets) const +void ON_3dmRenderSettings::GetRenderPresetList(ON_SimpleArray& presets) const { - presets.Append(uuidRenderSettingsPreset_Studio); - presets.Append(uuidRenderSettingsPreset_Exterior); - presets.Append(uuidRenderSettingsPreset_Interior); - presets.Append(uuidRenderSettingsPreset_Custom); + presets.Append(uuidRenderPreset_Studio); + presets.Append(uuidRenderPreset_Exterior); + presets.Append(uuidRenderPreset_Interior); + presets.Append(uuidRenderPreset_Custom); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmAnnotationSettingsPrivate +// + +class ON_3dmAnnotationSettingsPrivate +{ +public: + ON_3dmAnnotationSettingsPrivate() = default; + ~ON_3dmAnnotationSettingsPrivate() = default; + bool operator==(const ON_3dmAnnotationSettingsPrivate&) const; + bool operator!=(const ON_3dmAnnotationSettingsPrivate&) const; + +public: + float m_world_view_text_scale = 1.0f; + float m_world_view_hatch_scale = 1.0f; + bool m_use_dimension_layer = false; + ON_UUID m_dimension_layer_id = ON_nil_uuid; +}; + +static const ON_3dmAnnotationSettingsPrivate Default3dmAnnotationSettingsPrivate; + +bool ON_3dmAnnotationSettingsPrivate::operator==(const ON_3dmAnnotationSettingsPrivate& other) const +{ + if (this == &other) + return true; + + if (m_world_view_text_scale != other.m_world_view_text_scale) + return false; + + if (m_world_view_hatch_scale != other.m_world_view_hatch_scale) + return false; + + if (m_use_dimension_layer != other.m_use_dimension_layer) + return false; + + if (0 != ON_UuidCompare(m_dimension_layer_id, other.m_dimension_layer_id)) + return false; + + return true; +} + +bool ON_3dmAnnotationSettingsPrivate::operator!=(const ON_3dmAnnotationSettingsPrivate& other) const +{ + return !ON_3dmAnnotationSettingsPrivate::operator==(other); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1708,31 +1941,178 @@ void ON_3dmRenderSettings::GetRenderingPresets(ON_SimpleArray& presets) // ON_3dmAnnotationSettings // -void ON_3dmAnnotationSettings::Dump( ON_TextLog& text_log ) const +void ON_3dmAnnotationSettings::Internal_CopyFrom(const ON_3dmAnnotationSettings& src) +{ + m_dimscale = src.m_dimscale; + m_textheight = src.m_textheight; + m_dimexe = src.m_dimexe; + m_dimexo = src.m_dimexo; + m_arrowlength = src.m_arrowlength; + m_arrowwidth = src.m_arrowwidth; + m_centermark = src.m_centermark; + + m_b_V5_EnableAnnotationScaling = src.m_b_V5_EnableAnnotationScaling; + m_bEnableModelSpaceAnnotationScaling = src.m_bEnableModelSpaceAnnotationScaling; + m_bEnableLayoutSpaceAnnotationScaling = src.m_bEnableLayoutSpaceAnnotationScaling; + m_bEnableHatchScaling = src.m_bEnableHatchScaling; + + m_reserved1 = src.m_reserved1; + m_reserved2 = src.m_reserved2; + m_reserved3 = src.m_reserved3; + m_reserved4 = src.m_reserved4; + + m_dimunits = src.m_dimunits; + m_arrowtype = src.m_arrowtype; + m_angularunits = src.m_angularunits; + m_lengthformat = src.m_lengthformat; + m_angleformat = src.m_angleformat; + m_resolution = src.m_resolution; + m_facename = src.m_facename; + + if (src.m_private) + { + m_private = new ON_3dmAnnotationSettingsPrivate(); + *m_private = *src.m_private; + } +} + +void ON_3dmAnnotationSettings::Internal_Destroy() +{ + if (m_private) + { + delete m_private; + m_private = nullptr; + } +} + +ON_3dmAnnotationSettings::~ON_3dmAnnotationSettings() +{ + // NOTE WELL: + // ON_3dmAnnotationSettings::Default is a global const instance. + // Compilers like VS 2019 16.5.0 set the memory for that instance to read-only. + // This destructor must not write to memory used by const instances. + if (this != &ON_3dmAnnotationSettings::Default) + { + Internal_Destroy(); + } +} + +ON_3dmAnnotationSettings::ON_3dmAnnotationSettings(const ON_3dmAnnotationSettings& src) +{ + Internal_CopyFrom(src); +} + +ON_3dmAnnotationSettings& ON_3dmAnnotationSettings::operator=(const ON_3dmAnnotationSettings& src) +{ + if (this != &src) + { + Internal_Destroy(); + Internal_CopyFrom(src); + } + return *this; +} + +void ON_3dmAnnotationSettings::Dump(ON_TextLog& text_log) const { // TODO } double ON_3dmAnnotationSettings::WorldViewTextScale() const { - return m_world_view_text_scale; + double world_view_text_scale = Default3dmAnnotationSettingsPrivate.m_world_view_text_scale; + if (this != &ON_3dmAnnotationSettings::Default) + { + if (m_private) + world_view_text_scale = m_private->m_world_view_text_scale; + } + return world_view_text_scale; } double ON_3dmAnnotationSettings::WorldViewHatchScale() const { - return m_world_view_hatch_scale; + double world_view_hatch_scale = Default3dmAnnotationSettingsPrivate.m_world_view_hatch_scale; + if (this != &ON_3dmAnnotationSettings::Default) + { + if (m_private) + world_view_hatch_scale = m_private->m_world_view_text_scale; + } + return world_view_hatch_scale; } void ON_3dmAnnotationSettings::SetWorldViewTextScale(double world_view_text_scale ) { - if ( ON_IsValid(world_view_text_scale) && world_view_text_scale > 0.0 ) - m_world_view_text_scale = (float)world_view_text_scale; + if (this == &ON_3dmAnnotationSettings::Default) + return; + if (ON_IsValid(world_view_text_scale) && world_view_text_scale > 0.0) + { + if (WorldViewTextScale() != world_view_text_scale) + { + if (nullptr == m_private) + m_private = new ON_3dmAnnotationSettingsPrivate(); + m_private->m_world_view_text_scale = (float)world_view_text_scale; + } + } } -void ON_3dmAnnotationSettings::SetWorldViewHatchScale(double world_view_hatch_scale ) +void ON_3dmAnnotationSettings::SetWorldViewHatchScale(double world_view_hatch_scale) { - if ( ON_IsValid(world_view_hatch_scale) && world_view_hatch_scale > 0.0 ) - m_world_view_hatch_scale = (float)world_view_hatch_scale; + if (this == &ON_3dmAnnotationSettings::Default) + return; + if (ON_IsValid(world_view_hatch_scale) && world_view_hatch_scale > 0.0) + { + if (WorldViewHatchScale() != world_view_hatch_scale) + { + if (nullptr == m_private) + m_private = new ON_3dmAnnotationSettingsPrivate(); + m_private->m_world_view_hatch_scale = (float)world_view_hatch_scale; + } + } +} + +bool ON_3dmAnnotationSettings::UseDimensionLayer() const +{ + bool use_dimension_layer = Default3dmAnnotationSettingsPrivate.m_use_dimension_layer; + if (this != &ON_3dmAnnotationSettings::Default) + { + if (m_private) + use_dimension_layer = m_private->m_use_dimension_layer; + } + return use_dimension_layer; +} + +void ON_3dmAnnotationSettings::EnableUseDimensionLayer(bool use_dimension_layer) +{ + if (this == &ON_3dmAnnotationSettings::Default) + return; + if (UseDimensionLayer() != use_dimension_layer) + { + if (nullptr == m_private) + m_private = new ON_3dmAnnotationSettingsPrivate(); + m_private->m_use_dimension_layer = use_dimension_layer; + } +} + +ON_UUID ON_3dmAnnotationSettings::DimensionLayerId() const +{ + ON_UUID layer_id = Default3dmAnnotationSettingsPrivate.m_dimension_layer_id; + if (this != &ON_3dmAnnotationSettings::Default) + { + if (m_private) + layer_id = m_private->m_dimension_layer_id; + } + return layer_id; +} + +void ON_3dmAnnotationSettings::SetDimensionLayerId(const ON_UUID& dimension_layer_id) +{ + if (this == &ON_3dmAnnotationSettings::Default) + return; + if (DimensionLayerId() != dimension_layer_id) + { + if (nullptr == m_private) + m_private = new ON_3dmAnnotationSettingsPrivate(); + m_private->m_dimension_layer_id = dimension_layer_id; + } } bool ON_3dmAnnotationSettings::Is_V5_AnnotationScalingEnabled() const @@ -1776,7 +2156,6 @@ void ON_3dmAnnotationSettings::EnableHatchScaling( bool bEnable ) m_bEnableHatchScaling = bEnable?1:0; } - bool ON_3dmAnnotationSettings::Read( ON_BinaryArchive& file ) { *this = ON_3dmAnnotationSettings::Default; @@ -1858,9 +2237,9 @@ bool ON_3dmAnnotationSettings::Read( ON_BinaryArchive& file ) if ( minor_version >= 1 ) { // Added 25 August 2010 chunk version 1.1 - double d = m_world_view_text_scale; + double d = WorldViewTextScale(); if (rc) rc = file.ReadDouble(&d); - if (rc && ON_IsValid(d) && d >= 0.0 ) m_world_view_text_scale = (float)d; + if (rc && ON_IsValid(d) && d >= 0.0) SetWorldViewTextScale(d); if (rc) rc = file.ReadChar(&m_b_V5_EnableAnnotationScaling); if (rc) { @@ -1876,15 +2255,26 @@ bool ON_3dmAnnotationSettings::Read( ON_BinaryArchive& file ) if ( minor_version >= 2 ) { - d = m_world_view_hatch_scale; + d = WorldViewHatchScale(); if (rc) rc = file.ReadDouble(&d); - if (rc && ON_IsValid(d) && d >= 0.0) m_world_view_hatch_scale = (float)d; + if (rc && ON_IsValid(d) && d >= 0.0) SetWorldViewHatchScale(d); if (rc) rc = file.ReadChar(&m_bEnableHatchScaling); if (minor_version >= 3) { // [Lowell 3-28-2013] New fields for V6 if (rc) rc = file.ReadChar(&m_bEnableModelSpaceAnnotationScaling); if (rc) rc = file.ReadChar(&m_bEnableLayoutSpaceAnnotationScaling); + + // Added 17 April 2023 chunk version 1.4 + if (minor_version >= 4) + { + bool b = false; + ON_UUID s = ON_nil_uuid; + if (rc) rc = file.ReadBool(&b); + if (rc) EnableUseDimensionLayer(b); + if (rc) rc = file.ReadUuid(s); + if (rc) SetDimensionLayerId(s); + } } } } @@ -1900,7 +2290,7 @@ bool ON_3dmAnnotationSettings::Write( ON_BinaryArchive& file ) const { int minor_version = file.Archive3dmVersion() >= 60 - ? 3 + ? 4 : 2; unsigned int i; @@ -1944,15 +2334,16 @@ bool ON_3dmAnnotationSettings::Write( ON_BinaryArchive& file ) const if (rc) rc = file.WriteString( m_facename ); // Added 25 August 2010 chunk version 1.1 - double d = m_world_view_text_scale; + double d = WorldViewTextScale(); if (rc) rc = file.WriteDouble(d); if (rc) rc = file.WriteChar(m_b_V5_EnableAnnotationScaling); // Added 14 January 2011 chunk version 1.2 - d = m_world_view_hatch_scale; + d = WorldViewHatchScale(); if (rc) rc = file.WriteDouble(d); if (rc) rc = file.WriteChar(m_bEnableHatchScaling); + // Added 28 March 2013 chunk version 1.3 if (minor_version >= 3) { // [Lowell 3-28-2013] New fields for V6 @@ -1960,6 +2351,13 @@ bool ON_3dmAnnotationSettings::Write( ON_BinaryArchive& file ) const if (rc) rc = file.WriteChar(m_bEnableLayoutSpaceAnnotationScaling); } + // Added 17 April 2023 chunk version 1.4 + if (minor_version >= 4) + { + if (rc) rc = file.WriteBool(UseDimensionLayer()); + if (rc) rc = file.WriteUuid(DimensionLayerId()); + } + return rc; } diff --git a/opennurbs_3dm_settings.h b/opennurbs_3dm_settings.h index 71463471..d4a9015e 100644 --- a/opennurbs_3dm_settings.h +++ b/opennurbs_3dm_settings.h @@ -6,7 +6,7 @@ // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. -// +// // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// @@ -14,6 +14,7 @@ #if !defined(OPENNURBS_3DM_SETTINGS_INC_) #define OPENNURBS_3DM_SETTINGS_INC_ +#include "opennurbs_post_effects.h" /////////////////////////////////////////////////////////////////////// // @@ -121,16 +122,16 @@ class ON_CLASS ON_3dmAnnotationSettings { public: ON_3dmAnnotationSettings() = default; - ~ON_3dmAnnotationSettings() = default; - ON_3dmAnnotationSettings(const ON_3dmAnnotationSettings&) = default; - ON_3dmAnnotationSettings& operator=(const ON_3dmAnnotationSettings&) = default; + ~ON_3dmAnnotationSettings(); + ON_3dmAnnotationSettings(const ON_3dmAnnotationSettings&); + ON_3dmAnnotationSettings& operator=(const ON_3dmAnnotationSettings&); static const ON_3dmAnnotationSettings Default; - bool Read( ON_BinaryArchive& ); - bool Write( ON_BinaryArchive& ) const; + bool Read(ON_BinaryArchive&); + bool Write(ON_BinaryArchive&) const; - void Dump( ON_TextLog& text_log ) const; + void Dump(ON_TextLog& text_log) const; // these are the running defaults for making dimensions // they are also the things written to the 3dm file as dimension settings @@ -144,27 +145,25 @@ public: /* Returns: - Value of m_world_view_text_scale; + Gets the world view text scale. */ double WorldViewTextScale() const; /* - Parameters: - world_view_text_scale - [in] - Sets value of m_world_view_text_scale. + Description: + Sets the world view text scale. */ void SetWorldViewTextScale(double world_view_text_scale ); /* Returns: - Value of m_world_view_hatch_scale; + Gets the world view hatch scale. */ double WorldViewHatchScale() const; /* - Parameters: - world_view_hatch_scale - [in] - Sets value of m_world_view_hatch_scale. + Description: + Sets the world view hatch scale. */ void SetWorldViewHatchScale(double world_view_hatch_scale ); @@ -219,24 +218,38 @@ public: bEnable - [in] Sets value of m_bEnableHatchScaling. */ - void EnableHatchScaling( bool bEnable ); + void EnableHatchScaling(bool bEnable); + + /* + Returns: + If true, then new annotations are added to the layer with the id returned by DimensionLayerId(). + If false, then new annotations are added to the document's current layer. + */ + bool UseDimensionLayer() const; + + /* + Description: + Enables or disables the use of a dimension layer. + */ + void EnableUseDimensionLayer(bool bEnable); + + /* + Description: + Gets the id of the dimension layer. + Note, if id is ON_nil_uuid or if a layer with the id does not exist, + the current layer will be used. + */ + ON_UUID DimensionLayerId() const; + + /* + Description: + Sets the dimension layer id. To, clear, use ON_nil_uuid. + */ + void SetDimensionLayerId(const ON_UUID& dimension_layer_id); - // Present but not used in V4 or V5 - removed 5 August 2010 to make room - // for m_world_view_text_scale and m_bEnableAnnotationScaling - //// added 12/28/05 LW - //double m_dimdle; - //double m_dimgap; private: - // If m_bEnableAnnotationScaling is true, - // and ON_OBSOLETE_V5_Annotation::m_annotative_scale is true, - // and ON_OBSOLETE_V5_Annotation::m_type == ON::dtTextBlock, - // and the text object is being displayed in a world - // view (not a detail view and not a page view), - // then the text will be scaled by m_world_view_text_scale. - // The default is 1.0. Values <= 0.0 are not valid. - float m_world_view_text_scale = 1.0f; - float m_world_view_hatch_scale = 1.0f; - + mutable class ON_3dmAnnotationSettingsPrivate* m_private = nullptr; + private: // If m_bEnableAnnotationScaling is false: // * m_world_view_text_scale is ignored. @@ -288,6 +301,10 @@ public: // for decimal, digits past the decimal point ON_wString m_facename; // [LF_FACESIZE] // windows font name + +private: + void Internal_CopyFrom(const ON_3dmAnnotationSettings& src); + void Internal_Destroy(); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -789,47 +806,77 @@ public: void SetScaleBackgroundToFit( bool bScaleBackgroundToFit ); public: - // Access to Dithering information. - class ON_Dithering& Dithering(void) const; - // Access to Ground Plane information. - class ON_GroundPlane& GroundPlane(void) const; + class ON_GroundPlane& GroundPlane(void); + const ON_GroundPlane& GroundPlane(void) const; - // Access to Linear Workflow information. - class ON_LinearWorkflow& LinearWorkflow(void) const; - - // Access to Render Channels information. - class ON_RenderChannels& RenderChannels(void) const; + // Access to Dithering information. + class ON_Dithering& Dithering(void); + const ON_Dithering& Dithering(void) const; // Access to Safe Frame information. - class ON_SafeFrame& SafeFrame(void) const; + class ON_SafeFrame& SafeFrame(void); + const ON_SafeFrame& SafeFrame(void) const; // Access to Skylight information. - class ON_Skylight& Skylight(void) const; + class ON_Skylight& Skylight(void); + const ON_Skylight& Skylight(void) const; + + // Access to Linear Workflow information. + class ON_LinearWorkflow& LinearWorkflow(void); + const ON_LinearWorkflow& LinearWorkflow(void) const; + + // Access to Render Channels information. + class ON_RenderChannels& RenderChannels(void); + const ON_RenderChannels& RenderChannels(void) const; // Access to Sun information. - class ON_Sun& Sun(void) const; + class ON_Sun& Sun(void); + const ON_Sun& Sun(void) const; - // Access to background rendering environment information. - ON_UUID BackgroundRenderEnvironment(void) const; - void SetBackgroundRenderEnvironment(const ON_UUID& id); + // Access to Post Effect information. + class ON_PostEffects& PostEffects(void); + const ON_PostEffects& PostEffects(void) const; - // Access to skylighting rendering environment information. - bool SkylightingRenderEnvironmentOverride(void) const; - void SetSkylightingRenderEnvironmentOverride(bool on); - ON_UUID SkylightingRenderEnvironment(void) const; - void SetSkylightingRenderEnvironment(const ON_UUID& id); + // Access to information about which environments are used for rendering. - // Access to reflection / refraction rendering environment information. - bool ReflectionRenderEnvironmentOverride(void) const; - void SetReflectionRenderEnvironmentOverride(bool on); - ON_UUID ReflectionRenderEnvironment(void) const; - void SetReflectionRenderEnvironment(const ON_UUID& id); + enum class EnvironmentUsage : unsigned int + { + Background, // Specifies the 360 background environment. + Reflection, // Specifies the custom reflective environment. Also used for refraction. + Skylighting, // Specifies the custom skylighting environment. + }; - // Access to rendering presets. - ON_UUID CurrentRenderingPreset(void) const; - void SetCurrentRenderingPreset(const ON_UUID& uuid); - void GetRenderingPresets(ON_SimpleArray& presets) const; + enum class EnvironmentPurpose : unsigned int + { + Standard, // Used to directly get and set the environment instance id. + ForRendering, // Used to get the environment instance id to be used for actual rendering. + }; + + // Returns whether or not the rendering environment for a particular usage is overriding the background + // environment. Only really makes sense for usage Reflection and Skylighting, but for convenience, + // it also works for usage Background by checking if the background style is set to Environment. + bool RenderEnvironmentOverride(EnvironmentUsage usage) const; + + // Sets whether or not the rendering environment for a particular usage is overriding the background + // environment. Only works for usage Reflection and Skylighting. + void SetRenderEnvironmentOverride(EnvironmentUsage usage, bool on); + + // Returns the id of the rendering environment for a particular usage. + // Param 'purpose' specifies the purpose the environment will be used for: + // - If purpose is Standard, this directly returns the id of the environment. + // - If purpose is ForRendering, this returns the id of the environment to be used during rendering. + // It includes all the logic for checking if the environment is enabled and available and for + // deferring to other environments if the requested usage is not available. + ON_UUID RenderEnvironmentId(EnvironmentUsage usage, EnvironmentPurpose purpose) const; + + // Sets the id of the rendering environment for a particular usage. + void SetRenderEnvironmentId(EnvironmentUsage usage, const ON_UUID& id); + + // Access to render presets. + ON_UUID CurrentRenderPreset(void) const; + void SetCurrentRenderPreset(const ON_UUID& uuid); + void GetRenderPresetList(ON_SimpleArray& presets) const; private: unsigned short m_reserved1 = 0; @@ -933,15 +980,12 @@ private: unsigned short m_reserved7 = 0; unsigned short m_reserved8 = 0; -public: // For internal use only. - ON_XMLNode& RdkDocNode(void) const; - -private: +private: // For internal use only. + friend class ONX_ModelPrivate; friend class ON_3dmRenderSettingsPrivate; mutable class ON_3dmRenderSettingsPrivate* m_private = nullptr; }; - ////////////////////////////////////////////////////////////////////////////////////////// // // ON_EarthAnchorPoint diff --git a/opennurbs_archive_manifest.cpp b/opennurbs_archive_manifest.cpp index a952efa1..e27c7b9f 100644 --- a/opennurbs_archive_manifest.cpp +++ b/opennurbs_archive_manifest.cpp @@ -3471,7 +3471,10 @@ private: ) const; public: - enum : unsigned int { TableCount = 17 }; // Count of items in ON_ModelComponent::Type + enum : unsigned int + { + TableCount = (unsigned int)ON_ModelComponent::Type::NumOf + }; private: ON_ComponentManifestTableIndex m_table_index[ON_ComponentManifestImpl::TableCount]; @@ -3500,7 +3503,6 @@ private: // Currently Rhino CRhinoDoc model geometry and model lights are not derived from ON_ModelComponent. ON_ComponentIdHash32Table m_manifest_id_hash_table; - ON_ComponentNameHash32Table& ComponentNameHash32Table( ON_ModelComponent::Type component_type ); diff --git a/opennurbs_color.cpp b/opennurbs_color.cpp index 28d3cdab..a84fae88 100644 --- a/opennurbs_color.cpp +++ b/opennurbs_color.cpp @@ -22,6 +22,15 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif +bool ON_Color::IsSet() const +{ + return IsUnset() ? false : true; +} + +bool ON_Color::IsUnset() const +{ + return ON_Color::UnsetColor == *this; +} const ON_Color ON_Color::RandomColor() { diff --git a/opennurbs_color.h b/opennurbs_color.h index c3c1efe9..9155e8df 100644 --- a/opennurbs_color.h +++ b/opennurbs_color.h @@ -62,6 +62,12 @@ public: kAlphaByteIndex = 3 }; + /// True if this color is not equal to ON_Color::Unset. + bool IsSet() const; + + /// True if this color is equal to ON_Color::Unset. + bool IsUnset() const; + /* Returns: A random color. diff --git a/opennurbs_decals.cpp b/opennurbs_decals.cpp index 4d2c47c3..7fee6f16 100644 --- a/opennurbs_decals.cpp +++ b/opennurbs_decals.cpp @@ -527,7 +527,7 @@ const ON_Decal& ON_Decal::operator = (const ON_Decal& d) return *this; } -bool ON_Decal::operator == (const ON_Decal& d) +bool ON_Decal::operator == (const ON_Decal& d) const { // This only checks if the basic parameters are equal. It ignores any custom data. @@ -565,7 +565,7 @@ bool ON_Decal::operator == (const ON_Decal& d) return true; } -bool ON_Decal::operator != (const ON_Decal& d) +bool ON_Decal::operator != (const ON_Decal& d) const { return !(operator == (d)); } @@ -752,17 +752,17 @@ bool ON_Decal::SetCustomXML(const ON_UUID& renderEngineId, const ON_XMLNode& cus ON_XMLNode* custom_node = _impl->FindCustomNodeForRenderEngine(renderEngineId); if (nullptr != custom_node) { - ON_XMLNode* parent = custom_node->GetParent(); + ON_XMLNode* parent = custom_node->Parent(); if (nullptr != parent) { delete parent->DetachChild(*custom_node); } } - // Add the new custom node and set its 'renderer' property to be the render engine id. + // Attach the new custom node and set its 'renderer' property to be the render engine id. custom_node = _impl->Node().AttachChildNode(new ON_XMLNode(ON_DECAL_CUSTOM)); ON_XMLProperty prop(ON_DECAL_CUSTOM_RENDERER, renderEngineId); - custom_node->AddProperty(prop); + custom_node->SetProperty(prop); // Attach a copy of the custom param node to the custom node. custom_node->AttachChildNode(new ON_XMLNode(custom_param_node)); @@ -841,12 +841,12 @@ bool ON_Decal::GetTextureMapping(ON_TextureMapping& mappingOut) const return false; } -/** \class ON_DecalNodeReader +/* Class ON_DecalNodeReader - This object encapsulates the reading of all decal properties from XML nodes. - It is used by the decal CRC calculation in ComputeDecalCRC(). + This object encapsulates the reading of all decal properties from XML nodes. + It is used by the decal CRC calculation in ComputeDecalCRC(). - TODO: It could also be used by the ON_Decal XML node acccess. + TODO: It could also be used by the ON_Decal XML node acccess. */ class ON_DecalNodeReader diff --git a/opennurbs_decals.h b/opennurbs_decals.h index 71389228..30af54a4 100644 --- a/opennurbs_decals.h +++ b/opennurbs_decals.h @@ -22,10 +22,10 @@ public: ON_Decal(const ON_Decal& d); virtual ~ON_Decal(); - const ON_Decal& operator = (const ON_Decal& d); + virtual const ON_Decal& operator = (const ON_Decal& d); - bool operator == (const ON_Decal& d); - bool operator != (const ON_Decal& d); + virtual bool operator == (const ON_Decal& d) const; + virtual bool operator != (const ON_Decal& d) const; enum class Mappings : ON__INT32 { diff --git a/opennurbs_defines.h b/opennurbs_defines.h index 72c600da..c7b09b9b 100644 --- a/opennurbs_defines.h +++ b/opennurbs_defines.h @@ -222,6 +222,12 @@ double ON_RadiansFromDegrees( #define ON_DBL_MIN 2.22507385850720200e-308 #endif +#if defined(FLT_MAX) +#define ON_FLT_MAX FLT_MAX +#else +#define ON_FLT_MAX 3.402823466e+38F +#endif + // ON_EPSILON = 2^-52 #if defined(DBL_EPSILON) #define ON_EPSILON DBL_EPSILON diff --git a/opennurbs_dimension.cpp b/opennurbs_dimension.cpp index 7c32052f..64b030ab 100644 --- a/opennurbs_dimension.cpp +++ b/opennurbs_dimension.cpp @@ -2890,7 +2890,12 @@ bool ON_DimAngular::GetTextXform( // This gets the display text that's already on the dimension const ON_TextContent* text = Text(); if (nullptr == text) - return false; + { + UpdateDimensionText(dimstyle); + text = Text(); + if (nullptr == text) + return false; + } // See if the text needs remade because of some change in some property that // would change its appearance diff --git a/opennurbs_dithering.cpp b/opennurbs_dithering.cpp index d794eb29..0e3cf70a 100644 --- a/opennurbs_dithering.cpp +++ b/opennurbs_dithering.cpp @@ -22,13 +22,6 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif -// These settings are inside the ON_RDK_RENDERING section. - -#define ON_USE_DITHERING L"use-dithering" -#define ON_DITHERING_METHOD L"dithering" -#define ON_DITHERING_FLOYD_STEINBERG L"floyd-steinberg" -#define ON_DITHERING_SIMPLE_NOISE L"simple-noise" - class ON_Dithering::CImpl : public ON_InternalXMLImpl { public: @@ -74,7 +67,7 @@ const ON_Dithering& ON_Dithering::operator = (const ON_Dithering& dit) return *this; } -bool ON_Dithering::operator == (const ON_Dithering& dit) +bool ON_Dithering::operator == (const ON_Dithering& dit) const { if (On() != dit.On()) return false; if (Method() != dit.Method()) return false; @@ -82,25 +75,25 @@ bool ON_Dithering::operator == (const ON_Dithering& dit) return true; } -bool ON_Dithering::operator != (const ON_Dithering& dit) +bool ON_Dithering::operator != (const ON_Dithering& dit) const { return !(operator == (dit)); } bool ON_Dithering::On(void) const { - return m_impl->GetParameter(XMLPathDit(), ON_USE_DITHERING, false); + return m_impl->GetParameter(XMLPathDit(), ON_RDK_DITHERING_ON, false); } void ON_Dithering::SetOn(bool b) { - m_impl->SetParameter(XMLPathDit(), ON_USE_DITHERING, b); + m_impl->SetParameter(XMLPathDit(), ON_RDK_DITHERING_ON, b); } ON_Dithering::Methods ON_Dithering::Method(void) const { - const ON_wString s = m_impl->GetParameter(XMLPathDit(), ON_DITHERING_METHOD, L"").AsString(); - if (ON_DITHERING_FLOYD_STEINBERG == s) + const ON_wString s = m_impl->GetParameter(XMLPathDit(), ON_RDK_DITHERING_METHOD, L"").AsString(); + if (ON_RDK_DITHERING_METHOD_FLOYD_STEINBERG == s) return Methods::FloydSteinberg; return Methods::SimpleNoise; @@ -108,11 +101,11 @@ ON_Dithering::Methods ON_Dithering::Method(void) const void ON_Dithering::SetMethod(Methods m) { - const wchar_t* wsz = ON_DITHERING_SIMPLE_NOISE; + const wchar_t* wsz = ON_RDK_DITHERING_METHOD_SIMPLE_NOISE; if (Methods::FloydSteinberg == m) - wsz = ON_DITHERING_FLOYD_STEINBERG; + wsz = ON_RDK_DITHERING_METHOD_FLOYD_STEINBERG; - m_impl->SetParameter(XMLPathDit(), ON_DITHERING_METHOD, wsz); + m_impl->SetParameter(XMLPathDit(), ON_RDK_DITHERING_METHOD, wsz); } ON__UINT32 ON_Dithering::DataCRC(ON__UINT32 crc) const @@ -125,3 +118,12 @@ ON__UINT32 ON_Dithering::DataCRC(ON__UINT32 crc) const return crc; } + +void* ON_Dithering::EVF(const wchar_t* func, void* data) +{ + return nullptr; +} + +void ON_Dithering::InvalidateCache(void) +{ +} diff --git a/opennurbs_dithering.h b/opennurbs_dithering.h index e07aefd3..cc5a5c52 100644 --- a/opennurbs_dithering.h +++ b/opennurbs_dithering.h @@ -14,35 +14,42 @@ #if !defined(ON_DITHERING_INC_) #define ON_DITHERING_INC_ -class ON_CLASS ON_Dithering final +class ON_CLASS ON_Dithering { public: ON_Dithering(); ON_Dithering(ON_XMLNode& model_node); ON_Dithering(const ON_Dithering& dit); - ~ON_Dithering(); + virtual ~ON_Dithering(); - const ON_Dithering& operator = (const ON_Dithering& dit); + virtual const ON_Dithering& operator = (const ON_Dithering& dit); - bool operator == (const ON_Dithering& dit); - bool operator != (const ON_Dithering& dit); + virtual bool operator == (const ON_Dithering& dit) const; + virtual bool operator != (const ON_Dithering& dit) const; // Get dithering on / off state. - bool On(void) const; + virtual bool On(void) const; // Set dithering on or off. - void SetOn(bool b); + virtual void SetOn(bool b); enum class Methods { SimpleNoise, FloydSteinberg }; // Get the dithering method. - Methods Method(void) const; + virtual Methods Method(void) const; // Set the dithering method. - void SetMethod(Methods m); + virtual void SetMethod(Methods m); // Returns the CRC of the dithering state. - ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +private: // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + virtual void InvalidateCache(void); private: class CImpl; diff --git a/opennurbs_embedded_file.cpp b/opennurbs_embedded_file.cpp index 279ff877..c57c63a1 100644 --- a/opennurbs_embedded_file.cpp +++ b/opennurbs_embedded_file.cpp @@ -291,6 +291,11 @@ bool ON_EmbeddedFile::Clear(void) return true; } +void* ON_EmbeddedFile::EVF(const wchar_t* func, void* data) +{ + return nullptr; +} + const ON_EmbeddedFile* ON_EmbeddedFile::FromModelComponentRef(const ON_ModelComponentReference& ref, const ON_EmbeddedFile* none_return_value) // Static. { diff --git a/opennurbs_embedded_file.h b/opennurbs_embedded_file.h index 2e847cb5..ea979f7d 100644 --- a/opennurbs_embedded_file.h +++ b/opennurbs_embedded_file.h @@ -23,57 +23,60 @@ public: ON_EmbeddedFile(const ON_EmbeddedFile&); virtual ~ON_EmbeddedFile(); - const ON_EmbeddedFile& operator = (const ON_EmbeddedFile&); + virtual const ON_EmbeddedFile& operator = (const ON_EmbeddedFile&); // Returns the fully-qualified filename of the embedded file. This filename may or may not refer to a // local file depending on the way the embedded file was loaded. For example, if it was loaded from an // archive, the filename could be that of a file on a different computer. - ON_wString Filename(void) const; + virtual ON_wString Filename(void) const; // Sets the fully-qualified filename of the embedded file. This can be used to set a filename initially // when none has been loaded, or to change the filename prior to saving to an archive. - void SetFilename(const wchar_t* filename); + virtual void SetFilename(const wchar_t* filename); // Loads the embedded file from a local file. This also sets the filename. Returns true if successful, else false. - bool LoadFromFile(const wchar_t* filename); + virtual bool LoadFromFile(const wchar_t* filename); // Saves the embedded file to a local file. The embedded file must have been loaded by a call to // one of either LoadFromFile(), LoadFromBuffer() or Read() prior to calling this method. // Returns true if successful, else false. - bool SaveToFile(const wchar_t* filename) const; + virtual bool SaveToFile(const wchar_t* filename) const; // Loads the embedded file from a buffer. This does not set the filename; it merely loads the contents // of the buffer into the embedded file object. If you intend to call Write() after this, you may need // to call SetFilename() first. Returns true if successful, else false. - bool LoadFromBuffer(ON_Buffer& buf); + virtual bool LoadFromBuffer(ON_Buffer& buf); // Saves the embedded file to a buffer. The embedded file must have been loaded by a call to // one of either LoadFromFile(), LoadFromBuffer() or Read() prior to calling this method. // This does not save the filename to the buffer, it only saves the file contents. // Returns true if successful, else false. - bool SaveToBuffer(ON_Buffer& buf) const; + virtual bool SaveToBuffer(ON_Buffer& buf) const; // Reads an embedded file from an archive. The archive must be positioned at the start of suitable data // as created by the Write() method. It first loads the filename from the archive and then loads the file // contents. This method is an expert user tool. Incorrect use will have undefined results. // Returns true if successful, else false. - bool Read(ON_BinaryArchive& archive) override; + virtual bool Read(ON_BinaryArchive& archive) override; // Writes the embedded file to an archive. The embedded file must have been loaded by a call to // one of either LoadFromFile(), LoadFromBuffer() or Read() prior to calling this method. // This writes both the filename and the file contents to the archive. // Returns true if successful, else false. - bool Write(ON_BinaryArchive& archive) const override; + virtual bool Write(ON_BinaryArchive& archive) const override; // Returns the length of the loaded data. If the object is not loaded, this method returns zero. - size_t Length(void) const; + virtual size_t Length(void) const; // Returns the compressed length of the data loaded by Read(). If the object is not loaded or was // loaded by LoadFromFile() or LoadFromBuffer(), this method returns zero. - size_t CompressedLength(void) const; + virtual size_t CompressedLength(void) const; // Clears the embedded file data. Returns true if successful, else false. - bool Clear(void); + virtual bool Clear(void); + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); // If ON_EmbeddedFile::Cast(ref.ModelComponent()) is not null, // that pointer is returned. Otherwise, none_return_value is returned. diff --git a/opennurbs_extensions.cpp b/opennurbs_extensions.cpp index 274b8797..c4b9ce0c 100644 --- a/opennurbs_extensions.cpp +++ b/opennurbs_extensions.cpp @@ -308,9 +308,7 @@ public: bool PopulateRDKComponents(int archive_3dm_version); bool UpdateRDKUserData(int archive_3dm_version); bool CreateRenderContentFromXML(class ON_XMLNode& model_node, RenderContentKinds kind); - bool CreatePostEffectsFromXML(ON_XMLNode& model_node, ON_PostEffect::Types type); bool CreateXMLFromRenderContent(ON_XMLNode& model_node, RenderContentKinds kind) const; - bool CreateXMLFromPostEffects(ON_XMLNode& model_node, ON_PostEffect::Types type) const; bool SetRDKDocumentInformation(const wchar_t* xml, ONX_Model_UserData& docud, int archive_3dm_version) const; ON_XMLNode* GetRenderContentSectionNode(ON_XMLNode& model_node, RenderContentKinds kind) const; ON_XMLNode* GetPostEffectSectionNode(ON_XMLNode& model_node, ON_PostEffect::Types type) const; @@ -325,30 +323,44 @@ public: ON_InternalXMLImpl::~ON_InternalXMLImpl() { - if (nullptr != m_local_node) + if (nullptr != _local_node) { - delete m_local_node; - m_local_node = nullptr; + delete _local_node; + _local_node = nullptr; } } ON_XMLNode& ON_InternalXMLImpl::Node(void) const { - // If the model node pointer is set, return that. This is a pointer to a node owned by the ONX_Model which - // contains the entire RDK document XML. This is used by model objects (Ground Plane, etc.) that are owned - // by the ONX_Model. In the case of Ground Plane etc, it's a pointer into the ONX_Model's XML. + // If the model node pointer is set, return that. This is a pointer to a node owned by the ON_3dmRenderSettings + // which contains the entire RDK document XML. This is used by objects (Ground Plane, etc.) that are owned by the + // ON_3dmRenderSettings. In the case of Ground Plane etc, it's a pointer into the ON_3dmRenderSettings XML. // In the case of decals, it's a pointer into the decal collection's XML. - if (nullptr != m_model_node) - return *m_model_node; + if (nullptr != _model_node) + return *_model_node; // Since the model node is not set, we need a local node to hold the XML. If one has not been created yet, // create it. The local node is owned by this object. This case occurs for free-floating copies of model // objects and also for free-floating copies of decals and mesh modifiers. This node only contains the XML - // data that's relevant to the object it's for, not the entire XML. - if (nullptr == m_local_node) - m_local_node = new ON_XMLNode(NameOfRootNode()); + // data that's relevant to the object it's for, not the entire XML, but the object's node is still a child + // node under the same node hierarchy as if it were the entire XML. + if (nullptr == _local_node) + _local_node = new ON_XMLNode(NameOfRootNode()); - return *m_local_node; + return *_local_node; +} + +void ON_InternalXMLImpl::SetModelNode(ON_XMLNode& node) +{ + ON_ASSERT(_model_node == nullptr); + + if (nullptr != _local_node) + { + delete _local_node; + _local_node = nullptr; + } + + _model_node = &node; } ON_wString ON_InternalXMLImpl::NameOfRootNode(void) const @@ -368,7 +380,7 @@ ON_XMLVariant ON_InternalXMLImpl::GetParameter_NoType(const wchar_t* path_to_nod ON_XMLVariant ON_InternalXMLImpl::InternalGetParameter(const wchar_t* path_to_node, const wchar_t* param_name, const wchar_t* default_type, const ON_XMLVariant& def) const { - std::lock_guard lg(m_mutex); + std::lock_guard lg(_mutex); const ON_XMLNode* node_read = Node().GetNodeAtPath(path_to_node); if (nullptr == node_read) @@ -395,7 +407,7 @@ bool ON_InternalXMLImpl::SetParameter_NoType(const wchar_t* path_to_node, const bool ON_InternalXMLImpl::InternalSetParameter(const wchar_t* path_to_node, const wchar_t* param_name, bool write_type, const ON_XMLVariant& value) { - std::lock_guard lg(m_mutex); + std::lock_guard lg(_mutex); bool success = false; @@ -2166,7 +2178,8 @@ void ONX_Model::DumpComponentLists( ON_TextLog& dump ) const ON_ModelComponent::Type::HistoryRecord, ON_ModelComponent::Type::RenderContent, ON_ModelComponent::Type::EmbeddedFile, - ON_ModelComponent::Type::PostEffect, + //ON_ModelComponent::Type::ObsoleteValue, + ON_ModelComponent::Type::SectionStyle, ON_ModelComponent::Type::Unset // list terminator }; @@ -4837,38 +4850,20 @@ static const wchar_t* RenderContentKindString(RenderContentKinds kind) case RenderContentKinds::Material: return ON_KIND_MATERIAL; case RenderContentKinds::Environment: return ON_KIND_ENVIRONMENT; case RenderContentKinds::Texture: return ON_KIND_TEXTURE; + default: ON_ASSERT(false); return L""; } - - ON_ASSERT(false); - return L""; -} - -static const wchar_t* PostEffectTypeString(ON_PostEffect::Types type) -{ - switch (type) - { - case ON_PostEffect::Types::Early: - return ON_RDK_PEP_TYPE_EARLY; - case ON_PostEffect::Types::ToneMapping: - return ON_RDK_PEP_TYPE_TONE; - case ON_PostEffect::Types::Late: - return ON_RDK_PEP_TYPE_LATE; - case ON_PostEffect::Types::Unset: - break; - } - - ON_ASSERT(false); - return L""; } extern int ON_ComponentManifestImpl_TableCount(void); +ON_XMLNode& ON_GetRdkDocNode(const ON_3dmRenderSettings& rs); + ONX_ModelPrivate::ONX_ModelPrivate(ONX_Model& m) : m_model(m) { - // If this assert fires, you must change the TableCount enum in opennurbs_archive_manifest.cpp - // to be the same number as ON_ModelComponent::Type::NumOf. + // The TableCount enum in opennurbs_archive_manifest.cpp should always be + // equal to ON_ModelComponent::Type::NumOf. ON_ASSERT(int(ON_ModelComponent::Type::NumOf) == ON_ComponentManifestImpl_TableCount()); for (unsigned int i = 0; i < int(ON_ModelComponent::Type::NumOf); i++) @@ -4950,7 +4945,7 @@ static bool ContentIsKind(const ON_RenderContent* pContent, RenderContentKinds k ON_XMLNode* ONX_ModelPrivate::GetPostEffectSectionNode(ON_XMLNode& docNode, ON_PostEffect::Types type) const { ON_wString s = ON_RDK_DOCUMENT ON_RDK_SLASH ON_RDK_SETTINGS ON_RDK_SLASH ON_RDK_POST_EFFECTS ON_RDK_SLASH; - s += PostEffectTypeString(type); + s += ON_PostEffectTypeString(type); return docNode.CreateNodeAtPath(s); } @@ -5021,57 +5016,6 @@ bool ONX_ModelPrivate::CreateXMLFromRenderContent(ON_XMLNode& model_node, Render return true; } -bool ONX_ModelPrivate::CreatePostEffectsFromXML(ON_XMLNode& doc_root_node, ON_PostEffect::Types type) -{ - ON_XMLNode* pep_section_node = GetPostEffectSectionNode(doc_root_node, type); - if (nullptr == pep_section_node) - return false; - - auto it = pep_section_node->GetChildIterator(); - - ON_XMLNode* pPostEffectNode = it.GetNextChild(); - while (nullptr != pPostEffectNode) - { - ON_PostEffect pep(*pPostEffectNode, type); - const ON_ModelComponentReference ref = m_model.AddModelComponent(pep); - auto* pep_in_model = ON_PostEffect::Cast(ref.ModelComponent()); - if (nullptr != pep_in_model) - { - SetModel(*pep_in_model, m_model); - } - - pPostEffectNode = it.GetNextChild(); - } - - return true; -} - -bool ONX_ModelPrivate::CreateXMLFromPostEffects(ON_XMLNode& doc_root_node, ON_PostEffect::Types type) const -{ - ON_XMLNode* pep_section_node = GetPostEffectSectionNode(doc_root_node, type); - if (nullptr == pep_section_node) - return false; - - ONX_ModelComponentIterator it(m_model, ON_ModelComponent::Type::PostEffect); - const ON_ModelComponent* pComponent = it.FirstComponent(); - while (nullptr != pComponent) - { - const auto* pep = ON_PostEffect::Cast(pComponent); - if ((nullptr != pep) && (pep->Type() == type)) - { - ON_XMLNode* pep_node = FindPostEffectNodeForId(*pep_section_node, pep->Id()); - if (nullptr != pep_node) - { - *pep_node = pep->XML(); - } - } - - pComponent = it.NextComponent(); - } - - return true; -} - bool ONX_ModelPrivate::PopulateRDKComponents(int archive_3dm_version) { // Get the entire RDK document XML. This includes not only render contents @@ -5081,7 +5025,7 @@ bool ONX_ModelPrivate::PopulateRDKComponents(int archive_3dm_version) return false; // Read the entire XML into the document node. - ON_XMLNode& doc_node = m_model.m_settings.m_RenderSettings.RdkDocNode(); + ON_XMLNode& doc_node = ON_GetRdkDocNode(m_model.m_settings.m_RenderSettings); const auto read = doc_node.ReadFromStream(xml, false, true); if (ON_XMLNode::ReadError == read) return false; @@ -5091,11 +5035,6 @@ bool ONX_ModelPrivate::PopulateRDKComponents(int archive_3dm_version) CreateRenderContentFromXML(doc_node, RenderContentKinds::Environment); CreateRenderContentFromXML(doc_node, RenderContentKinds::Texture); - // Create the post effects from the relevant nodes. - CreatePostEffectsFromXML(doc_node, ON_PostEffect::Types::Early); - CreatePostEffectsFromXML(doc_node, ON_PostEffect::Types::ToneMapping); - CreatePostEffectsFromXML(doc_node, ON_PostEffect::Types::Late); - // Create the mesh modifiers. CreateMeshModifiersFromXML(m_model, archive_3dm_version); @@ -5107,18 +5046,13 @@ bool ONX_ModelPrivate::UpdateRDKUserData(int archive_3dm_version) if (0 == archive_3dm_version) archive_3dm_version = ON_BinaryArchive::CurrentArchiveVersion(); - ON_XMLNode& doc_node = m_model.m_settings.m_RenderSettings.RdkDocNode(); + ON_XMLNode& doc_node = ON_GetRdkDocNode(m_model.m_settings.m_RenderSettings); // For each kind, convert the render content hierarchy to fresh XML. CreateXMLFromRenderContent(doc_node, RenderContentKinds::Material); CreateXMLFromRenderContent(doc_node, RenderContentKinds::Environment); CreateXMLFromRenderContent(doc_node, RenderContentKinds::Texture); - // For each type, convert the post effects to fresh XML. - CreateXMLFromPostEffects(doc_node, ON_PostEffect::Types::Early); - CreateXMLFromPostEffects(doc_node, ON_PostEffect::Types::ToneMapping); - CreateXMLFromPostEffects(doc_node, ON_PostEffect::Types::Late); - // Convert the mesh modifier collection to fresh XML. CreateXMLFromMeshModifiers(m_model, archive_3dm_version); diff --git a/opennurbs_font.h b/opennurbs_font.h index af0b81a1..67173bf9 100644 --- a/opennurbs_font.h +++ b/opennurbs_font.h @@ -2247,7 +2247,7 @@ public: ON_wString m_en_field_20_postscript_cid; // from IDWriteGdiInterop.ConvertFontToLOGFONT - LOGFONT m_gdi_interop_logfont; + LOGFONT m_gdi_interop_logfont = {}; // from IDWriteGdiInterop.ConvertFontToLOGFONT bool m_gdi_interop_logfont_bIsSystemFont = false; diff --git a/opennurbs_ground_plane.cpp b/opennurbs_ground_plane.cpp index 5e7ba305..ba942e1f 100644 --- a/opennurbs_ground_plane.cpp +++ b/opennurbs_ground_plane.cpp @@ -22,204 +22,246 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif -#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" - -class ON_GroundPlane::CImpl : public ON_InternalXMLImpl -{ -public: - CImpl() { } - CImpl(ON_XMLNode& n) : ON_InternalXMLImpl(&n) { } -}; - static const wchar_t* XMLPath(void) { return ON_RDK_DOCUMENT ON_RDK_SLASH ON_RDK_SETTINGS ON_RDK_SLASH ON_RDK_GROUND_PLANE; } +class ON_GroundPlane::CImpl : public ON_InternalXMLImpl +{ +public: + CImpl() { } + CImpl(const CImpl& i) = delete; + CImpl(ON_XMLNode& n) : ON_InternalXMLImpl(&n) { } + + bool On(void) const { return GetParameter(XMLPath(), ON_RDK_GP_ON, false).AsBool(); } + bool ShowUnderside(void) const { return GetParameter(XMLPath(), ON_RDK_GP_SHOW_UNDERSIDE, false).AsBool(); } + double Altitude(void) const { return GetParameter(XMLPath(), ON_RDK_GP_ALTITUDE, 0.0).AsDouble(); } + bool AutoAltitude(void) const { return GetParameter(XMLPath(), ON_RDK_GP_AUTO_ALTITUDE, false).AsBool(); } + bool ShadowOnly(void) const { return GetParameter(XMLPath(), ON_RDK_GP_SHADOW_ONLY, true).AsBool(); } + bool TextureOffsetLocked(void) const { return GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_OFFSET_LOCKED, false).AsBool(); } + bool TextureSizeLocked(void) const { return GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_SIZE_LOCKED, false).AsBool(); } + double TextureRotation(void) const { return GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_ROTATION, false).AsDouble(); } + ON_2dVector TextureOffset(void) const { return ON_2dVector(GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_OFFSET, ON_nil_uuid).As2dPoint()); } + ON_2dVector TextureSize(void) const { return ON_2dVector(GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_SIZE, ON_nil_uuid).As2dPoint()); } + + ON_UUID MaterialInstanceId(void) const + { + const auto u = GetParameter(XMLPath(), ON_RDK_GP_MATERIAL_ID, ON_nil_uuid).AsUuid(); + if (ON_nil_uuid != u) + return u; + + return ON_UuidDefaultMaterialInstance; + } + + void SetOn(bool v) { SetParameter(XMLPath(), ON_RDK_GP_ON, v); } + void SetShowUnderside(bool v) { SetParameter(XMLPath(), ON_RDK_GP_SHOW_UNDERSIDE, v); } + void SetAltitude(double v) { SetParameter(XMLPath(), ON_RDK_GP_ALTITUDE, v); } + void SetAutoAltitude(bool v) { SetParameter(XMLPath(), ON_RDK_GP_AUTO_ALTITUDE, v); } + void SetShadowOnly(bool v) { SetParameter(XMLPath(), ON_RDK_GP_SHADOW_ONLY, v); } + void SetMaterialInstanceId(const ON_UUID& v) { SetParameter(XMLPath(), ON_RDK_GP_MATERIAL_ID, v); } + void SetTextureOffsetLocked(bool v) { SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_OFFSET_LOCKED, v); } + void SetTextureSizeLocked(bool v) { SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_SIZE_LOCKED, v); } + void SetTextureRotation(double v) { SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_ROTATION, v); } + void SetTextureOffset(const ON_2dVector& v) { const ON_2dPoint p = v; SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_OFFSET, p); } + void SetTextureSize(const ON_2dVector& v) { const ON_2dPoint p = v; SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_SIZE, p); } +}; + ON_GroundPlane::ON_GroundPlane() { - m_impl = new CImpl; + _impl = new CImpl; // Uses local node. } ON_GroundPlane::ON_GroundPlane(ON_XMLNode& model_node) { - m_impl = new CImpl(model_node); + _impl = new CImpl(model_node); } ON_GroundPlane::ON_GroundPlane(const ON_GroundPlane& gp) { - m_impl = new CImpl; + _impl = new CImpl; // Uses local node. operator = (gp); } ON_GroundPlane::~ON_GroundPlane() { - delete m_impl; - m_impl = nullptr; + delete _impl; + _impl = nullptr; } const ON_GroundPlane& ON_GroundPlane::operator = (const ON_GroundPlane& gp) { if (this != &gp) { - SetOn (gp.On()); - SetShowUnderside (gp.ShowUnderside()); - SetAutoAltitude (gp.AutoAltitude()); - SetShadowOnly (gp.ShadowOnly()); - SetMaterialInstanceId (gp.MaterialInstanceId()); - SetTextureOffset (gp.TextureOffset()); - SetTextureOffsetLocked(gp.TextureOffsetLocked()); - SetTextureRepeatLocked(gp.TextureRepeatLocked()); - SetTextureSize (gp.TextureSize()); - SetTextureRotation (gp.TextureRotation()); - SetAltitude (gp.Altitude()); + // When copying the object, we need to directly copy the underlying XML. So we can't allow + // virtual overrides to execute because they might shadow the real values we want to copy. + _impl->SetOn (gp._impl->On()); + _impl->SetShowUnderside (gp._impl->ShowUnderside()); + _impl->SetAltitude (gp._impl->Altitude()); + _impl->SetAutoAltitude (gp._impl->AutoAltitude()); + _impl->SetShadowOnly (gp._impl->ShadowOnly()); + _impl->SetMaterialInstanceId (gp._impl->MaterialInstanceId()); + _impl->SetTextureOffset (gp._impl->TextureOffset()); + _impl->SetTextureOffsetLocked(gp._impl->TextureOffsetLocked()); + _impl->SetTextureSize (gp._impl->TextureSize()); + _impl->SetTextureSizeLocked (gp._impl->TextureSizeLocked()); + _impl->SetTextureRotation (gp._impl->TextureRotation()); } return *this; } -bool ON_GroundPlane::operator == (const ON_GroundPlane& gp) +bool ON_GroundPlane::operator == (const ON_GroundPlane& gp) const { - if (On() != gp.On() ) return false; - if (ShowUnderside() != gp.ShowUnderside() ) return false; - if (AutoAltitude() != gp.AutoAltitude() ) return false; - if (ShadowOnly() != gp.ShadowOnly() ) return false; - if (MaterialInstanceId() != gp.MaterialInstanceId() ) return false; - if (TextureOffset() != gp.TextureOffset() ) return false; - if (TextureOffsetLocked() != gp.TextureOffsetLocked()) return false; - if (TextureRepeatLocked() != gp.TextureRepeatLocked()) return false; - if (TextureSize() != gp.TextureSize() ) return false; + // When checking equality, we need to directly check the underlying XML. So we can't allow + // virtual overrides to execute because they might shadow the real values we want to check. + if (_impl->On() != gp._impl->On() ) return false; + if (_impl->ShowUnderside() != gp._impl->ShowUnderside() ) return false; + if (_impl->AutoAltitude() != gp._impl->AutoAltitude() ) return false; + if (_impl->ShadowOnly() != gp._impl->ShadowOnly() ) return false; + if (_impl->MaterialInstanceId() != gp._impl->MaterialInstanceId() ) return false; + if (_impl->TextureOffset() != gp._impl->TextureOffset() ) return false; + if (_impl->TextureOffsetLocked() != gp._impl->TextureOffsetLocked()) return false; + if (_impl->TextureSize() != gp._impl->TextureSize() ) return false; + if (_impl->TextureSizeLocked() != gp._impl->TextureSizeLocked() ) return false; - if (!IsDoubleEqual(Altitude(), gp.Altitude())) return false; - if (!IsDoubleEqual(TextureRotation(), gp.TextureRotation())) return false; + if (!IsDoubleEqual(_impl->Altitude(), gp._impl->Altitude())) return false; + if (!IsDoubleEqual(_impl->TextureRotation(), gp._impl->TextureRotation())) return false; return true; } -bool ON_GroundPlane::operator != (const ON_GroundPlane& gp) +bool ON_GroundPlane::operator != (const ON_GroundPlane& gp) const { return !(operator == (gp)); } bool ON_GroundPlane::On(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_ON, false).AsBool(); + return _impl->On(); } bool ON_GroundPlane::ShowUnderside(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_SHOW_UNDERSIDE, false).AsBool(); + return _impl->ShowUnderside(); } double ON_GroundPlane::Altitude(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_ALTITUDE, 0.0).AsDouble(); + return _impl->Altitude(); } bool ON_GroundPlane::AutoAltitude(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_AUTO_ALTITUDE, false).AsBool(); + return _impl->AutoAltitude(); } bool ON_GroundPlane::ShadowOnly(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_SHADOW_ONLY, true).AsBool(); + return _impl->ShadowOnly(); } ON_UUID ON_GroundPlane::MaterialInstanceId(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_MATERIAL, ON_nil_uuid).AsUuid(); + return _impl->MaterialInstanceId(); } ON_2dVector ON_GroundPlane::TextureOffset(void) const { - return ON_2dVector(m_impl->GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_OFFSET, ON_nil_uuid).As2dPoint()); + return _impl->TextureOffset(); } bool ON_GroundPlane::TextureOffsetLocked(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_OFFSET_LOCK, false).AsBool(); -} - -bool ON_GroundPlane::TextureRepeatLocked(void) const -{ - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_REPEAT_LOCK, false).AsBool(); + return _impl->TextureOffsetLocked(); } ON_2dVector ON_GroundPlane::TextureSize(void) const { - return ON_2dVector(m_impl->GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_SIZE, ON_nil_uuid).As2dPoint()); + return _impl->TextureSize(); +} + +bool ON_GroundPlane::TextureSizeLocked(void) const +{ + return _impl->TextureSizeLocked(); } double ON_GroundPlane::TextureRotation(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_GP_TEXTURE_ROTATION, false).AsDouble(); + return _impl->TextureRotation(); } -void ON_GroundPlane::SetOn(bool b) +void ON_GroundPlane::SetOn(bool v) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_ON, b); + _impl->SetOn(v); } -void ON_GroundPlane::SetShowUnderside(bool b) +void ON_GroundPlane::SetShowUnderside(bool v) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_SHOW_UNDERSIDE, b); + _impl->SetShowUnderside(v); } -void ON_GroundPlane::SetAltitude(double d) +void ON_GroundPlane::SetAltitude(double v) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_ALTITUDE, d); + _impl->SetAltitude(v); } -void ON_GroundPlane::SetAutoAltitude(bool b) +void ON_GroundPlane::SetAutoAltitude(bool v) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_AUTO_ALTITUDE, b); + _impl->SetAutoAltitude(v); } -void ON_GroundPlane::SetShadowOnly(bool b) +void ON_GroundPlane::SetShadowOnly(bool v) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_SHADOW_ONLY, b); + _impl->SetShadowOnly(v); } -void ON_GroundPlane::SetMaterialInstanceId(const ON_UUID& u) +void ON_GroundPlane::SetMaterialInstanceId(const ON_UUID& v) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_MATERIAL, u); + _impl->SetMaterialInstanceId(v); } void ON_GroundPlane::SetTextureOffset(const ON_2dVector& v) { - const ON_2dPoint p = v; - m_impl->SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_OFFSET, p); + _impl->SetTextureOffset(v); } -void ON_GroundPlane::SetTextureOffsetLocked(bool b) +void ON_GroundPlane::SetTextureOffsetLocked(bool v) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_OFFSET_LOCK, b); -} - -void ON_GroundPlane::SetTextureRepeatLocked(bool b) -{ - m_impl->SetParameter(XMLPath(), ON_RDK_GP_REPEAT_LOCK, b); + _impl->SetTextureOffsetLocked(v); } void ON_GroundPlane::SetTextureSize(const ON_2dVector& v) { - const ON_2dPoint p = v; - m_impl->SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_SIZE, p); + _impl->SetTextureSize(v); } -void ON_GroundPlane::SetTextureRotation(double d) +void ON_GroundPlane::SetTextureSizeLocked(bool v) +{ + _impl->SetTextureSizeLocked(v); +} + +void ON_GroundPlane::SetTextureRotation(double v) +{ + _impl->SetTextureRotation(v); +} + +bool ON_GroundPlane::PopulateMaterial(ON_Material& mat) const +{ + mat = ON_Material::Default; + return true; +} + +void ON_GroundPlane::SetXMLNode(ON_XMLNode& node) const +{ + _impl->SetModelNode(node); +} + +void* ON_GroundPlane::EVF(const wchar_t* func, void* data) +{ + return nullptr; +} + +void ON_GroundPlane::InvalidateCache(void) { - m_impl->SetParameter(XMLPath(), ON_RDK_GP_TEXTURE_ROTATION, d); } diff --git a/opennurbs_ground_plane.h b/opennurbs_ground_plane.h index 7b3da12d..1b656068 100644 --- a/opennurbs_ground_plane.h +++ b/opennurbs_ground_plane.h @@ -14,88 +14,101 @@ #if !defined(ON_GROUND_PLANE_INC_) #define ON_GROUND_PLANE_INC_ -class ON_CLASS ON_GroundPlane final +class ON_CLASS ON_GroundPlane { public: ON_GroundPlane(); ON_GroundPlane(ON_XMLNode& model_node); ON_GroundPlane(const ON_GroundPlane& gp); - ~ON_GroundPlane(); + virtual ~ON_GroundPlane(); - const ON_GroundPlane& operator = (const ON_GroundPlane& gp); + virtual const ON_GroundPlane& operator = (const ON_GroundPlane& gp); - bool operator == (const ON_GroundPlane& gp); - bool operator != (const ON_GroundPlane& gp); + virtual bool operator == (const ON_GroundPlane& gp) const; + virtual bool operator != (const ON_GroundPlane& gp) const; // Returns true if the ground plane is enabled, else false. - bool On(void) const; + virtual bool On(void) const; // Returns true if ground plane backface is enabled, else false. - bool ShowUnderside(void) const; + virtual bool ShowUnderside(void) const; - // Returns the altitude of the ground plane. - double Altitude(void) const; + // Returns the altitude of the ground plane. Note that this merely returns the stored value. It does + // not do any auto-altitude computation. Auto-altitude must be computed by a subclass (e.g., in Rhino). + virtual double Altitude(void) const; // Returns true if auto-altitude is enabled. - bool AutoAltitude(void) const; + virtual bool AutoAltitude(void) const; // Returns true if the ground plane is set to shadow-only. - bool ShadowOnly(void) const; + virtual bool ShadowOnly(void) const; // Returns the instance id of the ground plane's material. - ON_UUID MaterialInstanceId(void) const; + virtual ON_UUID MaterialInstanceId(void) const; // Returns the texture offset of the ground plane in model units. - ON_2dVector TextureOffset(void) const; + virtual ON_2dVector TextureOffset(void) const; // Returns true if the texture offset x and y are locked together. - bool TextureOffsetLocked(void) const; - - // Returns true if the texture repeat x and y are locked together. - bool TextureRepeatLocked(void) const; + virtual bool TextureOffsetLocked(void) const; // Returns the texture size of the ground plane in model units. - ON_2dVector TextureSize(void) const; + virtual ON_2dVector TextureSize(void) const; + + // Returns true if the texture size x and y are locked together. + virtual bool TextureSizeLocked(void) const; // Returns the texture rotation of the ground plane in degrees. - double TextureRotation(void) const; + virtual double TextureRotation(void) const; // Set the ground plane enabled state. - void SetOn(bool b); + virtual void SetOn(bool on); // Set if the ground plane backface is enabled. - void SetShowUnderside(bool b); + virtual void SetShowUnderside(bool on); // Set the ground plane's altitude. - void SetAltitude(double d); + virtual void SetAltitude(double altitude); // Set if the ground plane is set to auto-altitude. - void SetAutoAltitude(bool b); + virtual void SetAutoAltitude(bool on); // Set if the ground plane is set to shadow-only. - void SetShadowOnly(bool b); + virtual void SetShadowOnly(bool on); // Set the instance id of the ground plane's material. - void SetMaterialInstanceId(const ON_UUID& uuid); + virtual void SetMaterialInstanceId(const ON_UUID& uuid); // Set the texture offset of the ground plane in model units. - void SetTextureOffset(const ON_2dVector& vec); + virtual void SetTextureOffset(const ON_2dVector& vec); // Set if the texture offset x and y are locked together. - void SetTextureOffsetLocked(bool b); - - // Set if the texture repeat x and y are locked together. - void SetTextureRepeatLocked(bool b); + virtual void SetTextureOffsetLocked(bool locked); // Set the texture size of the ground plane in model units. - void SetTextureSize(const ON_2dVector& vec); + virtual void SetTextureSize(const ON_2dVector& vec); + + // Set if the texture size x and y are locked together. + virtual void SetTextureSizeLocked(bool locked); // Set the texture rotation of the ground plane in degrees. - void SetTextureRotation(double d); + virtual void SetTextureRotation(double angle); + + // This method populates an ON_Material with default settings and returns true. This is designed + // to be overriden by a subclass that can return a more interesting material. + virtual bool PopulateMaterial(ON_Material& mat) const; + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +private: // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + void SetXMLNode(ON_XMLNode& node) const; + virtual void InvalidateCache(void); private: class CImpl; - CImpl* m_impl; + CImpl* _impl; }; #endif diff --git a/opennurbs_internal_defines.h b/opennurbs_internal_defines.h index b4d5bacb..c78e05fd 100644 --- a/opennurbs_internal_defines.h +++ b/opennurbs_internal_defines.h @@ -42,7 +42,6 @@ ON__INT64 Integerize(float dirty); ON__INT64 Integerize(double dirty); void SetModel(const class ON_RenderContent&, ONX_Model&); -void SetModel(const class ON_PostEffect&, ONX_Model&); ON_3dmObjectAttributes* GetComponentAttributes(const ON_ModelComponent& component); ON_RenderContent* NewRenderContentFromNode(const class ON_XMLNode& node); ON_PostEffect* NewPostEffectFromNode(ON_XMLNode& node); @@ -63,9 +62,15 @@ template inline T Lerp(double t, const T& l, const T& h) { return l + class ON_InternalXMLImpl { public: - ON_InternalXMLImpl(ON_XMLNode* n=nullptr) : m_model_node(n) { } + ON_InternalXMLImpl(ON_XMLNode* n=nullptr) : _model_node(n) { } + ON_InternalXMLImpl(const ON_InternalXMLImpl&) = delete; virtual ~ON_InternalXMLImpl(); + const ON_InternalXMLImpl& operator = (const ON_InternalXMLImpl&) = delete; + + bool operator == (const ON_InternalXMLImpl&) const = delete; + bool operator != (const ON_InternalXMLImpl&) const = delete; + ON_XMLVariant GetParameter(const wchar_t* path_to_node, const wchar_t* param_name, const ON_XMLVariant& def) const; bool SetParameter(const wchar_t* path_to_node, const wchar_t* param_name, const ON_XMLVariant& value); @@ -77,6 +82,8 @@ public: ON_XMLNode& Node(void) const; + void SetModelNode(ON_XMLNode& node); + protected: virtual ON_wString NameOfRootNode(void) const; @@ -85,9 +92,9 @@ private: bool InternalSetParameter(const wchar_t* path_to_node, const wchar_t* param_name, bool write_type, const ON_XMLVariant& value); public: - mutable std::recursive_mutex m_mutex; - mutable ON_XMLNode* m_local_node = nullptr; // Used when m_model_node is null. - ON_XMLNode* m_model_node; + mutable std::recursive_mutex _mutex; + mutable ON_XMLNode* _local_node = nullptr; // Used when m_model_node is null. + ON_XMLNode* _model_node; }; class ON_DecalCollection final @@ -143,27 +150,27 @@ public: } }; -class ON_EnvironmentsPrivate final : public ON_InternalXMLImpl +class ON_EnvironmentsImpl final : public ON_InternalXMLImpl { public: - ON_EnvironmentsPrivate() { } - ON_EnvironmentsPrivate(ON_XMLNode& n) : ON_InternalXMLImpl(&n) { } - ON_EnvironmentsPrivate(const ON_EnvironmentsPrivate&); + ON_EnvironmentsImpl() { } + ON_EnvironmentsImpl(ON_XMLNode& n) : ON_InternalXMLImpl(&n) { } + ON_EnvironmentsImpl(const ON_EnvironmentsImpl&); - ON_EnvironmentsPrivate& operator = (const ON_EnvironmentsPrivate&); + ON_EnvironmentsImpl& operator = (const ON_EnvironmentsImpl&); - bool operator == (const ON_EnvironmentsPrivate&); + bool operator == (const ON_EnvironmentsImpl&); - ON_UUID BackgroundRenderEnvironment(void) const; - void SetBackgroundRenderEnvironment(const ON_UUID& id); + ON_UUID BackgroundRenderEnvironmentId(void) const; + void SetBackgroundRenderEnvironmentId(const ON_UUID& id); bool SkylightingRenderEnvironmentOverride(void) const; void SetSkylightingRenderEnvironmentOverride(bool on); - ON_UUID SkylightingRenderEnvironment(void) const; - void SetSkylightingRenderEnvironment(const ON_UUID& id); + ON_UUID SkylightingRenderEnvironmentId(void) const; + void SetSkylightingRenderEnvironmentId(const ON_UUID& id); bool ReflectionRenderEnvironmentOverride(void) const; void SetReflectionRenderEnvironmentOverride(bool on); - ON_UUID ReflectionRenderEnvironment(void) const; - void SetReflectionRenderEnvironment(const ON_UUID& id); + ON_UUID ReflectionRenderEnvironmentId(void) const; + void SetReflectionRenderEnvironmentId(const ON_UUID& id); }; class ON_3dmRenderSettingsPrivate final @@ -171,21 +178,38 @@ class ON_3dmRenderSettingsPrivate final public: ON_3dmRenderSettingsPrivate(); ON_3dmRenderSettingsPrivate(const ON_3dmRenderSettingsPrivate&); + virtual ~ON_3dmRenderSettingsPrivate(); const ON_3dmRenderSettingsPrivate& operator = (const ON_3dmRenderSettingsPrivate&); void SetToDefaults(void); + void SpecializeGroundPlane(ON_GroundPlane& gp); + void SpecializeLinearWorkflow(ON_LinearWorkflow& lw); + void SpecializeSun(ON_SunEx& sun); + + static ON_3dmRenderSettingsPrivate& Get(const ON_3dmRenderSettings& rs); + +private: + void CreateDocumentObjects(void); + public: ON_XMLRootNode _rdk_document_data; - ON_Dithering _dithering; - ON_GroundPlane _ground_plane; - ON_LinearWorkflow _linear_workflow; - ON_RenderChannels _render_channels; - ON_SafeFrame _safe_frame; - ON_Skylight _skylight; - ON_Sun _sun; - ON_EnvironmentsPrivate _environments; + + // Document objects. The pointers to these objects are never null because + // they are created in every constructor. See CreateDocumentObjects(). + ON_GroundPlane* _ground_plane = nullptr; + ON_Dithering* _dithering = nullptr; + ON_SafeFrame* _safe_frame = nullptr; + ON_Skylight* _skylight = nullptr; + ON_LinearWorkflow* _linear_workflow = nullptr; + ON_RenderChannels* _render_channels = nullptr; + ON_SunEx* _sun = nullptr; + ON_EnvironmentsImpl* _environments; + ON_PostEffects* _post_effects; + bool _gp_specialized = false; + bool _lw_specialized = false; + bool _sun_specialized = false; }; //-------------------------------------------------------------------------------------------------- diff --git a/opennurbs_layer.cpp b/opennurbs_layer.cpp index fb278d5f..65c74517 100644 --- a/opennurbs_layer.cpp +++ b/opennurbs_layer.cpp @@ -33,12 +33,9 @@ public: ON_UuidList m_clipplane_list; bool m_clipping_proof = false; - ON::SectionFillRule m_section_fill_rule = ON::SectionFillRule::ClosedCurves; - 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_section_style; - std::shared_ptr m_custom_linetype; + bool m_visible_in_new_details = true; }; static const ON_LayerPrivate DefaultLayerPrivate; @@ -55,21 +52,21 @@ bool ON_LayerPrivate::operator==(const ON_LayerPrivate& other) const if (m_clipping_proof != other.m_clipping_proof) return false; - if (m_section_fill_rule != other.m_section_fill_rule) - return false; + { + const ON_SectionStyle* customThis = m_custom_section_style.get(); + const ON_SectionStyle* customOther = other.m_custom_section_style.get(); + if (nullptr == customThis && customOther) + return false; + if (customThis && nullptr == customOther) + return false; + if (customThis && customOther) + { + if ((*customThis) != (*customOther)) + return false; + } + } - if (m_section_hatch_index != other.m_section_hatch_index) - return false; - - if (m_section_hatch_scale != other.m_section_hatch_scale) - return false; - - 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) + if (m_visible_in_new_details != other.m_visible_in_new_details) return false; return true; @@ -291,14 +288,14 @@ void ON_Layer::Dump( ON_TextLog& dump ) const } } - int index = SectionHatchIndex(); - if (ON_UNSET_INT_INDEX == index) + const ON_SectionStyle* section_style = CustomSectionStyle(); + if (nullptr == section_style) { - dump.Print("no section hatch\n"); + dump.Print("No custom section style\n"); } else { - dump.Print("section hatch index = %d\n", index); + dump.Print("Has custome section style\n"); } } @@ -322,8 +319,14 @@ enum ON_LayerTypeCodes : unsigned char // 30 Nov 2022 S. Baer // chunk version 1.13: add custom linetype CustomLinetype = 33, + // 4 Apr 2023 D. Fugier + // chunk version 1.14: add visible in new detail + PerViewportIsVisibleInNewDetails = 34, + // 22 Apr 2023 S. Baer + // chunk version 1.15: custom section style + CustomSectionStyle = 35, - LastLayerTypeCode = 33 + LastLayerTypeCode = 35 }; bool ON_Layer::Write( @@ -332,7 +335,7 @@ bool ON_Layer::Write( { int i; - bool rc = file.Write3dmChunkVersion(1,13); + bool rc = file.Write3dmChunkVersion(1,15); while(rc) { // Save the visibility state this layer has when its parent @@ -497,43 +500,48 @@ bool ON_Layer::Write( } } + // 23 April 2023 S. Baer + // Stop writing individual section attributes. All section attribute IO has been + // moved to writing an ON_SectionStyle instance // 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; - } - } + const ON_SectionStyle* section_style = CustomSectionStyle(); + //if (section_style) + //{ + // if (section_style->HatchIndex() != ON_UNSET_INT_INDEX) + // { + // c = ON_LayerTypeCodes::SectionHatchIndex; + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern, section_style->HatchIndex()); + // if (!rc) break; + // } + // if (section_style->HatchScale() != 1.0) + // { + // c = ON_LayerTypeCodes::SectionHatchScale; + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.WriteDouble(section_style->HatchScale()); + // if (!rc) break; + // } + // if (section_style->HatchRotation() != 0.0) + // { + // c = ON_LayerTypeCodes::SectionHatchRotation; + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.WriteDouble(section_style->HatchRotation()); + // if (!rc) break; + // } - // section fill (1.12) - if (SectionFillRule() != ON::SectionFillRule::ClosedCurves) - { - c = ON_LayerTypeCodes::SectionFillRule; //32 - rc = file.WriteChar(c); - if (!rc) break; - rc = file.WriteChar((unsigned char)SectionFillRule()); - if (!rc) break; - } + // // section fill (1.12) + // if (section_style->SectionFillRule() != ON::SectionFillRule::ClosedCurves) + // { + // c = ON_LayerTypeCodes::SectionFillRule; //32 + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = file.WriteChar((unsigned char)section_style->SectionFillRule()); + // if (!rc) break; + // } + //} // custom linetype (1.13) // 17 Feb 2023 S. Baer @@ -551,6 +559,25 @@ bool ON_Layer::Write( // } //} + // visible in new detail (1.14) + if (PerViewportIsVisibleInNewDetails() != DefaultLayerPrivate.m_visible_in_new_details) + { + c = ON_LayerTypeCodes::PerViewportIsVisibleInNewDetails; //34 + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteBool(PerViewportIsVisibleInNewDetails()); + if (!rc) break; + } + + if (section_style) + { + c = ON_LayerTypeCodes::CustomSectionStyle; //35 + rc = file.WriteChar(c); + if (!rc) break; + rc = section_style->Write(file); + if (!rc) break; + } + // 0 indicates end of new non-default attributes c = 0; rc = file.WriteChar(c); @@ -763,7 +790,10 @@ bool ON_Layer::Read( int pattern = 0; rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern, &pattern); if (!rc) break; - SetSectionHatchIndex(pattern); + ON_SectionStyle section_style; + CustomSectionStyle(§ion_style); + section_style.SetHatchIndex(pattern); + SetCustomSectionStyle(section_style); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -773,7 +803,10 @@ bool ON_Layer::Read( double scale = 1; rc = file.ReadDouble(&scale); if (!rc) break; - SetSectionHatchScale(scale); + ON_SectionStyle section_style; + CustomSectionStyle(§ion_style); + section_style.SetHatchScale(scale); + SetCustomSectionStyle(section_style); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -783,7 +816,10 @@ bool ON_Layer::Read( double rotation = 0; rc = file.ReadDouble(&rotation); if (!rc) break; - SetSectionHatchRotation(rotation); + ON_SectionStyle section_style; + CustomSectionStyle(§ion_style); + section_style.SetHatchRotation(rotation); + SetCustomSectionStyle(section_style); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -796,7 +832,10 @@ bool ON_Layer::Read( unsigned char c = 0; rc = file.ReadChar(&c); if (!rc) break; - SetSectionFillRule(ON::SectionFillRuleFromUnsigned(c)); + ON_SectionStyle section_style; + CustomSectionStyle(§ion_style); + section_style.SetSectionFillRule(ON::SectionFillRuleFromUnsigned(c)); + SetCustomSectionStyle(section_style); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -818,12 +857,39 @@ bool ON_Layer::Read( 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; + // visible in new detail (1.14) + if (ON_LayerTypeCodes::PerViewportIsVisibleInNewDetails == itemid) + { + bool b = true; + rc = file.ReadBool(&b); + if (!rc) break; + SetPerViewportIsVisibleInNewDetails(b); + rc = file.ReadChar(&itemid); + if (!rc || 0 == itemid) break; + } + + if (minor_version <= 14) + break; + + if (ON_LayerTypeCodes::CustomSectionStyle == itemid) + { + ON_SectionStyle section_style; + rc = section_style.Read(file); + if (!rc) break; + SetCustomSectionStyle(section_style); + rc = file.ReadChar(&itemid); + if (!rc || 0 == itemid) break; + } + + // break if minor_version<=15. If itemid is non-zero and + // minor_version is not > 15, then we know we have an I/O + // reading bug that needs to be tracked down + if (minor_version <= 15) + 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. @@ -2457,55 +2523,42 @@ void ON_Layer::GetClipParticipation( } } -ON::SectionFillRule ON_Layer::SectionFillRule() const +void ON_Layer::SetCustomSectionStyle(const ON_SectionStyle& sectionStyle) { - return m_private ? m_private->m_section_fill_rule : DefaultLayerPrivate.m_section_fill_rule; + if (nullptr == m_private) + m_private = new ON_LayerPrivate(); + + m_private->m_custom_section_style.reset(new ON_SectionStyle(sectionStyle)); } -void ON_Layer::SetSectionFillRule(ON::SectionFillRule rule) +const ON_SectionStyle* ON_Layer::CustomSectionStyle(ON_SectionStyle* sectionStyle) const { - if (SectionFillRule() == rule) + const ON_SectionStyle* rc = nullptr; + if (m_private) + rc = m_private->m_custom_section_style.get(); + + if (sectionStyle && rc) + { + *sectionStyle = *rc; + } + + return rc; +} +void ON_Layer::RemoveCustomSectionStyle() +{ + if (m_private) + m_private->m_custom_section_style.reset(); +} + +bool ON_Layer::PerViewportIsVisibleInNewDetails() const +{ + return m_private ? m_private->m_visible_in_new_details : DefaultLayerPrivate.m_visible_in_new_details; +} + +void ON_Layer::SetPerViewportIsVisibleInNewDetails(bool bVisible) +{ + if (PerViewportIsVisibleInNewDetails() == bVisible) return; if (nullptr == m_private) m_private = new ON_LayerPrivate(); - m_private->m_section_fill_rule = rule; -} - -int ON_Layer::SectionHatchIndex() const -{ - return m_private ? m_private->m_section_hatch_index : DefaultLayerPrivate.m_section_hatch_index; -} -void ON_Layer::SetSectionHatchIndex(int index) -{ - if (SectionHatchIndex() == index) - return; - if (nullptr == m_private) - m_private = new ON_LayerPrivate(); - m_private->m_section_hatch_index = index; -} - -double ON_Layer::SectionHatchScale() const -{ - return m_private ? m_private->m_section_hatch_scale : DefaultLayerPrivate.m_section_hatch_scale; -} -void ON_Layer::SetSectionHatchScale(double scale) -{ - if (SectionHatchScale() == scale) - return; - if (nullptr == m_private) - m_private = new ON_LayerPrivate(); - m_private->m_section_hatch_scale = scale; -} - -double ON_Layer::SectionHatchRotation() const -{ - return m_private ? m_private->m_section_hatch_rotation : DefaultLayerPrivate.m_section_hatch_rotation; -} -void ON_Layer::SetSectionHatchRotation(double rotation) -{ - if (SectionHatchRotation() == rotation) - return; - if (nullptr == m_private) - m_private = new ON_LayerPrivate(); - m_private->m_section_hatch_rotation = rotation; -} - + m_private->m_visible_in_new_details = bVisible; +} \ No newline at end of file diff --git a/opennurbs_layer.h b/opennurbs_layer.h index ba2fd28a..5d886b1c 100644 --- a/opennurbs_layer.h +++ b/opennurbs_layer.h @@ -818,25 +818,36 @@ public: // this intersection can result in curves as well as hatches for the // closed curves generated - // When to fill/hatch the sections for an object can depend on the type of - // object being sectioned. See ON_SectionFillDecision for the choices of - // when to generate hatches. - ON::SectionFillRule SectionFillRule() const; - void SetSectionFillRule(ON::SectionFillRule rule); + /* + Description: + Layers can have optional custom section style associated with them. + This function adds a custom section style for this layer. + */ + void SetCustomSectionStyle(const ON_SectionStyle& sectionStyle); - // Hatch pattern index for hatch to use when drawing a closed section - // Default is ON_UNSET_INT_INDEX which means don't draw a hatch - int SectionHatchIndex() const; - void SetSectionHatchIndex(int index); + /* + Description: + Layers can have optional custom section styles associated with them. + This function returns the custom section style if one exists. + Parameters: + sectionStyle [out] - if not nullptr and a custom section style exists, + the data in the custom section style is copied to sectionStyle + */ + const ON_SectionStyle* CustomSectionStyle(ON_SectionStyle* sectionStyle = nullptr) const; - // Scale applied to the hatch pattern for a section - double SectionHatchScale() const; - void SetSectionHatchScale(double scale); + /* + Description: + Remove any custom section style associated with this layer + */ + void RemoveCustomSectionStyle(); - // Rotation angle in radians applied to hatch pattern for a section. - double SectionHatchRotation() const; - void SetSectionHatchRotation(double rotation); -#pragma endregion + /* + Description: + Returns true if a layer's per-viewport visiblity property will be true, + initially, in newly created detail views. + */ + bool PerViewportIsVisibleInNewDetails() const; + void SetPerViewportIsVisibleInNewDetails(bool bVisible); private: // The following information may not be accurate and is subject diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp index f1d9a072..2fc10cdb 100644 --- a/opennurbs_line.cpp +++ b/opennurbs_line.cpp @@ -211,6 +211,12 @@ bool ON_Line::Translate( return Transform( tr ); } + +double ON_Line::MaximumCoordinate() const +{ + return fmax( from.MaximumCoordinate(), to.MaximumCoordinate()); +} + int ON_ArePointsOnLine( // returns 0=no, 1 = yes, 2 = pointset is (to tolerance) a single point on the line int dim, // 2 or 3 bool is_rat, diff --git a/opennurbs_line.h b/opennurbs_line.h index 4af7eb81..5cd3a14b 100644 --- a/opennurbs_line.h +++ b/opennurbs_line.h @@ -277,6 +277,9 @@ public: const ON_3dVector& delta ); + // Maximum absolute coordinate of from and to. + double MaximumCoordinate() const; + public: ON_3dPoint from; // start point diff --git a/opennurbs_linear_workflow.cpp b/opennurbs_linear_workflow.cpp index 19fb95ad..77dd11b7 100644 --- a/opennurbs_linear_workflow.cpp +++ b/opennurbs_linear_workflow.cpp @@ -36,145 +36,160 @@ static const wchar_t* XMLPath(void) ON_LinearWorkflow::ON_LinearWorkflow() { - m_impl = new CImpl; + _impl = new CImpl; } ON_LinearWorkflow::ON_LinearWorkflow(ON_XMLNode& model_node) { - m_impl = new CImpl(model_node); + _impl = new CImpl(model_node); } ON_LinearWorkflow::ON_LinearWorkflow(const ON_LinearWorkflow& lw) { - m_impl = new CImpl; + _impl = new CImpl; operator = (lw); } ON_LinearWorkflow::~ON_LinearWorkflow() { - delete m_impl; - m_impl = nullptr; + delete _impl; + _impl = nullptr; } const ON_LinearWorkflow& ON_LinearWorkflow::operator = (const ON_LinearWorkflow& lw) { if (this != &lw) { - SetPreProcessTextures(lw.PreProcessTextures()); - SetPreProcessColors (lw.PreProcessColors()); - SetPreProcessGamma (lw.PreProcessGamma()); - SetPostProcessGamma (lw.PostProcessGamma()); - SetPostProcessGammaOn(lw.PostProcessGammaOn()); + // When copying the object, we need to directly copy the underlying XML. So we can't allow + // virtual overrides to execute because they might shadow the real values we want to copy. + using LW = ON_LinearWorkflow; + LW::SetPreProcessTexturesOn (lw.LW::PreProcessTexturesOn()); + LW::SetPreProcessColorsOn (lw.LW::PreProcessColorsOn()); + LW::SetPostProcessFrameBufferOn(lw.LW::PostProcessFrameBufferOn()); + LW::SetPreProcessGammaOn (lw.LW::PreProcessGammaOn()); + LW::SetPostProcessGammaOn (lw.LW::PostProcessGammaOn()); + LW::SetPreProcessGamma (lw.LW::PreProcessGamma()); + LW::SetPostProcessGamma (lw.LW::PostProcessGamma()); } return *this; } -bool ON_LinearWorkflow::operator == (const ON_LinearWorkflow& lw) +bool ON_LinearWorkflow::operator == (const ON_LinearWorkflow& lw) const { - if (PreProcessTextures() != lw.PreProcessTextures()) return false; - if (PreProcessColors() != lw.PreProcessColors()) return false; - if (PostProcessGammaOn() != lw.PostProcessGammaOn()) return false; - if (!IsFloatEqual(PreProcessGamma(), lw.PreProcessGamma())) return false; - if (!IsFloatEqual(PostProcessGamma(), lw.PostProcessGamma())) return false; + // When checking equality, we need to directly check the underlying XML. So we can't allow + // virtual overrides to execute because they might shadow the real values we want to check. + using LW = ON_LinearWorkflow; + if (LW::PreProcessTexturesOn() != lw.LW::PreProcessTexturesOn()) return false; + if (LW::PreProcessColorsOn() != lw.LW::PreProcessColorsOn()) return false; + if (LW::PostProcessFrameBufferOn() != lw.LW::PostProcessFrameBufferOn()) return false; + if (LW::PreProcessGammaOn() != lw.LW::PreProcessGammaOn()) return false; + if (LW::PostProcessGammaOn() != lw.LW::PostProcessGammaOn()) return false; + if (!IsFloatEqual(LW::PreProcessGamma(), lw.LW::PreProcessGamma())) return false; + if (!IsFloatEqual(LW::PostProcessGamma(), lw.LW::PostProcessGamma())) return false; return true; } -bool ON_LinearWorkflow::operator != (const ON_LinearWorkflow& sf) +bool ON_LinearWorkflow::operator != (const ON_LinearWorkflow& lw) const { - return !(operator == (sf)); + return !(operator == (lw)); } -bool ON_LinearWorkflow::PreProcessTextures(void) const +bool ON_LinearWorkflow::PreProcessTexturesOn(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_USE_LINEAR_WORKFLOW, false); + return _impl->GetParameter(XMLPath(), ON_RDK_PRE_PROCESS_GAMMA_ON, false); } -void ON_LinearWorkflow::SetPreProcessTextures(bool b) +void ON_LinearWorkflow::SetPreProcessTexturesOn(bool b) { - m_impl->SetParameter(XMLPath(), ON_RDK_USE_LINEAR_WORKFLOW, b); + _impl->SetParameter(XMLPath(), ON_RDK_PRE_PROCESS_GAMMA_ON, b); } -bool ON_LinearWorkflow::PreProcessColors(void) const +bool ON_LinearWorkflow::PreProcessColorsOn(void) const { - return PreProcessTextures(); + return _impl->GetParameter(XMLPath(), ON_RDK_PRE_PROCESS_GAMMA_ON, false); } -void ON_LinearWorkflow::SetPreProcessColors(bool b) +void ON_LinearWorkflow::SetPreProcessColorsOn(bool b) { - return SetPreProcessTextures(b); + _impl->SetParameter(XMLPath(), ON_RDK_PRE_PROCESS_GAMMA_ON, b); } -float ON_LinearWorkflow::PreProcessGamma(void) const +bool ON_LinearWorkflow::PreProcessGammaOn(void) const { - const float f = m_impl->GetParameter(XMLPath(), ON_RDK_GAMMA, 2.2f).AsFloat(); - return std::min(5.0f, std::max(0.2f, f)); + return _impl->GetParameter(XMLPath(), ON_RDK_PRE_PROCESS_GAMMA_ON, true); } -void ON_LinearWorkflow::SetPreProcessGamma(float gamma) +void ON_LinearWorkflow::SetPreProcessGammaOn(bool on) { - const float f = std::min(5.0f, std::max(0.2f, gamma)); - m_impl->SetParameter(XMLPath(), ON_RDK_GAMMA, f); + _impl->SetParameter(XMLPath(), ON_RDK_PRE_PROCESS_GAMMA_ON, on); } bool ON_LinearWorkflow::PostProcessGammaOn(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_USE_POST_PROCESS_GAMMA, true); + return _impl->GetParameter(XMLPath(), ON_RDK_POST_PROCESS_GAMMA_ON, true); } void ON_LinearWorkflow::SetPostProcessGammaOn(bool on) { - m_impl->SetParameter(XMLPath(), ON_RDK_USE_POST_PROCESS_GAMMA, on); + _impl->SetParameter(XMLPath(), ON_RDK_POST_PROCESS_GAMMA_ON, on); } -bool ON_LinearWorkflow::PostProcessFrameBuffer(void) const +bool ON_LinearWorkflow::PostProcessFrameBufferOn(void) const { return true; // Always on. For possible future use. } -void ON_LinearWorkflow::SetPostProcessFrameBuffer(bool) +void ON_LinearWorkflow::SetPostProcessFrameBufferOn(bool) { // Always on. Ignore the call. } +static float ClampGamma(float f) { return std::min(5.0f, std::max(0.2f, f)); } + +float ON_LinearWorkflow::PreProcessGamma(void) const +{ + return ON_LinearWorkflow::PostProcessGamma(); +} + +void ON_LinearWorkflow::SetPreProcessGamma(float gamma) +{ + ON_LinearWorkflow::SetPostProcessGamma(gamma); +} + float ON_LinearWorkflow::PostProcessGamma(void) const { - return PreProcessGamma(); + return ClampGamma(_impl->GetParameter(XMLPath(), ON_RDK_POST_PROCESS_GAMMA, 2.2f)); } void ON_LinearWorkflow::SetPostProcessGamma(float gamma) { - SetPreProcessGamma(gamma); -} - -float ON_LinearWorkflow::PostProcessGammaReciprocal(void) const -{ - return 1.0f / PostProcessGamma(); + _impl->SetParameter(XMLPath(), ON_RDK_POST_PROCESS_GAMMA, ClampGamma(gamma)); } void ON_LinearWorkflow::ApplyPreProcessGamma(ON_4fColor& col, bool for_texture) const { - const bool check = for_texture ? PreProcessTextures() : PreProcessColors(); + const bool check = for_texture ? PreProcessTexturesOn() : PreProcessColorsOn(); if (!check) return; - const float gamma = PreProcessGamma(); - if (!IsFloatEqual(gamma, 1.0f)) - { - float* f = col.FloatArray(); + const float gamma = PreProcessGamma(); + if (!IsFloatEqual(gamma, 1.0f)) + { + float* f = col.FloatArray(); - ON_ASSERT((f[0] >= 0.0) && (f[1] >= 0.0) && (f[2] >= 0.0)); + ON_ASSERT((f[0] >= 0.0) && (f[1] >= 0.0) && (f[2] >= 0.0)); - if (f[0] > 0.0) f[0] = powf(f[0], gamma); - if (f[1] > 0.0) f[1] = powf(f[1], gamma); - if (f[2] > 0.0) f[2] = powf(f[2], gamma); - } + if (f[0] > 0.0) f[0] = powf(f[0], gamma); + if (f[1] > 0.0) f[1] = powf(f[1], gamma); + if (f[2] > 0.0) f[2] = powf(f[2], gamma); + } } ON__UINT32 ON_LinearWorkflow::DataCRC(ON__UINT32 crc) const { - bool b[] = { PreProcessTextures(), PostProcessGammaOn(), PreProcessColors() }; + bool b[] = { PreProcessTexturesOn(), PreProcessColorsOn(), PostProcessFrameBufferOn(), PostProcessGammaOn() }; crc = ON_CRC32(crc, sizeof(b), b); ON__INT64 f[] = { Integerize(PreProcessGamma()), Integerize(PostProcessGamma()) }; @@ -182,3 +197,17 @@ ON__UINT32 ON_LinearWorkflow::DataCRC(ON__UINT32 crc) const return crc; } + +void ON_LinearWorkflow::SetXMLNode(ON_XMLNode& node) const +{ + _impl->SetModelNode(node); +} + +void* ON_LinearWorkflow::EVF(const wchar_t* func, void* data) +{ + return nullptr; +} + +void ON_LinearWorkflow::InvalidateCache(void) +{ +} diff --git a/opennurbs_linear_workflow.h b/opennurbs_linear_workflow.h index d537284c..5ccba899 100644 --- a/opennurbs_linear_workflow.h +++ b/opennurbs_linear_workflow.h @@ -14,72 +14,80 @@ #if !defined(ON_LINEAR_WORKFLOW_INC_) #define ON_LINEAR_WORKFLOW_INC_ -class ON_CLASS ON_LinearWorkflow final +class ON_CLASS ON_LinearWorkflow { public: ON_LinearWorkflow(); ON_LinearWorkflow(ON_XMLNode& model_node); ON_LinearWorkflow(const ON_LinearWorkflow& lw); - ~ON_LinearWorkflow(); + virtual ~ON_LinearWorkflow(); - const ON_LinearWorkflow& operator = (const ON_LinearWorkflow& lw); + virtual const ON_LinearWorkflow& operator = (const ON_LinearWorkflow& lw); - bool operator == (const ON_LinearWorkflow& lw); - bool operator != (const ON_LinearWorkflow& lw); + virtual bool operator == (const ON_LinearWorkflow& lw) const; + virtual bool operator != (const ON_LinearWorkflow& lw) const; - // Returns the linear workflow active state for textures. - bool PreProcessTextures(void) const; + // Returns the linear workflow (pre-process) active state for textures. + virtual bool PreProcessTexturesOn(void) const; // Sets the linear workflow (pre-process) active state for textures. - void SetPreProcessTextures(bool b); + virtual void SetPreProcessTexturesOn(bool b); // Returns the linear workflow active state for individual colors. - bool PreProcessColors(void) const; + virtual bool PreProcessColorsOn(void) const; // Sets the linear workflow (pre-process) active state for individual colors. - void SetPreProcessColors(bool b); - - // Returns the pre-process gamma for input textures and colors. - float PreProcessGamma(void) const; - - // Set pre-process gamma. This will generally be >= 1.0 (usually 2.2). - // This is the actual value applied in pre-process. - void SetPreProcessGamma(float gamma); - - // Get post-process gamma enabled state. - bool PostProcessGammaOn(void) const; - - // Set post-process gamma enabled state. - void SetPostProcessGammaOn(bool on); + virtual void SetPreProcessColorsOn(bool b); // Get post-process frame buffer enabled state. - bool PostProcessFrameBuffer(void) const; + virtual bool PostProcessFrameBufferOn(void) const; // Set post-process frame buffer enabled state. - void SetPostProcessFrameBuffer(bool on); + virtual void SetPostProcessFrameBufferOn(bool on); - // Returns the post-process gamma for frame buffer. This is not the value applied; it's the value that - // appears in the UI. See PostProcessGammaReciprocal(). - float PostProcessGamma(void) const; + // Get pre-process gamma enabled state. This is the same as 'use linear workflow'. + virtual bool PreProcessGammaOn(void) const; - // Set post-process gamma. This will generally be >= 1.0 (usually 2.2). - // The actual value to be applied in post-process is the reciprocal. - void SetPostProcessGamma(float gamma); + // Set pre-process gamma enabled state. This is the same as 'use linear workflow'. + virtual void SetPreProcessGammaOn(bool on); - // Returns: Gamma reciprocal for frame buffer. This is the value that is applied to each color channel - // for post-process and is supplied here as an optimization. - float PostProcessGammaReciprocal(void) const; + // Get the pre-process gamma for input textures and colors. This is currently the same as the post-process gamma value. + virtual float PreProcessGamma(void) const; + + // Set the pre-process gamma for input textures and colors. This is currently the same as the post-process gamma value. + virtual void SetPreProcessGamma(float gamma); + + // Get post-process gamma enabled state. + virtual bool PostProcessGammaOn(void) const; + + // Set post-process gamma enabled state. + virtual void SetPostProcessGammaOn(bool on); + + // Get the post-process gamma for the frame buffer. + virtual float PostProcessGamma(void) const; + + // Set the post-process gamma for the frame buffer. + virtual void SetPostProcessGamma(float gamma); // Applies pre-process gamma correction to a color if linear workflow is active. // for_texture is true if the color is part of a texture. */ - void ApplyPreProcessGamma(ON_4fColor& col, bool for_texture) const; + virtual void ApplyPreProcessGamma(ON_4fColor& col, bool for_texture) const; // Returns the CRC of gamma and linear workflow active state. - ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +private: + // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + void SetXMLNode(ON_XMLNode& node) const; + virtual void InvalidateCache(void); private: class CImpl; - CImpl* m_impl; + CImpl* _impl; }; #endif diff --git a/opennurbs_linetype.cpp b/opennurbs_linetype.cpp index d24fb18c..99de9ade 100644 --- a/opennurbs_linetype.cpp +++ b/opennurbs_linetype.cpp @@ -765,3 +765,9 @@ void ON_Linetype::RemoveTaper() { m_private->m_taper_points.Empty(); } + +void ON_Linetype::ClearBits() +{ + m_is_set_bits = 0; + m_is_locked_bits = 0; +} diff --git a/opennurbs_linetype.h b/opennurbs_linetype.h index 3f85c4d2..bc4c122a 100644 --- a/opennurbs_linetype.h +++ b/opennurbs_linetype.h @@ -274,6 +274,12 @@ public: */ void RemoveTaper(); + /* + Description: + For expert use only. + */ + void ClearBits(); + private: mutable class ON_LinetypePrivate* m_private = nullptr; unsigned char m_is_set_bits = 0; diff --git a/opennurbs_math.cpp b/opennurbs_math.cpp index 2d0c4d31..34597fd2 100644 --- a/opennurbs_math.cpp +++ b/opennurbs_math.cpp @@ -876,6 +876,54 @@ ON_EvCurvature( return rc; } + +bool +ON_EvCurvature1Der( + const ON_3dVector& D1, // first derivative + const ON_3dVector& D2, // second derivative + const ON_3dVector& D3, // third derivative + ON_3dVector& T, // Unit tangent returned here + ON_3dVector& K, // curvature vector(k*N). curvature k = K.Length() and Normal N=K.Unitize() + double* kprime, // first derivative of k + double* torsion) // torsion +{ + bool rc = false; + double dsdt = D1.Length(); + if (dsdt > 0) + { + T = (1 / dsdt) * D1; + // Differentiate the formula k = | q | / |D1|^3, where q = D1 x D2 + ON_3dVector q = ON_CrossProduct(D1, D2); + double qlen2 = q.LengthSquared(); + double dsdt2 = dsdt * dsdt; + K = (1.0/dsdt2) * (D2 - (D2*T) * T); + if (kprime) + { + ON_3dVector qprime = ON_CrossProduct(D1, D3); + if (qlen2 > 0) + { + *kprime = ((q * qprime) * D1.LengthSquared() - 3 * qlen2 * (D1 * D2)) / + (sqrt(qlen2) * pow(D1.Length(), 5.0)); + } + else + *kprime = qprime.Length() / pow(D1.Length(), 3); + rc = true; + } + if (torsion) + { + if (qlen2 > 0) + { + *torsion = q * D3 / qlen2; + rc = true; + } + else + rc = false; + } + + } + return rc; +} + bool ON_EvSectionalCurvature( const ON_3dVector& S10, const ON_3dVector& S01, diff --git a/opennurbs_math.h b/opennurbs_math.h index 1a31b6bb..56b2e313 100644 --- a/opennurbs_math.h +++ b/opennurbs_math.h @@ -1423,6 +1423,19 @@ bool ON_EvCurvature( ON_3dVector& // Curvature returned here ); +// Compute derivative of curvature as well as curvature and torsion. +// Returns false if either kprime or torsion are not well defined. +ON_DECL +bool ON_EvCurvature1Der( + const ON_3dVector& D1, // first derivative of curve + const ON_3dVector& D2, // second derivative of curve + const ON_3dVector& D3, // third derivative of curve + ON_3dVector& T, // Unit tangent returned here + ON_3dVector& K, // curvature vector(k*N). curvature k = K.Length() and Normal N=K.Unitize() + double* kprime, // first derivative of k + double* torsion); // torsion + + ON_DECL bool ON_EvPrincipalCurvatures( const ON_3dVector&, // Ds, diff --git a/opennurbs_md5.h b/opennurbs_md5.h index 56a54ddd..a7fea181 100644 --- a/opennurbs_md5.h +++ b/opennurbs_md5.h @@ -307,9 +307,9 @@ private: ON__UINT32 m_reserved = 0; // current "remainder" - ON__UINT8 m_buffer[64]; // bytes that didn't fit in last 64 byte chunk - ON__UINT32 m_bit_count[2]; // number of bits (lo, hi) - ON__UINT32 m_state[4]; // current state + ON__UINT8 m_buffer[64] = {}; // bytes that didn't fit in last 64 byte chunk + ON__UINT32 m_bit_count[2] = {}; // number of bits (lo, hi) + ON__UINT32 m_state[4] = {}; // current state // cached MD5 hash - valid if 2 = (2 & m_status_bits) mutable ON_MD5_Hash m_md5_hash; diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h index d8eb6278..801bf957 100644 --- a/opennurbs_mesh.h +++ b/opennurbs_mesh.h @@ -1752,7 +1752,7 @@ public: inline const unsigned int* Fvi(unsigned int face_index) const { - return (face_index < m_face_count) ? (m_faces + (face_index*m_face_stride)) : 0; + return (face_index < m_face_count) ? (m_faces + (face_index*m_face_stride)) : nullptr; } inline unsigned int* QuadFvi(unsigned int face_index, unsigned int buffer[4]) const diff --git a/opennurbs_model_component.cpp b/opennurbs_model_component.cpp index 2b34e9b1..c8182d45 100644 --- a/opennurbs_model_component.cpp +++ b/opennurbs_model_component.cpp @@ -69,8 +69,10 @@ const ON_wString ON_ModelComponent::ComponentTypeToString( return ON_wString("RenderContent"); case ON_ModelComponent::Type::EmbeddedFile: return ON_wString("EmbeddedFile"); - case ON_ModelComponent::Type::PostEffect: - return ON_wString("PostEffect"); + case ON_ModelComponent::Type::SectionStyle: + return ON_wString("SectionStyle"); + case ON_ModelComponent::Type::ObsoleteValue: + return ON_wString("ObsoleteValue"); case ON_ModelComponent::Type::Mixed: return ON_wString("Mixed"); default: @@ -331,7 +333,8 @@ ON_ModelComponent::Type ON_ModelComponent::ComponentTypeFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::HistoryRecord); ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::RenderContent); ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::EmbeddedFile); - ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::PostEffect); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::ObsoleteValue); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::SectionStyle); ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Mixed); } @@ -1962,9 +1965,9 @@ bool ON_ModelComponent::IndexRequired(ON_ModelComponent::Type component_type) { case ON_ModelComponent::Type::Image: case ON_ModelComponent::Type::TextureMapping: - case ON_ModelComponent::Type::RenderContent: // JohnC: I have no idea how to decide what to return. - case ON_ModelComponent::Type::EmbeddedFile: // JohnC: I have no idea how to decide what to return. - case ON_ModelComponent::Type::PostEffect: // JohnC: I have no idea how to decide what to return. + case ON_ModelComponent::Type::RenderContent: // } + case ON_ModelComponent::Type::EmbeddedFile: // } JohnC: I have no idea how to decide what to return. + case ON_ModelComponent::Type::ObsoleteValue: // } case ON_ModelComponent::Type::Material: // ON_Layer references render material by index case ON_ModelComponent::Type::LinePattern: // ON_Layer references line pattern by index case ON_ModelComponent::Type::Layer: @@ -1974,6 +1977,7 @@ bool ON_ModelComponent::IndexRequired(ON_ModelComponent::Type component_type) case ON_ModelComponent::Type::RenderLight: case ON_ModelComponent::Type::HatchPattern: case ON_ModelComponent::Type::InstanceDefinition: + case ON_ModelComponent::Type::SectionStyle: return true; case ON_ModelComponent::Type::ModelGeometry: @@ -2001,7 +2005,6 @@ bool ON_ModelComponent::UniqueNameIncludesParent(ON_ModelComponent::Type compone case ON_ModelComponent::Type::Material: case ON_ModelComponent::Type::RenderContent: // JohnC: TODO: Giulio's tweak above. Left for the wrapping phase. case ON_ModelComponent::Type::EmbeddedFile: // JohnC: TODO: Giulio's tweak above. Left for the wrapping phase. - case ON_ModelComponent::Type::PostEffect: // JohnC: TODO: Giulio's tweak above. Left for the wrapping phase. case ON_ModelComponent::Type::LinePattern: case ON_ModelComponent::Type::Group: case ON_ModelComponent::Type::TextStyle: @@ -2011,6 +2014,7 @@ bool ON_ModelComponent::UniqueNameIncludesParent(ON_ModelComponent::Type compone case ON_ModelComponent::Type::InstanceDefinition: case ON_ModelComponent::Type::ModelGeometry: case ON_ModelComponent::Type::HistoryRecord: + case ON_ModelComponent::Type::SectionStyle: return false; default: @@ -2034,14 +2038,14 @@ bool ON_ModelComponent::UniqueNameRequired(ON_ModelComponent::Type component_typ case ON_ModelComponent::Type::DimStyle: case ON_ModelComponent::Type::HatchPattern: case ON_ModelComponent::Type::InstanceDefinition: - case ON_ModelComponent::Type::EmbeddedFile: ///////////////// Giulio + case ON_ModelComponent::Type::EmbeddedFile: + case ON_ModelComponent::Type::SectionStyle: return true; case ON_ModelComponent::Type::Image: case ON_ModelComponent::Type::TextureMapping: case ON_ModelComponent::Type::Material: case ON_ModelComponent::Type::RenderContent: // JohnC: TODO: Giulio's tweak above. Left for the wrapping phase. - case ON_ModelComponent::Type::PostEffect: // JohnC: TODO: Giulio's tweak above. Left for the wrapping phase. case ON_ModelComponent::Type::RenderLight: case ON_ModelComponent::Type::ModelGeometry: case ON_ModelComponent::Type::HistoryRecord: diff --git a/opennurbs_model_component.h b/opennurbs_model_component.h index 3d300bd5..31579baf 100644 --- a/opennurbs_model_component.h +++ b/opennurbs_model_component.h @@ -84,8 +84,10 @@ public: RenderContent = 14, ///Embedded file for render texture. EmbeddedFile = 15, - ///Post Effect. - PostEffect = 16, + ///This value was never used and is now obsolete. + ObsoleteValue = 16, + ///Section style attributes + SectionStyle = 17, // If you add any more, add them here, above NumOf. diff --git a/opennurbs_plane.h b/opennurbs_plane.h index d1075899..1b54210e 100644 --- a/opennurbs_plane.h +++ b/opennurbs_plane.h @@ -580,27 +580,35 @@ public: // A point is visible if m_plane_equation.ValueAt(point) <= 0. // (This is the opposite convention from what OpenGL uses.) ON_PlaneEquation m_plane_equation; - ON_UUID m_plane_id; - bool m_bEnabled; + ON_UUID m_plane_id = ON_nil_uuid; + bool m_bEnabled = false; // A distance where the clipping plane does not clip geometry. - // By default, distance is ON_DBL_MAX which indicates that there is no - // distance being applied // A positive value is equivalent to placing another clipping plane at a // distance from this clipping plane along it's normal and then flipping it - double Distance() const; - void SetDistance(double distance); + // + // The depth must also be enabled to be effective + double Depth() const; + + // Negative depth values are currently not allowed. If a negative depth value + // is passed to this function, it will not the the internal depth value + void SetDepth(double depth); + + // Default is false + bool DepthEnabled() const; + void SetDepthEnabled(bool on); void Default(); bool Write( ON_BinaryArchive& ) const; bool Read( ON_BinaryArchive& ); private: - char m_reserved[3]; + bool m_depth_enabled = false; + char m_reserved[2] = {}; // This should be a double, but is a float in order to not change // the class size. When the Rhino SDK can break, this data type should change. - float m_distance = ON_UNSET_POSITIVE_FLOAT; + float m_depth = 0; }; class ON_CLASS ON_ClippingPlane @@ -619,22 +627,30 @@ public: ON_ClippingPlaneInfo ClippingPlaneInfo() const; // A distance where the clipping plane does not clip geometry. - // By default, distance is a negative value which indicates that there is no - // distance being applied // A positive value is equivalent to placing another clipping plane at a // distance from this clipping plane along it's normal and then flipping it - double Distance() const; - void SetDistance(double distance); + // + // The depth must also be enabled to be effective + double Depth() const; + + // Negative depth values are currently not allowed. If a negative depth value + // is passed to this function, it will not the the internal depth value + void SetDepth(double depth); + + // Default is false + bool DepthEnabled() const; + void SetDepthEnabled(bool on); bool Read( class ON_BinaryArchive& ); bool Write( class ON_BinaryArchive& ) const; private: - char m_reserved[3]; + bool m_depth_enabled = false; + char m_reserved[2]; // This should be a double, but is a float in order to not change // the class size. When the Rhino SDK can break, this data type should change. - float m_distance = ON_UNSET_POSITIVE_FLOAT; + float m_depth = 0; }; diff --git a/opennurbs_planesurface.cpp b/opennurbs_planesurface.cpp index 8bf4a32f..3113df04 100644 --- a/opennurbs_planesurface.cpp +++ b/opennurbs_planesurface.cpp @@ -670,9 +670,9 @@ bool ON_PlaneSurface::CreatePseudoInfinitePlaneTight( ON_3dPoint pt = edge.PointAt(t); plane.ClosestPointTo(pt, &u, &v); if (u < uext[0]) uext[0] = u; - else if (u > uext[1]) uext[1] = u; + if (u > uext[1]) uext[1] = u; if (v < vext[0]) vext[0] = v; - else if (v > vext[1]) vext[1] = v; + if (v > vext[1]) vext[1] = v; } *this = plane; uext.Expand(padding * uext.Length() + padding); @@ -828,39 +828,47 @@ ON_Mesh* ON_PlaneSurface::CreateMesh( return rc; } -static double ON_ClippingPlaneDistanceHelper(float d) +double ON_ClippingPlaneInfo::Depth() const { - if (ON_IsValidFloat(d)) - return d; - return ON_DBL_MAX; + return m_depth; +} +void ON_ClippingPlaneInfo::SetDepth(double depth) +{ + if (depth < 0.0) + return; + m_depth = (float)depth; } -static void ON_ClippingPlaneSetDistanceHelper(double distance, float& valueToChange) +bool ON_ClippingPlaneInfo::DepthEnabled() const { - if (ON_IsValid(distance) && distance < ON_UNSET_POSITIVE_FLOAT) - valueToChange = (float)distance; - else - valueToChange = ON_UNSET_POSITIVE_FLOAT; + return m_depth_enabled; } -double ON_ClippingPlaneInfo::Distance() const +void ON_ClippingPlaneInfo::SetDepthEnabled(bool on) { - return ON_ClippingPlaneDistanceHelper(m_distance); -} -void ON_ClippingPlaneInfo::SetDistance(double distance) -{ - ON_ClippingPlaneSetDistanceHelper(distance, m_distance); + m_depth_enabled = on; } void ON_ClippingPlaneInfo::Default() { memset(this,0,sizeof(*this)); - m_distance = ON_UNSET_POSITIVE_FLOAT; } bool ON_ClippingPlaneInfo::Write( ON_BinaryArchive& file ) const { - bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2); + // version 1.1 and 1.2 - write distance as a double + // In version 1.1 we wrote -1 as the default value for m_distance. + // This default has changed to ON_UNSET_POSITIVE_FLOAT. Bumping the minor + // version so we can properly handle the default case when reading. + // + // 28 Mar 2023 S. Baer (RH-73816) + // We went back to using a negative value to define "unset". We don't need + // to adjust the minor version number for this + // 6 April 2023 S. Baer (RH-74002) + // version 1.3 + // Added a bool toggle to state if depth is being used. This frees up the depth + // numeric value to be any number and not have reserved "unset" numbers + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3); if (!rc) return false; @@ -875,11 +883,11 @@ bool ON_ClippingPlaneInfo::Write( ON_BinaryArchive& file ) const rc = file.WriteBool(m_bEnabled); if (!rc) break; - // version 1.1 and 1.2 - write distance as a double - // In version 1.1 we wrote -1 as the default value for m_distance. - // This default has changed to ON_UNSET_POSITIVE_FLOAT. Bumping the minor - // version so we can properly handle the default case when reading. - rc = file.WriteDouble(m_distance); + rc = file.WriteDouble(m_depth); + if (!rc) break; + + // version 1.3, add depth enabled bool + rc = file.WriteBool(m_depth_enabled); if (!rc) break; break; @@ -917,18 +925,29 @@ bool ON_ClippingPlaneInfo::Read( ON_BinaryArchive& file ) if (minor_version > 0) { - double d = -1; + double d = 0; rc = file.ReadDouble(&d); if (!rc) break; - if (1 == minor_version) + if (1 == minor_version || 2 == minor_version) { - // negative values in 1.1 chunk meant that the distance was unset - if (d < 0.0) - d = ON_UNSET_VALUE; + if (d >= 0.0 && d != ON_UNSET_POSITIVE_FLOAT) + { + m_depth_enabled = true; + } + else + { + m_depth_enabled = false; + d = 0.0; + } } + SetDepth(d); + } - SetDistance(d); + if (minor_version >= 3) + { + rc = file.ReadBool(&m_depth_enabled); + if (!rc) break; } break; @@ -947,7 +966,8 @@ void ON_ClippingPlane::Default() m_viewport_ids.Empty(); m_plane_id = ON_nil_uuid; m_bEnabled = true; - m_distance = ON_UNSET_POSITIVE_FLOAT; + m_depth = 0; + m_depth_enabled = false; } ON_ClippingPlane::ON_ClippingPlane() @@ -965,17 +985,29 @@ ON_ClippingPlaneInfo ON_ClippingPlane::ClippingPlaneInfo() const info.m_plane_equation = m_plane.plane_equation; info.m_plane_id = m_plane_id; info.m_bEnabled = m_bEnabled; - info.SetDistance(m_distance); + info.SetDepth(m_depth); + info.SetDepthEnabled(m_depth_enabled); return info; } -double ON_ClippingPlane::Distance() const +double ON_ClippingPlane::Depth() const { - return ON_ClippingPlaneDistanceHelper(m_distance); + return m_depth; } -void ON_ClippingPlane::SetDistance(double distance) +void ON_ClippingPlane::SetDepth(double depth) { - ON_ClippingPlaneSetDistanceHelper(distance, m_distance); + if (depth < 0.0) + return; + m_depth = (float)depth; +} + +bool ON_ClippingPlane::DepthEnabled() const +{ + return m_depth_enabled; +} +void ON_ClippingPlane::SetDepthEnabled(bool on) +{ + m_depth_enabled = on; } bool ON_ClippingPlane::Read( ON_BinaryArchive& file ) @@ -1022,12 +1054,26 @@ bool ON_ClippingPlane::Read( ON_BinaryArchive& file ) rc = file.ReadDouble(&d); if (!rc) break; - if (2 == minor_version) + if (minor_version < 4) { - if (d < 0.0) - d = ON_UNSET_VALUE; + if (d >= 0.0 && d != ON_UNSET_POSITIVE_FLOAT) + { + m_depth_enabled = true; + } + else + { + m_depth_enabled = false; + d = 0.0; + } } - SetDistance(d); + + SetDepth(d); + } + + if (minor_version >= 4) + { + rc = file.ReadBool(&m_depth_enabled); + if (!rc) break; } break; @@ -1041,7 +1087,7 @@ bool ON_ClippingPlane::Read( ON_BinaryArchive& file ) bool ON_ClippingPlane::Write( ON_BinaryArchive& file ) const { - bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3); + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,4); if (!rc) return false; @@ -1070,7 +1116,11 @@ bool ON_ClippingPlane::Write( ON_BinaryArchive& file ) const //version 1.2 - write distance as double //version 1.3 - continue to write distance, but the reader now knows to // interpret the distance value in a different way - rc = file.WriteDouble(m_distance); + rc = file.WriteDouble(m_depth); + if (!rc) break; + + //version 1.4 - write enabled flag for depth + rc = file.WriteBool(m_depth_enabled); if (!rc) break; break; diff --git a/opennurbs_point.h b/opennurbs_point.h index 1f2b4f36..81758799 100644 --- a/opennurbs_point.h +++ b/opennurbs_point.h @@ -3679,10 +3679,10 @@ public: ON_2dPoint LiftInverse(ON_2dPoint p); ON_Interval m_dom[2]; - bool m_closed[2]; - double m_normband; + bool m_closed[2] = {}; + double m_normband = ON_DBL_QNAN; private: - int m_deck[2]; + int m_deck[2] = {}; ON_2dPoint m_nprev = ON_2dPoint::UnsetPoint; }; diff --git a/opennurbs_post_effects.cpp b/opennurbs_post_effects.cpp index eb5ab4b4..9151b7a7 100644 --- a/opennurbs_post_effects.cpp +++ b/opennurbs_post_effects.cpp @@ -22,19 +22,118 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif -#define ON_RDK_PEP_PLUG_IN L"plug-in" -#define ON_RDK_PEP_NAME L"name" -#define ON_RDK_PEP_ID L"id" -#define ON_RDK_PEP_SHOWN L"shown" -#define ON_RDK_PEP_ON L"on" -#define ON_RDK_PEP_STATE L"state" +// ON_PostEffectParams + +class ON_PostEffectParams::CImpl +{ +public: + CImpl() : _node(ON_RDK_PEP_PARAMS) { } + CImpl(const ON_XMLNode& n) : _node(n) { _node.SetTagName(ON_RDK_PEP_PARAMS); } + ~CImpl() { delete _params; _params = nullptr; } + + const ON_XMLParameters& AsXMLParameters(void) const + { + if (nullptr == _params) + { + _params = new ON_XMLParameters(_node); + } + + return *_params; + } + + ON_XMLNode _node; + mutable ON_XMLParameters* _params = nullptr; +}; + +ON_PostEffectParams::ON_PostEffectParams() +{ + _impl = new CImpl; +} + +ON_PostEffectParams::ON_PostEffectParams(const ON_XMLNode& n) +{ + _impl = new CImpl(n); +} + +ON_PostEffectParams::ON_PostEffectParams(const ON_PostEffectParams& src) +{ + _impl = new CImpl; + + operator = (src); +} + +ON_PostEffectParams::~ON_PostEffectParams() +{ + delete _impl; + _impl = nullptr; +} + +const ON_PostEffectParams& ON_PostEffectParams::operator = (const ON_PostEffectParams& src) +{ + _impl->_node = src._impl->_node; + + return *this; +} + +bool ON_PostEffectParams::GetParam(const wchar_t* name, ON_XMLVariant& value_out) const +{ + const ON_XMLParameters p(_impl->_node); + return p.GetParam(name, value_out); +} + +bool ON_PostEffectParams::SetParam(const wchar_t* name, const ON_XMLVariant& value) +{ + ON_XMLParameters p(_impl->_node); + p.SetParam(name, value); + return true; +} + +const ON_XMLParameters& ON_PostEffectParams::AsXMLParameters(void) const +{ + return _impl->AsXMLParameters(); +} + +ON__UINT32 ON_PostEffectParams::DataCRC(ON__UINT32 current_remainder) const +{ + return _impl->_node.DataCRC(current_remainder); +} + +void* ON_PostEffectParams::EVF(const wchar_t*, void*) +{ + return nullptr; +} // ON_PostEffect -// -// TODO: -// NOTE TO JOHN: On writing this I see I have made a terrible assumption that the model will only -// be loaded and saved once! It will need to be changed so that the nodes are not stolen out of the -// document, but they are copied. + +static ON_XMLRootNode g_panic_node; + +static const wchar_t* XMLPathPeps(void) +{ + return ON_RDK_DOCUMENT ON_RDK_SLASH ON_RDK_SETTINGS ON_RDK_SLASH ON_RDK_POST_EFFECTS; +} + +static const wchar_t* PostEffectTypeString(ON_PostEffect::Types type) +{ + switch (type) + { + case ON_PostEffect::Types::Early: return ON_RDK_PEP_TYPE_EARLY; + case ON_PostEffect::Types::ToneMapping: return ON_RDK_PEP_TYPE_TONE_MAPPING; + case ON_PostEffect::Types::Late: return ON_RDK_PEP_TYPE_LATE; + default: ON_ASSERT(false); return L""; + } +} + +static ON_XMLNode* GetPostEffectTypeNode(ON_XMLNode& post_effects_node, ON_PostEffect::Types type) +{ + ON_ASSERT(post_effects_node.TagName() == ON_RDK_POST_EFFECTS); + + return post_effects_node.CreateNodeAtPath(PostEffectTypeString(type)); +} + +const wchar_t* ON_PostEffectTypeString(ON_PostEffect::Types type) +{ + return PostEffectTypeString(type); +} ON_XMLNode* FindPostEffectNodeForId(const ON_XMLNode& pep_section_node, const ON_UUID& id) { @@ -59,102 +158,183 @@ ON_XMLNode* FindPostEffectNodeForId(const ON_XMLNode& pep_section_node, const ON class ON_PostEffect::CImpl { public: - CImpl(const ON_XMLNode& n, Types t) : m_node(n), m_type(t) { } + CImpl(ON_PostEffects* p, const wchar_t* n, const ON_UUID& u, Types t) + : _collection(p), _local_name(n), _id(u), _type(t) { } + ~CImpl(); + + ON_XMLNode* PepNode(void) const; ON_XMLVariant GetPropertyValue(const wchar_t* name) const; + void SetPropertyValue(const wchar_t* name, const ON_XMLVariant& v) const; public: - ONX_Model* m_model = nullptr; - ON_XMLNode m_node; - ON_PostEffect::Types m_type = ON_PostEffect::Types::Early; + ON_PostEffects* _collection; + mutable ON_wString _local_name; + mutable ON_XMLNode* _local_node = nullptr; + const ON_UUID _id; + const ON_PostEffect::Types _type; }; +ON_PostEffect::CImpl::~CImpl() +{ + if (nullptr != _local_node) + { + delete _local_node; + _local_node = nullptr; + } +} + +ON_XMLNode* ON_PostEffect::CImpl::PepNode(void) const +{ + if (nullptr != _collection) + { + ON_XMLNode& post_effects_node = _collection->WritablePostEffectsNode(); + ON_XMLNode* pep_type_node = GetPostEffectTypeNode(post_effects_node, _type); + if (nullptr == pep_type_node) + return nullptr; + + ON_XMLNode* pep_node = FindPostEffectNodeForId(*pep_type_node, _id); + if (nullptr == pep_node) + { + pep_node = pep_type_node->AttachChildNode(new ON_XMLNode(ON_RDK_PEP_PLUG_IN)); + } + + return pep_node; + } + + // This post effect is not a member of a collection; it's free-floating so we need to use + // a local node to hold its state. + if (nullptr == _local_node) + _local_node = new ON_XMLNode(ON_RDK_PEP_PLUG_IN); + + return _local_node; +} + ON_XMLVariant ON_PostEffect::CImpl::GetPropertyValue(const wchar_t* name) const { ON_XMLVariant v; - const auto* pProp = m_node.GetNamedProperty(name); - if (nullptr != pProp) + const auto* pep_node = PepNode(); + if (nullptr != pep_node) { - v = pProp->GetValue(); + const ON_XMLProperty* pProp = pep_node->GetNamedProperty(name); + if (nullptr != pProp) + { + v = pProp->GetValue(); + } } return v; } -void SetModel(const ON_PostEffect& pep, ONX_Model& m) +void ON_PostEffect::CImpl::SetPropertyValue(const wchar_t* name, const ON_XMLVariant& value) const { - pep.m_impl->m_model = &m; + auto* pep_node = PepNode(); + if (nullptr != pep_node) + { + pep_node->SetProperty(ON_XMLProperty(name, value)); + } } -ON_OBJECT_IMPLEMENT(ON_PostEffect, ON_ModelComponent, "A7755211-7C60-4C46-8705-1C91151C9CD9"); - -ON_PostEffect::ON_PostEffect() - : - ON_ModelComponent(ON_ModelComponent::Type::PostEffect) +ON_PostEffect::ON_PostEffect(ON_PostEffects& pe, Types type, const ON_UUID& id, const wchar_t* local_name) { - m_impl = new CImpl(nullptr, Types::Unset); -} - -ON_PostEffect::ON_PostEffect(const ON_XMLNode& node, Types type) - : - ON_ModelComponent(ON_ModelComponent::Type::PostEffect) -{ - m_impl = new CImpl(node, type); - - // Copy the name to the component name. - SetName(m_impl->GetPropertyValue(ON_RDK_PEP_NAME).AsString()); - - // Copy the XML instance id to the component id. This is the unique id of the PEP and as such - // it's not supposed to be changed. There is a lock flag in the ON_ModelComponent but how to use - // it is an impenetrable mystery to me. Anyway, if the user does set the id, it will be ignored - // because we don't copy it back to the XML. - const auto uuid = m_impl->GetPropertyValue(ON_RDK_PEP_ID).AsUuid(); - SetId(uuid); + _impl = new CImpl(&pe, local_name, id, type); } ON_PostEffect::ON_PostEffect(const ON_PostEffect& pep) - : - ON_ModelComponent(ON_ModelComponent::Type::PostEffect, pep) { - m_impl = new CImpl(pep.m_impl->m_node, pep.Type()); + _impl = new CImpl(nullptr, pep.LocalName(), pep.Id(), pep.Type()); operator = (pep); } ON_PostEffect::~ON_PostEffect() { - delete m_impl; - m_impl = nullptr; + delete _impl; + _impl = nullptr; } -ON_PostEffect::Types ON_PostEffect::Type(void) const +const ON_PostEffect& ON_PostEffect::operator = (const ON_PostEffect& other) { - return m_impl->m_type; -} - -const ON_PostEffect& ON_PostEffect::operator = (const ON_PostEffect& pep) -{ - ON_ModelComponent::operator = (pep); - - m_impl->m_type = pep.m_impl->m_type; - m_impl->m_node = pep.m_impl->m_node; + ON_XMLNode* pep_node_dest = _impl->PepNode(); + if (nullptr != pep_node_dest) + { + const ON_XMLNode* pep_node_srce = other._impl->PepNode(); + if (nullptr != pep_node_srce) + { + *pep_node_dest = *pep_node_srce; + } + } return *this; } +bool ON_PostEffect::operator == (const ON_PostEffect& pep) const +{ + if (_impl->_id != pep._impl->_id) return false; + if (_impl->_type != pep._impl->_type) return false; + if (_impl->_local_name != pep._impl->_local_name) return false; + + ON_PostEffectParams p1, p2; + GetAllParameters(p1); + pep.GetAllParameters(p2); + + bool equal = true; + auto* it = p1.AsXMLParameters().NewIterator(); + + ON_wString param_name; + ON_XMLVariant param_value1, param_value2; + while (equal && it->Next(param_name, param_value1)) + { + if (!p2.GetParam(param_name, param_value2)) + equal = false; + + if (param_value1 != param_value2) + equal = false; + } + + delete it; + + return equal; +} + +bool ON_PostEffect::operator != (const ON_PostEffect& pep) const +{ + return !(operator == (pep)); +} + +ON_PostEffect::Types ON_PostEffect::Type(void) const +{ + return _impl->_type; +} + +ON_UUID ON_PostEffect::Id(void) const +{ + return _impl->_id; +} + ON_wString ON_PostEffect::LocalName(void) const { - return m_impl->GetPropertyValue(ON_RDK_PEP_NAME); + return _impl->_local_name; } -bool ON_PostEffect::IsVisible(void) const +bool ON_PostEffect::On(void) const { - return m_impl->GetPropertyValue(ON_RDK_PEP_ON); + return _impl->GetPropertyValue(ON_RDK_PEP_ON).AsBool(); } -bool ON_PostEffect::IsActive(void) const +void ON_PostEffect::SetOn(bool b) { - return m_impl->GetPropertyValue(ON_RDK_PEP_SHOWN); + _impl->SetPropertyValue(ON_RDK_PEP_ON, b); +} + +bool ON_PostEffect::Shown(void) const +{ + return _impl->GetPropertyValue(ON_RDK_PEP_SHOWN).AsBool(); +} + +void ON_PostEffect::SetShown(bool b) +{ + _impl->SetPropertyValue(ON_RDK_PEP_SHOWN, b); } ON_XMLVariant ON_PostEffect::GetParameter(const wchar_t* param_name) const @@ -162,11 +342,15 @@ ON_XMLVariant ON_PostEffect::GetParameter(const wchar_t* param_name) const ON_XMLVariant value; value.SetNull(); - auto* node = m_impl->m_node.GetNodeAtPath(ON_RDK_PEP_STATE); - if (nullptr != node) + const ON_XMLNode* pep_node = _impl->PepNode(); + if (nullptr != pep_node) { - ON_XMLParameters p(*node); - p.GetParam(param_name, value); + const ON_XMLNode* pep_param_node = pep_node->GetNamedChild(ON_RDK_PEP_PARAMS); + if (nullptr != pep_param_node) + { + ON_XMLParameters p(*pep_param_node); + p.GetParam(param_name, value); + } } return value; @@ -174,11 +358,15 @@ ON_XMLVariant ON_PostEffect::GetParameter(const wchar_t* param_name) const bool ON_PostEffect::SetParameter(const wchar_t* param_name, const ON_XMLVariant& param_value) { - auto* node = m_impl->m_node.GetNodeAtPath(ON_RDK_PEP_STATE); - if (nullptr == node) + const ON_XMLNode* pep_node = _impl->PepNode(); + if (nullptr == pep_node) return false; - ON_XMLParameters p(*node); + ON_XMLNode* pep_param_node = pep_node->GetNamedChild(ON_RDK_PEP_PARAMS); + if (nullptr == pep_param_node) + return false; + + ON_XMLParameters p(*pep_param_node); ON_XMLVariant current_value; if (!p.GetParam(param_name, current_value)) @@ -190,24 +378,432 @@ bool ON_PostEffect::SetParameter(const wchar_t* param_name, const ON_XMLVariant& return true; } -const ON_XMLNode& ON_PostEffect::XML(void) const +bool ON_PostEffect::GetAllParameters(ON_PostEffectParams& params) const { - return m_impl->m_node; + const ON_XMLNode* pep_node = _impl->PepNode(); + if (nullptr == pep_node) + return false; + + const ON_XMLNode* pep_param_node = pep_node->GetNamedChild(ON_RDK_PEP_PARAMS); + if (nullptr == pep_param_node) + return false; + + params = ON_PostEffectParams(*pep_param_node); + + return true; +} + +bool ON_PostEffect::SetAllParameters(const ON_PostEffectParams& params) +{ + ON_XMLNode* pep_node = _impl->PepNode(); + if (nullptr == pep_node) + return false; + + ON_XMLNode* pep_param_node = pep_node->GetNamedChild(ON_RDK_PEP_PARAMS); + if (nullptr == pep_param_node) + return false; + + *pep_param_node = params.AsXMLParameters().Node(); + + return true; +} + +ON_XMLNode& ON_PostEffect::XMLNode(void) +{ + auto* pep_node = _impl->PepNode(); + if (nullptr != pep_node) + return *pep_node; + + // Should never get here. + ON_ASSERT(false); + return g_panic_node; +} + +const ON_XMLNode& ON_PostEffect::XMLNode(void) const +{ + return const_cast(this)->XMLNode(); } ON__UINT32 ON_PostEffect::DataCRC(ON__UINT32 crc) const { - crc = ON_ModelComponent::DataCRC(crc); - - return m_impl->m_node.DataCRC(crc); + return XMLNode().DataCRC(crc); } -const ON_PostEffect* ON_PostEffect::FromModelComponentRef(const ON_ModelComponentReference& ref, - const ON_PostEffect* none_return_value) // Static. +// ON_PostEffects + +class ON_PostEffects::CImpl : public ON_InternalXMLImpl { - const auto* pep = ON_PostEffect::Cast(ref.ModelComponent()); - if (nullptr != pep) - return pep; +public: + CImpl(ON_PostEffects& p) : _pe(p) { } + CImpl(ON_PostEffects& p, ON_XMLNode& n) : _pe(p), ON_InternalXMLImpl(&n) { } + ~CImpl(); - return none_return_value; + ON_XMLNode& PostEffectsNode(void) const; + + ON_SimpleArray& PostEffectList(void) { EnsurePopulated(); return _peps; } + const ON_SimpleArray& PostEffectList(void) const { EnsurePopulated(); return _peps; } + + void Clear(void); + + int IndexOfPostEffect(const ON_UUID& id) const; + +private: + void EnsurePopulated(void) const; + void Populate(ON_PostEffect::Types type) const; + +public: + mutable ON_SimpleArray _peps; + mutable bool _is_populated = false; + ON_PostEffects& _pe; +}; + +ON_PostEffects::CImpl::~CImpl() +{ + Clear(); +} + +ON_XMLNode& ON_PostEffects::CImpl::PostEffectsNode(void) const +{ + ON_XMLNode* post_effects_node = Node().CreateNodeAtPath(XMLPathPeps()); + if (nullptr != post_effects_node) + return *post_effects_node; + + // Should never get here. + ON_ASSERT(false); + return g_panic_node; +} + +int ON_PostEffects::CImpl::IndexOfPostEffect(const ON_UUID& id) const +{ + const auto& pel = PostEffectList(); + + for (int i = 0; i < pel.Count(); i++) + { + ON_PostEffect* pep = pel[i]; + if (pep->Id() == id) + return i; + } + + return -1; +} + +void ON_PostEffects::CImpl::Populate(ON_PostEffect::Types type) const +{ + const ON_XMLNode* pep_type_node = GetPostEffectTypeNode(PostEffectsNode(), type); + if (nullptr == pep_type_node) + return; + + auto it = pep_type_node->GetChildIterator(); + + ON_XMLNode* pep_node = it.GetNextChild(); + while (nullptr != pep_node) + { + if (pep_node->TagName() == ON_RDK_PEP_PLUG_IN) + { + ON_XMLProperty* prop_name = pep_node->GetNamedProperty(ON_RDK_PEP_LOCAL_NAME); + if (nullptr != prop_name) + { + const ON_wString local_name = prop_name->GetValue().AsString(); + + ON_XMLProperty* prop_id = pep_node->GetNamedProperty(ON_RDK_PEP_ID); + if (nullptr != prop_id) + { + const ON_UUID id = prop_id->GetValue().AsUuid(); + _peps.Append(new ON_PostEffect(_pe, type, id, local_name)); + } + } + } + + pep_node = it.GetNextChild(); + } +} + +void ON_PostEffects::CImpl::Clear(void) +{ + if (_is_populated) + { + for (int i = 0; i < _peps.Count(); i++) + { + delete _peps[i]; + } + + _peps.Destroy(); + + _is_populated = false; + } +} + +void ON_PostEffects::CImpl::EnsurePopulated(void) const +{ + if (_is_populated) + return; + + _is_populated = true; + + Populate(ON_PostEffect::Types::Early); + Populate(ON_PostEffect::Types::ToneMapping); + Populate(ON_PostEffect::Types::Late); +} + +ON_PostEffects::ON_PostEffects() +{ + _impl = new CImpl(*this); // Uses local node. +} + +ON_PostEffects::ON_PostEffects(ON_XMLNode& model_node) +{ + _impl = new CImpl(*this, model_node); +} + +ON_PostEffects::ON_PostEffects(const ON_PostEffects& pe) +{ + _impl = new CImpl(*this); // Uses local node. + operator = (pe); +} + +ON_PostEffects::~ON_PostEffects() +{ + delete _impl; + _impl = nullptr; +} + +ON_PostEffects& ON_PostEffects::operator = (const ON_PostEffects& peps) +{ + if (this != &peps) + { + SetPostEffectsNode(peps.PostEffectsNode()); + } + + return *this; +} + +bool ON_PostEffects::operator == (const ON_PostEffects& peps) const +{ + // We should not have to clear the lists here because they are always supposed to be consistent + // with the XML. But something is wrong that I don't have time to look into right now, and the + // easiest way to work around it is to clear the lists and make sure they get rebuilt. [MARKER] + _impl->Clear(); + peps._impl->Clear(); + + ON_SimpleArray a1; + GetPostEffects(a1); + + ON_SimpleArray a2; + peps.GetPostEffects(a2); + + if (a1.Count() != a2.Count()) + return false; + + for (int i = 0; i < a1.Count(); i++) + { + const ON_PostEffect& pep1 = *a1[i]; + const ON_PostEffect& pep2 = *a2[i]; + if (pep1 != pep2) + return false; + } + + return true; +} + +bool ON_PostEffects::operator != (const ON_PostEffects& p) const +{ + return !(operator == (p)); +} + +ON_PostEffect* ON_PostEffects::PostEffectFromId(const ON_UUID& id) +{ + const int index = _impl->IndexOfPostEffect(id); + if (index < 0) + return nullptr; + + return _impl->PostEffectList()[index]; +} + +const ON_PostEffect* ON_PostEffects::PostEffectFromId(const ON_UUID& id) const +{ + return const_cast(this)->PostEffectFromId(id); +} + +void ON_PostEffects::GetPostEffects(ON_PostEffect::Types type, ON_SimpleArray& a) +{ + const auto& pel = _impl->PostEffectList(); + + for (int i = 0; i < pel.Count(); i++) + { + ON_PostEffect* pep = pel[i]; + if (pep->Type() == type) + a.Append(pep); + } +} + +void ON_PostEffects::GetPostEffects(ON_PostEffect::Types type, ON_SimpleArray& a) const +{ + ON_SimpleArray pel; + const_cast(this)->GetPostEffects(type, pel); + for (int i = 0; i < pel.Count(); i++) + { + a.Append(pel[i]); + } +} + +void ON_PostEffects::GetPostEffects(ON_SimpleArray& a) +{ + a = _impl->PostEffectList(); +} + +void ON_PostEffects::GetPostEffects(ON_SimpleArray& a) const +{ + const auto& pel = _impl->PostEffectList(); + + for (int i = 0; i < pel.Count(); i++) + { + a.Append(pel[i]); + } +} + +ON_XMLNode& ON_PostEffects::WritablePostEffectsNode(void) +{ + return _impl->PostEffectsNode(); +} + +const ON_XMLNode& ON_PostEffects::PostEffectsNode(void) const +{ + return _impl->PostEffectsNode(); +} + +void ON_PostEffects::SetPostEffectsNode(const ON_XMLNode& post_effects_node) +{ + // This is an expert function needed by the RDK (at least for now). + + // Setting the XML invalidates any existing post effect list. + _impl->Clear(); + + // Copy the incoming node and make sure its tag name is correct. + // This is critical to ensure the correct node can be found from that name. + ON_XMLNode node = post_effects_node; + node.SetTagName(ON_RDK_POST_EFFECTS); + + // Copy the corrected node to the XML. + _impl->PostEffectsNode() = node; +} + +bool ON_PostEffects::AddPostEffect(ON_PostEffect::Types type, const ON_UUID& id, + const wchar_t* local_name, const ON_PostEffectParams& params, + bool is_listable, bool listable_on, bool listable_shown) +{ + if (ON_PostEffect::Types::Unset == type) + return false; + + if (ON_nil_uuid == id) + return false; + + if ((nullptr == local_name) || (0 == *local_name)) + return false; + + ON_ASSERT(nullptr == PostEffectFromId(id)); // Can't add the same id again. + + auto* pep = new ON_PostEffect(*this, type, id, local_name); + _impl->_peps.Append(pep); // Critical. + + auto& pep_node = pep->XMLNode(); + + // Write the localized name to the XML. This seems wrong at first because one would think we + // should be writing the English name, but this is not relied on programmatically. + // It's only written to make the XML text human-readable (assuming you can read that language). + pep_node.SetProperty(ON_XMLProperty(ON_RDK_PEP_LOCAL_NAME, local_name)); + + // Write the id to the XML. + pep_node.SetProperty(ON_XMLProperty(ON_RDK_PEP_ID, ON_IdToString(id))); + + if (is_listable) + { + // Write the listable properties to the XML. + pep_node.SetProperty(ON_XMLProperty(ON_RDK_PEP_ON, listable_on)); + pep_node.SetProperty(ON_XMLProperty(ON_RDK_PEP_SHOWN, listable_shown)); + } + + // Add the params to the XML. + ON_XMLNode* pep_param_node = pep_node.AttachChildNode(new ON_XMLNode(L"")); + if (nullptr != pep_param_node) + { + *pep_param_node = params.AsXMLParameters().Node(); + } + + return true; +} + +bool ON_PostEffects::MovePostEffectBefore(const ON_UUID& id_move, const ON_UUID& id_before) +{ + if (id_move == id_before) + return false; + + const int index_move = _impl->IndexOfPostEffect(id_move); + if (index_move < 0) + return false; + + auto& pel = _impl->PostEffectList(); + + ON_PostEffect* pep_move = pel[index_move]; + pel.Remove(index_move); + + ON_XMLNode& xml_move = pep_move->XMLNode(); + + ON_XMLNode* parent = xml_move.Parent(); + if (nullptr == parent) + return false; // Should never happen. + + if (ON_UuidIsNil(id_before)) + { + ON_XMLNode* xml_move_detached = parent->DetachChild(xml_move); + parent->AttachChildNode(xml_move_detached); + pel.Append(pep_move); + } + else + { + const int index_before = _impl->IndexOfPostEffect(id_before); + if (index_before < 0) + return false; + + pel.Insert(index_before, pep_move); + + ON_XMLNode& xml_before = pel[index_before]->XMLNode(); + xml_move.MoveBefore(xml_before); + } + + return true; +} + +bool ON_PostEffects::GetSelectedPostEffect(ON_PostEffect::Types type, ON_UUID& id_out) const +{ + const ON_XMLNode* pep_type_node = GetPostEffectTypeNode(_impl->PostEffectsNode(), type); + if (nullptr != pep_type_node) + { + ON_XMLProperty* prop = pep_type_node->GetNamedProperty(ON_RDK_PEP_SELECTION); + if (nullptr != prop) + { + id_out = prop->GetValue().AsUuid(); + return true; + } + } + + return false; +} + +void ON_PostEffects::SetSelectedPostEffect(ON_PostEffect::Types type, const ON_UUID& id) +{ + ON_XMLNode* pep_type_node = GetPostEffectTypeNode(_impl->PostEffectsNode(), type); + if (nullptr != pep_type_node) + { + pep_type_node->SetProperty(ON_XMLProperty(ON_RDK_PEP_SELECTION, id)); + } +} + +void* ON_PostEffects::EVF(const wchar_t*, void*) +{ + return nullptr; +} + +void ON_PostEffects::InvalidateCache(void) +{ + _impl->Clear(); } diff --git a/opennurbs_post_effects.h b/opennurbs_post_effects.h index d0538682..8994908a 100644 --- a/opennurbs_post_effects.h +++ b/opennurbs_post_effects.h @@ -14,68 +14,195 @@ #if !defined(ON_POST_EFFECTS_INC_) #define ON_POST_EFFECTS_INC_ -class ON_CLASS ON_PostEffect : public ON_ModelComponent +// Class ON_PostEffectParams represents a collection of arbitrary post effect parameters. +class ON_CLASS ON_PostEffectParams { - ON_OBJECT_DECLARE(ON_PostEffect); - public: - enum class Types : int + ON_PostEffectParams(); + ON_PostEffectParams(const ON_XMLNode&); + ON_PostEffectParams(const ON_PostEffectParams&); + virtual ~ON_PostEffectParams(); + + const ON_PostEffectParams& operator = (const ON_PostEffectParams&); + + bool operator == (const ON_PostEffectParams&) = delete; + bool operator != (const ON_PostEffectParams&) = delete; + + // Get a parameter from the collection by name. + virtual bool GetParam(const wchar_t* name, ON_XMLVariant& value_out) const; + + // Set a parameter to the collection by name. + virtual bool SetParam(const wchar_t* name, const ON_XMLVariant& value); + + // Get the parameter collection as an ON_XMLParameters object. + virtual const ON_XMLParameters& AsXMLParameters(void) const; + + // Get a data CRC of the collection. + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +private: + class CImpl; + CImpl* _impl; +}; + +// Class ON_PostEffect represents a single post effect. +class ON_CLASS ON_PostEffect +{ +public: + enum class Types : unsigned int { Unset, - Early, - ToneMapping, - Late + Early, // Early post effects operate on HDR (float) image data. + ToneMapping, // Tone mapping post effects convert HDR image data to LDR (8-bit) image data. + Late // Late post effects operate on LDR image data. }; - ON_PostEffect(); - ON_PostEffect(const ON_XMLNode& node, Types type); + ON_PostEffect() = delete; // You can't create a post effect with no name, id or type. + ON_PostEffect(class ON_PostEffects& peps, Types type, const ON_UUID& id, const wchar_t* local_name); ON_PostEffect(const ON_PostEffect& pep); virtual ~ON_PostEffect(); - const ON_PostEffect& operator = (const ON_PostEffect& pep); + virtual const ON_PostEffect& operator = (const ON_PostEffect& pep); - bool operator == (const ON_PostEffect& pep); - bool operator != (const ON_PostEffect& pep); + virtual bool operator == (const ON_PostEffect& pep) const; + virtual bool operator != (const ON_PostEffect& pep) const; + +public: // Read-only properties. // Returns the type of this post effect. - Types Type(void) const; + virtual Types Type(void) const; + + // Returns the unique id of this post effect. + virtual ON_UUID Id(void) const; // Returns the localized name of this post effect. - ON_wString LocalName(void) const; + virtual ON_wString LocalName(void) const; - // Returns true if the post effect is visible to the user. For early and late post effects, this is equivalent to 'shown'. - // For tone-mapping post effects, this is equivalent to 'selected'. - bool IsVisible(void) const; +public: // Read/write properties. - // Returns true if the post effect is active. For early and late post effects, this is equivalent to 'shown' and 'on'. - // For tone-mapping post effects, this is equivalent to 'selected'. - bool IsActive(void) const; + // Returns true if the post effect is 'on'. Only early and late post effects can be on. + // This corresponds to the check box next to the post effect's name in the user interface. + virtual bool On(void) const; - // Get a parameter. - // - param_name is the name of the parameter to set. + // Set if the post effect is 'on'. Only early and late post effects can be on. + // This corresponds to the check box next to the post effect's name in the user interface. + virtual void SetOn(bool on); + + // Returns true if the post effect is shown. Only early and late post effects can be shown. + // This corresponds to the post effect appearing in the list in the user interface. + virtual bool Shown(void) const; + + // Set if the post effect is shown. Only early and late post effects can be shown. + // This corresponds to the post effect appearing in the list in the user interface. + virtual void SetShown(bool shown); + +public: // Post effects can have any number of arbitrary parameters. + + // Get a parameter by its name. + // Param 'param_name' is the name of the parameter to set. // Returns the value if successful or null if the parameter was not found. - ON_XMLVariant GetParameter(const wchar_t* param_name) const; + virtual ON_XMLVariant GetParameter(const wchar_t* param_name) const; - // Set a parameter. - // - param_name is the name of the parameter to set. - // - param_value specifies the type and value to set. + // Set a parameter by its name. + // Param 'param_name' is the name of the parameter to set. + // Param 'param_value' specifies the type and value to set. // Returns true if successful or false if the parameter could not be set. - bool SetParameter(const wchar_t* param_name, const ON_XMLVariant& param_value); + virtual bool SetParameter(const wchar_t* param_name, const ON_XMLVariant& param_value); + + // Get all the parameters in one go. + // Returns true if successful or false if the parameters could not be retrieved. + virtual bool GetAllParameters(ON_PostEffectParams& params) const; + + // Set all the parameters in one go. + // Returns true if successful or false if the parameters could not be set. + virtual bool SetAllParameters(const ON_PostEffectParams& params); + +public: // Other. // Returns the XML node that stores the state of this post effect. - const ON_XMLNode& XML(void) const; + virtual ON_XMLNode& XMLNode(void); + virtual const ON_XMLNode& XMLNode(void) const; // Returns a CRC of the state of this post effect. - virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; - - // If ON_PostEffect::Cast(ref.ModelComponent()) is not null, - // that pointer is returned. Otherwise, none_return_value is returned. - static const ON_PostEffect* FromModelComponentRef(const ON_ModelComponentReference& ref, - const ON_PostEffect* none_return_value); + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; public: class CImpl; - CImpl* m_impl; + CImpl* _impl; }; +// Class ON_PostEffects represents a collection of post effects. +class ON_CLASS ON_PostEffects +{ +public: + ON_PostEffects(); + ON_PostEffects(ON_XMLNode& model_node); + ON_PostEffects(const ON_PostEffects& peps); + virtual ~ON_PostEffects(); + + virtual ON_PostEffects& operator = (const ON_PostEffects& peps); + + virtual bool operator == (const ON_PostEffects& peps) const; + virtual bool operator != (const ON_PostEffects& peps) const; + + // Find a post effect from its id. + virtual ON_PostEffect* PostEffectFromId(const ON_UUID& id); + virtual const ON_PostEffect* PostEffectFromId(const ON_UUID& id) const; + + // Get an array of post effects of a certain type. + virtual void GetPostEffects(ON_PostEffect::Types type, ON_SimpleArray< ON_PostEffect*>& a); + virtual void GetPostEffects(ON_PostEffect::Types type, ON_SimpleArray& a) const; + + // Get an array of all post effects. + virtual void GetPostEffects(ON_SimpleArray< ON_PostEffect*>& a); + virtual void GetPostEffects(ON_SimpleArray& a) const; + + // Add a new post effect to the collection. + virtual bool AddPostEffect(ON_PostEffect::Types type, const ON_UUID& id, + const wchar_t* local_name, const ON_PostEffectParams& params, + bool is_listable, bool listable_on, bool listable_shown); + + // Move a post effect before another post effect in the list. + // Param 'id_move' is the id of the post effect to move. + // Param 'id_before' is the id of a post effect before which the post effect should be moved. + // If this is nil, the post effect is moved to the end of the list. + // If the post effect identified by 'id_before' is not found, the method will fail. + // Returns true if successful, else false. + virtual bool MovePostEffectBefore(const ON_UUID& id_move, const ON_UUID& id_before); + + // Gets the selected post effect for a certain type into 'id_out'. + // Returns true if successful or false if the selection information could not be found. + virtual bool GetSelectedPostEffect(ON_PostEffect::Types type, ON_UUID& id_out) const; + + // Sets the selected post effect for a certain type. + virtual void SetSelectedPostEffect(ON_PostEffect::Types type, const ON_UUID& id); + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +public: // Expert access to the post effect XML. + + // Returns the XML node that stores the state of all the post effects (ON_RDK_POST_EFFECTS). + const ON_XMLNode& PostEffectsNode(void) const; + + // Sets the XML node that stores the state of all the post effects (ON_RDK_POST_EFFECTS). + void SetPostEffectsNode(const ON_XMLNode&); + +private: // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + friend class ON_PostEffect; + ON_XMLNode& WritablePostEffectsNode(void); + virtual void InvalidateCache(void); + +public: + class CImpl; + CImpl* _impl; +}; + +// Helper function to get the type of a post effect as a string. +ON_DECL const wchar_t* ON_PostEffectTypeString(ON_PostEffect::Types type); + #endif diff --git a/opennurbs_public.vcxproj b/opennurbs_public.vcxproj index bd2cc0b0..265c48dd 100644 --- a/opennurbs_public.vcxproj +++ b/opennurbs_public.vcxproj @@ -258,6 +258,7 @@ + @@ -435,6 +436,7 @@ + diff --git a/opennurbs_public.xcodeproj/project.pbxproj b/opennurbs_public.xcodeproj/project.pbxproj index 7f46659b..410afb33 100644 --- a/opennurbs_public.xcodeproj/project.pbxproj +++ b/opennurbs_public.xcodeproj/project.pbxproj @@ -339,6 +339,10 @@ 1DC319EC1ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */; }; 1DC319ED1ED6534E00DE6D26 /* opennurbs_zlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */; }; 1DC319EE1ED6534E00DE6D26 /* opennurbs_zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */; }; + 99274C5C29F24A48008E57C0 /* opennurbs_archivable_dictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99274C5B29F24A48008E57C0 /* opennurbs_archivable_dictionary.cpp */; }; + 99274C5E29F24A55008E57C0 /* opennurbs_sectionstyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99274C5D29F24A55008E57C0 /* opennurbs_sectionstyle.cpp */; }; + 99274C6029F24A66008E57C0 /* opennurbs_archivable_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 99274C5F29F24A66008E57C0 /* opennurbs_archivable_dictionary.h */; }; + 99274C6229F24A74008E57C0 /* opennurbs_sectionstyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 99274C6129F24A74008E57C0 /* opennurbs_sectionstyle.h */; }; 99D80C522888720000E95705 /* opennurbs_decals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99D80C512888720000E95705 /* opennurbs_decals.cpp */; }; 99D80C542888721000E95705 /* opennurbs_dithering.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99D80C532888721000E95705 /* opennurbs_dithering.cpp */; }; 99D80C562888722200E95705 /* opennurbs_ground_plane.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99D80C552888722200E95705 /* opennurbs_ground_plane.cpp */; }; @@ -701,6 +705,10 @@ 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib_memory.cpp; sourceTree = ""; }; 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib.cpp; sourceTree = ""; }; 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_zlib.h; sourceTree = ""; }; + 99274C5B29F24A48008E57C0 /* opennurbs_archivable_dictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_archivable_dictionary.cpp; sourceTree = ""; }; + 99274C5D29F24A55008E57C0 /* opennurbs_sectionstyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sectionstyle.cpp; sourceTree = ""; }; + 99274C5F29F24A66008E57C0 /* opennurbs_archivable_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_archivable_dictionary.h; sourceTree = ""; }; + 99274C6129F24A74008E57C0 /* opennurbs_sectionstyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_sectionstyle.h; sourceTree = ""; }; 99D80C512888720000E95705 /* opennurbs_decals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_decals.cpp; sourceTree = ""; }; 99D80C532888721000E95705 /* opennurbs_dithering.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_dithering.cpp; sourceTree = ""; }; 99D80C552888722200E95705 /* opennurbs_ground_plane.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_ground_plane.cpp; sourceTree = ""; }; @@ -770,6 +778,7 @@ 1D45569A216D9DAE00BC992F /* opennurbs_apple_nsfont.h */, 1DC3176A1ED652B700DE6D26 /* opennurbs_arc.h */, 1DC3176C1ED652B700DE6D26 /* opennurbs_arccurve.h */, + 99274C5F29F24A66008E57C0 /* opennurbs_archivable_dictionary.h */, 1DC3176F1ED652B700DE6D26 /* opennurbs_archive.h */, 1DC317701ED652B700DE6D26 /* opennurbs_array_defs.h */, 1DC317721ED652B700DE6D26 /* opennurbs_array.h */, @@ -885,6 +894,7 @@ 1DC319231ED6531C00DE6D26 /* opennurbs_revsurface.h */, 1DC319251ED6531C00DE6D26 /* opennurbs_rtree.h */, 99D80C6C288872CE00E95705 /* opennurbs_safe_frame.h */, + 99274C6129F24A74008E57C0 /* opennurbs_sectionstyle.h */, 1DC319271ED6531C00DE6D26 /* opennurbs_sha1.h */, 99D80C69288872CE00E95705 /* opennurbs_skylight.h */, 1D741C1A21B9E3C600AA10E5 /* opennurbs_sleeplock.h */, @@ -942,6 +952,7 @@ 1D455698216D9DA100BC992F /* opennurbs_apple_nsfont.cpp */, 1DC317691ED652B700DE6D26 /* opennurbs_arc.cpp */, 1DC3176B1ED652B700DE6D26 /* opennurbs_arccurve.cpp */, + 99274C5B29F24A48008E57C0 /* opennurbs_archivable_dictionary.cpp */, 1DC3176D1ED652B700DE6D26 /* opennurbs_archive_manifest.cpp */, 1DC3176E1ED652B700DE6D26 /* opennurbs_archive.cpp */, 1DC317711ED652B700DE6D26 /* opennurbs_array.cpp */, @@ -1064,6 +1075,7 @@ 1DC319221ED6531C00DE6D26 /* opennurbs_revsurface.cpp */, 1DC319241ED6531C00DE6D26 /* opennurbs_rtree.cpp */, 99D80C622888728400E95705 /* opennurbs_safe_frame.cpp */, + 99274C5D29F24A55008E57C0 /* opennurbs_sectionstyle.cpp */, 1DC319261ED6531C00DE6D26 /* opennurbs_sha1.cpp */, 99D80C5F2888728400E95705 /* opennurbs_skylight.cpp */, 1D741C1C21B9E3D600AA10E5 /* opennurbs_sleeplock.cpp */, @@ -1233,6 +1245,7 @@ 99D80C7D288872CF00E95705 /* opennurbs_mesh_modifiers.h in Headers */, 1DC319131ED652F800DE6D26 /* opennurbs_polylinecurve.h in Headers */, 1DC317CB1ED652B800DE6D26 /* opennurbs_3dm_properties.h in Headers */, + 99274C6229F24A74008E57C0 /* opennurbs_sectionstyle.h in Headers */, 1DC318261ED652B800DE6D26 /* opennurbs_freetype_include.h in Headers */, 1DC319B71ED6534E00DE6D26 /* opennurbs_surfaceproxy.h in Headers */, 1DC318AB1ED652F800DE6D26 /* opennurbs_hatch.h in Headers */, @@ -1288,6 +1301,7 @@ 1DC317E81ED652B800DE6D26 /* opennurbs_bounding_box.h in Headers */, 1DC318BA1ED652F800DE6D26 /* opennurbs_intersect.h in Headers */, 1DC317EA1ED652B800DE6D26 /* opennurbs_box.h in Headers */, + 99274C6029F24A66008E57C0 /* opennurbs_archivable_dictionary.h in Headers */, 1DC318CD1ED652F800DE6D26 /* opennurbs_locale.h in Headers */, 1DC319111ED652F800DE6D26 /* opennurbs_polyline.h in Headers */, 1DC318181ED652B800DE6D26 /* opennurbs_ellipse.h in Headers */, @@ -1382,6 +1396,7 @@ 1DC319B21ED6534E00DE6D26 /* opennurbs_sumsurface.cpp in Sources */, 1DC319321ED6531C00DE6D26 /* opennurbs_revsurface.cpp in Sources */, 1DC318E01ED652F800DE6D26 /* opennurbs_mesh.cpp in Sources */, + 99274C5E29F24A55008E57C0 /* opennurbs_sectionstyle.cpp in Sources */, 1DC318A81ED652F800DE6D26 /* opennurbs_hash_table.cpp in Sources */, 1DC319971ED6534E00DE6D26 /* opennurbs_std_string_utf.cpp in Sources */, 1DC319991ED6534E00DE6D26 /* opennurbs_string_compare.cpp in Sources */, @@ -1458,6 +1473,7 @@ 1DC317CA1ED652B800DE6D26 /* opennurbs_3dm_properties.cpp in Sources */, 1DC319AA1ED6534E00DE6D26 /* opennurbs_subd_matrix.cpp in Sources */, 1DC318231ED652B800DE6D26 /* opennurbs_font.cpp in Sources */, + 99274C5C29F24A48008E57C0 /* opennurbs_archivable_dictionary.cpp in Sources */, 1DC317DB1ED652B800DE6D26 /* opennurbs_base32.cpp in Sources */, 1DC319021ED652F800DE6D26 /* opennurbs_pluginlist.cpp in Sources */, 1DC319EA1ED6534E00DE6D26 /* opennurbs_xform.cpp in Sources */, diff --git a/opennurbs_public_staticlib.vcxproj b/opennurbs_public_staticlib.vcxproj index 677ebd46..4d5da8a3 100644 --- a/opennurbs_public_staticlib.vcxproj +++ b/opennurbs_public_staticlib.vcxproj @@ -258,6 +258,7 @@ + @@ -434,6 +435,7 @@ + diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 440d33c0..dead4409 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -14,10 +14,10 @@ // first step in each build. // #define RMA_VERSION_YEAR 2023 -#define RMA_VERSION_MONTH 2 -#define RMA_VERSION_DATE 27 -#define RMA_VERSION_HOUR 12 -#define RMA_VERSION_MINUTE 17 +#define RMA_VERSION_MONTH 4 +#define RMA_VERSION_DATE 23 +#define RMA_VERSION_HOUR 3 +#define RMA_VERSION_MINUTE 55 //////////////////////////////////////////////////////////////// // @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,0,23058,12170 -#define VERSION_WITH_PERIODS 8.0.23058.12170 +#define VERSION_WITH_COMMAS 8,0,23113,3550 +#define VERSION_WITH_PERIODS 8.0.23113.03550 #define COPYRIGHT "Copyright (C) 1993-2023, 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.23058.12170" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.23058.12170" +#define RMA_VERSION_WITH_PERIODS_STRING "8.0.23113.03550" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.23113.03550" diff --git a/opennurbs_quaternion.h b/opennurbs_quaternion.h index d7430987..0ba02386 100644 --- a/opennurbs_quaternion.h +++ b/opennurbs_quaternion.h @@ -18,7 +18,10 @@ class ON_CLASS ON_Quaternion { public: // quaternion = a + bi + cj + dk - double a,b,c,d; + double a; + double b; + double c; + double d; static const ON_Quaternion Zero; // 0 = (0,0,0,0 static const ON_Quaternion Identity; // 1 = (1,0,0,0) @@ -26,7 +29,7 @@ public: static const ON_Quaternion J; // "j" = (0,0,1,0) static const ON_Quaternion K; // "k" = (0,0,0,1) - ON_Quaternion() {} + ON_Quaternion() { a = b = c = d = 0.0; } ON_Quaternion(double qa, double qb, double qc, double qd); diff --git a/opennurbs_render_channels.cpp b/opennurbs_render_channels.cpp index 31bf2b25..19ab4cd5 100644 --- a/opennurbs_render_channels.cpp +++ b/opennurbs_render_channels.cpp @@ -110,7 +110,7 @@ static ON_wString GetSortedCustomListAsString(const ON_RenderChannels& rch) return GetSortedSemicolonDelimitedString(chan); } -bool ON_RenderChannels::operator == (const ON_RenderChannels& rch) +bool ON_RenderChannels::operator == (const ON_RenderChannels& rch) const { if (Mode() != rch.Mode()) return false; @@ -124,7 +124,7 @@ bool ON_RenderChannels::operator == (const ON_RenderChannels& rch) return true; } -bool ON_RenderChannels::operator != (const ON_RenderChannels& rch) +bool ON_RenderChannels::operator != (const ON_RenderChannels& rch) const { return !(operator == (rch)); } @@ -173,3 +173,12 @@ void ON_RenderChannels::SetCustomList(const ON_SimpleArray& chan) const ON_wString s = GetSortedSemicolonDelimitedString(chan); m_impl->SetParameter(XMLPath(), ON_RDK_RCH_LIST, s); } + +void* ON_RenderChannels::EVF(const wchar_t* func, void* data) +{ + return nullptr; +} + +void ON_RenderChannels::InvalidateCache(void) +{ +} diff --git a/opennurbs_render_channels.h b/opennurbs_render_channels.h index d02f52b6..2cfa9391 100644 --- a/opennurbs_render_channels.h +++ b/opennurbs_render_channels.h @@ -14,18 +14,18 @@ #if !defined(ON_RENDER_CHANNELS_INC_) #define ON_RENDER_CHANNELS_INC_ -class ON_CLASS ON_RenderChannels final +class ON_CLASS ON_RenderChannels { public: ON_RenderChannels(); ON_RenderChannels(ON_XMLNode& model_node); ON_RenderChannels(const ON_RenderChannels& rch); - ~ON_RenderChannels(); + virtual ~ON_RenderChannels(); - const ON_RenderChannels& operator = (const ON_RenderChannels& rch); + virtual const ON_RenderChannels& operator = (const ON_RenderChannels& rch); - bool operator == (const ON_RenderChannels& rch); - bool operator != (const ON_RenderChannels& rch); + virtual bool operator == (const ON_RenderChannels& rch) const; + virtual bool operator != (const ON_RenderChannels& rch) const; enum class Modes : unsigned int { @@ -34,18 +34,25 @@ public: }; // Get the mode. - Modes Mode(void) const; + virtual Modes Mode(void) const; // Set the mode. - void SetMode(Modes m); + virtual void SetMode(Modes m); // Get the list of channels to render when in 'custom' mode. // - 'chan' accepts the channel ids. */ - void GetCustomList(ON_SimpleArray& chan) const; + virtual void GetCustomList(ON_SimpleArray& chan) const; // Set the list of channels to render when in 'custom' mode. // - 'chan' contains the channel ids. - void SetCustomList(const ON_SimpleArray& chan); + virtual void SetCustomList(const ON_SimpleArray& chan); + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +private: // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + virtual void InvalidateCache(void); private: class CImpl; diff --git a/opennurbs_render_content.cpp b/opennurbs_render_content.cpp index 1483ef86..57b8413b 100644 --- a/opennurbs_render_content.cpp +++ b/opennurbs_render_content.cpp @@ -1275,9 +1275,16 @@ ON_RenderMaterial::~ON_RenderMaterial() { } -void ON_RenderMaterial::operator = (const ON_RenderMaterial& mat) +const ON_RenderContent& ON_RenderMaterial::operator = (const ON_RenderContent& c) +{ + ON_RenderContent::operator = (c); + return *this; +} + +const ON_RenderMaterial& ON_RenderMaterial::operator = (const ON_RenderMaterial& mat) { ON_RenderContent::operator = (mat); + return *this; } ON_XMLVariant ParamHelper(const ON_XMLParameters& p, const wchar_t* name) @@ -1422,8 +1429,16 @@ ON_RenderEnvironment::~ON_RenderEnvironment() { } -void ON_RenderEnvironment::operator = (const ON_RenderEnvironment& env) +const ON_RenderContent& ON_RenderEnvironment::operator = (const ON_RenderContent& c) { + ON_RenderContent::operator = (c); + return *this; +} + +const ON_RenderEnvironment& ON_RenderEnvironment::operator = (const ON_RenderEnvironment& env) +{ + ON_RenderContent::operator = (env); + return *this; } ON_Environment ON_RenderEnvironment::SimulatedEnvironment(void) const @@ -1479,8 +1494,16 @@ ON_RenderTexture::~ON_RenderTexture() { } -void ON_RenderTexture::operator = (const ON_RenderTexture& tex) +const ON_RenderContent& ON_RenderTexture::operator = (const ON_RenderContent& c) { + ON_RenderContent::operator = (c); + return *this; +} + +const ON_RenderTexture& ON_RenderTexture::operator = (const ON_RenderTexture& tex) +{ + ON_RenderContent::operator = (tex); + return *this; } ON_Texture ON_RenderTexture::SimulatedTexture(void) const diff --git a/opennurbs_render_content.h b/opennurbs_render_content.h index 19e09992..4be300c0 100644 --- a/opennurbs_render_content.h +++ b/opennurbs_render_content.h @@ -25,10 +25,10 @@ public: ON_Environment(const ON_Environment& src); virtual ~ON_Environment(); - const ON_Environment& operator = (const ON_Environment& src); + virtual const ON_Environment& operator = (const ON_Environment& src); - bool operator == (const ON_Environment& src) const; - bool operator != (const ON_Environment& src) const; + virtual bool operator == (const ON_Environment& src) const; + virtual bool operator != (const ON_Environment& src) const; enum class BackgroundProjections : unsigned int { @@ -44,20 +44,20 @@ public: Hemispherical = 9, }; - ON_Color BackgroundColor(void) const; - void SetBackgroundColor(ON_Color color); + virtual ON_Color BackgroundColor(void) const; + virtual void SetBackgroundColor(ON_Color color); - ON_Texture BackgroundImage(void) const; - void SetBackgroundImage(const ON_Texture& tex); + virtual ON_Texture BackgroundImage(void) const; + virtual void SetBackgroundImage(const ON_Texture& tex); - BackgroundProjections BackgroundProjection(void) const; - void SetBackgroundProjection(BackgroundProjections proj); + virtual BackgroundProjections BackgroundProjection(void) const; + virtual void SetBackgroundProjection(BackgroundProjections proj); static BackgroundProjections ProjectionFromString(const wchar_t* proj); static const wchar_t* StringFromProjection(BackgroundProjections proj); protected: - /** Emergency virtual function for future expansion. */ + // Emergency virtual function for future expansion. virtual void* EVF(const wchar_t* wszFunc, void* pvData); private: @@ -71,78 +71,78 @@ class ON_CLASS ON_RenderContent : public ON_ModelComponent public: ON_RenderContent(const wchar_t* kind); - ON_RenderContent(const ON_RenderContent& c); + ON_RenderContent(const ON_RenderContent&); virtual ~ON_RenderContent(); - const ON_RenderContent& operator = (const ON_RenderContent& rc); + virtual const ON_RenderContent& operator = (const ON_RenderContent&); // Returns: The internal name of the content type. - ON_wString TypeName(void) const; + virtual ON_wString TypeName(void) const; // Set the content's type name. - void SetTypeName(const wchar_t* name); + virtual void SetTypeName(const wchar_t* name); // Returns: The unique id of the content type. - ON_UUID TypeId(void) const; + virtual ON_UUID TypeId(void) const; // Set the content's type id. - void SetTypeId(const ON_UUID& uuid); + virtual void SetTypeId(const ON_UUID& uuid); // Returns: The content's render-engine id. - ON_UUID RenderEngineId(void) const; + virtual ON_UUID RenderEngineId(void) const; // Set the content's render-engine id. - void SetRenderEngineId(const ON_UUID& uuid); + virtual void SetRenderEngineId(const ON_UUID& uuid); // Returns: The content's plug-in id. - ON_UUID PlugInId(void) const; + virtual ON_UUID PlugInId(void) const; // Set the content's plug-in id. - void SetPlugInId(const ON_UUID& uuid); + virtual void SetPlugInId(const ON_UUID& uuid); // Returns: The content's notes. - ON_wString Notes(void) const; + virtual ON_wString Notes(void) const; // Sets the content's notes. - void SetNotes(const wchar_t* notes); + virtual void SetNotes(const wchar_t* notes); // Returns: The content's tags. - ON_wString Tags(void) const; + virtual ON_wString Tags(void) const; // Sets the content's tags. - void SetTags(const wchar_t* tags); + virtual void SetTags(const wchar_t* tags); // Returns: The content's group id. - ON_UUID GroupId(void) const; + virtual ON_UUID GroupId(void) const; // Sets the content's group id. - void SetGroupId(const ON_UUID& group); + virtual void SetGroupId(const ON_UUID& group); // Returns: True if the content is hidden. - bool Hidden(void) const; + virtual bool Hidden(void) const; // Sets whether or not the content is hidden. - void SetHidden(bool hidden); + virtual void SetHidden(bool hidden); // Returns: True if the content is a reference content. - bool Reference(void) const; + virtual bool Reference(void) const; // Sets whether or not the content is a reference content. - void SetReference(bool ref); + virtual void SetReference(bool ref); // Returns: True if the content is automatically deleted when not in use. - bool AutoDelete(void) const; + virtual bool AutoDelete(void) const; // Sets whether or not the content is automatically deleted when not in use. - void SetAutoDelete(bool autodel); + virtual void SetAutoDelete(bool autodel); // Gets a variant giving the type and value of the parameter, if found. // If the parameter is not found, the function returns a null variant. - ON_XMLVariant GetParameter(const wchar_t* name) const; + virtual ON_XMLVariant GetParameter(const wchar_t* name) const; // Sets the value of a named parameter. // Returns: True if successful, else false. - bool SetParameter(const wchar_t* name, const ON_XMLVariant& value); + virtual bool SetParameter(const wchar_t* name, const ON_XMLVariant& value); class ON_CLASS ChildIterator { @@ -153,7 +153,7 @@ public: virtual ON_RenderContent* GetNextChild(void); protected: - /** Emergency virtual function for future expansion. */ + // Emergency virtual function for future expansion. virtual void* EVF(const wchar_t* func, void* data); private: @@ -162,25 +162,25 @@ public: }; // Returns: An iterator for iterating over the content's children. - ChildIterator GetChildIterator(void) const; + virtual ChildIterator GetChildIterator(void) const; // Returns: The parent content or null if this is the top level object. - const ON_RenderContent* Parent(void) const; + virtual const ON_RenderContent* Parent(void) const; // Returns: The first child of this content or null if none. - const ON_RenderContent* FirstChild(void) const; + virtual const ON_RenderContent* FirstChild(void) const; // Returns: The first sibling of this content or null if none. - const ON_RenderContent* NextSibling(void) const; + virtual const ON_RenderContent* NextSibling(void) const; // Returns: The top level parent of this content. - const ON_RenderContent& TopLevel(void) const; + virtual const ON_RenderContent& TopLevel(void) const; // Returns: True if this is a top-level content (i.e., has no parent; is not a child). - bool IsTopLevel(void) const; + virtual bool IsTopLevel(void) const; // Returns: True if this is a child content (i.e., has a parent; is not top-level). - bool IsChild(void) const; + virtual bool IsChild(void) const; // Sets another content as a child of this content. // Param child is the content to set as a child of this content. This content is copied and the @@ -189,41 +189,41 @@ public: // Param childSlotName is the child slot name that will be assigned to this child. // The child slot name cannot be an empty string. If it is, the function will fail. // Returns: True if successful, else false. - bool SetChild(const ON_RenderContent& child, const wchar_t* child_slot_name); + virtual bool SetChild(const ON_RenderContent& child, const wchar_t* child_slot_name); // Returns: The content's child-slot name. - ON_wString ChildSlotName(void) const; + virtual ON_wString ChildSlotName(void) const; // Sets the content's child-slot name. - void SetChildSlotName(const wchar_t* child_slot_name); + virtual void SetChildSlotName(const wchar_t* child_slot_name); // Returns true if the child slot with the specified child slot name is turned on, else false. // Also returns false if there is no child with the specified child slot name. - bool ChildSlotOn(const wchar_t* child_slot_name) const; + virtual bool ChildSlotOn(const wchar_t* child_slot_name) const; // Turns the child slot with the specified child slot name on or off. // Returns: True if successful, else false. - bool SetChildSlotOn(bool on, const wchar_t* child_slot_name); + virtual bool SetChildSlotOn(bool on, const wchar_t* child_slot_name); // Deletes any existing child with the specified child slot name. // Returns: True if successful, else false. - bool DeleteChild(const wchar_t* child_slot_name); + virtual bool DeleteChild(const wchar_t* child_slot_name); // Returns: The child with the specified child slot name, or null if no such child exists. - const ON_RenderContent* FindChild(const wchar_t* child_slot_name) const; + virtual const ON_RenderContent* FindChild(const wchar_t* child_slot_name) const; // Get the render content's state as an XML string. - ON_wString XML(bool recursive) const; + virtual ON_wString XML(bool recursive) const; // Set the render content's state from an XML string. - bool SetXML(const wchar_t* xml); + virtual bool SetXML(const wchar_t* xml); // Returns the XML node that stores the state of this render content. Note that this does not include // any children of the render content. An ON_RenderContent only stores its own XML. - const ON_XMLNode& XMLNode(void) const; + virtual const ON_XMLNode& XMLNode(void) const; // Returns the kind of render content as a string. - ON_wString Kind(void) const; + virtual ON_wString Kind(void) const; // If ON_RenderContent::Cast(ref.ModelComponent()) is not null, // that pointer is returned. Otherwise, none_return_value is returned. @@ -231,7 +231,7 @@ public: const ON_RenderContent* none_return_value); protected: - /** Emergency virtual function for future expansion. */ + // Emergency virtual function for future expansion. virtual void* EVF(const wchar_t* func, void* data); public: @@ -251,9 +251,10 @@ public: ON_RenderMaterial(const ON_RenderMaterial& m); virtual ~ON_RenderMaterial(); - void operator = (const ON_RenderMaterial&); + virtual const ON_RenderContent& operator = (const ON_RenderContent&) override; + virtual const ON_RenderMaterial& operator = (const ON_RenderMaterial&); - ON_Material SimulatedMaterial(void) const; + virtual ON_Material SimulatedMaterial(void) const; }; class ON_CLASS ON_RenderEnvironment : public ON_RenderContent @@ -265,9 +266,10 @@ public: ON_RenderEnvironment(const ON_RenderEnvironment& e); virtual ~ON_RenderEnvironment(); - void operator = (const ON_RenderEnvironment&); + virtual const ON_RenderContent& operator = (const ON_RenderContent&) override; + virtual const ON_RenderEnvironment& operator = (const ON_RenderEnvironment&); - ON_Environment SimulatedEnvironment(void) const; + virtual ON_Environment SimulatedEnvironment(void) const; }; class ON_CLASS ON_RenderTexture : public ON_RenderContent @@ -279,9 +281,10 @@ public: ON_RenderTexture(const ON_RenderTexture& t); virtual ~ON_RenderTexture(); - void operator = (const ON_RenderTexture&); + virtual const ON_RenderContent& operator = (const ON_RenderContent&) override; + virtual const ON_RenderTexture& operator = (const ON_RenderTexture&); - ON_Texture SimulatedTexture(void) const; + virtual ON_Texture SimulatedTexture(void) const; }; #endif diff --git a/opennurbs_safe_frame.cpp b/opennurbs_safe_frame.cpp index 91f89e48..3421447e 100644 --- a/opennurbs_safe_frame.cpp +++ b/opennurbs_safe_frame.cpp @@ -22,18 +22,6 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif -#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" - class ON_SafeFrame::CImpl : public ON_InternalXMLImpl { public: @@ -125,7 +113,7 @@ const ON_SafeFrame& ON_SafeFrame::operator = (const ON_SafeFrame& sf) return *this; } -bool ON_SafeFrame::operator == (const ON_SafeFrame& sf) +bool ON_SafeFrame::operator == (const ON_SafeFrame& sf) const { if (On() != sf.On()) return false; if (PerspectiveOnly() != sf.PerspectiveOnly()) return false; @@ -144,7 +132,7 @@ bool ON_SafeFrame::operator == (const ON_SafeFrame& sf) return true; } -bool ON_SafeFrame::operator != (const ON_SafeFrame& sf) +bool ON_SafeFrame::operator != (const ON_SafeFrame& sf) const { return !(operator == (sf)); } @@ -171,12 +159,12 @@ void ON_SafeFrame::SetPerspectiveOnly(bool b) bool ON_SafeFrame::FieldGridOn(void) const { - return m_impl->GetBaseParameter(ON_RDK_SF_FIELD_DISPLAY_ON, false).AsBool(); + return m_impl->GetBaseParameter(ON_RDK_SF_4x3_FIELD_GRID_ON, false).AsBool(); } void ON_SafeFrame::SetFieldGridOn(bool b) { - m_impl->SetBaseParameter(ON_RDK_SF_FIELD_DISPLAY_ON, b); + m_impl->SetBaseParameter(ON_RDK_SF_4x3_FIELD_GRID_ON, b); } bool ON_SafeFrame::LiveFrameOn(void) const @@ -268,3 +256,12 @@ void ON_SafeFrame::SetTitleFrameYScale(double d) { m_impl->SetFrameParameter(ON_RDK_SF_TITLE_FRAME, ON_RDK_SFF_YSCALE, d); } + +void* ON_SafeFrame::EVF(const wchar_t* func, void* data) +{ + return nullptr; +} + +void ON_SafeFrame::InvalidateCache(void) +{ +} diff --git a/opennurbs_safe_frame.h b/opennurbs_safe_frame.h index 8c0d67d8..147a1d80 100644 --- a/opennurbs_safe_frame.h +++ b/opennurbs_safe_frame.h @@ -14,90 +14,101 @@ #if !defined(ON_SAFE_FRAME_INC_) #define ON_SAFE_FRAME_INC_ -class ON_CLASS ON_SafeFrame final +class ON_CLASS ON_SafeFrame { public: ON_SafeFrame(); ON_SafeFrame(ON_XMLNode& model_node); ON_SafeFrame(const ON_SafeFrame&); - ~ON_SafeFrame(); + virtual ~ON_SafeFrame(); - const ON_SafeFrame& operator = (const ON_SafeFrame& sf); + virtual const ON_SafeFrame& operator = (const ON_SafeFrame& sf); - bool operator == (const ON_SafeFrame& sf); - bool operator != (const ON_SafeFrame& sf); + virtual bool operator == (const ON_SafeFrame& sf) const; + virtual bool operator != (const ON_SafeFrame& sf) const; // Returns true if the safe frame is on. - bool On(void) const; + virtual bool On(void) const; // Sets the safe frame on or off. - void SetOn(bool b); + virtual void SetOn(bool b); // Returns true if the safe frame is only displayed in perspective views. - bool PerspectiveOnly(void) const; + virtual bool PerspectiveOnly(void) const; // Sets whether or not the safe frame is only displayed in perspective views. - void SetPerspectiveOnly(bool b); + virtual void SetPerspectiveOnly(bool b); // Returns true if the 4x3 field grid is on. - bool FieldGridOn(void) const; + virtual bool FieldGridOn(void) const; // Sets whether or not the 4x3 field grid is on. - void SetFieldGridOn(bool b); + virtual void SetFieldGridOn(bool b); // Returns true if the live frame is on. - bool LiveFrameOn(void) const; + virtual bool LiveFrameOn(void) const; // Sets whether or not the live frame is on. - void SetLiveFrameOn(bool b); + virtual void SetLiveFrameOn(bool b); // Returns true if the action frame is on. - bool ActionFrameOn(void) const; + virtual bool ActionFrameOn(void) const; // Sets whether or not the action frame is on. - void SetActionFrameOn(bool b); + virtual void SetActionFrameOn(bool b); // Returns true if the action frame X and Y scales are linked. - bool ActionFrameLinked(void) const; + virtual bool ActionFrameLinked(void) const; // Sets whether or not the action frame X and Y scales are linked. - void SetActionFrameLinked(bool b); + virtual void SetActionFrameLinked(bool b); // Returns the action frame X scale. - double ActionFrameXScale(void) const; + virtual double ActionFrameXScale(void) const; // Sets the action frame X scale. - void SetActionFrameXScale(double d); + // This value should be in the range 0..1 but it is not clamped. + virtual void SetActionFrameXScale(double d); // Returns the action frame Y scale. - double ActionFrameYScale(void) const; + virtual double ActionFrameYScale(void) const; // Sets the action frame Y scale. - void SetActionFrameYScale(double d); + // This value should be in the range 0..1 but it is not clamped. + virtual void SetActionFrameYScale(double d); // Returns true if the title frame is on. - bool TitleFrameOn(void) const; + virtual bool TitleFrameOn(void) const; // Sets whether or not the title frame is on. - void SetTitleFrameOn(bool b); + virtual void SetTitleFrameOn(bool b); // Returns true if the title frame X and Y scales are linked. - bool TitleFrameLinked(void) const; + virtual bool TitleFrameLinked(void) const; // Sets whether or not the title frame X and Y scales are linked. - void SetTitleFrameLinked(bool b); + virtual void SetTitleFrameLinked(bool b); // Returns the title frame X scale. - double TitleFrameXScale(void) const; + virtual double TitleFrameXScale(void) const; // Sets the title frame X scale. - void SetTitleFrameXScale(double d); + // This value should be in the range 0..1 but it is not clamped. + virtual void SetTitleFrameXScale(double d); // Returns the title frame Y scale. - double TitleFrameYScale(void) const; + virtual double TitleFrameYScale(void) const; // Sets the title frame Y scale. - void SetTitleFrameYScale(double d); + // This value should be in the range 0..1 but it is not clamped. + virtual void SetTitleFrameYScale(double d); + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +private: // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + virtual void InvalidateCache(void); private: class CImpl; diff --git a/opennurbs_sectionstyle.cpp b/opennurbs_sectionstyle.cpp new file mode 100644 index 00000000..e9b0d07c --- /dev/null +++ b/opennurbs_sectionstyle.cpp @@ -0,0 +1,632 @@ +// +// Copyright (c) 1993-2023 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +class ON_SectionStylePrivate +{ +public: + ON_SectionStyle::SectionBackgroundFillMode m_background_fill_mode = ON_SectionStyle::SectionBackgroundFillMode::Viewport; + ON::SectionFillRule m_fill_rule = ON::SectionFillRule::ClosedCurves; + int m_hatch_index = ON_UNSET_INT_INDEX; // ON_HatchPattern::Unset.Index(); + double m_hatch_scale = 1.0; + double m_hatch_rotation = 0.0; + ON_Color m_background_fill_color = ON_Color::UnsetColor; + ON_Color m_background_fill_print_color = ON_Color::UnsetColor; + ON_Color m_boundary_color = ON_Color::UnsetColor; + ON_Color m_boundary_print_color = ON_Color::UnsetColor; + ON_Color m_hatch_color = ON_Color::UnsetColor; + ON_Color m_hatch_print_color = ON_Color::UnsetColor; + bool m_boundary_visible = true; + double m_boundary_width_scale = 3.0; + + bool operator==(const ON_SectionStylePrivate& other) const; +}; + +static ON_SectionStylePrivate DefaultSectionStylePrivate; + +bool ON_SectionStylePrivate::operator==(const ON_SectionStylePrivate& other) const +{ + return m_background_fill_mode == other.m_background_fill_mode && + m_fill_rule == other.m_fill_rule && + m_hatch_index == other.m_hatch_index && + m_hatch_rotation == other.m_hatch_rotation && + m_background_fill_color == other.m_background_fill_color && + m_background_fill_print_color == other.m_background_fill_print_color && + m_boundary_color == other.m_boundary_color && + m_boundary_print_color == other.m_boundary_print_color && + m_hatch_color == other.m_hatch_color && + m_hatch_print_color == other.m_hatch_print_color && + m_boundary_visible == other.m_boundary_visible && + m_boundary_width_scale == other.m_boundary_width_scale; +} + +////////////////////////////////////////////////////////////////////// +// class ON_SectionStyle + +ON_OBJECT_IMPLEMENT( ON_SectionStyle, ON_ModelComponent, "6D0DE8B6-598B-4784-B2CA-A42BEB77B521" ); + +ON_SectionStyle::ON_SectionStyle() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::SectionStyle) +{ +} + +ON_SectionStyle::ON_SectionStyle( const ON_SectionStyle& src ) + : ON_ModelComponent(ON_ModelComponent::Type::SectionStyle,src) +{ + if (src.m_private) + m_private = new ON_SectionStylePrivate(*src.m_private); +} + +ON_SectionStyle::~ON_SectionStyle() +{ + if (m_private) + delete m_private; +} + +ON_SectionStyle& ON_SectionStyle::operator=(const ON_SectionStyle& other) +{ + if (this != &other) + { + ON_ModelComponent::operator=(other); + if (m_private) + { + delete m_private; + m_private = nullptr; + } + if (other.m_private) + m_private = new ON_SectionStylePrivate(*other.m_private); + } + return *this; +} +bool ON_SectionStyle::IsValid( ON_TextLog* text_log ) const +{ + if (false == ON_ModelComponent::IsValid(text_log)) + return false; + + // An ON_SectionStyle with an empty name is valid. + if (BoundaryWidthScale() <= 0) + { + if ( text_log ) + text_log->Print("ON_SectionStyle::BoundaryWidthScale <= 0\n"); + return false; + } + + if (HatchScale() <= 0) + { + if (text_log) + text_log->Print("ON_SectionStyle::SectionHatchScale <= 0\n"); + return false; + } + + return true; +} + +// 12 Aug 2021 S. Baer +// When adding new fields written to 3dm files, always add information to this +// Dump function. Dump is used by the opennurbs file testing framework to +// perform comparisons and is useful for manual comparison in when tests fail. +void ON_SectionStyle::Dump( ON_TextLog& dump ) const +{ + ON_ModelComponent::Dump(dump); + + switch (BackgroundFillMode()) + { + case ON_SectionStyle::SectionBackgroundFillMode::None: + dump.Print("No background fill\n"); + break; + case ON_SectionStyle::SectionBackgroundFillMode::Viewport: + dump.Print("Viewport background fill\n"); + break; + case ON_SectionStyle::SectionBackgroundFillMode::SolidColor: + dump.Print("Solid color background fill\n"); + break; + } + + if (BoundaryVisible()) + dump.Print("Boundary visible\n"); + else + dump.Print("Boundary not visible\n"); + + dump.Print("Boundary width scale = %g\n", BoundaryWidthScale()); + switch (SectionFillRule()) + { + case ON::SectionFillRule::ClosedCurves: + dump.Print("Fill when section makes closed curves\n"); + break; + case ON::SectionFillRule::SolidObjects: + dump.Print("Fill only with solid objects\n"); + break; + } + + dump.Print("Hatch index = %d\n", HatchIndex()); + dump.Print("Hatch scale = %g\n", HatchScale()); + dump.Print("Hatch rotation = %g\n", HatchRotation()); + + dump.Print("Boundary color"); + dump.PrintColor(BoundaryColor(false)); + dump.Print("\n"); + dump.Print("Boundary print color"); + dump.PrintColor(BoundaryColor(true)); + dump.Print("\n"); + dump.Print("Background fill color"); + dump.PrintColor(BackgroundFillColor(false)); + dump.Print("\n"); + dump.Print("Background fill print color"); + dump.PrintColor(BackgroundFillColor(true)); + dump.Print("\n"); + dump.Print("Hatch color"); + dump.PrintColor(HatchColor(false)); + dump.Print("\n"); + dump.Print("Hatch print color"); + dump.PrintColor(HatchColor(true)); + dump.Print("\n"); +} + +// 29 Nov. 2022 S. Baer +// This enum is patterned off of ON_3dmObjectAttributeTypeCode +enum ON_SectionStyleTypeCodes : unsigned char +{ + BackgroundFillMode = 1, + BackgroundFillColor = 2, + BoundaryVisible = 3, + BoundaryColor = 4, + BoundaryWidthScale = 5, + SectionFillRule = 6, + HatchIndex = 7, + HatchScale = 8, + HatchRotation = 9, + HatchColor = 10, + + LastSectionStyleTypeCode = 10 +}; + +bool ON_SectionStyle::Write( ON_BinaryArchive& file) const +{ + bool rc = false; + { + const int minor_version = 0; + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, minor_version)) + return false; + for (;;) + { + // chunk version 1.0 fields + if (!file.WriteModelComponentAttributes(*this,ON_ModelComponent::Attributes::BinaryArchiveAttributes)) + break; + + // Only write non-default values in a similar fashion as ON_3dmObjectAttributes + if (BackgroundFillMode() != ON_SectionStyle::Unset.BackgroundFillMode()) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::BackgroundFillMode; // 1 + if (!file.WriteChar(itemType)) + break; + unsigned char mode = (unsigned char)BackgroundFillMode(); + if (!file.WriteChar(mode)) + break; + } + if (BackgroundFillColor(false).IsSet() || BackgroundFillColor(true).IsSet()) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::BackgroundFillColor; // 2 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteColor(BackgroundFillColor(false))) + break; + if (!file.WriteColor(BackgroundFillColor(true))) + break; + } + if (BoundaryVisible() != true) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::BoundaryVisible; // 3 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteBool(BoundaryVisible())) + break; + } + if (BoundaryColor(false).IsSet() || BoundaryColor(true).IsSet()) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::BoundaryColor; // 4 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteColor(BoundaryColor(false))) + break; + if (!file.WriteColor(BoundaryColor(true))) + break; + } + if (fabs(BoundaryWidthScale() - DefaultSectionStylePrivate.m_boundary_width_scale) > ON_EPSILON) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::BoundaryWidthScale; // 5 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteDouble(BoundaryWidthScale())) + break; + } + if (SectionFillRule() != DefaultSectionStylePrivate.m_fill_rule) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::SectionFillRule; // 6 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteChar((unsigned char)SectionFillRule())) + break; + } + if (HatchIndex() != DefaultSectionStylePrivate.m_hatch_index) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::HatchIndex; // 7 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteInt(HatchIndex())) + break; + } + if (fabs(HatchScale() - 1.0) > ON_EPSILON) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::HatchScale; // 8 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteDouble(HatchScale())) + break; + } + if (HatchRotation() != 0.0) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::HatchRotation; // 9 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteDouble(HatchRotation())) + break; + } + if (HatchColor(false).IsSet() || HatchColor(true).IsSet()) + { + const unsigned char itemType = ON_SectionStyleTypeCodes::HatchColor; // 10 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteColor(HatchColor(false))) + break; + if (!file.WriteColor(HatchColor(true))) + break; + } + + // 0 indicates end of new linetype attributes + const unsigned char attributes_end = 0; + if (!file.WriteChar(attributes_end)) + break; + + rc = true; + break; + } + } + if (!file.EndWrite3dmChunk()) + rc = false; + return rc; +} + +static ON_SectionStyle::SectionBackgroundFillMode SectionBackgroundFillModeFromUnsigned(unsigned int i) +{ + if ((unsigned int)ON_SectionStyle::SectionBackgroundFillMode::None == i) + return ON_SectionStyle::SectionBackgroundFillMode::None; + if ((unsigned int)ON_SectionStyle::SectionBackgroundFillMode::SolidColor == i) + return ON_SectionStyle::SectionBackgroundFillMode::SolidColor; + + return ON_SectionStyle::SectionBackgroundFillMode::Viewport; +} + +bool ON_SectionStyle::Read( ON_BinaryArchive& file) +{ + *this = ON_SectionStyle(); + + int major_version=0; + int minor_version=0; + if (!file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version )) + return false; + + bool rc = false; + + if ( 1 == major_version ) + { + for (;;) + { + // chunk version 1.0 fields + unsigned int model_component_attributes_filter = 0; + if (!file.ReadModelComponentAttributes(*this,&model_component_attributes_filter)) + break; + + unsigned char item_id = 0; + if (!file.ReadChar(&item_id)) + break; + + if (ON_SectionStyleTypeCodes::BackgroundFillMode == item_id) // 1 + { + unsigned char c = 0; + if (!file.ReadChar(&c)) + break; + + SectionBackgroundFillMode mode = SectionBackgroundFillModeFromUnsigned(c); + SetBackgroundFillMode(mode); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::BackgroundFillColor == item_id) // 2 + { + ON_Color c = ON_Color::UnsetColor; + if (!file.ReadColor(c)) + break; + SetBackgroundFillColor(c, false); + if (!file.ReadColor(c)) + break; + SetBackgroundFillColor(c, true); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::BoundaryVisible == item_id) // 3 + { + bool b = true; + if (!file.ReadBool(&b)) + break; + SetBoundaryVisible(b); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::BoundaryColor == item_id) // 4 + { + ON_Color c = ON_Color::UnsetColor; + if (!file.ReadColor(c)) + break; + SetBoundaryColor(c, false); + if (!file.ReadColor(c)) + break; + SetBoundaryColor(c, true); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::BoundaryWidthScale == item_id) // 5 + { + double scale = 1.0; + if (!file.ReadDouble(&scale)) + break; + SetBoundaryWidthScale(scale); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::SectionFillRule == item_id) // 6 + { + unsigned char rule = 0; + if (!file.ReadChar(&rule)) + break; + SetSectionFillRule(ON::SectionFillRuleFromUnsigned(rule)); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::HatchIndex == item_id) // 7 + { + int index = 0; + if (!file.ReadInt(&index)) + break; + SetHatchIndex(index); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::HatchScale == item_id) // 8 + { + double scale = 1.0; + if (!file.ReadDouble(&scale)) + break; + SetHatchScale(scale); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::HatchRotation == item_id) // 9 + { + double rotation = 0; + if (!file.ReadDouble(&rotation)) + break; + SetHatchRotation(rotation); + if (!file.ReadChar(&item_id)) + break; + } + if (ON_SectionStyleTypeCodes::HatchColor == item_id) // 10 + { + ON_Color c = ON_Color::UnsetColor; + if (!file.ReadColor(c)) + break; + SetHatchColor(c, false); + if (!file.ReadColor(c)) + break; + SetHatchColor(c, true); + if (!file.ReadChar(&item_id)) + break; + } + + + if (item_id > ON_SectionStyleTypeCodes::LastSectionStyleTypeCode) + { + // we are reading file written with code newer + // than this code (minor_version > 0) + item_id = 0; + } + + rc = true; + break; + } + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_SectionStyle::SectionAttributesEqual(const ON_SectionStyle& other) const +{ + if (nullptr == m_private && nullptr == other.m_private) + return true; + + if (m_private && other.m_private) + { + return (*m_private) == (*other.m_private); + } + + return false; +} + +ON_SectionStyle::SectionBackgroundFillMode ON_SectionStyle::BackgroundFillMode() const +{ + return m_private ? m_private->m_background_fill_mode : DefaultSectionStylePrivate.m_background_fill_mode; +} +void ON_SectionStyle::SetBackgroundFillMode(ON_SectionStyle::SectionBackgroundFillMode mode) +{ + if (BackgroundFillMode() == mode) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + m_private->m_background_fill_mode = mode; +} + +ON_Color ON_SectionStyle::BackgroundFillColor(bool print) const +{ + if (m_private) + return print ? m_private->m_background_fill_print_color : m_private->m_background_fill_color; + return ON_Color::UnsetColor; +} +void ON_SectionStyle::SetBackgroundFillColor(const ON_Color& color, bool print) +{ + if (BackgroundFillColor(print) == color) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + + if (print) + m_private->m_background_fill_print_color = color; + else + m_private->m_background_fill_color = color; +} + +bool ON_SectionStyle::BoundaryVisible() const +{ + return m_private ? m_private->m_boundary_visible : DefaultSectionStylePrivate.m_boundary_visible; +} +void ON_SectionStyle::SetBoundaryVisible(bool on) +{ + if (BoundaryVisible() == on) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + m_private->m_boundary_visible = on; +} + +ON_Color ON_SectionStyle::BoundaryColor(bool print) const +{ + if (m_private) + return print ? m_private->m_boundary_print_color : m_private->m_boundary_color; + return ON_Color::UnsetColor; +} +void ON_SectionStyle::SetBoundaryColor(const ON_Color& color, bool print) +{ + if (BoundaryColor(print) == color) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + + if (print) + m_private->m_boundary_print_color = color; + else + m_private->m_boundary_color = color; +} + +double ON_SectionStyle::BoundaryWidthScale() const +{ + return m_private ? m_private->m_boundary_width_scale : DefaultSectionStylePrivate.m_boundary_width_scale; +} +void ON_SectionStyle::SetBoundaryWidthScale(double scale) +{ + if (fabs(BoundaryWidthScale() - scale) < ON_EPSILON) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + m_private->m_boundary_width_scale = scale; +} + +ON::SectionFillRule ON_SectionStyle::SectionFillRule() const +{ + return m_private ? m_private->m_fill_rule : DefaultSectionStylePrivate.m_fill_rule; +} +void ON_SectionStyle::SetSectionFillRule(ON::SectionFillRule rule) +{ + if (SectionFillRule() == rule) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + m_private->m_fill_rule = rule; +} + +int ON_SectionStyle::HatchIndex() const +{ + return m_private ? m_private->m_hatch_index : DefaultSectionStylePrivate.m_hatch_index; +} +void ON_SectionStyle::SetHatchIndex(int index) +{ + if (HatchIndex() == index) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + m_private->m_hatch_index = index; +} + +double ON_SectionStyle::HatchScale() const +{ + return m_private ? m_private->m_hatch_scale : DefaultSectionStylePrivate.m_hatch_scale; +} +void ON_SectionStyle::SetHatchScale(double scale) +{ + if (fabs(HatchScale() - scale) < ON_EPSILON) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + m_private->m_hatch_scale = scale; +} + +double ON_SectionStyle::HatchRotation() const +{ + return m_private ? m_private->m_hatch_rotation : DefaultSectionStylePrivate.m_hatch_rotation; +} +void ON_SectionStyle::SetHatchRotation(double rotation) +{ + if (fabs(HatchRotation() - rotation) < ON_EPSILON) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + m_private->m_hatch_rotation = rotation; +} + +ON_Color ON_SectionStyle::HatchColor(bool print) const +{ + if (m_private) + return print ? m_private->m_hatch_print_color : m_private->m_hatch_color; + return ON_Color::UnsetColor; +} +void ON_SectionStyle::SetHatchColor(const ON_Color& color, bool print) +{ + if (HatchColor(print) == color) + return; + if (nullptr == m_private) + m_private = new ON_SectionStylePrivate(); + + if (print) + m_private->m_hatch_print_color = color; + else + m_private->m_hatch_color = color; +} diff --git a/opennurbs_sectionstyle.h b/opennurbs_sectionstyle.h new file mode 100644 index 00000000..64f8634c --- /dev/null +++ b/opennurbs_sectionstyle.h @@ -0,0 +1,146 @@ +// +// Copyright (c) 1993-2023 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_SECTIONSTYLE_INC_) +#define OPENNURBS_SECTIONSTYLE_INC_ + + +////////////////////////////////////////////////////////////////////// +// class ON_SectionStyle + +class ON_CLASS ON_SectionStyle : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_SectionStyle); + +public: + ON_SectionStyle() ON_NOEXCEPT; + ~ON_SectionStyle(); + ON_SectionStyle(const ON_SectionStyle& other); + ON_SectionStyle& operator=(const ON_SectionStyle& other); + + static const ON_SectionStyle Unset; // index = ON_UNSET_INT_INDEX, id = nil + + /* + Description: + Tests that name is set and there is at least one non-zero length segment + */ + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + /* + Description: + Write to file (serialize definition to binary archive) + */ + bool Write(ON_BinaryArchive&) const override; + + /* + Description: + Read from file (restore definition from binary archive) + */ + bool Read(ON_BinaryArchive&) override; + + + /* + Description: + Test only the section style attributes below for equality. Does not + perform any testing of the ON_ModelComponent fields + Parameters: + other: other section style to compare against this + */ + bool SectionAttributesEqual(const ON_SectionStyle& other) const; + +#pragma region RH_C_SHARED_ENUM [SectionBackgroundFillMode] [Rhino.DocObjects.SectionBackgroundFillMode] [byte] + /// + /// Defines how a background fill is applied to a section + /// + enum class SectionBackgroundFillMode : unsigned char + { + /// + /// Do not fill in the background of a section + /// + None = 0, + /// + /// Use the current display mode settings to determine how to apply a fill. + /// Wireframe will not fill while shaded and rendered will fill using colors + /// and materials matching how the object is drawn. + /// + Viewport = 1, + /// + /// Use a solid color for a background fill. If BackgroundFillColor is set + /// on this section style, then that color will be used. Otherwise, the item's + /// color that this section style is associated will be used + /// + SolidColor = 2 + }; +#pragma endregion + + // How a background fill is applied + SectionBackgroundFillMode BackgroundFillMode() const; + void SetBackgroundFillMode(SectionBackgroundFillMode mode); + + + // Custom background fill color. If unset (default), the object's color or + // material is used for a fill + ON_Color BackgroundFillColor(bool print) const; + void SetBackgroundFillColor(const ON_Color& color, bool print); + + // Should the boundary curves for a section be shown (default is true) + bool BoundaryVisible() const; + void SetBoundaryVisible(bool on); + + // Custom boundary color for a section. If unset (default), the object's + // color is used + ON_Color BoundaryColor(bool print) const; + void SetBoundaryColor(const ON_Color& color, bool print); + + // Width scale to apply to an object's linetype for sections. Default is 3 + double BoundaryWidthScale() const; + void SetBoundaryWidthScale(double scale); + + // When to fill/hatch the sections for an object can depend on the type of + // object being sectioned. See ON_SectionFillRule for the choices of + // when to generate hatches. + ON::SectionFillRule SectionFillRule() const; + void SetSectionFillRule(ON::SectionFillRule rule); + + // Hatch pattern index for hatch to use when drawing a closed section + // Default is ON_UNSET_INT_INDEX which means don't draw a hatch + int HatchIndex() const; + void SetHatchIndex(int index); + + // Scale applied to the hatch pattern for a section + double HatchScale() const; + void SetHatchScale(double scale); + + // Rotation angle in radians applied to hatch pattern for a section. + double HatchRotation() const; + void SetHatchRotation(double rotation); + + // Custom hatch pattern color for a section. If unset (default), the object's + // color is used + ON_Color HatchColor(bool print) const; + void SetHatchColor(const ON_Color& color, bool print); + +private: + class ON_SectionStylePrivate* m_private = nullptr; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray; +#endif + +#endif + diff --git a/opennurbs_sha1.h b/opennurbs_sha1.h index 9542ca06..7883ef2c 100644 --- a/opennurbs_sha1.h +++ b/opennurbs_sha1.h @@ -584,9 +584,9 @@ private: ON__UINT32 m_reserved = 0; // current "remainder" - ON__UINT8 m_buffer[64]; // bytes that didn't fit in last 64 byte chunk - ON__UINT32 m_bit_count[2]; // number of bits (lo, hi) - ON__UINT32 m_state[5]; // current state + ON__UINT8 m_buffer[64] = {}; // bytes that didn't fit in last 64 byte chunk + ON__UINT32 m_bit_count[2] = {}; // number of bits (lo, hi) + ON__UINT32 m_state[5] = {}; // current state // cached SHA1 hash - valid if 2 = (2 & m_status_bits) mutable ON_SHA1_Hash m_sha1_hash; diff --git a/opennurbs_skylight.cpp b/opennurbs_skylight.cpp index 67e37c4f..47e4f276 100644 --- a/opennurbs_skylight.cpp +++ b/opennurbs_skylight.cpp @@ -22,11 +22,6 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif -#define ON_RDK_SKYLIGHT_ON L"skylight-on" -#define ON_RDK_SKYLIGHT_SHADOW_INTENSITY L"skylight-shadow-intensity" -#define ON_RDK_SKYLIGHT_CUSTOM_ENVIRONMENT_ON L"skylight-custom-environment-on" -#define ON_RDK_SKYLIGHT_CUSTOM_ENVIRONMENT L"skylight-custom-environment" - class ON_Skylight::CImpl : public ON_InternalXMLImpl { public: @@ -65,66 +60,71 @@ const ON_Skylight& ON_Skylight::operator = (const ON_Skylight& sl) { if (this != &sl) { - SetOn (sl.On()); - SetCustomEnvironmentOn(sl.CustomEnvironmentOn()); - SetCustomEnvironment (sl.CustomEnvironment()); - SetShadowIntensity (sl.ShadowIntensity()); + SetOn(sl.On()); + SetShadowIntensity(sl.ShadowIntensity()); } return *this; } -bool ON_Skylight::operator == (const ON_Skylight& sl) +bool ON_Skylight::operator == (const ON_Skylight& sl) const { - if (On() != sl.On() ) return false; - if (CustomEnvironmentOn() != sl.CustomEnvironmentOn()) return false; - if (CustomEnvironment() != sl.CustomEnvironment() ) return false; - if (ShadowIntensity() != sl.ShadowIntensity() ) return false; + if (On() != sl.On()) return false; + if (ShadowIntensity() != sl.ShadowIntensity()) return false; return true; } -bool ON_Skylight::operator != (const ON_Skylight& gp) +bool ON_Skylight::operator != (const ON_Skylight& sl) const { - return !(operator == (gp)); + return !(operator == (sl)); } bool ON_Skylight::On(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_SKYLIGHT_ON, false).AsBool(); + return m_impl->GetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_ON, false).AsBool(); } void ON_Skylight::SetOn(bool b) { - m_impl->SetParameter(XMLPath(), ON_RDK_SKYLIGHT_ON, b); -} - -bool ON_Skylight::CustomEnvironmentOn(void) const -{ - return m_impl->GetParameter(XMLPath(), ON_RDK_SKYLIGHT_CUSTOM_ENVIRONMENT_ON, false).AsBool(); -} - -void ON_Skylight::SetCustomEnvironmentOn(bool on) -{ - m_impl->SetParameter(XMLPath(), ON_RDK_SKYLIGHT_CUSTOM_ENVIRONMENT_ON, on); -} - -ON_UUID ON_Skylight::CustomEnvironment(void) const -{ - return m_impl->GetParameter(XMLPath(), ON_RDK_SKYLIGHT_CUSTOM_ENVIRONMENT, ON_nil_uuid).AsUuid(); -} - -void ON_Skylight::SetCustomEnvironment(const ON_UUID& uuid) -{ - m_impl->SetParameter(XMLPath(), ON_RDK_SKYLIGHT_CUSTOM_ENVIRONMENT, uuid); + m_impl->SetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_ON, b); } double ON_Skylight::ShadowIntensity(void) const { - return m_impl->GetParameter(XMLPath(), ON_RDK_SKYLIGHT_SHADOW_INTENSITY, 1.0).AsDouble(); + return m_impl->GetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_SHADOW_INTENSITY, 1.0).AsDouble(); } void ON_Skylight::SetShadowIntensity(double d) { - m_impl->SetParameter(XMLPath(), ON_RDK_SKYLIGHT_SHADOW_INTENSITY, d); + m_impl->SetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_SHADOW_INTENSITY, d); +} + +void* ON_Skylight::EVF(const wchar_t*, void*) +{ + return nullptr; +} + +bool ON_Skylight::EnvironmentOverride(void) const +{ + return m_impl->GetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_OVERRIDE, false).AsBool(); +} + +void ON_Skylight::SetEnvironmentOverride(bool on) +{ + m_impl->SetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_OVERRIDE, on); +} + +ON_UUID ON_Skylight::EnvironmentId(void) const +{ + return m_impl->GetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_ID, ON_nil_uuid).AsUuid(); +} + +void ON_Skylight::SetEnvironmentId(const ON_UUID& id) +{ + m_impl->SetParameter(XMLPath(), ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_ID, id); +} + +void ON_Skylight::InvalidateCache(void) +{ } diff --git a/opennurbs_skylight.h b/opennurbs_skylight.h index c97cd66c..d2f2422f 100644 --- a/opennurbs_skylight.h +++ b/opennurbs_skylight.h @@ -14,42 +14,44 @@ #if !defined(ON_SKYLIGHT_INC_) #define ON_SKYLIGHT_INC_ -class ON_CLASS ON_Skylight final +class ON_CLASS ON_Skylight { public: ON_Skylight(); ON_Skylight(ON_XMLNode& model_node); ON_Skylight(const ON_Skylight& sl); - ~ON_Skylight(); + virtual ~ON_Skylight(); - const ON_Skylight& operator = (const ON_Skylight& sl); + virtual const ON_Skylight& operator = (const ON_Skylight& sl); - bool operator == (const ON_Skylight& sl); - bool operator != (const ON_Skylight& sl); + virtual bool operator == (const ON_Skylight& sl) const; + virtual bool operator != (const ON_Skylight& sl) const; // Returns true if the skylight is enabled, else false. - bool On(void) const; + virtual bool On(void) const; // Set the skylight enabled state. - void SetOn(bool b); - - // Returns true if the skylight custom environment is on, else false. - bool CustomEnvironmentOn(void) const; - - // Returns the skylight custom environment instance id. If none has been set, returns ON_nil_uuid. - ON_UUID CustomEnvironment(void) const; - - // Set the skylight custom environment on or off. - void SetCustomEnvironmentOn(bool on); - - // Set the skylight custom environment instance id. - void SetCustomEnvironment(const ON_UUID& uuid); + virtual void SetOn(bool b); // Returns the skylight shadow intensity. This is unused at present. - double ShadowIntensity(void) const; + virtual double ShadowIntensity(void) const; // Set the skylight shadow intensity. This is unused at present. - void SetShadowIntensity(double d); + virtual void SetShadowIntensity(double d); + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +public: // These methods are only here to support deprecated C# SDK methods. + // Please use the equivalent methods in ON_3dmRenderSettings. + /* DEPRECATED*/ bool EnvironmentOverride(void) const; + /* DEPRECATED*/ void SetEnvironmentOverride(bool on); + /* DEPRECATED*/ ON_UUID EnvironmentId(void) const; + /* DEPRECATED*/ void SetEnvironmentId(const ON_UUID& id); + +private: // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + virtual void InvalidateCache(void); private: class CImpl; diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp index 0d4526ed..94fe92ad 100644 --- a/opennurbs_statics.cpp +++ b/opennurbs_statics.cpp @@ -101,204 +101,6 @@ ON__UINT64 ON_NextContentSerialNumber() return (0 != ++serial_number) ? serial_number : ++serial_number; } - -// It is critical that ON_ModelComponent::Internal_RuntimeSerialNumberGenerator -// be constructed before any instance of a class derived from ON_ModelComponent. -// That is why it is above the ClassId stuff in this .cpp file. -// Serial numbers below UINT32_MAX + 1 are reserved for Rhino use. -std::atomic ON_ModelComponent::Internal_RuntimeSerialNumberGenerator(UINT32_MAX + 1ULL); - -std::atomic ON_SubDimple::Internal_RuntimeSerialNumberGenerator; - -const double ON_SubDExpandEdgesParameters::OffsetTolerance = 0.001; - -const double ON_SubDExpandEdgesParameters::MinimumOffset = 0.05; - -const double ON_SubDExpandEdgesParameters::MaximumOffset = 0.95; - -const double ON_SubDExpandEdgesParameters::SmallOffset = 0.125; - -const double ON_SubDExpandEdgesParameters::MediumOffset = 0.25; - -const double ON_SubDExpandEdgesParameters::LargeOffset = 0.5; - -const ON_SubDExpandEdgesParameters ON_SubDExpandEdgesParameters::Default; - -const ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponentLocation::Surface; - -// 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 ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Zero; - -const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Nan = ON_SubDEdgeSharpness::FromConstant(ON_DBL_QNAN); - -// NOTE WELL: -// It is required that (3 + 2^ON_SubDEdgeSharpness::Maximum) <= ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity -const double ON_SubDEdgeSharpness::Maximum = 4.0; -const double ON_SubDEdgeSharpness::Tolerance = 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; - -const ON_SubDSectorId ON_SubDSectorId::Zero; -const ON_SubDSectorId ON_SubDSectorId::Invalid = ON_SubDSectorId::Create(nullptr, nullptr); - -const ON_SubDHash ON_SubDHash::Empty; - - -// {C3D8DD54-F8C8-4455-BB0E-2A2F4988EC81} -const ON_UUID ON_SubD::FastAndSimpleFacePackingId = -{ 0xc3d8dd54, 0xf8c8, 0x4455, { 0xbb, 0xe, 0x2a, 0x2f, 0x49, 0x88, 0xec, 0x81 } }; - -// ON_SubD::DefaultFacePackingId must always identitify a built-in face packing -// algoritm. If a new built-in algorithm is developed that produces generally -// better packings and is as fast and reliable as the current default, then -// ON_SubD::DefaultFacePackingId can be changed. Under no circumstances, should -// the default be changed to anything that is more than 1.5 times slower than -// the either the fast and simple or the current default on large models -// like the Mt St Helens Subd. -// -// ** If it's not really fast, then it cannot be the the default. ** -const ON_UUID ON_SubD::DefaultFacePackingId = ON_SubD::FastAndSimpleFacePackingId; - -const ON_SubDToBrepParameters Internal_SubDToBrepParameters(bool bPackedFaces) -{ - ON_SubDToBrepParameters p; - p.SetPackFaces(bPackedFaces); - return p; -} - -const ON_SubDToBrepParameters ON_SubDToBrepParameters::Default; -const ON_SubDToBrepParameters ON_SubDToBrepParameters::DefaultUnpacked = Internal_SubDToBrepParameters(false); -const ON_SubDToBrepParameters ON_SubDToBrepParameters::DefaultPacked = Internal_SubDToBrepParameters(true); - - -const ON_SubDRTreeVertexFinder ON_SubDRTreeVertexFinder::Unset; - - -ON_ClassId* ON_ClassId::m_p0 = 0; // static pointer to first id in list -ON_ClassId* ON_ClassId::m_p1 = 0; // static pointer to last id in list -int ON_ClassId::m_mark0 = 0; - -// {1311ADCB-D89E-4051-A3F0-F64441FB8EC6} -const ON_UUID ON_StandardDisplayModeId::Wireframe = -{ 0x1311ADCB,0xD89E,0x4051,{ 0xA3,0xF0,0xF6,0x44,0x41,0xFB,0x8E,0xC6 } }; - -// {8BC8DEBE-C83B-4c47-B13C-9DB074510CAC} -const ON_UUID ON_StandardDisplayModeId::Shaded = -{ 0x8BC8DEBE,0xC83B,0x4c47,{ 0xB1,0x3C,0x9D,0xB0,0x74,0x51,0x0C,0xAC } }; - -// {CAE60BAE-2D51-4299-ABF7-A339FCA86F3B} -const ON_UUID ON_StandardDisplayModeId::Rendered = -{ 0xCAE60BAE,0x2D51,0x4299,{ 0xAB,0xF7,0xA3,0x39,0xFC,0xA8,0x6F,0x3B } }; - -// {FF608B97-81D3-4186-831C-41F7DC140881} -const ON_UUID ON_StandardDisplayModeId::Ghosted = -{ 0xFF608B97,0x81D3,0x4186,{ 0x83,0x1C,0x41,0xF7,0xDC,0x14,0x08,0x81 } }; - -// {B5C19D5D-0AEC-4ff7-A10E-E052E660263A} -const ON_UUID ON_StandardDisplayModeId::XrayShade = -{ 0xB5C19D5D,0x0AEC,0x4ff7,{ 0xA1,0x0E,0xE0,0x52,0xE6,0x60,0x26,0x3A } }; - -// {A5545314-9D87-428d-95AE-91052EEAD0FA} -const ON_UUID ON_StandardDisplayModeId::RenderedShadows = -{ 0xA5545314,0x9D87,0x428d,{ 0x95,0xAE,0x91,0x05,0x2E,0xEA,0xD0,0xFA } }; - -// {63612C72-778F-4afd-B81B-17426FDFE8A6} -const ON_UUID ON_StandardDisplayModeId::Technical = -{ 0x63612C72,0x778F,0x4afd,{ 0xB8,0x1B,0x17,0x42,0x6F,0xDF,0xE8,0xA6 } }; - -// {B46AB226-05A0-4568-B454-4B1AB721C675} -const ON_UUID ON_StandardDisplayModeId::Artistic = -{ 0xB46AB226,0x05A0,0x4568,{ 0xB4,0x54,0x4B,0x1A,0xB7,0x21,0xC6,0x75 } }; - -// {F4616FA5-A831-4620-A97E-9B807D5EC376} -const ON_UUID ON_StandardDisplayModeId::Pen = -{ 0xF4616FA5,0xA831,0x4620,{ 0xA9,0x7E,0x9B,0x80,0x7D,0x5E,0xC3,0x76 } }; - -// {C32B72C3-41BD-4ADC-82A8-B7AEF4456A37} -const ON_UUID ON_StandardDisplayModeId::AmbientOcclusion = -{ 0xc32b72c3, 0x41bd, 0x4adc, { 0x82, 0xa8, 0xb7, 0xae, 0xf4, 0x45, 0x6a, 0x37 } }; - -// {69E0C7A5-1C6A-46C8-B98B-8779686CD181} -const ON_UUID ON_StandardDisplayModeId::Raytraced = -{ 0x69e0c7a5, 0x1c6a, 0x46c8, { 0xb9, 0x8b, 0x87, 0x79, 0x68, 0x6c, 0xd1, 0x81 } }; - - -const ON_UUID ON_nil_uuid = { 0,0,0,{ 0,0,0,0,0,0,0,0 } }; -const ON_UUID ON_max_uuid = { 0xFFFFFFFF,0xFFFF,0xFFFF,{ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }; - -const ON_UUID ON_rhino2_id = { 0x16d0eca6, 0x359, 0x4e4c,{ 0x9f, 0xe, 0xf2, 0x69, 0xfd, 0x47, 0x6c, 0xc4 } }; - -const ON_UUID ON_rhino3_id = { 0xA7BBFF3C, 0xFF19, 0x4883,{ 0x85, 0x8D, 0xB1, 0xE7, 0xDB, 0x4F, 0x1A, 0x7E } }; - -// {E2143A46-BB01-4b0c-AC4D-C34B5652FAE0} -const ON_UUID ON_rhino4_id = { 0xe2143a46, 0xbb01, 0x4b0c,{ 0xac, 0x4d, 0xc3, 0x4b, 0x56, 0x52, 0xfa, 0xe0 } }; - -// {60515F84-8F7F-41da-801D-1C87E32F88F5} -const ON_UUID ON_rhino5_id = { 0x60515f84, 0x8f7f, 0x41da,{ 0x80, 0x1d, 0x1c, 0x87, 0xe3, 0x2f, 0x88, 0xf5 } }; - -// {06BB1079-5A56-47A1-AD6D-0B45183D894B} -const ON_UUID ON_rhino6_id = { 0x6bb1079, 0x5a56, 0x47a1,{ 0xad, 0x6d, 0xb, 0x45, 0x18, 0x3d, 0x89, 0x4b } }; - -// {78464C2C-9AEB-456E-8C27-865A524F5CA0} -const ON_UUID ON_rhino7_id = { 0x78464c2c, 0x9aeb, 0x456e,{ 0x8c, 0x27, 0x86, 0x5a, 0x52, 0x4f, 0x5c, 0xa0 } }; - -// {868c63f5-3760-4a45-8600-5399cc57b47c} -const ON_UUID ON_rhino8_id = { 0x868c63f5, 0x3760, 0x4a45,{ 0x86, 0x00, 0x53, 0x99, 0xcc, 0x57, 0xb4, 0x7c } }; - -// ON_rhino_id is always set to the value for the current version -// of Rhino. ON_rhino_id is the id that should be used as the -// userdata application id for userdata class definitions that are -// in the core Rhino executable. -const ON_UUID ON_rhino_id = ON_rhino8_id; - -// Used to identifiy userdata read from V2 files -// which were written before userdata had application ids. -// {132F2340-DB90-494e-BF02-C36F0EA3197C} -const ON_UUID ON_v2_userdata_id = { 0x132f2340, 0xdb90, 0x494e,{ 0xbf, 0x2, 0xc3, 0x6f, 0xe, 0xa3, 0x19, 0x7c } }; - -// Used to identifiy userdata read from V3 files -// which were written before userdata had application ids. -// {4307B91D-6A9D-478e-B0A2-7C577997C663} -const ON_UUID ON_v3_userdata_id = { 0x4307b91d, 0x6a9d, 0x478e,{ 0xb0, 0xa2, 0x7c, 0x57, 0x79, 0x97, 0xc6, 0x63 } }; - -// Used to identifiy userdata read from V4 files -// which were written before opennurbs 200609190 -// required application ids. -// {F73F2953-A244-44c2-B7C2-7E27390D1196} -const ON_UUID ON_v4_userdata_id = { 0xf73f2953, 0xa244, 0x44c2,{ 0xb7, 0xc2, 0x7e, 0x27, 0x39, 0xd, 0x11, 0x96 } }; - -// {17B3ECDA-17BA-4e45-9E67-A2B8D9BE520D} -const ON_UUID ON_opennurbs4_id = { 0x17b3ecda, 0x17ba, 0x4e45,{ 0x9e, 0x67, 0xa2, 0xb8, 0xd9, 0xbe, 0x52, 0xd } }; - -// {C8CDA597-D957-4625-A4B3-A0B510FC30D4} -const ON_UUID ON_opennurbs5_id = { 0xc8cda597, 0xd957, 0x4625,{ 0xa4, 0xb3, 0xa0, 0xb5, 0x10, 0xfc, 0x30, 0xd4 } }; - -// {7B0B585D-7A31-45D0-925E-BDD7DDF3E4E3} -const ON_UUID ON_opennurbs6_id = { 0x7b0b585d, 0x7a31, 0x45d0,{ 0x92, 0x5e, 0xbd, 0xd7, 0xdd, 0xf3, 0xe4, 0xe3 } }; - -// {523bfe6e-ef49-4b75-a8d6-253faf5044d3} -const ON_UUID ON_opennurbs7_id = { 0x523bfe6e, 0xef49, 0x4b75,{ 0xa8, 0xd6, 0x25, 0x3f, 0xaf, 0x50, 0x44, 0xd3 } }; - -// {50EDE5C9-1487-4B4C-B3AA-6840B460E3CF} -const ON_UUID ON_opennurbs8_id = { 0x50ede5c9, 0x1487, 0x4b4c, { 0xb3, 0xaa, 0x68, 0x40, 0xb4, 0x60, 0xe3, 0xcf } }; - - -// ON_opennurbs_id is always set to the value for the current version -// of opennurbs. ON_opennurbs_id is the id that should be used as -// the userdata application id for userdata classes definitions that -// are in the opennurbs library. -const ON_UUID ON_opennurbs_id = ON_opennurbs8_id; - -const ON_UuidPairList ON_UuidPairList::EmptyList; - -const ON_COMPONENT_INDEX ON_COMPONENT_INDEX::UnsetComponentIndex; -const ON_COMPONENT_INDEX ON_COMPONENT_INDEX::WholeObject; -const ON_ComponentIndexAndNumber ON_ComponentIndexAndNumber::UnsetAndNan = ON_ComponentIndexAndNumber::Create(ON_COMPONENT_INDEX::UnsetComponentIndex, ON_DBL_QNAN); -const ON_ComponentIndexAndNumber ON_ComponentIndexAndNumber::UnsetAndZero = ON_ComponentIndexAndNumber::Create(ON_COMPONENT_INDEX::UnsetComponentIndex, 0.0); - // All opennurbs static members are initialized here so that initialization happens in a predictable order. /* IEEE 754 @@ -489,6 +291,209 @@ const float ON_FLT_QNAN = ON__fltinithelper(1); const float ON_FLT_PINF = ON__fltinithelper(2); const float ON_FLT_NINF = -ON__fltinithelper(2); +// It is critical that ON_ModelComponent::Internal_RuntimeSerialNumberGenerator +// be constructed before any instance of a class derived from ON_ModelComponent. +// That is why it is above the ClassId stuff in this .cpp file. +// Serial numbers below UINT32_MAX + 1 are reserved for Rhino use. +std::atomic ON_ModelComponent::Internal_RuntimeSerialNumberGenerator(UINT32_MAX + 1ULL); + +std::atomic ON_SubDimple::Internal_RuntimeSerialNumberGenerator; + +const double ON_SubDExpandEdgesParameters::OffsetTolerance = 0.001; + +const double ON_SubDExpandEdgesParameters::MinimumOffset = 0.05; + +const double ON_SubDExpandEdgesParameters::MaximumOffset = 0.95; + +const double ON_SubDExpandEdgesParameters::SmallOffset = 0.125; + +const double ON_SubDExpandEdgesParameters::MediumOffset = 0.25; + +const double ON_SubDExpandEdgesParameters::LargeOffset = 0.5; + +const ON_SubDExpandEdgesParameters ON_SubDExpandEdgesParameters::Default; + +const ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponentLocation::Surface; + +// 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; + + +// NOTE WELL: +// It is required that (3 + 2^ON_SubDEdgeSharpness::MaximumValue) <= ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity +const double ON_SubDEdgeSharpness::MaximumValue = 4.0; +const double ON_SubDEdgeSharpness::SmoothValue = 0.0; +const double ON_SubDEdgeSharpness::CreaseValue = ON_SubDEdgeSharpness::MaximumValue + 1.0; +const double ON_SubDEdgeSharpness::Tolerance = 0.01; + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Smooth; + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Nan = ON_SubDEdgeSharpness::FromConstant(ON_DBL_QNAN); + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Crease = ON_SubDEdgeSharpness::FromConstant(ON_SubDEdgeSharpness::CreaseValue); + +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; + +const ON_SubDSectorId ON_SubDSectorId::Zero; +const ON_SubDSectorId ON_SubDSectorId::Invalid = ON_SubDSectorId::Create(nullptr, nullptr); + +const ON_SubDHash ON_SubDHash::Empty; + + +// {C3D8DD54-F8C8-4455-BB0E-2A2F4988EC81} +const ON_UUID ON_SubD::FastAndSimpleFacePackingId = +{ 0xc3d8dd54, 0xf8c8, 0x4455, { 0xbb, 0xe, 0x2a, 0x2f, 0x49, 0x88, 0xec, 0x81 } }; + +// ON_SubD::DefaultFacePackingId must always identitify a built-in face packing +// algoritm. If a new built-in algorithm is developed that produces generally +// better packings and is as fast and reliable as the current default, then +// ON_SubD::DefaultFacePackingId can be changed. Under no circumstances, should +// the default be changed to anything that is more than 1.5 times slower than +// the either the fast and simple or the current default on large models +// like the Mt St Helens Subd. +// +// ** If it's not really fast, then it cannot be the the default. ** +const ON_UUID ON_SubD::DefaultFacePackingId = ON_SubD::FastAndSimpleFacePackingId; + +const ON_SubDToBrepParameters Internal_SubDToBrepParameters(bool bPackedFaces) +{ + ON_SubDToBrepParameters p; + p.SetPackFaces(bPackedFaces); + return p; +} + +const ON_SubDToBrepParameters ON_SubDToBrepParameters::Default; +const ON_SubDToBrepParameters ON_SubDToBrepParameters::DefaultUnpacked = Internal_SubDToBrepParameters(false); +const ON_SubDToBrepParameters ON_SubDToBrepParameters::DefaultPacked = Internal_SubDToBrepParameters(true); + + +const ON_SubDRTreeVertexFinder ON_SubDRTreeVertexFinder::Unset; + + +ON_ClassId* ON_ClassId::m_p0 = 0; // static pointer to first id in list +ON_ClassId* ON_ClassId::m_p1 = 0; // static pointer to last id in list +int ON_ClassId::m_mark0 = 0; + +// {1311ADCB-D89E-4051-A3F0-F64441FB8EC6} +const ON_UUID ON_StandardDisplayModeId::Wireframe = +{ 0x1311ADCB,0xD89E,0x4051,{ 0xA3,0xF0,0xF6,0x44,0x41,0xFB,0x8E,0xC6 } }; + +// {8BC8DEBE-C83B-4c47-B13C-9DB074510CAC} +const ON_UUID ON_StandardDisplayModeId::Shaded = +{ 0x8BC8DEBE,0xC83B,0x4c47,{ 0xB1,0x3C,0x9D,0xB0,0x74,0x51,0x0C,0xAC } }; + +// {CAE60BAE-2D51-4299-ABF7-A339FCA86F3B} +const ON_UUID ON_StandardDisplayModeId::Rendered = +{ 0xCAE60BAE,0x2D51,0x4299,{ 0xAB,0xF7,0xA3,0x39,0xFC,0xA8,0x6F,0x3B } }; + +// {FF608B97-81D3-4186-831C-41F7DC140881} +const ON_UUID ON_StandardDisplayModeId::Ghosted = +{ 0xFF608B97,0x81D3,0x4186,{ 0x83,0x1C,0x41,0xF7,0xDC,0x14,0x08,0x81 } }; + +// {B5C19D5D-0AEC-4ff7-A10E-E052E660263A} +const ON_UUID ON_StandardDisplayModeId::XrayShade = +{ 0xB5C19D5D,0x0AEC,0x4ff7,{ 0xA1,0x0E,0xE0,0x52,0xE6,0x60,0x26,0x3A } }; + +// {A5545314-9D87-428d-95AE-91052EEAD0FA} +const ON_UUID ON_StandardDisplayModeId::RenderedShadows = +{ 0xA5545314,0x9D87,0x428d,{ 0x95,0xAE,0x91,0x05,0x2E,0xEA,0xD0,0xFA } }; + +// {63612C72-778F-4afd-B81B-17426FDFE8A6} +const ON_UUID ON_StandardDisplayModeId::Technical = +{ 0x63612C72,0x778F,0x4afd,{ 0xB8,0x1B,0x17,0x42,0x6F,0xDF,0xE8,0xA6 } }; + +// {B46AB226-05A0-4568-B454-4B1AB721C675} +const ON_UUID ON_StandardDisplayModeId::Artistic = +{ 0xB46AB226,0x05A0,0x4568,{ 0xB4,0x54,0x4B,0x1A,0xB7,0x21,0xC6,0x75 } }; + +// {F4616FA5-A831-4620-A97E-9B807D5EC376} +const ON_UUID ON_StandardDisplayModeId::Pen = +{ 0xF4616FA5,0xA831,0x4620,{ 0xA9,0x7E,0x9B,0x80,0x7D,0x5E,0xC3,0x76 } }; + +// {C32B72C3-41BD-4ADC-82A8-B7AEF4456A37} +const ON_UUID ON_StandardDisplayModeId::AmbientOcclusion = +{ 0xc32b72c3, 0x41bd, 0x4adc, { 0x82, 0xa8, 0xb7, 0xae, 0xf4, 0x45, 0x6a, 0x37 } }; + +// {69E0C7A5-1C6A-46C8-B98B-8779686CD181} +const ON_UUID ON_StandardDisplayModeId::Raytraced = +{ 0x69e0c7a5, 0x1c6a, 0x46c8, { 0xb9, 0x8b, 0x87, 0x79, 0x68, 0x6c, 0xd1, 0x81 } }; + + +const ON_UUID ON_nil_uuid = { 0,0,0,{ 0,0,0,0,0,0,0,0 } }; +const ON_UUID ON_max_uuid = { 0xFFFFFFFF,0xFFFF,0xFFFF,{ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }; + +const ON_UUID ON_rhino2_id = { 0x16d0eca6, 0x359, 0x4e4c,{ 0x9f, 0xe, 0xf2, 0x69, 0xfd, 0x47, 0x6c, 0xc4 } }; + +const ON_UUID ON_rhino3_id = { 0xA7BBFF3C, 0xFF19, 0x4883,{ 0x85, 0x8D, 0xB1, 0xE7, 0xDB, 0x4F, 0x1A, 0x7E } }; + +// {E2143A46-BB01-4b0c-AC4D-C34B5652FAE0} +const ON_UUID ON_rhino4_id = { 0xe2143a46, 0xbb01, 0x4b0c,{ 0xac, 0x4d, 0xc3, 0x4b, 0x56, 0x52, 0xfa, 0xe0 } }; + +// {60515F84-8F7F-41da-801D-1C87E32F88F5} +const ON_UUID ON_rhino5_id = { 0x60515f84, 0x8f7f, 0x41da,{ 0x80, 0x1d, 0x1c, 0x87, 0xe3, 0x2f, 0x88, 0xf5 } }; + +// {06BB1079-5A56-47A1-AD6D-0B45183D894B} +const ON_UUID ON_rhino6_id = { 0x6bb1079, 0x5a56, 0x47a1,{ 0xad, 0x6d, 0xb, 0x45, 0x18, 0x3d, 0x89, 0x4b } }; + +// {78464C2C-9AEB-456E-8C27-865A524F5CA0} +const ON_UUID ON_rhino7_id = { 0x78464c2c, 0x9aeb, 0x456e,{ 0x8c, 0x27, 0x86, 0x5a, 0x52, 0x4f, 0x5c, 0xa0 } }; + +// {868c63f5-3760-4a45-8600-5399cc57b47c} +const ON_UUID ON_rhino8_id = { 0x868c63f5, 0x3760, 0x4a45,{ 0x86, 0x00, 0x53, 0x99, 0xcc, 0x57, 0xb4, 0x7c } }; + +// ON_rhino_id is always set to the value for the current version +// of Rhino. ON_rhino_id is the id that should be used as the +// userdata application id for userdata class definitions that are +// in the core Rhino executable. +const ON_UUID ON_rhino_id = ON_rhino8_id; + +// Used to identifiy userdata read from V2 files +// which were written before userdata had application ids. +// {132F2340-DB90-494e-BF02-C36F0EA3197C} +const ON_UUID ON_v2_userdata_id = { 0x132f2340, 0xdb90, 0x494e,{ 0xbf, 0x2, 0xc3, 0x6f, 0xe, 0xa3, 0x19, 0x7c } }; + +// Used to identifiy userdata read from V3 files +// which were written before userdata had application ids. +// {4307B91D-6A9D-478e-B0A2-7C577997C663} +const ON_UUID ON_v3_userdata_id = { 0x4307b91d, 0x6a9d, 0x478e,{ 0xb0, 0xa2, 0x7c, 0x57, 0x79, 0x97, 0xc6, 0x63 } }; + +// Used to identifiy userdata read from V4 files +// which were written before opennurbs 200609190 +// required application ids. +// {F73F2953-A244-44c2-B7C2-7E27390D1196} +const ON_UUID ON_v4_userdata_id = { 0xf73f2953, 0xa244, 0x44c2,{ 0xb7, 0xc2, 0x7e, 0x27, 0x39, 0xd, 0x11, 0x96 } }; + +// {17B3ECDA-17BA-4e45-9E67-A2B8D9BE520D} +const ON_UUID ON_opennurbs4_id = { 0x17b3ecda, 0x17ba, 0x4e45,{ 0x9e, 0x67, 0xa2, 0xb8, 0xd9, 0xbe, 0x52, 0xd } }; + +// {C8CDA597-D957-4625-A4B3-A0B510FC30D4} +const ON_UUID ON_opennurbs5_id = { 0xc8cda597, 0xd957, 0x4625,{ 0xa4, 0xb3, 0xa0, 0xb5, 0x10, 0xfc, 0x30, 0xd4 } }; + +// {7B0B585D-7A31-45D0-925E-BDD7DDF3E4E3} +const ON_UUID ON_opennurbs6_id = { 0x7b0b585d, 0x7a31, 0x45d0,{ 0x92, 0x5e, 0xbd, 0xd7, 0xdd, 0xf3, 0xe4, 0xe3 } }; + +// {523bfe6e-ef49-4b75-a8d6-253faf5044d3} +const ON_UUID ON_opennurbs7_id = { 0x523bfe6e, 0xef49, 0x4b75,{ 0xa8, 0xd6, 0x25, 0x3f, 0xaf, 0x50, 0x44, 0xd3 } }; + +// {50EDE5C9-1487-4B4C-B3AA-6840B460E3CF} +const ON_UUID ON_opennurbs8_id = { 0x50ede5c9, 0x1487, 0x4b4c, { 0xb3, 0xaa, 0x68, 0x40, 0xb4, 0x60, 0xe3, 0xcf } }; + + +// ON_opennurbs_id is always set to the value for the current version +// of opennurbs. ON_opennurbs_id is the id that should be used as +// the userdata application id for userdata classes definitions that +// are in the opennurbs library. +const ON_UUID ON_opennurbs_id = ON_opennurbs8_id; + +const ON_UuidPairList ON_UuidPairList::EmptyList; + +const ON_COMPONENT_INDEX ON_COMPONENT_INDEX::UnsetComponentIndex; +const ON_COMPONENT_INDEX ON_COMPONENT_INDEX::WholeObject; +const ON_ComponentIndexAndNumber ON_ComponentIndexAndNumber::UnsetAndNan = ON_ComponentIndexAndNumber::Create(ON_COMPONENT_INDEX::UnsetComponentIndex, ON_DBL_QNAN); +const ON_ComponentIndexAndNumber ON_ComponentIndexAndNumber::UnsetAndZero = ON_ComponentIndexAndNumber::Create(ON_COMPONENT_INDEX::UnsetComponentIndex, 0.0); + + const ON_2dex ON_2dex::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); const ON_2dex ON_2dex::Zero(0, 0); const ON_3dex ON_3dex::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); @@ -3032,3 +3037,4 @@ unsigned int ON_ModelComponent::Internal_SystemComponentHelper() return rc; } +const ON_SectionStyle ON_SectionStyle::Unset; diff --git a/opennurbs_string.cpp b/opennurbs_string.cpp index 8eba8759..782fb4f6 100644 --- a/opennurbs_string.cpp +++ b/opennurbs_string.cpp @@ -1507,7 +1507,7 @@ void ON_String::TrimLeft(const char* s) { for (i = 0; 0 != (c = m_s[i]); i++) { - if ( c < 0 || c > ON_String::Space ) + if ( c < 0 || (c > ON_String::Space && c != ON_String::Delete)) break; } } @@ -1547,7 +1547,7 @@ void ON_String::TrimRight(const char* s) { for (i--; i >= 0 && 0 != (c = m_s[i]); i--) { - if ( c < 0 || c > ON_String::Space ) + if ( c < 0 || (c > ON_String::Space && c != ON_String::Delete)) break; } } diff --git a/opennurbs_string.h b/opennurbs_string.h index c8762786..98314e4a 100644 --- a/opennurbs_string.h +++ b/opennurbs_string.h @@ -1010,6 +1010,9 @@ public: /// TILDE U+007E (~) static const char Tilde = (char)ON_UnicodeCodePoint::ON_Tilde; + /// DELETE U+007F + static const char Delete = (char)ON_UnicodeCodePoint::ON_Delete; + /// Period decimal point (.) static const char DecimalAsPeriod = (char)ON_UnicodeCodePoint::ON_Period; @@ -2602,6 +2605,9 @@ public: /// TILDE U+007E (~) static const wchar_t Tilde = (wchar_t)ON_UnicodeCodePoint::ON_Tilde; + /// DELETE U+007F + static const wchar_t Delete = (wchar_t)ON_UnicodeCodePoint::ON_Delete; + /// Period decimal point (.) static const wchar_t DecimalAsPeriod = (wchar_t)ON_UnicodeCodePoint::ON_Period; diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 1cee6fdc..75161299 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -548,6 +548,16 @@ bool ON_SubD::IsValidSectorFaceCount( // ON_SubDVertexPtr // // +bool operator==(ON_SubDVertexPtr lhs, ON_SubDVertexPtr rhs) +{ + return lhs.m_ptr == rhs.m_ptr; +} + +bool operator!=(ON_SubDVertexPtr lhs, ON_SubDVertexPtr rhs) +{ + return lhs.m_ptr != rhs.m_ptr; +} + bool ON_SubDVertexPtr::IsNull() const { return (nullptr == ON_SUBD_VERTEX_POINTER(m_ptr)); @@ -623,7 +633,15 @@ const ON_SubDVertexPtr ON_SubDVertexPtr::Create( // // ON_SubDEdgePtr // +bool operator==(ON_SubDEdgePtr lhs, ON_SubDEdgePtr rhs) +{ + return lhs.m_ptr == rhs.m_ptr; +} +bool operator!=(ON_SubDEdgePtr lhs, ON_SubDEdgePtr rhs) +{ + return lhs.m_ptr != rhs.m_ptr; +} bool ON_SubDEdgePtr::IsNull() const { @@ -671,6 +689,24 @@ bool ON_SubDEdgePtr::EdgeIsSmooth() const return (nullptr != e) ? e->IsSmooth() : false; } +bool ON_SubDEdgePtr::EdgeIsSmoothNotSharp() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsSmoothNotSharp() : false; +} + +bool ON_SubDEdgePtr::EdgeIsSharp() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsSharp() : false; +} + +bool ON_SubDEdgePtr::EdgeIsCreaseOrSharp() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsCreaseOrSharp() : false; +} + bool ON_SubDEdgePtr::EdgeIsCrease() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); @@ -697,13 +733,11 @@ bool ON_SubDEdgePtr::HasInteriorEdgeTopology( return (nullptr != e) ? e->HasInteriorEdgeTopology(bRequireOppositeFaceDirections) : false; } - ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const { return ON_SUBD_EDGE_DIRECTION(m_ptr); } - const ON_3dPoint ON_SubDEdgePtr::SubdivisionPoint() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); @@ -744,6 +778,14 @@ const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex( return nullptr; } +ON_SubDVertexTag ON_SubDEdgePtr::RelativeVertexTag( + int relative_vertex_index +) const +{ + const ON_SubDVertex* v = this->RelativeVertex(relative_vertex_index); + return (nullptr != v) ? v->m_vertex_tag : ON_SubDVertexTag::Unset; +} + const ON_3dPoint ON_SubDEdgePtr::RelativeVertexPoint( int relative_vertex_index, ON_SubDComponentLocation point_location @@ -762,6 +804,7 @@ const ON_3dPoint ON_SubDEdgePtr::RelativeVertexSurfacePoint( return (nullptr != v) ? v->SurfacePoint() : ON_3dPoint::NanPoint; } + unsigned ON_SubDEdgePtr::RelativeVertexId(int relative_vertex_index) const { const ON_SubDVertex* v = this->RelativeVertex(relative_vertex_index); @@ -843,15 +886,15 @@ void ON_SubDEdgePtr::SetRelativeSectorCoefficientForExperts( } -const ON_SubDEdgeSharpness ON_SubDEdgePtr::RelativeSharpness() const +const ON_SubDEdgeSharpness ON_SubDEdgePtr::RelativeSharpness(bool bUseCreaseSharpness) const { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); if (nullptr != edge) { - const ON_SubDEdgeSharpness s = edge->Sharpness(); + const ON_SubDEdgeSharpness s = edge->Sharpness(bUseCreaseSharpness); return (0 == ON_SUBD_EDGE_DIRECTION(m_ptr)) ? s : s.Reversed(); } - return ON_SubDEdgeSharpness::Zero; + return ON_SubDEdgeSharpness::Nan; } void ON_SubDEdgePtr::SetRelativeSharpness(ON_SubDEdgeSharpness relative_sharpness) const @@ -882,6 +925,16 @@ const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const return ON_3dVector::NanVector; } +const class ON_SubDFace* ON_SubDEdgePtr::EdgeFace( + int edge_face_index +) const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + if (nullptr == e) + return nullptr; // null input + return (edge_face_index >= 0 && edge_face_index < (int)(e->m_face_count)) ? e->Face((unsigned)edge_face_index) : nullptr; +} + const class ON_SubDFace* ON_SubDEdgePtr::RelativeFace( int relative_face_index ) const @@ -1020,6 +1073,16 @@ const ON_SubDEdgePtr ON_SubDEdgePtr::CreateFromEndVertex( // ON_SubDFacePtr // +bool operator==(ON_SubDFacePtr lhs, ON_SubDFacePtr rhs) +{ + return lhs.m_ptr == rhs.m_ptr; +} + +bool operator!=(ON_SubDFacePtr lhs, ON_SubDFacePtr rhs) +{ + return lhs.m_ptr != rhs.m_ptr; +} + bool ON_SubDFacePtr::IsNull() const { return (nullptr == ON_SUBD_FACE_POINTER(m_ptr)); @@ -1141,6 +1204,16 @@ int ON_SubDFacePtr::CompareFacePointer( // ON_SubDComponentPtr // +bool operator==(ON_SubDComponentPtr lhs, ON_SubDComponentPtr rhs) +{ + return lhs.m_ptr == rhs.m_ptr; +} + +bool operator!=(ON_SubDComponentPtr lhs, ON_SubDComponentPtr rhs) +{ + return lhs.m_ptr != rhs.m_ptr; +} + bool ON_SubDComponentPtr::IsNull() const { return nullptr == ComponentBase(); @@ -3822,9 +3895,11 @@ bool ON_SubDEdge::IsSmoothX() const return (ON_SubDEdgeTag::SmoothX == m_edge_tag) ? true : false; } -const ON_SubDEdgeSharpness ON_SubDEdge::Sharpness() const +const ON_SubDEdgeSharpness ON_SubDEdge::Sharpness(bool bUseCreaseSharpness) const { - return IsSmooth() ? m_sharpness : ON_SubDEdgeSharpness::Zero; + return IsSmooth() + ? (m_sharpness.IsValid() ? m_sharpness : ON_SubDEdgeSharpness::Smooth) + : ((bUseCreaseSharpness && IsCrease()) ? ON_SubDEdgeSharpness ::Crease : ON_SubDEdgeSharpness::Smooth); } double ON_SubDEdge::EndSharpness( @@ -3853,7 +3928,7 @@ const ON_SubDEdgeSharpness ON_SubDEdge::SubdivideSharpness( bool bReverse ) const { - const ON_SubDEdgeSharpness s = Sharpness().Subdivided(evi); + const ON_SubDEdgeSharpness s = Sharpness(false).Subdivided(evi); return bReverse ? s.Reversed() : s; } @@ -3869,27 +3944,257 @@ const ON_SubDEdgeSharpness ON_SubDEdge::SubdivideSharpness( if (end_vertex == this->m_vertex[1]) return this->SubdivideSharpness(1U, bReverse); } - return ON_SubDEdgeSharpness::Zero; + return ON_SubDEdgeSharpness::Smooth; } bool ON_SubDEdge::ClearSharpnessForExperts() { - const bool bChanged = IsSmooth() && (m_sharpness != ON_SubDEdgeSharpness::Zero); - m_sharpness = ON_SubDEdgeSharpness::Zero; + const bool bChanged = IsSmooth() && (m_sharpness != ON_SubDEdgeSharpness::Smooth); + m_sharpness = ON_SubDEdgeSharpness::Smooth; return bChanged; } void ON_SubDEdge::SetSharpnessForExperts(ON_SubDEdgeSharpness sharpness) { - m_sharpness = IsSmooth() ? sharpness : ON_SubDEdgeSharpness::Zero; + m_sharpness + = IsSmooth() && sharpness.IsValid() + ? sharpness + : ON_SubDEdgeSharpness::Smooth; } +double ON_SubDEdgeSharpness::ToPercentage(double sharpness, double crease_percentage) +{ + if (0.0 <= sharpness && sharpness <= ON_SubDEdgeSharpness::MaximumValue) + return 100.0 * (sharpness / ON_SubDEdgeSharpness::MaximumValue); + if (ON_SubDEdgeSharpness::CreaseValue == sharpness) + return crease_percentage; + return ON_DBL_QNAN; +} + +const ON_wString ON_SubDEdgeSharpness::ToPercentageText(double sharpness) +{ + if ( ON_SubDEdgeSharpness::IsValidValue(sharpness,false) ) + { + const double s = sharpness / ON_SubDEdgeSharpness::MaximumValue; + double m = 1.0; + unsigned max_decimal_places = 4; + for (unsigned decimal_places = 0; decimal_places <= max_decimal_places; ++decimal_places, m *= 10.0 ) + { + double p = 100.0 * m * s; + double f = floor(p); + if (p - f > 0.5) + f = f + 1.0; + f /= m; + if (fabs(f - 100.0*s) <= 0.00005) + { + double minf = 1.0 / m; + if (f < minf && sharpness > 0.0) + { + if (decimal_places < max_decimal_places) + continue; + f = minf; + } + else if (f > 100.0 - minf && sharpness < ON_SubDEdgeSharpness::MaximumValue) + { + if (decimal_places < max_decimal_places) + continue; + f = 100.0 - minf; + } + if (0 == decimal_places) + return ON_wString::FormatToString(L"%g%%", f); + const ON_wString format = ON_wString::FormatToString(L"%%.%df%%%%", decimal_places); + return ON_wString::FormatToString(format, f); + } + } + return ON_wString::FormatToString(L"%g%%", s*100.0); + } + + if (ON_SubDEdgeSharpness::CreaseValue == sharpness) + return ON_wString(L"crease"); + + return ON_wString(ON_wString::WarningSign); +} + +const ON_wString ON_SubDEdgeSharpness::ToPercentageText( bool bVarableMinToMax ) const +{ + if (IsValid()) + { + if ( IsConstant() ) + return ON_SubDEdgeSharpness::ToPercentageText(EndSharpness(0)); + const int i0 = (bVarableMinToMax && EndSharpness(0) > EndSharpness(2)) ? 1 : 0; + const double s0 = EndSharpness(i0); + const double s1 = EndSharpness(1-i0); + return + ON_SubDEdgeSharpness::ToPercentageText(s0) + + ON_wString("-") + + ON_SubDEdgeSharpness::ToPercentageText(s1); + } + + if (IsCrease()) + return ON_SubDEdgeSharpness::ToPercentageText(ON_SubDEdgeSharpness::CreaseValue); + + // invalid sharpness + return ON_SubDEdgeSharpness::ToPercentageText(ON_DBL_QNAN); +} bool ON_SubDEdgeSharpness::IsConstant() const { - return m_edge_sharpness[0] == m_edge_sharpness[1] && 0.0f <= m_edge_sharpness[0] && m_edge_sharpness[0] <= ((float)ON_SubDEdgeSharpness::Maximum); + // NOTE WELL: + // ON_SubDEdgeSharpness::Crease::IsConstant() and ON_SubDEdgeSharpness::Nan::IsConstant() + // must be false. + return m_edge_sharpness[0] == m_edge_sharpness[1] && 0.0f <= m_edge_sharpness[0] && m_edge_sharpness[0] <= ((float)ON_SubDEdgeSharpness::MaximumValue); } +bool ON_SubDEdgeSharpness::IsConstant(bool bCreaseResult) const +{ + return + (m_edge_sharpness[0] == m_edge_sharpness[1]) + && + ( + (0.0f <= m_edge_sharpness[0] && m_edge_sharpness[0] <= ((float)ON_SubDEdgeSharpness::MaximumValue)) + || + (bCreaseResult && m_edge_sharpness[0] == ((float)ON_SubDEdgeSharpness::CreaseValue)) + ); +} + +bool ON_SubDEdgeSharpness::IsIncreasing() const +{ + return (m_edge_sharpness[0] < m_edge_sharpness[1]); +} + +bool ON_SubDEdgeSharpness::IsDecreasing() const +{ + return (m_edge_sharpness[0] > m_edge_sharpness[1]); +} + +int ON_SubDEdgeSharpness::Trend() const +{ + + if (m_edge_sharpness[0] < m_edge_sharpness[1]) + return 1; + if (m_edge_sharpness[0] > m_edge_sharpness[1]) + return -1; + if (m_edge_sharpness[0] == m_edge_sharpness[1]) + return 0; + return ON_UNSET_INT_INDEX; +} + + +double ON_SubDEdgeSharpness::Delta() const +{ + + if (m_edge_sharpness[0] != m_edge_sharpness[1]) + return ((double)m_edge_sharpness[1]) - ((double)m_edge_sharpness[0]); + if (m_edge_sharpness[0] == m_edge_sharpness[1]) + return 0.0; + return ON_DBL_QNAN; +} + +bool ON_SubDEdgeSharpness::EqualEndSharpness( + ON_SubDEdgeSharpness s0, + ON_SubDEdgeSharpness s1 +) +{ + return (s0.m_edge_sharpness[1] == s1.m_edge_sharpness[0]); +} + +bool ON_SubDEdgeSharpness::EqualTrend( + ON_SubDEdgeSharpness s0, + ON_SubDEdgeSharpness s1 +) +{ + if (false == (s0.m_edge_sharpness[1] == s1.m_edge_sharpness[0])) + return false; + return (s0.Trend() == s1.Trend()); +} + + +bool ON_SubDEdgeSharpness::EqualDelta( + ON_SubDEdgeSharpness s0, + ON_SubDEdgeSharpness s1 +) +{ + if (false == (s0.m_edge_sharpness[1] == s1.m_edge_sharpness[0])) + return false; + + const double delta0 = s0.Delta(); + const double delta1 = s1.Delta(); + + if (0.0 != delta0 && 0.0 != delta1) + { + // fuzzy compare for nonzero deltas + const double d = fabs(delta0 - delta1); + return (d <= ON_SubDEdgeSharpness::Tolerance); + } + + // strict compare for zero deltas. + return 0.0 == delta0 && 0.0 == delta1; +} + +bool ON_SubDEdgeSharpness::EqualEndSharpness( + const class ON_SubDEdgePtr& eptr0, + const class ON_SubDEdgePtr& eptr1 +) +{ + const ON_SubDEdge* e[2] = { eptr0.Edge(), eptr1.Edge() }; + if (nullptr == e[0] || nullptr == e[1] || e[0] == e[1]) + return false; + + if (e[0]->m_edge_tag != e[1]->m_edge_tag) + return false; + + const ON_SubDVertex* v = eptr0.RelativeVertex(1); + if (nullptr == v) + return false; + if (v != eptr1.RelativeVertex(0)) + return false; + + return ON_SubDEdgeSharpness::EqualEndSharpness(eptr0.RelativeSharpness(true), eptr1.RelativeSharpness(true)); +} + +bool ON_SubDEdgeSharpness::EqualTrend( + const ON_SubDEdgePtr& eptr0, + const ON_SubDEdgePtr& eptr1 +) +{ + const ON_SubDEdge* e[2] = { eptr0.Edge(), eptr1.Edge() }; + if (nullptr == e[0] || nullptr == e[1] || e[0] == e[1]) + return false; + + if (e[0]->m_edge_tag != e[1]->m_edge_tag) + return false; + + const ON_SubDVertex* v = eptr0.RelativeVertex(1); + if (nullptr == v) + return false; + if (v != eptr1.RelativeVertex(0)) + return false; + + return ON_SubDEdgeSharpness::EqualTrend(eptr0.RelativeSharpness(true), eptr1.RelativeSharpness(true)); +} + +bool ON_SubDEdgeSharpness::EqualDelta( + const ON_SubDEdgePtr& eptr0, + const ON_SubDEdgePtr& eptr1 +) +{ + const ON_SubDEdge* e[2] = { eptr0.Edge(), eptr1.Edge() }; + if (nullptr == e[0] || nullptr == e[1] || e[0] == e[1]) + return false; + + if (e[0]->m_edge_tag != e[1]->m_edge_tag) + return false; + + const ON_SubDVertex* v = eptr0.RelativeVertex(1); + if (nullptr == v) + return false; + if (v != eptr1.RelativeVertex(0)) + return false; + + return ON_SubDEdgeSharpness::EqualDelta(eptr0.RelativeSharpness(true), eptr1.RelativeSharpness(true)); +} + + bool ON_SubDEdgeSharpness::IsVariable() const { return m_edge_sharpness[0] != m_edge_sharpness[1] && IsValid(); @@ -3897,21 +4202,51 @@ bool ON_SubDEdgeSharpness::IsVariable() const bool ON_SubDEdgeSharpness::IsZero() const { + // NOTE WELL: + // ON_SubDEdgeSharpness::Crease::IsZero() and ON_SubDEdgeSharpness::Nan::IsZero() + // must be false. return (0.0f == m_edge_sharpness[0] && 0.0f == m_edge_sharpness[1]); } -bool ON_SubDEdgeSharpness::IsNotZero() const +bool ON_SubDEdgeSharpness::IsCrease() const { - return (m_edge_sharpness[0] != 0.0f || m_edge_sharpness[1] != 0.0f) && IsValid(); + const float x = (float)ON_SubDEdgeSharpness::CreaseValue; + return (x == m_edge_sharpness[0] && x == m_edge_sharpness[1]); +} + +bool ON_SubDEdgeSharpness::IsSharp() const +{ + return (m_edge_sharpness[0] > 0.0f || m_edge_sharpness[1] > 0.0f) && IsValid(); +} + + +bool ON_SubDEdgeSharpness::IsCreaseOrSharp() const +{ + return (m_edge_sharpness[0] > 0.0f || m_edge_sharpness[1] > 0.0f) && IsValid(true); +} + +bool ON_SubDEdgeSharpness::IsValidValue( + double candidate_value, + bool bCreaseResult +) +{ + if (candidate_value >= 0.0 && candidate_value <= ON_SubDEdgeSharpness::MaximumValue) + return true; + if (bCreaseResult && ON_SubDEdgeSharpness::MaximumValue == candidate_value) + return true; + return false; } bool ON_SubDEdgeSharpness::IsValid() const { + // NOTE WELL: + // ON_SubDEdgeSharpness::Crease::IsValid() and ON_SubDEdgeSharpness::Nan::IsValid() + // must be false. return m_edge_sharpness[0] >= 0.0f - && m_edge_sharpness[0] <= ((float)ON_SubDEdgeSharpness::Maximum) + && m_edge_sharpness[0] <= ((float)ON_SubDEdgeSharpness::MaximumValue) && m_edge_sharpness[1] >= 0.0f - && m_edge_sharpness[1] <= ((float)ON_SubDEdgeSharpness::Maximum) + && m_edge_sharpness[1] <= ((float)ON_SubDEdgeSharpness::MaximumValue) ; } @@ -3920,12 +4255,28 @@ bool ON_SubDEdgeSharpness::IsNotValid() const return (false == IsValid()); } + +bool ON_SubDEdgeSharpness::IsValid(bool bCreaseResult) const +{ + // The logic (bCreaseResult && IsCrease()) avoids a useless + // call to IsCrease() in the most common case. + return (bCreaseResult && IsCrease()) ? true : IsValid(); +} + +bool ON_SubDEdgeSharpness::IsNotValid(bool bCreaseResult) const +{ + return IsCrease() ? bCreaseResult : (false == IsValid()); +} + const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::FromConstant( double sharpness ) { ON_SubDEdgeSharpness s; - s.m_edge_sharpness[0] = (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::Maximum) ? ((float)ON_SubDEdgeSharpness::Sanitize(sharpness, 0.0)) : ON_FLT_QNAN; + s.m_edge_sharpness[0] + = (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::MaximumValue) + ? ((float)ON_SubDEdgeSharpness::Sanitize(sharpness, 0.0)) + : (ON_SubDEdgeSharpness::CreaseValue == sharpness ? ((float)ON_SubDEdgeSharpness::CreaseValue) : ON_FLT_QNAN); s.m_edge_sharpness[1] = s.m_edge_sharpness[0]; return s; } @@ -3935,13 +4286,19 @@ const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::FromInterval( double sharpness1 ) { - if (sharpness0 >= 0.0 && sharpness0 <= ON_SubDEdgeSharpness::Maximum && sharpness1 >= 0.0 && sharpness1 <= ON_SubDEdgeSharpness::Maximum) + if (sharpness0 >= 0.0 && sharpness0 <= ON_SubDEdgeSharpness::MaximumValue && sharpness1 >= 0.0 && sharpness1 <= ON_SubDEdgeSharpness::MaximumValue) { ON_SubDEdgeSharpness s; s.m_edge_sharpness[0] = (float)ON_SubDEdgeSharpness::Sanitize(sharpness0, 0.0); s.m_edge_sharpness[1] = (float)ON_SubDEdgeSharpness::Sanitize(sharpness1, 0.0); return s; } + + if (ON_SubDEdgeSharpness::CreaseValue == sharpness0 && ON_SubDEdgeSharpness::CreaseValue == sharpness1) + { + return ON_SubDEdgeSharpness::Crease; + } + return ON_SubDEdgeSharpness::Nan; } @@ -3957,13 +4314,14 @@ const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Union( { float s[4] = {}; int count = 0; - if (a.m_edge_sharpness[0] > 0.0f || a.m_edge_sharpness[1] > 0.0f) + + if (a.IsSharp()) { s[0] = a.m_edge_sharpness[0]; s[1] = a.m_edge_sharpness[1]; count = 2; } - if (b.m_edge_sharpness[0] > 0.0f || b.m_edge_sharpness[1] > 0.0f) + if (b.IsSharp()) { s[count] = b.m_edge_sharpness[0]; s[count+1] = b.m_edge_sharpness[1]; @@ -3989,17 +4347,17 @@ double ON_SubDEdgeSharpness::Sanitize( // When sharpness is withing ON_SubDEdgeSharpness::Tolerance of an integer value, // snap to that integer value. - if (false == (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::Maximum)) + if (false == (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::MaximumValue)) return invalid_input_result; // when sharpness is nan, invalid_input_result is returned here. // When sharpness is close to an integer value, snap to the integer value. // This results in cleaner looking limit surfaces and faster evaluation. const double f = floor(sharpness); - if (f >= 0.0 && f <= ON_SubDEdgeSharpness::Maximum) + if (f >= 0.0 && f <= ON_SubDEdgeSharpness::MaximumValue) { if (sharpness - f <= ON_SubDEdgeSharpness::Tolerance) sharpness = f; - else if (f + 1.0 - sharpness <= ON_SubDEdgeSharpness::Tolerance && f + 1.0 <= ON_SubDEdgeSharpness::Maximum) + else if (f + 1.0 - sharpness <= ON_SubDEdgeSharpness::Tolerance && f + 1.0 <= ON_SubDEdgeSharpness::MaximumValue) sharpness = f + 1.0; } @@ -4027,7 +4385,7 @@ const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Subdivided(int end_index) const const double mids = (s[0] == s[1]) ? s[0] : ON_SubDEdgeSharpness::Sanitize(0.5 * (s[0] + s[1])); return (0 == end_index) ? ON_SubDEdgeSharpness::FromInterval(s[0], mids) : ON_SubDEdgeSharpness::FromInterval(mids, s[1]); } - return ON_SubDEdgeSharpness::Zero; + return ON_SubDEdgeSharpness::Smooth; } const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Reversed() const @@ -4047,14 +4405,22 @@ bool operator!=(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs { const float* l = (const float*)(&lhs); const float* r = (const float*)(&rhs); - return l[0] != r[0] || l[1] != r[1]; + + // The compare must be done this way so + // any nan results in false being returned. + // (Note: x==x and x!=x are both false when x = nan.) + if (l[0] != r[0]) + return (l[1] == l[1] && r[1] == r[1]); + if (l[1] != r[1]) + return (l[0] == l[0] && r[0] == r[0]); + return false; } bool operator==(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs) { const float* l = (const float*)(&lhs); const float* r = (const float*)(&rhs); - return l[0] == l[1] && r[0] == r[1]; + return l[0] == r[0] && l[1] == r[1]; } double ON_SubDEdgeSharpness::Average() const @@ -4158,20 +4524,85 @@ double ON_SubDEdgeSharpness::SharpnessFromSliderValue( { if (slider_domain.IsInterval() && slider_value >= slider_domain.Min() && slider_value <= slider_domain.Max()) { + if (0.0 == slider_domain[0] && ON_SubDEdgeSharpness::MaximumValue == slider_domain[1]) + return slider_value; const double s = (0.0 == slider_domain[0] && 1.0 == slider_domain[1]) ? slider_value : slider_domain.NormalizedParameterAt(slider_value); if (s >= 0.0 && s <= 1.0) { - const ON_Interval ZeroToMaximumSharpness(0.0, ON_SubDEdgeSharpness::Maximum); + const ON_Interval ZeroToMaximumSharpness(0.0, ON_SubDEdgeSharpness::MaximumValue); double sharpness = ON_SubDEdgeSharpness::Sanitize(ZeroToMaximumSharpness.ParameterAt(s), invalid_input_result); - if (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::Maximum) + if (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::MaximumValue) return sharpness; } } return invalid_input_result; } +unsigned ON_SubDEdgeSharpness::SetEdgeChainSharpness( + ON_Interval chain_sharpness_range, + unsigned edge_count, + ON_SimpleArray& chain_edge_sharpness +) +{ + if (edge_count <= 0 || edge_count >= ON_UNSET_UINT_INDEX) + return 0; + chain_edge_sharpness.Reserve(edge_count); + chain_edge_sharpness.SetCount(edge_count); + const unsigned rc = ON_SubDEdgeSharpness::SetEdgeChainSharpness(chain_sharpness_range, edge_count, chain_edge_sharpness.Array()); + if (rc <= 0) + chain_edge_sharpness.SetCount(0); + return rc; +} +unsigned ON_SubDEdgeSharpness::SetEdgeChainSharpness( + ON_Interval chain_sharpness_range, + unsigned edge_count, + ON_SubDEdgeSharpness* chain_edge_sharpness +) +{ + if (edge_count <= 0 || edge_count >= ON_UNSET_UINT_INDEX || nullptr == chain_edge_sharpness) + return 0; + + if (chain_sharpness_range[0] == chain_sharpness_range[1]) + { + const ON_SubDEdgeSharpness c = ON_SubDEdgeSharpness::FromConstant(chain_sharpness_range[0]); + if (false == c.IsValid(true)) + return 0; + for (unsigned i = 0; i < edge_count; ++i) + chain_edge_sharpness[i] = c; + return 1; + } + + if (false == ON_SubDEdgeSharpness::IsValidValue(chain_sharpness_range[0], false)) + return false; + if (false == ON_SubDEdgeSharpness::IsValidValue(chain_sharpness_range[1], false)) + return false; + + const double d = (double)edge_count; + ON_Interval r(ON_DBL_QNAN, chain_sharpness_range[0]); + for (unsigned i = 1; i <= edge_count; ++i) + { + r.m_t[0] = r.m_t[1]; + r.m_t[1] + = (i < edge_count) + ? chain_sharpness_range.ParameterAt(((double)i / d)) + : chain_sharpness_range[1]; + const ON_SubDEdgeSharpness v = ON_SubDEdgeSharpness::FromInterval(r); + if (v.IsValid()) + { + chain_edge_sharpness[i - 1] = v; + continue; + } + return 0; + } + + return + (chain_edge_sharpness[0].EndSharpness(0) != chain_edge_sharpness[edge_count - 1].EndSharpness(1)) + ? edge_count + : 1; +} + bool ON_SubD::HasSharpEdges() const { bool bHasSharpEdges = 0 != (ON_ComponentAttributes::EdgeAttributes::InteriorSharp & this->AggregateEdgeAttributes()); @@ -4188,14 +4619,14 @@ bool ON_SubD::HasSharpEdges() const unsigned int ON_SubD::SharpEdgeCount(ON_SubDEdgeSharpness& sharpness_range) const { - sharpness_range = ON_SubDEdgeSharpness::Zero; + sharpness_range = ON_SubDEdgeSharpness::Smooth; unsigned int sharp_edge_count = 0; ON_SubDEdgeIterator eit = this->EdgeIterator(); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (e->IsSharp()) { - sharpness_range = ON_SubDEdgeSharpness::Union(sharpness_range, e->Sharpness()); + sharpness_range = ON_SubDEdgeSharpness::Union(sharpness_range, e->Sharpness(false)); ++sharp_edge_count; } } @@ -4204,7 +4635,7 @@ unsigned int ON_SubD::SharpEdgeCount(ON_SubDEdgeSharpness& sharpness_range) cons unsigned int ON_SubD::SharpEdgeCount() const { - ON_SubDEdgeSharpness sharpness_range = ON_SubDEdgeSharpness::Zero; + ON_SubDEdgeSharpness sharpness_range = ON_SubDEdgeSharpness::Smooth; return SharpEdgeCount(sharpness_range); } @@ -4251,8 +4682,49 @@ double ON_SubDVertex::VertexSharpness() const return GetSharpSubdivisionPoint(sharp_subdivision_point); } - double ON_SubDVertex::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const +{ + unsigned count = 0; + const ON_SubDVertex* v[3] = {}; + double c[3] = {}; + double vs = this->GetSharpSubdivisionPoint(count,v,c); + if (vs > 0.0 && count > 0U && nullptr != v[0]) + { + switch (count) + { + case 1: + sharp_subdivision_point = c[0] * v[0]->ControlNetPoint(); + break; + case 2: + sharp_subdivision_point + = c[0] * v[0]->ControlNetPoint() + + c[1]*v[1]->ControlNetPoint(); + break; + case 3: + sharp_subdivision_point + = c[0] * v[0]->ControlNetPoint() + + c[1] * v[1]->ControlNetPoint() + + c[2] * v[2]->ControlNetPoint(); + break; + default: + vs = 0.0; + sharp_subdivision_point = ON_3dPoint::NanPoint; + break; + } + } + else + { + vs = 0.0; + sharp_subdivision_point = ON_3dPoint::NanPoint; + } + return vs; +} + +double ON_SubDVertex::GetSharpSubdivisionPoint( + unsigned& count, + const ON_SubDVertex* v[3], + double c[3] +) const { // Below "sharp edge" means an edge attached to this vertex that has a smooth tag // and a nonzero sharpness at the end attached to this vertex. @@ -4274,6 +4746,9 @@ double ON_SubDVertex::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_poi // (1-s)*(ordinary subdivision point) + s*sharp_subdivision_point, // where s = min(returned vertex sharpness, 1). + count = 0; + v[0] = v[1] = v[2] = nullptr; + c[0] = c[1] = c[2] = 0.0; if (this->IsSmoothOrDartOrCrease() && nullptr != m_edges) { unsigned int sharp_edge_count = 0; @@ -4315,28 +4790,38 @@ double ON_SubDVertex::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_poi if (2 == other_v_count && nullptr != other_v[0] && nullptr != other_v[1]) { // 2 creases and sharps - "crease" subdivision point - sharp_subdivision_point = 0.125 * (6.0 * this->ControlNetPoint() + other_v[0]->ControlNetPoint() + other_v[1]->ControlNetPoint()); + c[0] = 0.75; + c[1] = 0.125; + c[2] = 0.125; + v[0] = this; + v[1] = other_v[0]; + v[2] = other_v[1]; + count = 3; } else { // 3 or more creases and sharps - "corner" subdivision point - sharp_subdivision_point = this->ControlNetPoint(); + c[0] = 1.0; + v[0] = this; + count = 1; } return vertex_sharpness; } } - - sharp_subdivision_point = ON_3dPoint::NanPoint; return 0.0; } -unsigned int ON_SubDVertex::SharpEdgeCount( bool bEndCheck ) const +unsigned int ON_SubDVertex::SharpEdgeCount( + bool bCountCreasesAsSharp, + bool bEndCheck +) const { ON_Interval sharpness_range; - return SharpEdgeCount(bEndCheck, sharpness_range); + return SharpEdgeCount(bCountCreasesAsSharp, bEndCheck, sharpness_range); } unsigned int ON_SubDVertex::SharpEdgeCount( + bool bCountCreasesAsSharp, bool bEndCheck, ON_Interval& sharpness_range ) const @@ -4344,30 +4829,31 @@ unsigned int ON_SubDVertex::SharpEdgeCount( unsigned int sharp_edge_count = 0; double mins = 0.0; double maxs = 0.0; - if (IsSmoothOrDartOrCrease() && nullptr != m_edges) + if (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->IsSharp() : false) + if (nullptr == e || 2 != e->m_face_count) + continue; + if (false == e->IsSharp()) { - const double s - = bEndCheck - ? e->EndSharpness(this) - : e->Sharpness().MaximumEndSharpness(); - if (s > 0.0) + if (false == bCountCreasesAsSharp || false == e->IsCrease()) + continue; + } + const double s = e->IsCrease() ? ON_SubDEdgeSharpness::CreaseValue : e->EndSharpness(this); + if (false == bEndCheck || s > 0.0) + { + if (0 == sharp_edge_count) { - if (0 == sharp_edge_count) - { - mins = s; - maxs = s; - } - else if (s < mins) - mins = s; - else if (s > maxs) - maxs = s; - ++sharp_edge_count; + mins = s; + maxs = s; } + else if (s < mins) + mins = s; + else if (s > maxs) + maxs = s; + ++sharp_edge_count; } } } @@ -4379,26 +4865,31 @@ bool ON_SubDEdge::IsSharp() const { return (ON_SubDEdgeTag::Smooth == m_edge_tag || ON_SubDEdgeTag::SmoothX == m_edge_tag) - && m_sharpness.IsNotZero(); + && m_sharpness.IsSharp(); +} + +bool ON_SubDEdge::IsCreaseOrSharp() const +{ + return (ON_SubDEdgeTag::Crease == m_edge_tag || this->IsSharp()); } bool ON_SubDEdge::IsSmoothNotSharp() const { return (ON_SubDEdgeTag::Smooth == m_edge_tag || ON_SubDEdgeTag::SmoothX == m_edge_tag) - && (false == m_sharpness.IsNotZero()); + && (false == m_sharpness.IsSharp()); } bool ON_SubDEdge::IsSmoothNotXNotSharp() const { - return ON_SubDEdgeTag::Smooth == m_edge_tag && (false == m_sharpness.IsNotZero()); + return ON_SubDEdgeTag::Smooth == m_edge_tag && (false == m_sharpness.IsSharp()); } double ON_SubDEdge::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const { if (IsSharp()) { - const double s = Sharpness().Average(); + const double s = Sharpness(false).Average(); sharp_subdivision_point = this->ControlNetCenterPoint(); return s; } @@ -6349,10 +6840,15 @@ bool ON_SubDEdge::RemoveFaceFromArray( removed_face = m_facex[i-2]; } - unsigned int j = i+1; + unsigned int j = i+1U; - while (j < 2 && j < count ) + while (j < 2 && j < count) + { + // NOTE: + // 0 <= i < j < min(2,count) <= 2 so any warning that the following line may + // result in an invalid write is incorrect. m_face2[i++] = m_face2[j++]; + } if (count > 2) { @@ -7927,7 +8423,7 @@ unsigned int ON_SubD::DumpTopology( ); } - ON_SubDEdgeSharpness subd_sharpness_range = ON_SubDEdgeSharpness::Zero; + ON_SubDEdgeSharpness subd_sharpness_range = ON_SubDEdgeSharpness::Smooth; const unsigned int subd_sharp_edge_count = SharpEdgeCount(subd_sharpness_range); if (subd_sharp_edge_count > 0) { @@ -8311,7 +8807,7 @@ static void Internal_AccumulateEdgeHash( sha1.AccumulateBool(bIsCrease); if (false == bIsCrease && ON_SubDHashType::Geometry == hash_type && e->IsSharp()) { - const ON_SubDEdgeSharpness s = e->Sharpness(); + const ON_SubDEdgeSharpness s = e->Sharpness(false); const double a[2] = { s[0] ,s[1] }; sha1.AccumulateDoubleArray(2, a); } @@ -11316,7 +11812,7 @@ bool ON_SubD::RemoveFaceEdgeConnection( ) { removed_edge = ON_SubDEdgePtr::Null; - if ( nullptr == face && i >= (unsigned int)face->m_edge_count ) + if ( nullptr == face || i >= (unsigned int)face->m_edge_count ) { return ON_SUBD_RETURN_ERROR(false); } @@ -13714,7 +14210,7 @@ bool ON_SubDimple::LocalSubdivide( { ON_SubDEdge* e = edges[edex]; - const ON_SubDEdgeSharpness e_sharpness = e->Sharpness(); + const ON_SubDEdgeSharpness e_sharpness = e->Sharpness(false); e->EdgeModifiedNofification(); const ON_SubDEdge* new_edge = SplitEdge(e, edge_points[edex]); @@ -13946,8 +14442,8 @@ unsigned int ON_SubDimple::GlobalSubdivide() class ON_SubDEdge* e11 = AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); if ( e0->IsSharp() ) { - const ON_SubDEdgeSharpness e0_sharpness = e0->Sharpness(); - if (e0_sharpness.IsNotZero()) + const ON_SubDEdgeSharpness e0_sharpness = e0->Sharpness(false); + if (e0_sharpness.IsSharp()) { if (nullptr != e10) e10->SetSharpnessForExperts(e0_sharpness.Subdivided(0)); @@ -18313,6 +18809,936 @@ unsigned int ON_SubD::TransformComponents( return TransformComponents(xform,cptr_list.Array(),cptr_list.UnsignedCount(),component_location); } +unsigned ON_SubDVertexQuadSector::SectorVertexCount(ON_SubDVertexTag vertex_tag, unsigned sector_face_count) +{ + const unsigned center_vertex_edge_count = ON_SubDVertexQuadSector::CenterVertexEdgeCount(vertex_tag, sector_face_count); + return (center_vertex_edge_count >= 2U) ? (1U + center_vertex_edge_count + sector_face_count) : 0U; +} + +unsigned ON_SubDVertexQuadSector::SectorEdgeCount(ON_SubDVertexTag vertex_tag, unsigned sector_face_count) +{ + if (sector_face_count <= ON_SubDVertex::MaximumFaceCount) + { + const unsigned min_sector_face_count = ON_SubDVertexQuadSector::MinimumSectorFaceCount(vertex_tag); + if (sector_face_count >= min_sector_face_count) + return (2U - min_sector_face_count) + 3U * sector_face_count; + } + return 0U; +} + +unsigned ON_SubDVertexQuadSector::CenterVertexEdgeCount(ON_SubDVertexTag center_vertex_tag, unsigned sector_face_count) +{ + unsigned center_vertex_edge_count; + + switch (center_vertex_tag) + { + case ON_SubDVertexTag::Smooth: + case ON_SubDVertexTag::Dart: + center_vertex_edge_count = (sector_face_count >= 2U) ? sector_face_count : 0U; + break; + + case ON_SubDVertexTag::Crease: + case ON_SubDVertexTag::Corner: + center_vertex_edge_count = (sector_face_count >= 1U) ? (sector_face_count + 1U) : 0U; + break; + + case ON_SubDVertexTag::Unset: + default: + center_vertex_edge_count = 0U; + break; + } + + return center_vertex_edge_count; +} + +unsigned ON_SubDVertexQuadSector::MinimumSectorFaceCount( + ON_SubDVertexTag vertex_tag +) +{ + unsigned min_sector_face_count; + switch (vertex_tag) + { + case ON_SubDVertexTag::Smooth: + case ON_SubDVertexTag::Dart: + min_sector_face_count = 2U; + break; + + case ON_SubDVertexTag::Crease: + case ON_SubDVertexTag::Corner: + min_sector_face_count = 1U; + break; + + case ON_SubDVertexTag::Unset: + default: + min_sector_face_count = 0U; + break; + } + + return min_sector_face_count; +} + + +void ON_SubDVertexQuadSector::Internal_Destroy() +{ + void* heap = this->m_heap; + this->m_heap = nullptr; + this->m_v = nullptr; + this->m_e = nullptr; + this->m_f = nullptr; + this->m_sector_coefficient = ON_DBL_QNAN; + this->m_maximum_edge_end_sharpness = ON_DBL_QNAN; + this->m_sector_face_count = 0; + this->m_center_vertex_edge_count = 0; + if (nullptr != heap) + onfree(heap); +} + +ON_SubDVertexQuadSector::~ON_SubDVertexQuadSector() +{ + this->Internal_Destroy(); +} + +void ON_SubDVertexQuadSector::Internal_CopyFrom(const ON_SubDVertexQuadSector& src) +{ + const ON_SubDVertexTag center_vertex_tag = src.CenterVertexTag(); + if (false == this->Initialize(center_vertex_tag, src.SectorFaceCount(), nullptr, nullptr)) + return; + + if ( ON_SubDVertexTag::Corner == center_vertex_tag) + this->m_sector_coefficient = src.m_sector_coefficient; + + const unsigned subdivision_level = src.SubdivisionLevel(); + + const unsigned vertex_count = src.SectorVertexCount(); + for (unsigned vi = 0; vi < vertex_count; ++vi) + { + ON_SubDVertex& v = m_v[vi]; + const ON_SubDVertex& srcv = src.m_v[vi]; + const ON_3dPoint p = srcv.ControlNetPoint(); + if (p.IsValid()) + v.SetControlNetPoint(p, false); + else + v.UnsetControlNetPoint(); + v.SetSubdivisionLevel(srcv.SubdivisionLevel()); + } + + const unsigned center_vertex_edge_count = src.CenterVertexEdgeCount(); + const unsigned edge_count = src.SectorEdgeCount(); + for (unsigned ei = 0; ei < edge_count; ++ei) + { + ON_SubDEdge& e = m_e[ei]; + const ON_SubDEdge& srce = src.m_e[ei]; + if (ei < center_vertex_edge_count) + { + const ON_SubDEdgeSharpness s = srce.Sharpness(false); + if (s.IsSharp()) + e.SetSharpnessForExperts(s); + if (e.IsSmooth()) + e.m_sector_coefficient[0] = this->m_sector_coefficient; + } + } +} + +ON_SubDVertexQuadSector::ON_SubDVertexQuadSector(const ON_SubDVertexQuadSector& src) +{ + this->Internal_CopyFrom(src); +} + +ON_SubDVertexQuadSector& ON_SubDVertexQuadSector::operator=(const ON_SubDVertexQuadSector& src) +{ + if (this != &src) + { + this->Internal_Destroy(); + this->Internal_CopyFrom(src); + } + return *this; +} + +bool ON_SubDVertexQuadSector::Initialize( + ON_SubDVertexTag center_vertex_tag, + unsigned sector_face_count, + const ON_3dPoint* sector_control_net_points, + const ON_SubDEdgeSharpness* center_edge_sharpnesses +) +{ + this->m_sector_coefficient = ON_DBL_QNAN; + this->m_maximum_edge_end_sharpness = ON_DBL_QNAN; + + const unsigned sector_vertex_count = ON_SubDVertexQuadSector::SectorVertexCount(center_vertex_tag, sector_face_count); + const unsigned sector_edge_count = ON_SubDVertexQuadSector::SectorEdgeCount(center_vertex_tag, sector_face_count); + bool bValidInput = sector_vertex_count >= 4U && sector_edge_count >= sector_face_count && sector_face_count >= 1U; + if (false == bValidInput) + { + ON_SUBD_ERROR("Invalid input."); + this->Internal_Destroy(); + return false; + } + + const bool bInteriorSector = (ON_SubDVertexTag::Smooth == center_vertex_tag || ON_SubDVertexTag::Dart == center_vertex_tag); + + const unsigned center_vertex_edge_count = sector_face_count + (bInteriorSector ? 0U : 1U); + const unsigned ptr_count = sector_face_count + center_vertex_edge_count + 8 * (center_vertex_edge_count + sector_face_count); + const size_t sizeofv = sector_vertex_count * sizeof(m_v[0]); + const size_t sizeofe = sector_edge_count * sizeof(m_e[0]); + const size_t sizeoff = sector_face_count * sizeof(m_f[0]); + const size_t sizeofp = ptr_count * sizeof(ON__UINT_PTR); + const size_t sizeofr = sector_vertex_count * sizeof(m_r[0]); + const size_t sz = sizeofv + sizeofe + sizeoff + sizeofp + sizeofr; + if ( nullptr == m_heap || sector_face_count != this->m_sector_face_count || center_vertex_edge_count != this->m_center_vertex_edge_count ) + { + Internal_Destroy(); + m_heap = onmalloc(sz); + if (nullptr == m_heap) + return false; + } + + memset(m_heap, 0, sz); + m_v = (ON_SubDVertex*)m_heap; + m_e = (ON_SubDEdge*)(m_v + sector_vertex_count); + m_f = (ON_SubDFace*)(m_e + sector_edge_count); + ON__UINT_PTR* p = (ON__UINT_PTR*)(m_f + sector_face_count); + m_r = (ON_SubDComponentPtr*)(p + ptr_count); + m_sector_face_count = sector_face_count; + m_center_vertex_edge_count = center_vertex_edge_count; + + for (unsigned vi = 0; vi < sector_vertex_count; ++vi) + { + ON_SubDVertex* v = &m_v[vi]; + v->m_vertex_tag = ON_SubDVertexTag::Smooth; + v->m_edge_capacity = (unsigned short)(0 == vi ? center_vertex_edge_count : 4U); + v->m_face_capacity = (unsigned short)(0 == vi ? sector_face_count : 4U); + v->m_edges = (ON_SubDEdgePtr*)p; + p += v->m_edge_capacity; + v->m_faces = (const ON_SubDFace**)p; + p += v->m_face_capacity; + if (1U == (vi % 2U)) + { + const unsigned ei = (vi - 1U) / 2U; + // When vi is odd, m_v[vi] is a ring vertex for edge m_e[ei] + m_e[ei].m_vertex[0] = &m_v[0]; + m_e[ei].m_vertex[1] = v; + } + } + + for (unsigned fi = 0; fi < sector_face_count; ++fi) + { + ON_SubDFace* f = &m_f[fi]; + const unsigned vi = 2U * (fi + 1U); + const unsigned ei = center_vertex_edge_count + 2U * fi; + ON_SubDEdge* e4[4] = { &m_e[fi], &m_e[ei] , &m_e[ei + 1], &m_e[(fi + 1U) % center_vertex_edge_count] }; + ON_SubDVertex* v4[4] = { + &m_v[0], + const_cast(e4[0]->m_vertex[1]), + &m_v[vi], + const_cast(e4[3]->m_vertex[1]) + }; + + e4[1]->m_vertex[0] = v4[1]; + e4[1]->m_vertex[1] = v4[2]; + + e4[2]->m_vertex[0] = v4[2]; + e4[2]->m_vertex[1] = v4[3]; + + for (unsigned fei = 0; fei < 4U; ++fei) + { + const ON__INT_PTR dir = (3 == fei) ? 1 : 0; + f->m_edge4[fei] = ON_SubDEdgePtr::Create(e4[fei], dir); + e4[fei]->m_face2[e4[fei]->m_face_count++] = ON_SubDFacePtr::Create(f, dir); + } + f->m_edge_count = 4; + + for (unsigned fvi = 0; fvi < 4U; ++fvi) + { + v4[fvi]->m_faces[v4[fvi]->m_face_count++] = &m_f[fi]; + } + } + + for (unsigned ei = 0; ei < sector_edge_count; ++ei) + { + ON_SubDEdge* e = &m_e[ei]; + e->m_edge_tag = ON_SubDEdgeTag::Smooth; + for (unsigned evi = 0; evi < 2; ++evi) + { + ON_SubDVertex* v = const_cast(e->m_vertex[evi]); + v->m_edges[v->m_edge_count++] = ON_SubDEdgePtr::Create(e, evi); + } + } + + for (unsigned vi = 0; vi < sector_vertex_count; ++vi) + { + m_v[vi].UnsetControlNetPoint(); + m_v[vi].m_id = vi + 1U; + } + + for (unsigned ei = 0; ei < sector_edge_count; ++ei) + { + m_e[ei].m_id = ei + 1U; + } + + for (unsigned fi = 0; fi < sector_face_count; ++fi) + { + m_f[fi].m_id = fi + 1U; + } + + if (false == SetCenterVertexTag(center_vertex_tag)) + { + this->Internal_Destroy(); + return false; + } + + if (nullptr != sector_control_net_points) + { + for (unsigned vi = 0; vi < sector_vertex_count; ++vi) + { + const ON_3dPoint P = sector_control_net_points[vi]; + if ( P.IsValid()) + m_v[vi].SetControlNetPoint(P,false); + } + + + if (ON_SubDVertexTag::Corner == center_vertex_tag) + { + // The sector coefficient on smooth edges ending at the center vertex depends on the + // angle bewteen the bounding crease edges. + ON_SubDEdge* creases[2] = { &m_e[0], &m_e[m_sector_face_count] }; + const bool bValidEdges = creases[0]->ControlNetLine().IsValid() && creases[1]->ControlNetLine().IsValid(); + const double angle_radians + = bValidEdges + ? ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(ON_SubDEdgePtr::Create(creases[0], 0), ON_SubDEdgePtr::Create(creases[1], 0)) + : ON_HALFPI; + const double sector_coefficient = ON_SubDSectorType::CornerSectorCoefficient(m_sector_face_count, angle_radians); + this->m_sector_coefficient = sector_coefficient; + for ( unsigned ei = 1; ei < m_sector_face_count; ++ei) + m_e[ei].m_sector_coefficient[0] = sector_coefficient; + } + } + + if (nullptr != center_edge_sharpnesses) + { + this->m_maximum_edge_end_sharpness = 0.0; + for (unsigned ei = 0; ei < m_center_vertex_edge_count; ++ei) + { + if (m_e[ei].IsCrease()) + { + m_e[ei].ClearSharpnessForExperts(); + } + else + { + m_e[ei].SetSharpnessForExperts(center_edge_sharpnesses[ei]); + const double x = m_e[ei].Sharpness(false).MaximumEndSharpness(); + if (x > this->m_maximum_edge_end_sharpness) + this->m_maximum_edge_end_sharpness = x; + } + } + } + + m_r[0] = ON_SubDComponentPtr::Create(&m_v[0]); + for (unsigned ri = 1u; ri < sector_vertex_count; ri += 2u) + m_r[ri] = ON_SubDComponentPtr::Create(&m_e[ri / 2], 0); + for (unsigned ri = 2u; ri < sector_vertex_count; ri += 2u) + m_r[ri] = ON_SubDComponentPtr::Create(&m_f[ri / 2u - 1u]); + + return true; +} + +bool ON_SubDVertexQuadSector::Initialize( + ON_SubDVertexTag center_vertex_tag, + unsigned sector_face_count, + const ON_SimpleArray& sector_control_net_points +) +{ + const unsigned sector_vertex_count = ON_SubDVertexQuadSector::SectorVertexCount(center_vertex_tag, sector_face_count); + return (sector_vertex_count > 0U && sector_vertex_count == sector_control_net_points.UnsignedCount()) + ? this->Initialize(center_vertex_tag, sector_face_count, sector_control_net_points.Array(), nullptr) + : false; +} + +bool ON_SubDVertexQuadSector::Initialize( + ON_SubDVertexTag center_vertex_tag, + unsigned sector_face_count, + const ON_SimpleArray& sector_control_net_points, + const ON_SimpleArray& center_edge_sharpnesses +) +{ + const unsigned sector_vertex_count = ON_SubDVertexQuadSector::SectorVertexCount(center_vertex_tag, sector_face_count); + const unsigned center_vertex_edge_count = ON_SubDVertexQuadSector::CenterVertexEdgeCount(center_vertex_tag, sector_face_count); + return ( + sector_vertex_count > 0U + && (sector_vertex_count == sector_control_net_points.UnsignedCount() || 0 == sector_control_net_points.UnsignedCount()) + && (center_vertex_edge_count == center_edge_sharpnesses.UnsignedCount() || 0 == center_edge_sharpnesses.UnsignedCount()) + ) + ? this->Initialize(center_vertex_tag, sector_face_count, + (sector_vertex_count == sector_control_net_points.UnsignedCount()) ? sector_control_net_points.Array() : nullptr, + (center_vertex_edge_count == center_edge_sharpnesses.UnsignedCount()) ? center_edge_sharpnesses.Array() : nullptr + ) + : false; +} + +double ON_SubDVertexQuadSector::MaximumCenterVertexEdgeEndSharpness() const +{ + if (false == (this->m_maximum_edge_end_sharpness >= 0.0)) + { + //const ON_SubDVertexTag center_vertex_tag = this->CenterVertexTag(); + const unsigned crease_count = this->SectorCreaseEdgeCount(); + double maxs = 0.0; + unsigned n = this->CenterVertexEdgeCount(); + if (n > 0U && 2U == crease_count) + --n; + for (unsigned ei = (0U == crease_count) ? 0U : 1U; ei < n; ++ei) + { + const double x = m_e[ei].Sharpness(false).MaximumEndSharpness(); + if (x > maxs) + maxs = x; + this->m_maximum_edge_end_sharpness = maxs; + } + } + return this->m_maximum_edge_end_sharpness; +} + +bool ON_SubDVertexQuadSector::SetCenterVertexTag(ON_SubDVertexTag center_vertex_tag) +{ + this->m_sector_coefficient = ON_DBL_QNAN; + bool bValidTag = false; + ON_SubDEdge* creases[2] = {}; + double sector_coefficient = ON_DBL_QNAN; + switch (center_vertex_tag) + { + case ON_SubDVertexTag::Unset: + bValidTag = false; + break; + case ON_SubDVertexTag::Smooth: + if (m_sector_face_count >= 2 && m_center_vertex_edge_count == m_sector_face_count) + { + bValidTag = true; + sector_coefficient = ON_SubDSectorType::SmoothSectorCoefficient(); + } + break; + case ON_SubDVertexTag::Crease: + if (m_sector_face_count >= 1 && m_center_vertex_edge_count == m_sector_face_count + 1U) + { + bValidTag = true; + creases[0] = &m_e[0]; + creases[1] = &m_e[m_sector_face_count]; + sector_coefficient = ON_SubDSectorType::CreaseSectorCoefficient(m_sector_face_count); + } + break; + case ON_SubDVertexTag::Corner: + if (m_sector_face_count >= 1 && m_center_vertex_edge_count == m_sector_face_count + 1U) + { + bValidTag = true; + creases[0] = &m_e[0]; + creases[1] = &m_e[m_sector_face_count]; + bool bValidEdges = creases[0]->ControlNetLine().IsValid() && creases[1]->ControlNetLine().IsValid(); + double angle_radians + = bValidEdges + ? ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(ON_SubDEdgePtr::Create(creases[0], 0), ON_SubDEdgePtr::Create(creases[1], 0)) + : ON_HALFPI; + sector_coefficient = ON_SubDSectorType::CornerSectorCoefficient(m_sector_face_count, angle_radians); + } + break; + case ON_SubDVertexTag::Dart: + if (m_sector_face_count >= 2 && m_center_vertex_edge_count == m_sector_face_count) + { + bValidTag = true; + creases[0] = &m_e[0]; + sector_coefficient = ON_SubDSectorType::DartSectorCoefficient(m_sector_face_count); + } + break; + default: + bValidTag = false; + } + + if (false == bValidTag) + return ON_SUBD_RETURN_ERROR(false); + + if (false == (sector_coefficient > 0.0 && sector_coefficient < 1.0)) + sector_coefficient = 0.0; + + this->m_sector_coefficient = sector_coefficient; + + m_v[0].m_vertex_tag = center_vertex_tag; + this->m_maximum_edge_end_sharpness = 0.0; + for (unsigned ei = 0; ei < m_center_vertex_edge_count; ++ei) + { + ON_SubDEdge* e = &m_e[ei]; + if (e == creases[0] || e == creases[1]) + { + e->m_edge_tag = ON_SubDEdgeTag::Crease; + e->m_sector_coefficient[0] = 0.0; + e->m_sector_coefficient[1] = 0.0; + e->ClearSharpnessForExperts(); + } + else + { + e->m_edge_tag = ON_SubDEdgeTag::Smooth; + e->m_sector_coefficient[0] = sector_coefficient; + e->m_sector_coefficient[1] = 0.0; + const double x = e->Sharpness(false).MaximumEndSharpness(); + if (x > this->m_maximum_edge_end_sharpness) + this->m_maximum_edge_end_sharpness = x; + } + } + + return true; +} + +bool ON_SubDVertexQuadSector::GetSectorControlNetPoints( + ON_SimpleArray& sector_control_net_points +) const +{ + sector_control_net_points.SetCount(0); + const unsigned sector_vertex_count = this->SectorVertexCount(); + if (sector_vertex_count > 0) + { + sector_control_net_points.Reserve(sector_vertex_count); + for (unsigned vi = 0; vi < sector_vertex_count; ++vi) + sector_control_net_points.Append(m_v[vi].ControlNetPoint()); + } + return sector_control_net_points.Count() > 0; +} + +bool ON_SubDVertexQuadSector::InitializeFromSubdividedSectorComponents( + const ON_SimpleArray& sector_ring_components +) +{ + return this->InitializeFromSubdividedSectorComponents(sector_ring_components.Array(), sector_ring_components.Count()); +} + +bool ON_SubDVertexQuadSector::InitializeFromSubdividedSectorComponents(const ON_SubDComponentPtr* sector_ring_components, size_t sector_components_count) +{ + for (;;) + { + const unsigned sector_ring_component_count = (nullptr != sector_ring_components) ? ((unsigned)sector_components_count) : 0u; + if (sector_ring_component_count < 4U) + { + ON_SUBD_ERROR("sector_ring_component_count is too small."); + break; + } + + const ON_SubDVertex* center_vertex = sector_ring_components[0].Vertex(); + if (nullptr == center_vertex) + { + ON_SUBD_ERROR("sector_ring_components[0] must be the sector center vertex."); + break; + } + + const ON_SubDVertexTag center_vertex_tag = (nullptr != center_vertex) ? center_vertex->m_vertex_tag : ON_SubDVertexTag::Unset; + + unsigned min_ring_count = 0U; + switch (center_vertex_tag) + { + case ON_SubDVertexTag::Unset: + break; + case ON_SubDVertexTag::Smooth: + case ON_SubDVertexTag::Dart: + min_ring_count = 5U; + break; + case ON_SubDVertexTag::Crease: + case ON_SubDVertexTag::Corner: + min_ring_count = 4U; + break; + default: + break; + } + + if (min_ring_count < 4U || sector_ring_component_count < min_ring_count || (sector_ring_component_count % 2U) != (min_ring_count % 2U)) + { + ON_SUBD_ERROR("Invalid combination of center vertex tag and sector_component_ring_count."); + break; + } + + if ( + ON_SubDVertexTag::Dart == center_vertex_tag + && false == sector_ring_components[1].EdgePtr().EdgeIsCrease() + && 1U == (sector_ring_component_count % 2U) + ) + { + for (unsigned i = 1; i < sector_ring_component_count; i += 2) + { + if (false == sector_ring_components[i].EdgePtr().EdgeIsCrease()) + continue; + + // Roll the edges and face portion of sector_ring_components[] + // so the dart's crease is the first edge in local_sector_ring_components[]. + ON_SimpleArray local_sector_ring_components(sector_ring_component_count); + local_sector_ring_components[0] = sector_ring_components[0]; + for (unsigned k = 3U; k < sector_ring_component_count; k += 2) + { + local_sector_ring_components.Append(sector_ring_component_count - k, sector_ring_components + k); + local_sector_ring_components.Append(k - 1U, sector_ring_components + 1U); + } + // check that there will not be infinite recursion into this roll clause + // and then use local_sector_ring_components[]. + if (local_sector_ring_components[0].IsVertex() && local_sector_ring_components[1].EdgePtr().EdgeIsCrease()) + return this->InitializeFromSubdividedSectorComponents(local_sector_ring_components); + + break; + } + + ON_SUBD_ERROR("Dart sectors must have exactly one crease in sector_ring_components[]."); + break; + } + + const bool bCreaseOrCornerSector = ON_SubDVertexTag::Crease == center_vertex_tag || ON_SubDVertexTag::Corner == center_vertex_tag; + const unsigned crease_dex[2] = { + bCreaseOrCornerSector || ON_SubDVertexTag::Dart == center_vertex_tag ? 1U : ON_UNSET_UINT_INDEX, + bCreaseOrCornerSector ? (sector_ring_component_count - 1U) : ON_UNSET_UINT_INDEX, + }; + + // ring_points[i] = sector_ring_components[i].SubdivisionPoint(); + ON_SimpleArray ring_points(sector_ring_component_count); + + // ring_sharpness[ei] = correctly oriented and subdividided sector_ring_components[2*ei+1].EdgeSharpness() + ON_SimpleArray ring_sharpness(sector_ring_component_count/2); + + const ON_SubDEdge* e = nullptr; + const ON_SubDFace* f = nullptr; + for (unsigned i = 0U; i < sector_ring_component_count; ++i) + { + if (0U == i) + { + ring_points.Append(center_vertex->SubdivisionPoint()); + } + else if (1 == (i % 2U)) + { + // sector_ring_components[i] must be an edge with Direction() set so + // that center_vertex = RelativeVertex(0) + ON_SubDEdgePtr eptr = sector_ring_components[i].EdgePtr(); + if (center_vertex == eptr.RelativeVertex(1)) + eptr = eptr.Reversed(); + + if (center_vertex != eptr.RelativeVertex(0)) + break; + + e = sector_ring_components[i].Edge(); + if (nullptr == e) + break; + + if (bCreaseOrCornerSector && i + 1U == sector_ring_component_count) + { + if (nullptr != f && f->EdgeArrayIndex(e) >= (unsigned)f->m_edge_count) + break; + } + + if (i == crease_dex[0] || i == crease_dex[1]) + { + if (false == e->IsCrease()) + break; + } + else + { + if (false == e->IsSmooth() || ((unsigned short)2) != e->m_face_count) + break; + } + + ring_points.Append(e->SubdivisionPoint()); + ring_sharpness.Append(eptr.RelativeSharpness(false).Subdivided(0)); + } + else + { + // sector_ring_components[i] must be a face + f = sector_ring_components[i].Face(); + if (nullptr == f || f->EdgeArrayIndex(e) >= (unsigned)f->m_edge_count) + break; + ring_points.Append(f->SubdivisionPoint()); + } + } + + if (ring_points.UnsignedCount() != sector_ring_component_count) + { + ON_SUBD_ERROR("The edges and faces in sector_ring_components[] must alternate and be radially sorted."); + break; + } + + if (false == bCreaseOrCornerSector) + { + if (nullptr == f || f->EdgeArrayIndex(sector_ring_components[1].Edge()) >= (unsigned)f->m_edge_count) + break; + } + + const unsigned sector_face_count = (sector_ring_component_count - 1U) / 2U; + + if (false == this->Initialize(center_vertex_tag, sector_face_count, ring_points, ring_sharpness)) + break; + + if (sector_ring_component_count != this->SectorVertexCount()) + break; + + const unsigned subdivision_level = center_vertex->SubdivisionLevel() + 1U; + + const unsigned sector_vertex_count = this->SectorVertexCount(); + for (unsigned vi = 0; vi < sector_vertex_count; ++vi) + { + m_v[vi].SetSubdivisionLevel(subdivision_level); + } + + const unsigned sector_edge_count = this->SectorEdgeCount(); + for (unsigned ei = 0; ei < sector_edge_count; ++ei) + { + m_e[ei].SetSubdivisionLevel(subdivision_level); + } + + for (unsigned fi = 0; fi < sector_face_count; ++fi) + { + m_f[fi].SetSubdivisionLevel(subdivision_level); + } + + // success + return true; + } + + this->Internal_Destroy(); + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDVertexQuadSector::InitializeFromSubdividedSectorIterator(const ON_SubDSectorIterator& sit) +{ + for (;;) + { + ON_SubDSectorIterator local_sit = sit; + const ON_SubDVertex* center_vertex = local_sit.CenterVertex(); + if (nullptr == center_vertex) + break; + + const ON_SubDVertexTag center_vertex_tag = center_vertex->m_vertex_tag; + if (ON_SubDVertexTag::Unset == center_vertex_tag) + break; + + if (center_vertex->m_face_count < ON_SubDVertexQuadSector::MinimumSectorFaceCount(center_vertex_tag)) + break; + + const unsigned max_component_count = 1U + center_vertex->m_edge_count + center_vertex->m_face_count; + ON_SimpleArray sector_ring_components(max_component_count); + + + if (ON_SubDVertexTag::Smooth != center_vertex_tag) + { + if (nullptr == local_sit.IncrementToCrease(-1)) + break; + } + + const bool bSmoothOrDartSector = ON_SubDVertexTag::Smooth == center_vertex_tag || ON_SubDVertexTag::Dart == center_vertex_tag; + sector_ring_components.Append(ON_SubDComponentPtr::Create(center_vertex)); + const ON_SubDEdge* first_e = local_sit.CurrentEdge(0); + for (unsigned i = 0; sector_ring_components.UnsignedCount() < max_component_count; ++i) + { + ON_SubDEdgePtr eptr = local_sit.CurrentEdgePtr(0); + const ON_SubDEdge* e = eptr.Edge(); + if (nullptr == e) + break; + sector_ring_components.Append(ON_SubDComponentPtr::Create(eptr)); + if (i > 0 && e->IsCrease()) + break; + const ON_SubDFace* f = local_sit.CurrentFace(); + if (nullptr == f) + break; + sector_ring_components.Append(ON_SubDComponentPtr::Create(f)); + eptr = local_sit.CurrentEdgePtr(1); + e = eptr.Edge(); + if (nullptr == e || first_e == e) + break; + if (e->IsCrease()) + { + sector_ring_components.Append(ON_SubDComponentPtr::Create(eptr)); + break; + } + if (nullptr == local_sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) + break; + } + + if ((bSmoothOrDartSector ? 1U : 0U) == (sector_ring_components.UnsignedCount() % 2U)) + return this->InitializeFromSubdividedSectorComponents(sector_ring_components); + + break; + } + + this->Internal_Destroy(); + return ON_SUBD_RETURN_ERROR(false); + +} + +bool ON_SubDVertexQuadSector::Subdivide() +{ + const ON_SubDVertex* center_vertex = this->CenterVertex(); + if (nullptr == center_vertex) + return ON_SUBD_RETURN_ERROR(false); + const unsigned sector_face_count = this->SectorFaceCount(); + if (sector_face_count < 1U || sector_face_count != center_vertex->FaceCount()) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned center_vertex_edge_count = this->CenterVertexEdgeCount(); + const bool bCreaseOrCornerSector = center_vertex->IsCreaseOrCorner(); + const bool bCreaseOrCornerOrDartSector = bCreaseOrCornerSector || center_vertex->IsDart(); + if (center_vertex_edge_count < 2U || center_vertex_edge_count != center_vertex->EdgeCount()) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned sector_vertex_count = this->SectorVertexCount(); + const unsigned sector_edge_count = this->SectorEdgeCount(); + + ON_SimpleArray s(center_vertex_edge_count); + + // Get subdivision points + ON_SimpleArray R(sector_vertex_count); + R.SetCount(sector_vertex_count); + R[0] = center_vertex->SubdivisionPoint(); + for (unsigned ei = 0; ei < center_vertex_edge_count; ++ei) + { + R[1U + 2U * ei] = m_e[ei].SubdivisionPoint(); + s.Append(m_e[ei].Sharpness(false).Subdivided(0)); + } + for (unsigned fi = 0; fi < sector_face_count; ++fi) + { + R[2U + 2U * fi] = m_f[fi].SubdivisionPoint(); + } + + // subdivide the sector + const unsigned subdivision_level = center_vertex->SubdivisionLevel() + 1U; + for (unsigned vi = 0; vi < sector_vertex_count; ++vi) + { + m_v[vi].SetControlNetPoint(R[vi],false); + m_v[vi].SetSubdivisionLevel(subdivision_level); + if (1 == vi) + m_v[vi].m_vertex_tag = bCreaseOrCornerOrDartSector ? ON_SubDVertexTag::Crease : ON_SubDVertexTag::Smooth; + else if (vi == center_vertex_edge_count) + m_v[vi].m_vertex_tag = bCreaseOrCornerSector ? ON_SubDVertexTag::Crease : ON_SubDVertexTag::Smooth; + else if (vi > 0U) + m_v[vi].m_vertex_tag = ON_SubDVertexTag::Smooth; + } + + this->m_maximum_edge_end_sharpness = 0.0; + for (unsigned ei = 0; ei < sector_edge_count; ++ei) + { + m_e[ei].ClearSavedSubdivisionPoints(); + if (ei < center_vertex_edge_count) + { + if (0 == ei) + m_e[ei].m_edge_tag = bCreaseOrCornerOrDartSector ? ON_SubDEdgeTag::Crease : ON_SubDEdgeTag::Smooth; + else if (ei+1 == center_vertex_edge_count) + m_e[ei].m_edge_tag = bCreaseOrCornerSector ? ON_SubDEdgeTag::Crease : ON_SubDEdgeTag::Smooth; + else + m_e[ei].m_edge_tag = ON_SubDEdgeTag::Smooth; + m_e[ei].m_sector_coefficient[1] = 0.0; + const double x = (ON_SubDEdgeTag::Smooth == m_e[ei].m_edge_tag) ? s[ei].MaximumEndSharpness() : 0.0; + if (x > 0.0) + { + m_e[ei].SetSharpnessForExperts(s[ei]); + if (x > this->m_maximum_edge_end_sharpness) + this->m_maximum_edge_end_sharpness = x; + } + else + m_e[ei].ClearSharpnessForExperts(); + } + else + { + m_e[ei].m_edge_tag = ON_SubDEdgeTag::Smooth; + m_e[ei].m_sector_coefficient[0] = 0.0; + m_e[ei].m_sector_coefficient[1] = 0.0; + m_e[ei].ClearSharpnessForExperts(); + } + m_e[ei].SetSubdivisionLevel(subdivision_level); + } + + for (unsigned fi = 0; fi < sector_face_count; ++fi) + { + m_f[fi].ClearSavedSubdivisionPoints(); + m_f[fi].SetSubdivisionLevel(subdivision_level); + } + + return true; +} + +bool ON_SubDVertexQuadSector::SubdivideUntilEdgeSharpnessIsZero() +{ + bool rc = true; + double maxs = this->MaximumCenterVertexEdgeEndSharpness(); + if (maxs > 0.0) + { + // for(i < n) used to prevent infinite looping when this content is not valid. + const unsigned n = (unsigned)ceil(maxs); + for (unsigned i = 0; i < n && maxs > 0.0 && rc; ++i) + { + rc = Subdivide(); + maxs = this->MaximumCenterVertexEdgeEndSharpness(); + } + if (rc && false == (0.0 == maxs)) + rc = false; + } + return rc; +} + +const ON_SubDVertexQuadSector ON_SubDVertexQuadSector::Empty; + +const ON_SubDVertex* ON_SubDVertexQuadSector::CenterVertex() const +{ + const ON_SubDVertex* center_vertex + = (this->m_sector_face_count > 0 && nullptr != m_heap && m_heap == (void*)m_v) + ? &m_v[0] + : nullptr; + return center_vertex; +} + +ON_SubDVertexTag ON_SubDVertexQuadSector::CenterVertexTag() const +{ + const ON_SubDVertex* center_vertex = this->CenterVertex(); + return (nullptr != center_vertex) ? center_vertex->m_vertex_tag : ON_SubDVertexTag::Unset; +} + +unsigned ON_SubDVertexQuadSector::CenterVertexEdgeCount() const +{ + return m_center_vertex_edge_count; +} + +unsigned ON_SubDVertexQuadSector::SectorCreaseEdgeCount() const +{ + return ON_SubDVertexQuadSector::SectorCreaseEdgeCount(this->CenterVertexTag()); +} + +unsigned ON_SubDVertexQuadSector::SectorCreaseEdgeCount(ON_SubDVertexTag center_vertex_tag) +{ + switch (center_vertex_tag) + { + case ON_SubDVertexTag::Unset: + break; + case ON_SubDVertexTag::Smooth: + return 0U; + break; + case ON_SubDVertexTag::Crease: + return 2U; + break; + case ON_SubDVertexTag::Corner: + return 2U; + break; + case ON_SubDVertexTag::Dart: + return 1U; + break; + default: + break; + } + return 0U; +} + + +unsigned ON_SubDVertexQuadSector::SectorFaceCount() const +{ + return m_sector_face_count; +} + +unsigned ON_SubDVertexQuadSector::SectorVertexCount() const +{ + return 1U + m_center_vertex_edge_count + m_sector_face_count; +} + +unsigned ON_SubDVertexQuadSector::SectorEdgeCount() const +{ + return m_center_vertex_edge_count + 2 * m_sector_face_count; +} + +unsigned ON_SubDVertexQuadSector::SubdivisionLevel() const +{ + const ON_SubDVertex* center_vertex = this->CenterVertex(); + return (nullptr != center_vertex) ? center_vertex->SubdivisionLevel() : 0U; +} + static unsigned int Internal_MarkStuffAndMaybeMoveVertices( @@ -22483,25 +23909,11 @@ unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { - unsigned int chain_count = 0; - if (unsorted_edges.Array() == sorted_edges.Array()) - { - const ON_SimpleArray< ON_SubDEdgePtr > local_unsorted_edges(unsorted_edges); - chain_count = Internal_MuchImprovedSortEdgesIntoChains( - (const ON__UINT_PTR * )local_unsorted_edges.Array(), - local_unsorted_edges.UnsignedCount(), - sorted_edges - ); - } - else - { - chain_count = Internal_MuchImprovedSortEdgesIntoChains( - (const ON__UINT_PTR*)unsorted_edges.Array(), - unsorted_edges.UnsignedCount(), - sorted_edges - ); - } - return chain_count; + return ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + unsorted_edges.Array(), + unsorted_edges.Count(), + sorted_edges + ); } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( @@ -22509,25 +23921,24 @@ unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { - unsigned int chain_count = 0; - if ((const void*)unsorted_edges.Array() == (const void*)sorted_edges.Array()) - { - const ON_SimpleArray< const ON_SubDEdge* > local_unsorted_edges(unsorted_edges); - chain_count = Internal_MuchImprovedSortEdgesIntoChains( - (const ON__UINT_PTR*)local_unsorted_edges.Array(), - local_unsorted_edges.UnsignedCount(), - sorted_edges - ); - } - else - { - chain_count = Internal_MuchImprovedSortEdgesIntoChains( - (const ON__UINT_PTR*)unsorted_edges.Array(), - unsorted_edges.UnsignedCount(), - sorted_edges - ); - } - return chain_count; + return ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + unsorted_edges.Array(), + unsorted_edges.Count(), + sorted_edges + ); +} + +unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + const ON_SubDEdge* const* unsorted_edges, + unsigned unsorted_edge_count, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges +) +{ + return ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + (const ON_SubDEdgePtr*)unsorted_edges, + unsorted_edge_count, + sorted_edges + ); } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( @@ -22536,12 +23947,28 @@ unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( ) { unsigned int chain_count = 0; - if ((const void*)unsorted_edges.Array() == (const void*)sorted_edges.Array()) + bool bCopyInput = (const void*)unsorted_edges.Array() == (const void*)sorted_edges.Array(); + const unsigned unsorted_edges_count = unsorted_edges.Count(); + for (unsigned i = 0; i < unsorted_edges_count; ++i) { - const ON_SimpleArray< ON_SubDComponentPtr > local_unsorted_edges(unsorted_edges); + ON_SubDComponentPtr cptr = unsorted_edges[i]; + if (cptr.IsEdge() || cptr.IsNull()) + continue; + bCopyInput = true; + break; + } + if (bCopyInput) + { + ON_SimpleArray< ON_SubDEdgePtr > local_unsorted_edges(unsorted_edges_count); + for (unsigned i = 0; i < unsorted_edges_count; ++i) + { + ON_SubDComponentPtr cptr = unsorted_edges[i]; + if (cptr.IsEdge()) + local_unsorted_edges.Append(cptr.EdgePtr()); + } chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)local_unsorted_edges.Array(), - local_unsorted_edges.UnsignedCount(), + unsorted_edges.UnsignedCount(), sorted_edges ); } @@ -22556,6 +23983,37 @@ unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( return chain_count; } +unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + const ON_SubDEdgePtr* unsorted_edges, + unsigned unsorted_edge_count, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges +) +{ + unsigned int chain_count = 0; + if (nullptr != unsorted_edges && unsorted_edge_count > 0 && unsorted_edge_count < ON_UNSET_UINT_INDEX) + { + if (sorted_edges.Capacity() > 0 && unsorted_edges >= sorted_edges.Array() && unsorted_edges < sorted_edges.Array() + sorted_edges.Capacity()) + { + ON_SimpleArray< ON_SubDEdgePtr > local_unsorted_edges; + local_unsorted_edges.Append(unsorted_edge_count, unsorted_edges); + chain_count = Internal_MuchImprovedSortEdgesIntoChains( + (const ON__UINT_PTR*)local_unsorted_edges.Array(), + local_unsorted_edges.UnsignedCount(), + sorted_edges + ); + } + else + { + chain_count = Internal_MuchImprovedSortEdgesIntoChains( + (const ON__UINT_PTR*)unsorted_edges, + unsorted_edge_count, + sorted_edges + ); + } + } + return chain_count; +} + unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SubD& subd, const ON_SimpleArray< ON_COMPONENT_INDEX >& unsorted_edges, @@ -22584,6 +24042,503 @@ unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( ); } +unsigned ON_SubDEdgeChain::RefineEdgeChains( + const ON_SimpleArray& unconditional_edge_chains, + bool bOrdinarySmoothChains, + bool bOrdinaryCreaseChains, + ON_SimpleArray& refined_edge_chains +) +{ + const unsigned unconditional_edge_count = unconditional_edge_chains.UnsignedCount(); + + if (unconditional_edge_count > 0 && unconditional_edge_chains.Array() == refined_edge_chains.Array()) + { + // copy unconditional_edge_chains[] to a local array so we can modify refined_edge_chains[] + const ON_SimpleArray local_unconditional_edge_chains(unconditional_edge_chains); + refined_edge_chains.SetCount(0); // In this uniquecase we do not append because in[ut array = output array. + return ON_SubDEdgeChain::RefineEdgeChains( + local_unconditional_edge_chains, + bOrdinarySmoothChains, + bOrdinaryCreaseChains, + refined_edge_chains + ); + } + + if (refined_edge_chains.UnsignedCount() > 0 && refined_edge_chains.Last()->IsNotNull()) + { + // Append a null separator to refined_edge_chains[]. + refined_edge_chains.Append(ON_SubDEdgePtr::Null); + } + + unsigned int refined_chain_count = 0; + unsigned i1 = 0U; + for (unsigned i0 = 0; i0 < unconditional_edge_count; i0 = (i0 < i1) ? i1 : (i0 + 1U)) + { + const ON_SubDEdgePtr eptr0 = unconditional_edge_chains[i0]; + const ON_SubDEdge* e0 = eptr0.Edge(); + if (nullptr == e0) + continue; + + refined_edge_chains.Append(eptr0); + + const unsigned short edge_face_count = e0->m_face_count; + const bool bSmoothChain = e0->IsSmooth(); + const bool bSharpChain = bSmoothChain ? e0->IsSharp() : false; + ON_SubDEdgeSharpness prev_sharpness = bSharpChain ? eptr0.RelativeSharpness(false) : ON_SubDEdgeSharpness::Smooth; + + for (i1 = i0 + 1; i1 < unconditional_edge_count; ++i1) + { + const ON_SubDEdgePtr eptr = unconditional_edge_chains[i1]; + const ON_SubDEdge* e = eptr.Edge(); + if (nullptr == e) + break; + if (edge_face_count != e->m_face_count) + break; + if (bSmoothChain != e->IsSmooth()) + break; + if (bSharpChain) + { + if (false == e->IsSharp()) + break; + const ON_SubDEdgeSharpness s = eptr.RelativeSharpness(false); + if (false == (prev_sharpness.EndSharpness(1) == s.EndSharpness(0))) + break; + prev_sharpness = s; + } + + // Add eptr to current chain + refined_edge_chains.Append(eptr); + + const ON_SubDVertex* v = eptr.RelativeVertex(1); + if (nullptr == v) + { + ++i1; + break; + } + + if (bSmoothChain) + { + if (false == bOrdinarySmoothChains) + continue; + if (false == v->IsSmooth()) + { + ++i1; + break; + } + if (false == bSharpChain && 4 != v->m_edge_count) + { + ++i1; + break; + } + } + else + { + if (false == bOrdinaryCreaseChains) + continue; + if (false == v->IsCrease()) + { + ++i1; + break; + } + } + } + + // finished with this chain. + ++refined_chain_count; + refined_edge_chains.Append(ON_SubDEdgePtr::Null); + } + + return refined_chain_count; +} + +unsigned ON_SubDEdgeChain::RefineEdgeChains( + const ON_SimpleArray& unconditional_edge_chains, + ON__UINT_PTR callback_context, + bool (*continue_chain_callback_function)(ON__UINT_PTR, ON_SubDEdgePtr, ON_SubDEdgePtr), + ON_SimpleArray& refined_edge_chains +) +{ + if (nullptr == continue_chain_callback_function) + { + continue_chain_callback_function = ON_SubDEdgeChain::ContinueChainDefaultCallback; + } + + const unsigned unconditional_edge_count = unconditional_edge_chains.UnsignedCount(); + + if (unconditional_edge_count > 0 && unconditional_edge_chains.Array() == refined_edge_chains.Array()) + { + // copy unconditional_edge_chains[] to a local array so we can modify refined_edge_chains[] + const ON_SimpleArray local_unconditional_edge_chains(unconditional_edge_chains); + refined_edge_chains.SetCount(0); // In this uniquecase we do not append because in[ut array = output array. + return ON_SubDEdgeChain::RefineEdgeChains( + local_unconditional_edge_chains, + callback_context, + continue_chain_callback_function, + refined_edge_chains + ); + } + + if (refined_edge_chains.UnsignedCount() > 0 && refined_edge_chains.Last()->IsNotNull()) + { + // Append a null separator to refined_edge_chains[]. + refined_edge_chains.Append(ON_SubDEdgePtr::Null); + } + + unsigned int refined_chain_count = 0; + unsigned i1 = 0U; + for (unsigned i0 = 0; i0 < unconditional_edge_count; i0 = (i0 < i1) ? i1 : (i0 + 1U)) + { + const ON_SubDEdgePtr eptr0 = unconditional_edge_chains[i0]; + const ON_SubDEdge* e0 = eptr0.Edge(); + if (nullptr == e0) + continue; + + refined_edge_chains.Append(eptr0); + + ON_SubDEdgePtr prev_eptr = eptr0; + + for (i1 = i0 + 1; i1 < unconditional_edge_count; ++i1) + { + const ON_SubDEdgePtr eptr = unconditional_edge_chains[i1]; + const ON_SubDEdge* e = eptr.Edge(); + if (nullptr == e) + break; + if (false == continue_chain_callback_function(callback_context, prev_eptr, eptr)) + break; + + // Add eptr to current chain + refined_edge_chains.Append(eptr); + prev_eptr = eptr; + } + + // finished with this chain. + ++refined_chain_count; + refined_edge_chains.Append(ON_SubDEdgePtr::Null); + } + + return refined_chain_count; +} + +class Internal_MergeCrossingEdgeChainsDoubleCallbackContext +{ +public: + Internal_MergeCrossingEdgeChainsDoubleCallbackContext( + ON__UINT_PTR c, + bool (*f)(ON__UINT_PTR, ON_SubDEdgePtr, ON_SubDEdgePtr) + ) + : m_c(c) + , m_f(f) + {} + + ~Internal_MergeCrossingEdgeChainsDoubleCallbackContext() = default; + + const ON__UINT_PTR m_c; + bool (*m_f)(ON__UINT_PTR, ON_SubDEdgePtr, ON_SubDEdgePtr); + +private: + Internal_MergeCrossingEdgeChainsDoubleCallbackContext() = delete; + Internal_MergeCrossingEdgeChainsDoubleCallbackContext(const Internal_MergeCrossingEdgeChainsDoubleCallbackContext&) = delete; + Internal_MergeCrossingEdgeChainsDoubleCallbackContext& operator=(const Internal_MergeCrossingEdgeChainsDoubleCallbackContext&) = delete; +}; + +static bool Internal_MergeCrossingEdgeChainsDoubleCallback( + ON__UINT_PTR callback_context, + ON_SubDEdgePtr left_eptr, + ON_SubDEdgePtr right_eptr +) +{ + const Internal_MergeCrossingEdgeChainsDoubleCallbackContext* double_context = (Internal_MergeCrossingEdgeChainsDoubleCallbackContext*)callback_context; + if (nullptr == double_context) + return false; + + // ON_SubDEdgeChain::ContinueChainDefaultCallback(256,...) is the crossing edge check + return + ON_SubDEdgeChain::ContinueChainDefaultCallback(256, left_eptr, right_eptr) + && double_context->m_f(double_context->m_c, left_eptr, right_eptr) + ; +} + +unsigned ON_SubDEdgeChain::MergeCrossingEdgeChains( + const ON_SimpleArray& edge_chains, + ON__UINT_PTR callback_context, + bool (*continue_chain_callback_function)(ON__UINT_PTR, ON_SubDEdgePtr, ON_SubDEdgePtr), + ON_SimpleArray& merged_edge_chains +) +{ + const int input_edge_count = edge_chains.Count(); + if (0 == input_edge_count) + { + merged_edge_chains.SetCount(0); + return 0; + } + + if (edge_chains.Array() == merged_edge_chains.Array()) + { + const ON_SimpleArray local_edge_chains(edge_chains); + merged_edge_chains.SetCount(0); + return ON_SubDEdgeChain::MergeCrossingEdgeChains( + local_edge_chains, + callback_context, + continue_chain_callback_function, + merged_edge_chains + ); + } + + if (merged_edge_chains.UnsignedCount() > 0 && merged_edge_chains.Last()->IsNotNull()) + { + // Append a null separator to merged_edge_chains[]. + merged_edge_chains.Append(ON_SubDEdgePtr::Null); + } + + // ON_SubDEdgeChain::ContinueChainDefaultCallback(256,...) is the crossing edge check + const Internal_MergeCrossingEdgeChainsDoubleCallbackContext double_context(callback_context, continue_chain_callback_function); + if (nullptr == double_context.m_f || ON_SubDEdgeChain::ContinueChainDefaultCallback == double_context.m_f) + { + callback_context |= 256; + continue_chain_callback_function = ON_SubDEdgeChain::ContinueChainDefaultCallback; + } + else + { + callback_context = (ON__UINT_PTR)(&double_context); + continue_chain_callback_function = Internal_MergeCrossingEdgeChainsDoubleCallback; + } + + unsigned chain_count = 0; + ON_SimpleArray chaindex(32); + ON_2dex d(0,0); + for ( d.i = 0; d.i < input_edge_count; d.i = (d.i < d.j) ? d.j : (d.i+1) ) + { + if (nullptr == ON_SUBD_EDGE_POINTER(edge_chains[d.i].m_ptr)) + continue; + for (d.j = d.i + 1; d.j < input_edge_count; ++d.j) + { + if (nullptr == ON_SUBD_EDGE_POINTER(edge_chains[d.j].m_ptr)) + break; + } + chaindex.Append(d); + } + + // look for open chains that begin and end at a 4 valent vertex + merged_edge_chains.Reserve(merged_edge_chains.Count() + edge_chains.Count()); + ON_SimpleArray merged_dex(32); + const unsigned chaindex_count = chaindex.UnsignedCount(); + for (unsigned k = 0; k < chaindex_count; ++k) + { + d = chaindex[k]; + if (d.j <= d.i) + continue; + chaindex[k] = ON_2dex::Zero; + ON_SubDEdgePtr eptr[2] = { edge_chains[d.i], edge_chains[d.j - 1] }; + const ON_SubDVertex* v[2] = { + eptr[0].RelativeVertex(0), + eptr[1].RelativeVertex(1) + }; + if (nullptr == v[0] || nullptr == v[1] || v[0] == v[1]) + { + // Append this damaged chain as is. + merged_edge_chains.Append(d.j - d.i, edge_chains.Array() + d.i); + + // mark the end of the chain with a null. + merged_edge_chains.Append(ON_SubDEdgePtr::Null); + ++chain_count; + continue; + } + + merged_dex.SetCount(0); + merged_dex.Append(ON_3dex(d.i, d.j, 0)); + for (unsigned n = 0; n < 2; ++n) + { + + // look for another chain that begins or ends at v[n] + for (unsigned k1 = k + 1; k1 < chaindex_count; ++k1) + { + // this 4-valent v[n] test happens every time because the code below can + // change it. + if (4 != v[n]->m_edge_count || 4 != v[n]->m_face_count) + break; + + d = chaindex[k1]; + if (d.j <= d.i) + continue; + + const ON_SubDEdgePtr eptr1[2] = { edge_chains[d.i], edge_chains[d.j - 1] }; + const ON_SubDVertex* v1[2] = { eptr1[0].RelativeVertex(0), eptr1[1].RelativeVertex(1)}; + if (nullptr == v1[0] || nullptr == v1[1] || v1[0] == v1[1]) + continue; + + const int merged_dex_count0 = merged_dex.Count(); + if (v1[0] == v[n]) + { + if (0 == n) + { + if (continue_chain_callback_function(callback_context, eptr1[0].Reversed(), eptr[0])) + { + eptr[0] = eptr1[1].Reversed(); + v[0] = v1[1]; + merged_dex.Insert(0, ON_3dex(d.i, d.j, 1)); + } + } + else + { + if (continue_chain_callback_function(callback_context, eptr[1], eptr1[0])) + { + eptr[1] = eptr1[1]; + v[1] = v1[1]; + merged_dex.Append(ON_3dex(d.i, d.j, 0)); + } + } + } + else if (v1[1] == v[n]) + { + if (0 == n) + { + if (continue_chain_callback_function(callback_context, eptr1[1], eptr[0])) + { + eptr[0] = eptr1[0]; + v[0] = v1[0]; + merged_dex.Insert(0, ON_3dex(d.i, d.j, 0)); + } + } + else + { + if (continue_chain_callback_function(callback_context, eptr[1], eptr1[1].Reversed())) + { + eptr[1] = eptr1[0].Reversed(); + v[1] = v1[0]; + merged_dex.Append(ON_3dex(d.i, d.j, 1)); + } + } + } + + if (merged_dex_count0 < merged_dex.Count()) + { + // this input chain will be merged with an earlier one. + chaindex[k1] = ON_2dex::Zero; + } + } + } + + const unsigned merged_dex_count = merged_dex.UnsignedCount(); + if (merged_dex_count > 0) + { + for (unsigned n = 0; n < merged_dex_count; ++n) + { + ON_3dex d3 = merged_dex[n]; + if (d3.i >= d3.j) + continue; // should never happen + if (0 == d3.k) + { + // add this chain to merged_edge_chains[] using its current orientation + merged_edge_chains.Append(d3.j - d3.i, edge_chains.Array() + d3.i); + } + else + { + // add this chain to merged_edge_chains[] using its reversed orientation + for (int i = d3.j - 1; i >= d3.i; --i) + merged_edge_chains.Append(edge_chains[i].Reversed()); + } + } + // mark the end of the chain with a null. + merged_edge_chains.Append(ON_SubDEdgePtr::Null); + ++chain_count; + } + } + + return chain_count; +} + +bool ON_SubDEdgeChain::ContinueChainDefaultCallback( + ON__UINT_PTR continue_condition, + ON_SubDEdgePtr left_eptr, + ON_SubDEdgePtr right_eptr +) +{ + // Perform edge chain topology check + const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(left_eptr.m_ptr); + if (nullptr == e0) + return false; + const unsigned e0dir = (unsigned)(left_eptr.m_ptr & 1U); + const ON_SubDVertex* v0[2] = { e0->m_vertex[e0dir], e0->m_vertex[1-e0dir] } ; + if (nullptr == v0[0] || nullptr == v0[1]) + return false; + if (v0[0] == v0[1]) + return false; + const ON_SubDEdge* e1 = ON_SUBD_EDGE_POINTER(right_eptr.m_ptr); + if (nullptr == e1) + return false; + // edges must be distinct + if (e0 == e1) + return false; + // edges must have a common middle vertex + const unsigned e1dir = (unsigned)(right_eptr.m_ptr & 1); + const ON_SubDVertex* v1[2] = { e1->m_vertex[e1dir], e1->m_vertex[1 - e1dir] }; + if (v0[1] != v1[0]) + return false; + if (nullptr == v1[1]) + return false; + if (v1[0] == v1[1]) + return false; + + // edge chain topology check passed. + if (0 == continue_condition) + return true; + + if (0 != (continue_condition & 1)) + { + // same face count check + if (e0->m_face_count != e1->m_face_count) + return false; + } + + if (0 != (continue_condition & 4)) + { + // same smooth / crease property + if (e0->IsSharp() != e1->IsSharp()) + return false; + } + + if (0 != (continue_condition & 8)) + { + // equal sharpness at the common vertex check + if ( false == (e0->Sharpness(true).EndSharpness(1-e0dir) == e1->Sharpness(true).EndSharpness(e1dir))) + return false; + } + + while (0 != (continue_condition & (16+32+64+128))) + { + // vertex tag filter + const ON_SubDVertexTag vtag = v0[1]->m_vertex_tag; + bool bPass = (0 != (continue_condition & 16) && ON_SubDVertexTag::Smooth == vtag); + if (false == bPass) + bPass = (0 != (continue_condition & 32) && ON_SubDVertexTag::Crease == vtag); + if (false == bPass) + bPass = (0 != (continue_condition & 64) && ON_SubDVertexTag::Dart == vtag); + if (false == bPass) + bPass = (0 != (continue_condition & 128) && ON_SubDVertexTag::Corner == vtag); + + if (false == bPass) + return false; + + break; + } + + if (0 != (continue_condition & 256) && 4 == v0[1]->m_edge_count && 4 == v0[1]->m_face_count) + { + // opposite interior crease check + const ON_SubDFace* f0[2] = { ON_SUBD_FACE_POINTER(e0->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(e0->m_face2[1].m_ptr) }; + if (nullptr == f0[0] || nullptr == f0[1]) + return false; + const ON_SubDFace* f1[2] = { ON_SUBD_FACE_POINTER(e1->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(e1->m_face2[1].m_ptr) }; + if (nullptr == f1[0] || nullptr == f1[1]) + return false; + if (f0[0] == f1[0] || f0[0] == f1[1] || f0[1] == f1[0] || f0[1] == f1[1]) + return false; // the edges share a face + } + + // All checks passed + return true; +} + bool ON_SubDEdgeChain::IsSingleEdgeChain( const ON_SimpleArray& edges, bool& bIsClosed, diff --git a/opennurbs_subd.h b/opennurbs_subd.h index 5ee49190..66aeb119 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -252,42 +252,307 @@ public: ON_SubDEdgeSharpness& operator=(const ON_SubDEdgeSharpness&) = default; public: + /// + /// ON_SubDEdgeSharpness::MaximumValue = 4. + /// SubD edge sharpness values are <= ON_SubDEdgeSharpness::MaximumValue. + /// + static const double MaximumValue; + + /// + /// ON_SubDEdgeSharpness::SmoothValue = 0.0. + /// Valid SubD edge sharpness values are <= ON_SubDEdgeSharpness::MaximumValue. + /// Smooth edges have a sharpeness property of ON_SubDEdgeSharpness::Smooth. + /// + static const double SmoothValue; + + /// + /// ON_SubDEdgeSharpness::CreaseValue = ON_SubDEdgeSharpness::MaximumValue + 1. + /// Valid SubD edge sharpness values are <= ON_SubDEdgeSharpness::MaximumValue. + /// This value is used when it is convenient to use and ON_SubDEdgeSharpness to + /// indicate an edge has a crease tag. Edges with crease tags always have a + /// sharpeness property of ON_SubDEdgeSharpness::Smooth. + /// + static const double CreaseValue; + + /// + /// ON_SubDEdgeSharpness::Tolerance = 0.01 + /// If an edge has sharpness within ON_SubDEdgeSharpness::Tolerance of an integer value, + /// the sharpness is set to that integer value. + /// + static const double Tolerance; + /// /// An edge sharpness with contant value 0.0. /// - static const ON_SubDEdgeSharpness Zero; + static const ON_SubDEdgeSharpness Smooth; /// /// An edge sharpness with both end values = ON_DBL_QNAN. /// static const ON_SubDEdgeSharpness Nan; - /// True if the sharpness value is valid and contant. + /// + /// An edge sharpness with both end values = ON_SubDEdgeSharpness::CreaseValue. + /// This value is not a valid sharpness value for a sharp edge + /// (A sharp edge a smooth edge with nonzero sharpness). + /// When working with edges, it is sometimes convienient to have + /// an ON_SubDEdgeSharpness value that indicated the edge is a crease. + /// ON_SubDEdgeSharpness::Crease is used for this purpose. + /// + static const ON_SubDEdgeSharpness Crease; + + /// + /// Create a text string describing the sharpness as a percentage. + /// This is useful in user interface code that experesses sharpness in percentages. + /// If 0 <= sharpenss <= ON_SubDEdgeSharpness::MaximumValue, + /// valid, a number followed by a percent sign is returned. + /// If sharpness = ON_SubDEdgeSharpness::CreaseValue, "crease" is returned. + /// If the sharpness is not valid, a warning sign is returned. + /// + /// + /// + static const ON_wString ToPercentageText(double sharpness); + + /// + /// Convert sharpness to a percentage from 0 to 100.0. + /// This is useful in user interface code that experesses sharpness in percentages. + /// + /// + /// + /// + /// If 0 <= sharpenss <= ON_SubDEdgeSharpness::MaximumValue, then 100.0*sharpenss/ON_SubDEdgeSharpness::MaximumValue is returned. + /// If sharpenss = ON_SubDEdgeSharpness::CreaseValue, then crease_percentage is returned. + /// Otherwise ON_DBL_QNAN is returned. + /// + static double ToPercentage(double sharpness, double crease_percentage); + + /// + /// Create a text string describing the sharpness as a percentage. + /// If the sharpenss is constant, and single percentage is returned. + /// If the sharpenss is varable, percentage range returned. + /// If the sharpness is not valid, a warning sign is returned. + /// + /// + /// If the sharpness is not contanst and bVarableMinToMax is true, then + /// the string has the format min%-max%. + /// If the sharpness is not contanst and bVarableMinToMax is false, then + /// the string has the format EndSharpness(0)%-EndSharpness(1)%. + /// + /// A string describing the sharpness as a percentage range. + const ON_wString ToPercentageText( bool bVarableMinToMax ) const; + + + /// + /// If the sharpness value is valid and contant, returns true. + /// Otherwise returns false. + /// Note that ON_SubDEdgeSharpness::Crease.IsConstant() and ON_SubDEdgeSharpness::Nan.IsConstant() are both false. + /// bool IsConstant() const; - /// True if the sharpness value is valid and variable. + /// + /// Value to return when this is equal to ON_SubDEdgeSharpness::Crease. + /// + /// + /// If this is equal to ON_SubDEdgeSharpness::Crease, returns bCreaseResult. + /// If the sharpness value is valid and contant, returns true. + /// Otherwise returns false. + /// + bool IsConstant( bool bCreaseResult ) const; + + /// + /// If EndSharpness(0) > EndSharpness(1), true is returned. + /// Otherwise false is returned. + /// + bool IsIncreasing() const; + + /// + /// If EndSharpness(0) < EndSharpness(1), true is returned. + /// Otherwise false is returned. + /// + bool IsDecreasing() const; + + /// + /// True if the sharpness value is valid and variable. + /// Note that ON_SubDEdgeSharpness::Crease.IsVariable() and ON_SubDEdgeSharpness::Nan.IsVariable() are both false. + /// bool IsVariable() const; - /// True if the sharpness valid is zero. + /// + /// If EndSharpness(0) < EndSharpness(1), +1 is returned. + /// If EndSharpness(0) > EndSharpness(1), -1 is returned. + /// If EndSharpness(0) = EndSharpness(1), 0 is returned. + /// Otherwise ON_UNSET_INT_INDEX is returned. + /// + int Trend() const; + + /// + /// If IsValid() or IsCrease() is true, then EndSharpness(1) - EndSharpness(0) is returned. + /// Otherwise ON_DBL_QNAN is returned. + /// + double Delta() const; + + /// Determine if sharpnesses have equal adjacent end sharpness values. + /// + /// + /// + /// If s0.EndSharpness(1) == s1.EndSharpness(0), then true is returned. + /// Otherwise false is returned. + /// + static bool EqualEndSharpness( + ON_SubDEdgeSharpness s0, + ON_SubDEdgeSharpness s1 + ); + + /// Determine if sharpnesses have the same trend and equal adjacent end sharpness values. + /// + /// + /// + /// If s0.Trend() = s1.Trend() and s0.EndSharpness(1) == s1.EndSharpness(0), then true is returned. + /// Otherwise false is returned. + /// + static bool EqualTrend( + ON_SubDEdgeSharpness s0, + ON_SubDEdgeSharpness s1 + ); + + + /// Determine if sharpnesses have the same delta and equal adjacent end sharpness values. + /// + /// + /// + /// If s0.Delta() = s1.Delta() and s0.EndSharpness(1) == s1.EndSharpness(0), then true is returned. + /// Otherwise false is returned. + /// + static bool EqualDelta( + ON_SubDEdgeSharpness s0, + ON_SubDEdgeSharpness s1 + ); + + + /// + /// Determine if edges are adjacent and have equal adjacent sharpness values. + /// + /// + /// + /// + /// If the edges have the same tag, eptr0.RelativeVertex(1) = eptr1.RelativeVertex(0), and + /// then ON_SubDEdgeSharpness::EqualEndSharpness(eptr0.RelativeSharpness(),eptr1.RelativeSharpness()) is returned. + /// Otherwise false is returned. + /// + static bool EqualEndSharpness( + const class ON_SubDEdgePtr& eptr0, + const class ON_SubDEdgePtr& eptr1 + ); + + /// + /// Determine if edges are adjacent, have the same sharpness trend, and equal adjacent sharpness values. + /// + /// + /// + /// + /// If the edges have the same tag, eptr0.RelativeVertex(1) = eptr1.RelativeVertex(0), and + /// then ON_SubDEdgeSharpness::EqualTrend(eptr0.RelativeSharpness(),eptr1.RelativeSharpness()) is returned. + /// Otherwise false is returned. + /// + static bool EqualTrend( + const class ON_SubDEdgePtr& eptr0, + const class ON_SubDEdgePtr& eptr1 + ); + + /// + /// Determine if edges are adjacent, have the same sharpness trend, and equal adjacent sharpness values. + /// + /// + /// + /// + /// If the edges have the same tag, eptr0.RelativeVertex(1) = eptr1.RelativeVertex(0), and + /// then ON_SubDEdgeSharpness::EqualDelta(eptr0.RelativeSharpness(),eptr1.RelativeSharpness()) is returned. + /// Otherwise false is returned. + /// + static bool EqualDelta( + const class ON_SubDEdgePtr& eptr0, + const class ON_SubDEdgePtr& eptr1 + ); + + /// True if the sharpness is zero. bool IsZero() const; - /// True if the sharpness value is valid either end sharpness value is not zero. - bool IsNotZero() const; + /// + /// If both end sharpness values are >= 0 and <= ON_SubDEdgeSharpness::MaximumValue + /// and at least one end sharpness value is > 0, returns true. + /// Otherwise returns false. + /// Note that ON_SubDEdgeSharpness::Crease.IsSharp() and ON_SubDEdgeSharpness::Nan.IsSharp() are both false; + /// + bool IsSharp() const; - /// True if both end sharpness values are >= 0 and <= ON_SubDEdgeSharpness::Maximum. + /// + /// If the sharpness value is (ON_SubDEdgeSharpness::CreaseValue,ON_SubDEdgeSharpness::CreaseValue), returns true. + /// Otherwise false is returned. + /// In particular, ON_SubDEdgeSharpness::Crease.IsCrease() is true and all other values return false. + /// + bool IsCrease() const; + + + /// + /// (IsCrease() || IsSharp()) + /// + bool IsCreaseOrSharp() const; + + + /// + /// If both end sharpness values are >= 0 and <= ON_SubDEdgeSharpness::MaximumValue, returns true. + /// Note that ON_SubDEdgeSharpness::Crease.IsValid() and ON_SubDEdgeSharpness::Nan.IsValid() are both false. + /// bool IsValid() const; - /// True if IsValid() is false. + + /// + /// Returns the opposite of IsValid(). + /// Note that ON_SubDEdgeSharpness::Crease.IsNotValid() and ON_SubDEdgeSharpness::Nan.IsNotValid() are both true. + /// bool IsNotValid() const; + /// + /// Value to return when this is equal to ON_SubDEdgeSharpness::Crease. + /// + /// + /// If both end sharpness values are >= 0 and <= ON_SubDEdgeSharpness::MaximumValue, returns true. + /// If this is equal to ON_SubDEdgeSharpness::Crease, returns bCreaseResult. + /// Otherwise returns false. + /// + bool IsValid(bool bCreaseResult) const; + + /// + /// Value to return when this is equal to ON_SubDEdgeSharpness::Crease. + /// + /// + /// If this is equal to ON_SubDEdgeSharpness::Crease, returns bCreaseResult. + /// Otherwise returns the opposite of IsValid(). + /// + bool IsNotValid(bool bCreaseResult) const; + + /// + /// Determine if candidate_value is a valid edge end sharpness value. + /// + /// + /// value to check + /// + /// + /// Value to return when ON_SubDEdgeSharpness::CreaseValue == candidate_value + /// + static bool IsValidValue( + double candidate_value, + bool bCreaseResult + ); + /// /// Create a constant ON_SubDEdgeSharpness; /// - /// 0 <= sharpness <= ON_SubDEdgeSharpness::Maximum + /// 0 <= sharpness <= ON_SubDEdgeSharpness::MaximumValue /// /// If the input values is valid, an ON_SubDEdgeSharpness /// with constant value sharpness is returned. - /// If the input value is a nan, ON_SubDEdgeSharpness Nan is returned. + /// If the input vaue is ON_SubDEdgeSharpness::CreaseValue, ON_SubDEdgeSharpness::Crease is returned. /// Otherwise ON_SubDEdgeSharpness::Nan is returned. /// static const ON_SubDEdgeSharpness FromConstant(double sharpness); @@ -295,12 +560,12 @@ public: /// /// Create a variable ON_SubDEdgeSharpness; /// - /// 0 <= sharpness0 <= ON_SubDEdgeSharpness::Maximum - /// 0 <= sharpness1 <= ON_SubDEdgeSharpness::Maximum + /// 0 <= sharpness0 <= ON_SubDEdgeSharpness::MaximumValue + /// 0 <= sharpness1 <= ON_SubDEdgeSharpness::MaximumValue /// /// If both input values are valid, an edge sharpness /// with start value sharpness0 and end value sharpness1 is returned. - /// If either end value is a nan, ON_SubDEdgeSharpness Nan is returned. + /// If both input values are ON_SubDEdgeSharpness::CreaseValue, ON_SubDEdgeSharpness::Crease::Crease is returned. /// Otherwise ON_SubDEdgeSharpness::Nan is returned. /// static const ON_SubDEdgeSharpness FromInterval(double sharpness0, double sharpness1); @@ -308,11 +573,11 @@ public: /// /// Create a variable ON_SubDEdgeSharpness; /// - /// 0 <= sharpness0 <= ON_SubDEdgeSharpness::Maximum + /// 0 <= sharpness0 <= ON_SubDEdgeSharpness::MaximumValue /// /// If the interval's values are valid, an edge sharpness /// with start value sharpness_interval[0] and end value sharpness_interval[1] is returned. - /// If either end value is a nan, ON_SubDEdgeSharpness Nan is returned. + /// If both sharpness_interval[] values are ON_SubDEdgeSharpness::CreaseValue, ON_SubDEdgeSharpness::Crease::Crease is returned. /// Otherwise ON_SubDEdgeSharpness::Nan is returned. /// static const ON_SubDEdgeSharpness FromInterval(const class ON_Interval& sharpness_interval); @@ -323,9 +588,11 @@ public: /// /// /// - /// If a or b is not zero, then the returned interval is the union of the nonzero - /// input intervals. Otherwise ON_SubDEdgeSharpness::Zero is returned. - /// The returned sharpenss always has sharpness[0] <= sharpness[1]. + /// If an input parameter is ON_SubDEdgeSharpness::Smooth, ON_SubDEdgeSharpness::Crease, or ON_SubDEdgeSharpness::Nan, + /// then that parameter it is ignored. + /// The returned interval is the union of the nonzero valid input intervals. + /// If no input intervals are nonzero, then ON_SubDEdgeSharpness::Smooth is returned. + /// Either the returned sharpness is ON_SubDEdgeSharpness::Smooth or has 0.0 < sharpness[0] <= sharpness[1] <= ON_SubDEdgeSharpness::MaximumValue. /// static const ON_SubDEdgeSharpness Union( const ON_SubDEdgeSharpness& a, @@ -336,7 +603,7 @@ public: /// Sharpness value for a subdivided edge. /// /// - /// Subdivided sharpness or ON_SubDEdgeSharpness::Zero if index is out of range. + /// Subdivided sharpness or ON_SubDEdgeSharpness::Smooth if index is out of range. const ON_SubDEdgeSharpness Subdivided(int end_index) const; const ON_SubDEdgeSharpness Reversed() const; @@ -345,7 +612,7 @@ public: /// Convert a user facing slider value to a SubD edge end sharpness value. /// /// Non empty slider domain. (Often ON_Interval::ZeroToOne.) - /// A value in the slider_domain. slider_domain[0] returns 0.0. slider_domain[1] returns ON_SubDEdgeSharpness::Maximum. + /// A value in the slider_domain. slider_domain[0] returns 0.0. slider_domain[1] returns ON_SubDEdgeSharpness::MaximumValue. /// Value to return if the input is not valid. /// The slider value converted to an ON_SubDEdge sharpness value. static double SharpnessFromSliderValue( @@ -354,6 +621,44 @@ public: double invalid_input_result ); + /// + /// Set chain_edge_sharpness to a sequence of evenly changing sharpnesses + /// beginning with chain_sharpness_range[0] and ending with chain_sharpness_range[1]. + /// + /// Beginning and ending sharpnesses. + /// Number of edges in the chain. + /// The list of sharpnesses is returned here. + /// + /// If the sharpness is constant, 1 is returned. + /// If the sharpness varies from edge to edge, edge_count is returned. + /// Otherwise 0 is returned. + /// + static unsigned SetEdgeChainSharpness( + ON_Interval chain_sharpness_range, + unsigned edge_count, + ON_SimpleArray& chain_edge_sharpness + ); + + /// + /// Set chain_edge_sharpness to a sequence of evenly changing sharpnesses + /// beginning with chain_sharpness_range[0] and ending with chain_sharpness_range[1]. + /// + /// Beginning and ending sharpnesses. + /// Number of edges in the chain. + /// + /// The list of sharpnesses is returned here. + /// chain_edge_sharpness[] must have a capacity >= edge_count. + /// + /// If the sharpness is constant, 1 is returned. + /// If the sharpness varies from edge to edge, edge_count is returned. + /// Otherwise 0 is returned. + /// + static unsigned SetEdgeChainSharpness( + ON_Interval chain_sharpness_range, + unsigned edge_count, + ON_SubDEdgeSharpness* chain_edge_sharpness + ); + /// /// Convert a user facing slider value to a SubD edge end sharpness value. /// @@ -362,7 +667,7 @@ public: /// Value to return if the input is not valid. /// /// If 0 <= normalized_slider_value <= 1, the normalized slider value converted to an ON_SubDEdge sharpness value - /// from 0 to ON_SubDEdgeSharpness::Maximum. Otherwise, ON_DBL_QNAN is returned. + /// from 0 to ON_SubDEdgeSharpness::MaximumValue. Otherwise, ON_DBL_QNAN is returned. /// static double SharpnessFromNormalizedValue( double normalized_slider_value @@ -424,20 +729,7 @@ public: ); /// - /// ON_SubDEdgeSharpness::Maximum = 4. - /// SubD edge sharpness values are <= ON_SubDEdgeSharpness::Maximum. - /// - static const double Maximum; - - /// - /// ON_SubDEdgeSharpness::Tolerance = 0.01 - /// If an edge has sharpness within ON_SubDEdgeSharpness::Tolerance of an integer value, - /// the sharpness is set to that integer value. - /// - static const double Tolerance; - - /// - /// Verify 0 <= sharpness <= ON_SubDEdgeSharpness::Maximum and return an integer value when + /// Verify 0 <= sharpness <= ON_SubDEdgeSharpness::MaximumValue and return an integer value when /// the input sharpenss is within ON_SubDEdgeSharpness::Tolerance of an integer. /// /// @@ -449,7 +741,7 @@ public: ); /// - /// Verify 0 <= sharpness <= ON_SubDEdgeSharpness::Maximum and return an integer value when + /// Verify 0 <= sharpness <= ON_SubDEdgeSharpness::MaximumValue and return an integer value when /// the input sharpenss is within ON_SubDEdgeSharpness::Tolerance of an integer. /// /// @@ -912,6 +1204,12 @@ public: }; +ON_DECL +bool operator==(ON_SubDVertexPtr lhs, ON_SubDVertexPtr rhs); + +ON_DECL +bool operator!=(ON_SubDVertexPtr lhs, ON_SubDVertexPtr rhs); + #if defined(ON_DLL_TEMPLATE) ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; @@ -991,6 +1289,28 @@ public: */ bool EdgeIsSmooth() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->IsSmoothNotSharp() is returned. + Otherwise, false is returned. + */ + bool EdgeIsSmoothNotSharp() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->IsSharp() is returned. + Otherwise, false is returned. + */ + bool EdgeIsSharp() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->IsCreaseOrSharp() is returned. + Otherwise, false is returned. + */ + bool EdgeIsCreaseOrSharp() const; + /* Returns: If Edge() is not nullptr, Edge()->IsCrease() is returned. @@ -998,6 +1318,7 @@ public: */ bool EdgeIsCrease() const; + /* Returns: If Edge() is not nullptr, Edge()->IsHardCrease() is returned. @@ -1041,6 +1362,20 @@ public: int relative_vertex_index ) const; + /* + Parameters: + relative_vertex_index - [in] + 0: return Edge()->Vertex(EdgeDirection())->m_vertex_tag + 1: return Edge()->Vertex(1-EdgeDirection())->m_vertex_tag + Returns: + The requested vertex with EdgeDirection() taken into account. + If Edge() or Edge()->Vertex(EdgeDirection()) is nullptr, + then ON_SubDVertexTag::Unset is returned. + */ + ON_SubDVertexTag RelativeVertexTag( + int relative_vertex_index + ) const; + /* Parameters: relative_vertex_index - [in] @@ -1097,6 +1432,7 @@ public: ON_SubDComponentLocation point_location ) const; + bool RelativeVertexMark( int relative_vertex_index, bool missing_vertex_return_value @@ -1139,8 +1475,15 @@ public: double relative_sector_coefficient ) const; - - const ON_SubDEdgeSharpness RelativeSharpness() const; + /// + /// Edge sharpness oriented with respect to this ON_SubDEdgePointer's direction. + /// + /// + /// If the edge is a crease and bUseCreaseSharpness is false, then ON_SubDEdgeSharpness::Smooth is returned. + /// If the edge is a crease and bUseCreaseSharpness is true, then ON_SubDEdgeSharpness::Crease is returned. + /// + /// + const ON_SubDEdgeSharpness RelativeSharpness( bool bUseCreaseSharpness ) const; void SetRelativeSharpness(ON_SubDEdgeSharpness relative_sharpness) const; @@ -1165,6 +1508,21 @@ public: int relative_face_index ) const; + /// + /// Get the edge's face ignoring orientation. + /// + /// + /// ON_SubDEdge face index. + /// + /// + /// If this->Edge() is not nullptr, then this->Edge()->Face(edge_face_index) is returned. + /// Otherwise nullptr is returned. + /// + const class ON_SubDFace* EdgeFace( + int edge_face_index + ) const; + + /* Description: Return the neighboring face. @@ -1374,7 +1732,14 @@ public: ) const; ON__UINT8 ClearMarkBits() const; -}; // ON_SubDFace +}; + +ON_DECL +bool operator==(ON_SubDEdgePtr lhs, ON_SubDEdgePtr rhs); + +ON_DECL +bool operator!=(ON_SubDEdgePtr lhs, ON_SubDEdgePtr rhs); + #if defined(ON_DLL_TEMPLATE) ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; @@ -1527,6 +1892,12 @@ public: }; +ON_DECL +bool operator==(ON_SubDFacePtr lhs, ON_SubDFacePtr rhs); + +ON_DECL +bool operator!=(ON_SubDFacePtr lhs, ON_SubDFacePtr rhs); + #if defined(ON_DLL_TEMPLATE) ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; #endif @@ -1913,6 +2284,12 @@ public: }; +ON_DECL +bool operator==(ON_SubDComponentPtr lhs, ON_SubDComponentPtr rhs); + +ON_DECL +bool operator!=(ON_SubDComponentPtr lhs, ON_SubDComponentPtr rhs); + #if defined(ON_DLL_TEMPLATE) ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; #endif @@ -4211,20 +4588,18 @@ public: Parameters: subd_type - [in] A quad based subdivision algorithm. - bFirstPass - [in] - If bFirstPass is true and the components are in standard form for the vertex - and subdivision type, then locations of the component vertices opposite the - center vertex are returned in the point ring. - bSecondPass - [in] - If bSecondtPass is true and the first pass is disable or does not succeed, - then the component subdivision locations are returned in the point ring. - vertex0 - [in] - If not null, then vertex0->m_edges and vertex0->m_faces must - be radially sorted and span a single sector and component_ring[] - is ignored. + bPermitNoSubdivisions - [in] + When in doubt, pass false. + If bPermitNoSubdivisions is true and no extraordinary components + are in the ring, then locations of the input component + control net are returned. + Otherwise at one or more subdivisions are applied to obtain the output ring points. + bObsoleteAndIgnoredParameter - [in] + Obsolete and ignored parameter. Pass false. + obsolete_and_ignored_parameter - [in] + Obsolete and ignored parameter. Pass nullptr. component_ring_count - [in] - If vertex0 is null, then component_ring_count specifies the number - of components in the component_ring[] array. + component_ring_count specifies the number of components in the component_ring[] array. component_ring[] - [in] If vertex0 is null, then component_ring[0] is the central vertex, component_ring[1] and subsequent components with odd indices are @@ -4243,9 +4618,9 @@ public: you want a crash proof call. */ static unsigned int GetQuadSectorPointRing( - bool bFirstPass, - bool bSecondPass, - const class ON_SubDVertex* vertex0, + bool bPermitNoSubdivisions, + bool bObsoleteAndIgnoredParameter, + const class ON_SubDVertex* obsolete_and_ignored_parameter, const class ON_SubDComponentPtr* component_ring, size_t component_ring_count, double* point_ring, @@ -8222,16 +8597,16 @@ public: ON_SubDVertexTag VertexTag() const; - + /// Number of edges attached to the center vertex. unsigned int EdgeCount() const; + /// Number of faces in the sector. unsigned int FaceCount() const; /* Returns: Number of points in the point ring. - For quad subds, this is 1 + FaceCount() + EdgeCount(). - For tri subds, this is 1 + EdgeCount(). + (1 + FaceCount() + EdgeCount()). */ unsigned int PointRingCount() const; @@ -9290,12 +9665,18 @@ public: unsigned int DisplayDensityReduction() const; private: - unsigned char m_reserved; + unsigned char m_reserved1; + +private: + unsigned char m_reserved2; public: - unsigned char m_reserved1 = 0; unsigned char m_side_segment_count; // = 2^n for non-empty grids (0 <= n <= 8) - + +private: + unsigned char m_reserved3; + +public: // m_F_count = number of quads // = m_side_segment_count*m_side_segment_count // (0,1,4,16,256,1024,4096) After 0, each permitted value is 4x previous value @@ -9309,6 +9690,12 @@ public: unsigned short m_F_level_of_detail; unsigned short m_F_stride; + +private: + ON__UINT16 m_reserved4; + ON__UINT32 m_reserved5; + +public: const unsigned int* m_F; const unsigned int* m_S; // [4*m_side_segment_count + 1] indices that form the polyline boundary. const ON_SubDMeshFragmentGrid* m_prev_level_of_detail; // nullptr or the previous level with 4 times the number of quads. @@ -11803,6 +12190,12 @@ public: bool bClearNeighborhoodCache ); + /// + /// Sets the control net point to ON_3dPoint::NanPoint and clears + /// saved subdivision points. + /// + void UnsetControlNetPoint(); + ON_BoundingBox ControlNetBoundingBox() const; @@ -12058,12 +12451,21 @@ public: /// can be zero. /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// + /// + /// When bCountCreasesAsSharp is true, crease edges are counted. + /// /// /// When bEndCheck is false, the check looks for edges with any nonzero sharpness. /// When bEndCheck is true, the check looks for edges with nonzero sharpness at this vertex. /// - /// Number of sharp edges attached to this vertex. - unsigned int SharpEdgeCount( bool bEndCheck ) const; + /// + /// If bEndCheck is false, returns the number of sharp edges attached to this vertex. + /// If bEndCheck is true, returns the number of edges with nonzero end sharpness at this vertex. + /// + unsigned int SharpEdgeCount( + bool bCountCreasesAsSharp, + bool bEndCheck + ) const; /// /// Get the range of sharpness values assigned to sharp edges @@ -12073,21 +12475,29 @@ public: /// can be zero. /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// + /// + /// When bCountCreasesAsSharp is true, creases are counted and ON_SubDEdgeSharpness::CreaseValue + /// is used as a crease edge sharpness. + /// /// /// When bEndCheck is false, the check looks for edges with any nonzero sharpness. /// When bEndCheck is true, the check looks for edges with nonzero sharpness at this vertex. /// /// /// The range of sharpness values is returned here. - /// If bEndCheck is true, the minimum and maximum of nonzero attached_edge->EndSharpness() at this vertex is returned in sharpness_range. - /// If bEndCheck is false, the minimum and maximum of nonzero attached_edge->MaximumEndSharpness() is returned in sharpness_range. + /// If bEndCheck is false, sharpness_range is the minimum and maximum of attached_edge->MaximumEndSharpness() for any attached sharp edge. + /// If bEndCheck is false, sharpness_range is the minimum and maximum of nonzero attached_edge->MaximumEndSharpness() for any attached sharp edge. /// If no sharp edges are attached, then (0,0) is returned. /// /// - /// If bEndCheck is true, the number of edges with nonzero sharpness at this vertex is returned. - /// If bEndCheck is false, the number of edges attached to this vertex with nonzero sharpness is returned. + /// If bEndCheck is false, returns the number of sharp edges attached to this vertex. + /// If bEndCheck is true, returns the number of edges with nonzero end sharpness at this vertex. /// - unsigned int SharpEdgeCount(bool bEndCheck, ON_Interval& sharpness_range) const; + unsigned int SharpEdgeCount( + bool bCountCreasesAsSharp, + bool bEndCheck, + ON_Interval& sharpness_range + ) const; /// /// Sharp vertices are smooth, crease or dart vertices attached to at least one sharp edge @@ -12111,6 +12521,34 @@ public: ///< / returns> double GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const; + /// + /// Sharp vertices are smooth, crease or dart vertices attached to at least one sharp edge + /// with nonzero sharpness at the vertex. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. + /// + /// + /// The returned value is 0 to 3 and is the number of output values vertices in v[] and c[]. + /// If 1 <= count <=3, then + /// sharp_subdivision_point = sum(0 <= i < count, c[i]*v[i]->ControlNetPoint()). + /// Otherwise the vertex is not sharp. + /// + /// + /// Output subdivision point vertices are returned here. + /// + /// + /// Output subdivision point sum coefficients are returned here. + /// + /// + /// The vertex sharpness is returned ( >= 0.0 ). + /// If the returned value is > 0.0, then the sharp subdivision point + /// = sum(0 <= i < count, c[i]*v[i]->ControlNetPoint()) + ///< / returns> + double GetSharpSubdivisionPoint( + unsigned& count, + const ON_SubDVertex* v[3], + double c[3] + ) const; + /* Returns: True if m_vertex_tag is ON_SubDVertexTag::Crease. @@ -12870,7 +13308,7 @@ private: // For a smooth edge, m_sharpness is the edge's sharpness. // Edge sharpenss has no meaning for edges with a crease tag. // ON_SubDEdge::Sharpness() comments for details. - ON_SubDEdgeSharpness m_sharpness = ON_SubDEdgeSharpness::Zero; + ON_SubDEdgeSharpness m_sharpness = ON_SubDEdgeSharpness::Smooth; public: @@ -12879,18 +13317,24 @@ public: /// The final subdivision point is (sharpness >= 1.0) ? sharp_subdivision_point : (1.0-sharpness)(smooth subdivsion point)+sharpness*sharp_subdivision_point. /// See ON_SubDEdge::IsSharp() for more information about sharp 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. - /// When the returned sharpness is >= 1, the sharp subdivision point is used - /// in place of the smooth subdivision point. + /// + /// If the returned sharpness is > 0, + /// then the sharp subdivision point (=edge->ControlNetCenterPoint()) is returned. + /// Otherwise ON_3dPoint::NanPoint is returned. + /// + /// The relationship between + /// 3d point C = ordinary Catmull-Clark edge subdivision point, + /// 3d point P = sharp_subdivision_point, + /// s = this->GetSharpSubdivisionPoint(P), and + /// 3d point E = this->SubdivisionPoint() is + /// E = (s >= 1.0) ? P : ((s > 0.0) ? (s*P + (1-s)*C) : C); + /// + /// NOTE WELL: when the returned value is zero, S is ON_3dPoint::NanPoint and any + /// calculation using S will results in nans. /// /// - /// If the edge is sharp, a value > 0 and < ON_SubDEdgeSharpness::Maximum is returned. - /// If the edge is smooth with zero sharpness, 0 is returned. - /// If the edge is a crease, 0 is returned. + /// If the edge is sharp, a value > 0 and < ON_SubDEdgeSharpness::MaximumValue is returned. + /// Otherwise 0.0 is returned and sharp_subdivision_point = ON_3dPoint::NanPoint. /// double GetSharpSubdivisionPoint( ON_3dPoint& sharp_subdivision_point @@ -13213,14 +13657,25 @@ public: /// The limit surface has a continuous normal along a sharp edge. /// A sharp edge has a smooth tag, /// has sharpness > 0 at at least one end, - /// and has sharpness < ON_SubDEdgeSharpness::Maximum at at least one end. + /// and has sharpness < ON_SubDEdgeSharpness::MaximumValue at at least one end. /// Sharpness has no meaning for edges with crease tags. /// Both sharpness values are zero for an ordinary smooth edge. - /// Edge sharpness steadily decreases during subdivision and becomes zero after at most ON_SubDEdgeSharpness::Maximum subdivisions. + /// Edge sharpness steadily decreases during subdivision and becomes zero after at most ON_SubDEdgeSharpness::MaximumValue subdivisions. /// - /// True if the edge is tagged as smooth, h and at least one end with sharpness > 0 and < ON_SubDEdgeSharpness::Maximum. + /// + /// True if the edge is tagged as smooth, + /// and has at least one end with sharpness > 0 and < ON_SubDEdgeSharpness::MaximumValue. + /// bool IsSharp() const; + /// + /// Crease edges have tag = ON_SubDEdgeTag::Crease. Sharp edges have + /// tag = ON_SubDEdgeTag::Smooth or ON_SubDEdgeTag::SmoothX and have + /// nonzero sharpness. + /// + /// (this->IsCrease() || this->IsSharp()). + bool IsCreaseOrSharp() const; + /// /// Determine if an edge is smooth and is not sharp. /// @@ -13237,13 +13692,18 @@ public: /// Get the edge's sharpness. /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// + /// + /// If the edge is a crease and bUseCreaseSharpness is false, then ON_SubDEdgeSharpness::Smooth is returned. + /// If the edge is a crease and bUseCreaseSharpness is true, then ON_SubDEdgeSharpness::Crease is returned. + /// /// - /// If the edge is sharp, the interval[0] value is the sharpness at the start of the edge and - /// the interval[1] value is the sharpness at the end of the edge. - /// If the edge is smooth or a crease, (0,0) is returned. - /// Otherwise, (0,0) is returned. + /// If the edge is smooth, then the sharpness property is returned. + /// If the edge is a crease and bUseCreaseSharpness is true, then ON_SubDEdgeSharpness::Crease is returned. + /// In all other cases, ON_SubDEdgeSharpness::Smooth is returned. /// - const ON_SubDEdgeSharpness Sharpness() const; + const ON_SubDEdgeSharpness Sharpness( + bool bUseCreaseSharpness + ) const; /// /// Get the edge's sharpness at the end with the specified vertex. @@ -13303,7 +13763,7 @@ public: /// A collection of ON_SubD::SetEdgeSharpness() functions provide the easiest way to /// set and change edge sharpness. /// Set the edge sharpness values to (sharpness[0],sharpness[1]). - /// The interval values must be >= 0 and <= ON_SubDEdgeSharpness::Maximum. + /// The interval values must be >= 0 and <= ON_SubDEdgeSharpness::MaximumValue. /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// /// End sharpenss values. @@ -17624,6 +18084,12 @@ public: ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ); + static unsigned int SortEdgesIntoEdgeChains( + const ON_SubDEdgePtr* unsorted_edges, + unsigned unsorted_edge_count, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges + ); + /* Description: Unconditionally sort edges into chains. @@ -17654,6 +18120,13 @@ public: ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ); + static unsigned int SortEdgesIntoEdgeChains( + const ON_SubDEdge* const * unsorted_edges, + unsigned unsorted_edge_count, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges + ); + + /* Description: Unconditionally sort edges into chains. @@ -17715,6 +18188,188 @@ public: ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ); + /// + /// Refine unconditional edge chains returned by the + /// ON_SubDEdgeChain::SortEdgesIntoEdgeChains() functions. + /// ON_SubDEdgeChain::SortEdgesIntoEdgeChains() unconditionally sorts + /// the input edges into edge chains; + /// ending at where vertices where there the number of + /// candidates for the next edge is zero or two or more. + /// + /// In many contexts, it is often necessary to further refine + /// the unconditionally sorted edge chains. + /// RefineEdgeChains() refines the unconditional edge chains + /// into chains that have homogeneous edge tags and continuous sharpness. + /// When bOrdinarySmoothChains is true, + /// the interior vertices of smooth chains will always + /// be smooth vertices with 4 edges and faces. + /// When bOrdinaryCreaseChains is true, + /// the interior vertices of crease chains will + /// alwasys be crease vertices. + /// + /// To clean edge chains in place, pass the same array as both edge chains parameters. + /// + /// + /// ON_SubDEdgePtr::Null elements separate the edge chains. + /// Typically this arrary is the output from a call to + /// ON_SubDEdgeChain::SortEdgesIntoEdgeChains(). + /// + /// + /// + /// + /// The refined edge chains are appended to refined_edge_chains[]. + /// ON_SubDEdgePtr::Null elements separate the edge chains and refined_edge_chains[] + /// ends with a ON_SubDEdgePtr::Null. + /// + /// Number of refined edge chains appended to refined_edge_chains[]. + static unsigned RefineEdgeChains( + const ON_SimpleArray& unconditional_edge_chains, + bool bOrdinarySmoothChains, + bool bOrdinaryCreaseChains, + ON_SimpleArray& refined_edge_chains + ); + + + /// + /// Refine unconditional edge chains returned by the + /// ON_SubDEdgeChain::SortEdgesIntoEdgeChains() functions. + /// ON_SubDEdgeChain::SortEdgesIntoEdgeChains() unconditionally sorts + /// the input edges into edge chains + /// ending at where vertices where there the number of + /// candidates for the next edge is zero or two or more. + /// In particular, the unconditionally sorted chains may contain + /// mixed edge tags and have extraordinary vertices as interior vertices. + /// + /// In many contexts, it is often necessary to further refine + /// the unconditionally sorted edge chains. + /// The callback function is used to determine where the unconditional chain is refined. + /// + /// To refine edge chains in place, pass the same array as both edge chains parameters. + /// + /// + /// + /// + /// The callback function + /// continue_chain_callback_function(callback_context,left_etpr,right_etpr) + /// is called with pairs of unconditionally chained edges. + /// If the callback function return false, the unconditional chain is broken between + /// the edges. + /// If continue_chain_callback_function is nullptr, + /// then ON_SubDEdgeChain::ContinueChainDefaultCallback() + /// is used and callback_context is passed as the continue_condition parameter. + /// + /// The refined edge chains are appended to refined_edge_chains[]. + /// ON_SubDEdgePtr::Null elements separate the edge chains and refined_edge_chains[] + /// ends with a ON_SubDEdgePtr::Null. + /// + /// Number of refined edge chains appended to refined_edge_chains[]. + static unsigned RefineEdgeChains( + const ON_SimpleArray& unconditional_edge_chains, + ON__UINT_PTR callback_context, + bool (*continue_chain_callback_function)(ON__UINT_PTR,ON_SubDEdgePtr, ON_SubDEdgePtr), + ON_SimpleArray& refined_edge_chains + ); + + /// + /// Merge crossing edge chains returned by the + /// ON_SubDEdgeChain::SortEdgesIntoEdgeChains() + /// or ON_SubDEdgeChain::RefineEdgeChains() functions. + /// ON_SubDEdgeChain::SortEdgesIntoEdgeChains() unconditionally sorts + /// breaks edge chains at vertices where 3 or more edges meet. + /// + /// In some contexts, it is often desirable to have edge chains + /// cross each other at 4 valent vertices. + /// ON_SubDEdgeChain::MergeCrossingEdgeChains() search for 4 valent vertices + /// where 4 input chains terminate and uses the call back function + /// to determine if the chains opposite each other should be merged inot a + /// single chain. + /// + /// To merge edge chains in place, pass the same array as both edge chains parameters. + /// + /// + /// + /// + /// The callback function + /// continue_chain_callback_function(callback_context,left_etpr,right_etpr) + /// is called with pairs of crossing edges at the ends of input chains to see if + /// they should be merged into the same chain. + /// If the callback function returns true, input chains are merged. + /// Often times, ON_SubDEdgeChain::ContinueChainDefaultCallback can be used as the callback function. + /// If continue_chain_callback_function is nullptr, + /// then ON_SubDEdgeChain::ContinueChainDefaultCallback() + /// is used and callback_context is passed as the continue_condition parameter. + /// + /// The merged edge chains are appended to merged_edge_chains[]. + /// ON_SubDEdgePtr::Null elements separate the edge chains and merged_edge_chains[] + /// ends with a ON_SubDEdgePtr::Null. + /// + /// Number of edge chains appended to merged_edge_chains[]. + static unsigned MergeCrossingEdgeChains( + const ON_SimpleArray& edge_chains, + ON__UINT_PTR callback_context, + bool (*continue_chain_callback_function)(ON__UINT_PTR, ON_SubDEdgePtr, ON_SubDEdgePtr), + ON_SimpleArray& merged_edge_chains + ); + + + /// + /// ON_SubDEdgeChain::ContinueChainDefaultCallback() can be used as a callback function + /// for ON_SubDEdgeChain::RefineEdgeChains() and ON_SubDEdgeChain::MergeCrossingEdgeChains(). + /// + /// Edge chain topology check + /// This check is alsways performed. + /// If all edge and vertex pointers are not nullptr, + /// and left_eptr.RelativeVertex(1) == right_eptr.RelativeVertex(0), + /// and left_eptr.RelativeVertex(0), left_eptr.RelativeVertex(1, right_eptr.RelativeVertex(1) are distinct, + /// then the edge chain topology check passes. + /// If the chain topology check fails, then no further checks are performed. + /// + /// If the valid topology check passes, the continue_condition is treated as a bitfield. + /// Set bits cause additional checks to be performed. + /// + /// bit 1: Same face count check: + /// Passes when both edges have the same number of faces. + /// + /// bit 2: Same EdgeIsSmooth() check: + /// Passes when left_ptr.EdgeIsSmooth() == right_eptr.EdgeIsSmooth(). + /// + /// bit 4: Same EdgeIsSharp() check: + /// Passes when left_ptr.EdgeIsSharp() == right_eptr.EdgeIsSharp(). + /// + /// bit 8: EqualEndSharpness() check: + /// Passes when ON_SubDEdgeSharpness::EqualEndSharpness(left_eptr,right_eptr) is true. + /// + /// bits 16, 32, 64, 128: Vertex tag filter check. + /// If none of these bits are set, this check is not performed. + /// If any of these bits are set, they specify a filter for a vertex tag check. + /// If bit 16 is set and the common vertex tag is ON_SubDVertexTag::Smooth, the check passes. + /// If bit 32 is set and the common vertex tag is ON_SubDVertexTag::Crease, the check passes. + /// If bit 64 is set and the common vertex tag is ON_SubDVertexTag::Dart, the check passes. + /// If bit 128 is set and the common vertex tag is ON_SubDVertexTag::Corner, the check passes. + /// Otherwise the vertex tag filter check fails. + /// + /// bit 256: opposite edge check + /// If the common vertex has 4 faces and 4 edges and left_ept and right_eptr are opposite edges, + /// the opposited edge check passes. + /// + /// If the chain topology check passes and all tests specified by set bits pass, then true is returned. + /// + /// + /// Make no modifications including Mark() and MarkBits() modifications in the callback. + /// + /// + /// + /// + /// + /// If the chain topology check passes and all tests specified by set bits pass, then true is returned. + /// Otherwise false is returned. + /// + static bool ContinueChainDefaultCallback( + ON__UINT_PTR continue_condition, + ON_SubDEdgePtr left_eptr, + ON_SubDEdgePtr right_eptr + ); + /* Description: Determine if the array of ON_SubDEdgePtrs in edges[] can be sorted diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp index 5599bb5d..cc64164b 100644 --- a/opennurbs_subd_data.cpp +++ b/opennurbs_subd_data.cpp @@ -663,6 +663,14 @@ bool ON_SubDVertex::Transform( return true; } +void ON_SubDVertex::UnsetControlNetPoint() +{ + m_P[0] = ON_DBL_QNAN; + m_P[1] = ON_DBL_QNAN; + m_P[2] = ON_DBL_QNAN; + ClearSavedSubdivisionPoints(); +} + bool ON_SubDVertex::SetControlNetPoint( ON_3dPoint control_net_point, bool bClearNeighborhoodCache @@ -1019,7 +1027,7 @@ ON_BoundingBox ON_SubDVertex::ControlNetBoundingBox() const { ON_BoundingBox bbox; bbox.m_min = m_P; - bbox.m_min = bbox.m_min; + bbox.m_max = bbox.m_min; return bbox; } diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 2cf7e52c..68a58dbe 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -2196,14 +2196,17 @@ public: void ReturnVertex(class ON_SubDVertex* v) { - if (nullptr != v && v->SubdivisionLevel() < m_levels.UnsignedCount()) + if (nullptr != v) { - ON_SubDLevel* level = m_levels[v->SubdivisionLevel()]; - if (level) - level->RemoveVertex(v); + if (v->SubdivisionLevel() < m_levels.UnsignedCount()) + { + ON_SubDLevel* level = m_levels[v->SubdivisionLevel()]; + if (level) + level->RemoveVertex(v); + } + v->ClearSavedSubdivisionPoints(); // return extras to pool + m_heap.ReturnVertex(v); } - v->ClearSavedSubdivisionPoints(); // return extras to pool - m_heap.ReturnVertex(v); } ON_SubDEdge* AllocateEdge( @@ -3046,13 +3049,15 @@ public: size_t cv_capacity, ON_3dPoint* cvs ) const; + + ON_3dPoint PointAt(double normalized_parameter) const; public: // NOTE WELL: // It is required that: // 1) MaximumControlPointCapacity = 2*MinimumControlPointCapacity+1; - // 2) MaximumControlPointCapacity >= 3 + 2^ON_SubDEdgeSharpness::Maximum + // 2) MaximumControlPointCapacity >= 3 + 2^ON_SubDEdgeSharpness::MaximumValue enum : unsigned char { MinimumControlPointCapacity = 9, @@ -3069,13 +3074,379 @@ public: unsigned char m_cv_capacity = ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity; unsigned short m_reserved2 = 0; unsigned int m_reserved3 = 0; - double m_cv5[ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity][3]; // initial 4 or 5 cvs are here. + double m_cv9[ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity][3]; // Up to ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity cvs are here. // If m_cv_capacity > ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity and m_cvx != nullptr, // m_cvx points to an array of 3*(ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity-ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity) // doubles and m_cv_capacity = ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity; double* m_cvx = nullptr; }; +class ON_SubDVertexQuadSector +{ +public: + ON_SubDVertexQuadSector() = default; + ~ON_SubDVertexQuadSector(); + ON_SubDVertexQuadSector(const ON_SubDVertexQuadSector& src); + ON_SubDVertexQuadSector& operator=(const ON_SubDVertexQuadSector& src); + +public: + static const ON_SubDVertexQuadSector Empty; + + /// + /// The number of vertices in the quad sector is determined by the vertex tag and number of faces. + /// and is equal to 1 + ON_SubDVertexQuadSector::SectorEdgeCount(center_vertex_tag,sector_face_count) + sector_face_count. + /// + /// + /// The center_vertex_tag must be one of ON_SubDVertexTag::Smooth, + /// ON_SubDVertexTag::Dart, ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. + /// + /// + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then 2 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// If center_vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then 1 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// + /// If the input is valid, then the number of vertices in the quad sector + /// with the specified center_vertex_tag and sector_face_count is returned. + /// Otherwise 0 is returned. + /// + static unsigned SectorVertexCount( + ON_SubDVertexTag center_vertex_tag, + unsigned sector_face_count + ); + + /// + /// The number of edges in the quad sector is determined by the vertex tag and number of faces. + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then the number of edges in the sector is = 3*sector_face_count. + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then the number of edges in the sector is = 1 + 3*sector_face_count. + /// + /// + /// The center_vertex_tag must be one of ON_SubDVertexTag::Smooth, + /// ON_SubDVertexTag::Dart, ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. + /// + /// + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then 2 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// If center_vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then 1 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// + /// If the input is valid, then the number of edges in the quad sector + /// with the specified center_vertex_tag and sector_face_count is returned. + /// Otherwise 0 is returned. + /// + static unsigned SectorEdgeCount( + ON_SubDVertexTag center_vertex_tag, + unsigned sector_face_count + ); + + /// + /// The number of edges attached to the center vertex is determined by the vertex tag and number of faces. + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then the number of edges attached to the center vertex is sector_face_count. + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then the number of edges attached to the center vertex is 1 + sector_face_count. + /// + /// + /// The center_vertex_tag must be one of ON_SubDVertexTag::Smooth, + /// ON_SubDVertexTag::Dart, ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. + /// + /// + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then 2 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// If center_vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then 1 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// + /// If the input is valid, then the number of edges attached to the center vertex + /// is returned. + /// Otherwise 0 is returned. + /// + static unsigned CenterVertexEdgeCount( + ON_SubDVertexTag center_vertex_tag, + unsigned sector_face_count + ); + + /// + /// The minimum number of faces in a sector depends on the sector's center vertex tag. + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then the sector must have at least 2 faces. + /// If center_vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then the sector must have at least 1 face. + /// + /// + /// The center_vertex_tag must be one of ON_SubDVertexTag::Smooth, + /// ON_SubDVertexTag::Dart, ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. + /// + /// + /// If the input center_vertex_tag is valid, the minimum number of faces for a sector with that tag is returned. + /// Otherwise 0 is returned. + /// + static unsigned MinimumSectorFaceCount( + ON_SubDVertexTag center_vertex_tag + ); + + /// + /// Initialize this ON_SubDVertexQuadSector topology and component tags + /// assuming the the smooth edges attached to the center vertex have + /// a smooth outer ring vertex. + /// + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then sector_edge_count = 3*sector_face_count. + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then sector_edge_count = 1+3*sector_face_count. + /// The sector's vertex_count = 1 + sector_edge_count + sector_face_count. + /// + /// + /// The center_vertex_tag must be one of ON_SubDVertexTag::Smooth, + /// ON_SubDVertexTag::Dart, ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. + /// + /// + /// If center_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then 2 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// If center_vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then 1 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// + /// + /// Either sector_control_net_points is nullptr or sector_control_net_points[] is an array + /// of ON_SubDVertexQuadSector::SectorVertexCount(center_vertex_tag,sector_face_count) points + /// that are used to initialize the vertex control net points. + /// sector_control_net_points[0] = center vertex control net point. + /// sector_control_net_points[1] = first edge outer vertex control net point. + /// sector_control_net_points[2] = first quad face outer vertex control net point. + /// The subsequent sector_control_net_points[] continue alternate between outer edge and outer quad face control net points. + /// If center_vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then the sector_control_net_points[ON_SubDVertexQuadSector::SectorVertexCount(center_vertex_tag,sector_face_count)-1] + /// is the final crease edge outer control net point. + /// + /// + /// Either center_edge_sharpnesses is nullptr or center_edge_sharpnesses[] is an array + /// of ON_SubDVertexQuadSector::CenterVertexEdgeCount(center_vertex_tag,sector_face_count) + /// edge sharpnesses oriented with center_edge_sharpnesses[ei].EndSharpness(0) being the edge sharpness at the center vertex. + /// + /// True if successful. False if input is not valid. + bool Initialize( + ON_SubDVertexTag center_vertex_tag, + unsigned sector_face_count, + const ON_3dPoint* sector_control_net_points, + const ON_SubDEdgeSharpness* center_edge_sharpnesses + ); + + /// + /// Initialize this ON_SubDVertexQuadSector topology and component tags + /// assuming the the smooth edges attached to the center vertex have + /// a smooth outer ring vertex and are not sharp. + /// + /// If vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then sector_edge_count = 3*sector_face_count. + /// If vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then sector_edge_count = 1+3*sector_face_count. + /// The sector's vertex_count = 1 + sector_edge_count + sector_face_count. + /// + /// + /// The vertex_tag must be one of ON_SubDVertexTag::Smooth, + /// ON_SubDVertexTag::Dart, ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. + /// + /// + /// If vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// then 2 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// If vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then 1 <= sector_face_count <= ON_SubDVertex::MaximumFaceCount. + /// + /// + /// sector_control_net_points[] is an array of ON_SubDVertexQuadSector::SectorVertexCount(vertex_tag,sector_face_count) points + /// that are used to initialize the vertex control net points. + /// sector_control_net_points[0] = center vertex control net point. + /// sector_control_net_points[1] = first edge outer vertex control net point. + /// sector_control_net_points[2] = first quad face outer vertex control net point. + /// The subsequent sector_control_net_points[] continue alternate between outer edge and outer quad face control net points. + /// If vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// then the sector_control_net_points[ON_SubDVertexQuadSector::SectorVertexCount(vertex_tag,sector_face_count)-1] + /// is the final crease edge outer control net point. + /// + /// True if successful. False if input is not valid. + bool Initialize( + ON_SubDVertexTag vertex_tag, + unsigned sector_face_count, + const ON_SimpleArray& sector_control_net_points + ); + + bool Initialize( + ON_SubDVertexTag vertex_tag, + unsigned sector_face_count, + const ON_SimpleArray& sector_control_net_points, + const ON_SimpleArray& center_edge_sharpnesses + ); + + /// + /// Initialize this ON_SubDVertexQuadSector by subdividing the + /// ring of edges and face in sit. + /// + /// + /// True if successful. False if input is not valid. + bool InitializeFromSubdividedSectorIterator( + const ON_SubDSectorIterator& sit + ); + + /// + /// Initialize this ON_SubDVertexQuadSector by subdividing the + /// ring of components in sector_components[]. + /// + /// + /// sector_components[0] = center vertex + /// sector_components[odd index] = sector edges radially sorted + /// sector_components[even index >= 2] = sector faces radially sorted + /// + /// If the center vertex tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// sector_components[] must have at least two faces and have the same number of edges and faces. + /// The edges and faces must form a complete ring around the center vertex. + /// + /// If the center vertex tag is ON_SubDVertexTag::Smooth, all edges + /// must be smooth (their edge tag is ON_SubDEdgeTag::Smooth or ON_SubDEdgeTag::SmoothX). + /// + /// If the center vertex tag is ON_SubDVertexTag::Dart, one edge must be an + /// interior crease and the remaining edges must be smooth. + /// + /// If the center vertex tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + /// sector_components[] must have at least one face and one more edge than face. + /// sector_components[1] and sector_components[].Last() must be crease edges and + /// and any additional edges must be smooth. + /// + /// True if successful. False if input is not valid. + bool InitializeFromSubdividedSectorComponents( + const ON_SimpleArray& sector_components + ); + bool InitializeFromSubdividedSectorComponents( + const ON_SubDComponentPtr* sector_components, + size_t sector_components_count + ); + + bool GetSectorControlNetPoints( + ON_SimpleArray& sector_control_net_points + ) const; + + bool SetCenterVertexTag(ON_SubDVertexTag center_vertex_tag); + + double MaximumCenterVertexEdgeEndSharpness() const; + + /// + /// Subdivide the vertex sector. + /// + /// + /// If successful, true is returned. Otherwise false is returned. + /// + bool Subdivide(); + + /// + /// Subdivide the vertex sector until all the edges attached to the center + /// vertex have zero sharpness. + /// + /// + /// If successful, true is returned. Otherwise false is returned. + /// + bool SubdivideUntilEdgeSharpnessIsZero(); + + const ON_SubDVertex* CenterVertex() const; + + ON_SubDVertexTag CenterVertexTag() const; + + /// + /// When the center vertex is smooth or dart, the number of edges attached to + /// the center vertex = number of faces in the sector. + /// When the center vertex is crease or corner, the number of edges attached to + /// the center vertex = 1 + number of faces in the sector. + /// + /// Number of sector edges attached to the center vertex. + unsigned CenterVertexEdgeCount() const; + + /// + /// The center vertex tag determines how many crease edges are attached to the center vertex. + /// When the center vertex tag is ON_SubDVertexTag::Smooth, 0 crease edges are attached to the center vertex. + /// When the center vertex tag is ON_SubDVertexTag::Dart, 1 crease edge is attached to the center vertex. + /// When the center vertex tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, 2 crease edges are attached to the center vertex. + /// + /// Number crease edges attached to the center vertex. + unsigned SectorCreaseEdgeCount() const; + + /// + /// The center vertex tag determines how many crease edges are attached to the center vertex. + /// When the center vertex tag is ON_SubDVertexTag::Smooth, 0 crease edges are attached to the center vertex. + /// When the center vertex tag is ON_SubDVertexTag::Dart, 1 crease edge is attached to the center vertex. + /// When the center vertex tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, 2 crease edges are attached to the center vertex. + /// + /// Number crease edges attached to the center vertex. + static unsigned SectorCreaseEdgeCount(ON_SubDVertexTag center_vertex_tag); + + + /// + /// All faces are quad faces. + /// + /// Number of faces in the sector. + unsigned SectorFaceCount() const; + + /// + /// There are 1 + CenterVertexEdgeCount() + SectorFaceCount() vertices + /// in a sector. + /// + /// Total number of vertices in this sector. + unsigned SectorVertexCount() const; + + /// + /// There are CenterVertexEdgeCount() + 2*SectorFaceCount() edges + /// in a sector. + /// + /// Total number of edges in this sector + unsigned SectorEdgeCount() const; + + unsigned SubdivisionLevel() const; + +public: + // m_v[] is an array of SectorVertexCount() vertices. + // When an ON_SubDVertexSector is destroyed, it is not necessary + // to call the explicit ON_SubDVertex destructor because no + // managed memory vertex features are used. + ON_SubDVertex* m_v = nullptr; + + // m_e[] is an array of SectorEdgeCount() edges. + // The edges {m_e[0], ..., m_e[CenterVertexEdgeCount()-1]} are + // the edge attached to the center vertex. + // They are oriented to that m_vertex[0] = the center vertex. + // When an ON_SubDVertexSector is destroyed, it is not necessary + // to call the explicit ON_SubDEdge destructor because no + // managed memory edge features are used. + ON_SubDEdge* m_e = nullptr; + + // m_f[] is an array of SectorFaceCount() faces. + // When an ON_SubDVertexSector is destroyed, it is not necessary + // to call the explicit ON_SubDFace destructor because no + // managed memory face features are used. + ON_SubDFace* m_f = nullptr; + + // m_component_ring[] is an array of (1 + CenterVertexEdgeCount() + SectorFaceCount()) components. + // m_component_ring[0] = center vertex. + // m_component_ring[odd] = center edges radially sorted and oriented + // so that m_component_ring[odd].RelativeVertex(0) = center vertex. + // m_component_ring[even>=2] = sector faces radially sorted. + ON_SubDComponentPtr* m_r = nullptr; + + double m_sector_coefficient = ON_DBL_QNAN; + mutable double m_maximum_edge_end_sharpness = ON_DBL_QNAN; + +private: + // number of faces in the sector + unsigned m_sector_face_count = 0; + // number of edges attached to the center vertex + // total number of edges = m_center_vertex_edge_count + 2*m_face_count. + unsigned m_center_vertex_edge_count = 0; + // All m_v[], m_e[], m_f[0], vertex edge list, and vertex face list + // memory is from a single heap allocation. + void* m_heap = nullptr; + +private: + void Internal_Destroy(); + void Internal_CopyFrom(const ON_SubDVertexQuadSector& src); +}; + #endif // ON_COMPILING_OPENNURBS) #endif // OPENNURBS_SUBD_DATA_INC_ diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp index a5c31ac6..3d43aa93 100644 --- a/opennurbs_subd_eval.cpp +++ b/opennurbs_subd_eval.cpp @@ -48,6 +48,8 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( ON_SubDEdgeTag edge_tag0; ON_SubDEdgeTag edge_tag1; + const bool bSmoothOrDartSector = (ON_SubDVertexTag::Smooth == vertex_tag || ON_SubDVertexTag::Dart == vertex_tag); + switch (vertex_tag) { case ON_SubDVertexTag::Smooth: @@ -88,9 +90,13 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( break; } - unsigned int sector_angle_index = ON_SubDSectorType::CornerAngleIndexFromCornerAngleRadians( - ON_SubDSectorType::ClampCornerSectorAngleRadians(sector_angle_radians) - ); + unsigned int sector_angle_index + = bSmoothOrDartSector + ? ON_UNSET_UINT_INDEX + : ON_SubDSectorType::CornerAngleIndexFromCornerAngleRadians( + ON_SubDSectorType::ClampCornerSectorAngleRadians(sector_angle_radians) + ); + if (sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex && fabs(ON_SubDSectorType::AngleRadiansFromCornerAngleIndex(sector_angle_index) - sector_angle_radians) <= 1.0e-6 ) diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp index a25ead1d..432de774 100644 --- a/opennurbs_subd_heap.cpp +++ b/opennurbs_subd_heap.cpp @@ -2278,8 +2278,8 @@ ON_SubDEdgeSurfaceCurve* ON_SubDHeap::CopyEdgeSurfaceCurve(const ON_SubDEdge* so return ON_SUBD_RETURN_ERROR(nullptr); } } - const size_t sz5 = sizeof(desination_curve->m_cv5); - memcpy(desination_curve->m_cv5, source_curve->m_cv5, sz5); + const size_t sz9 = sizeof(desination_curve->m_cv9); + memcpy(desination_curve->m_cv9, source_curve->m_cv9, sz9); if (cv_count > ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity && nullptr != desination_curve->m_cvx && nullptr != source_curve->m_cvx) { const size_t szx = ((size_t)(cv_count - ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity)) * 24; diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp index 32f8de6f..72e1fd01 100644 --- a/opennurbs_subd_limit.cpp +++ b/opennurbs_subd_limit.cpp @@ -2978,7 +2978,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - edge1.SetRelativeSharpness(prev_edge0_ptr.RelativeSharpness().Subdivided(1)); + edge1.SetRelativeSharpness(prev_edge0_ptr.RelativeSharpness(false).Subdivided(1)); face1_eptrs[1] = edge1; @@ -2990,7 +2990,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - edge1.SetRelativeSharpness(edge0_ptr.RelativeSharpness().Subdivided(0)); + edge1.SetRelativeSharpness(edge0_ptr.RelativeSharpness(false).Subdivided(0)); face1_eptrs[2] = edge1; face1 = m_fsh.AllocateQuad(level_zero_face_id, parent_face_idX, face1_eptrs); diff --git a/opennurbs_subd_matrix.cpp b/opennurbs_subd_matrix.cpp index 326e8afd..7c4c9791 100644 --- a/opennurbs_subd_matrix.cpp +++ b/opennurbs_subd_matrix.cpp @@ -1299,7 +1299,7 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( sin0 = 0.0; ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); //y = 0.5*(3.0 * sqrt((1.0 + cos1 / 9.0) / (1.0 + cos1)) - 1.0); - y = 2.0*(4.0*lambda - 1.0) / (1.0 + cos1) - 1; + y = (0.25 == lambda) ? -1.0 : (2.0*(4.0*lambda - 1.0) / (1.0 + cos1) - 1.0); E1[0] = 0.0; E2[0] = 0.0; E1[1] = sin0; @@ -2741,6 +2741,21 @@ double ON_SubDMatrix::TestMatrix() const } } + // A smooth sector with 2 faces is degenerate and does not have nice eigenvalues and eigenvectors + // that give a well defined surface normal. In this case we use a hueristic for the normal. + // When bSmoothTwoFaceCase E1 = {0,0,0,0,0}, lengthE1 = 0, lengthE1 = 0. + const bool bSmoothTwoFaceCase + = (m_sector_type.IsSmoothSector() || m_sector_type.IsDartSector()) + && 2u == m_sector_type.FaceCount() + && 2u == m_sector_type.EdgeCount() + ; + const bool bDartTwoFaceCase + = (m_sector_type.IsSmoothSector() || m_sector_type.IsDartSector()) + && 2u == m_sector_type.FaceCount() + && 2u == m_sector_type.EdgeCount() + ; + const bool bSmoothOrDartTwoFaceCase = bSmoothTwoFaceCase || bDartTwoFaceCase; + double lengthE1 = 0.0; double lengthE2 = 0.0; double lengthL1 = 0.0; @@ -2752,7 +2767,7 @@ double ON_SubDMatrix::TestMatrix() const lengthL1 += L1[i] * L1[i]; lengthL2 += L2[i] * L2[i]; } - if (!(lengthE1 > 0.0)) + if (false == bSmoothOrDartTwoFaceCase && !(lengthE1 > 0.0)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); if (!(lengthE2 > 0.0)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); @@ -2764,7 +2779,7 @@ double ON_SubDMatrix::TestMatrix() const lengthE2 = sqrt(lengthE2); lengthL1 = sqrt(lengthL1); lengthL2 = sqrt(lengthL2); - if (!(lengthE1 > 0.0)) + if (false == bSmoothOrDartTwoFaceCase && !(lengthE1 > 0.0)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); if (!(lengthE2 > 0.0)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); @@ -2792,7 +2807,7 @@ double ON_SubDMatrix::TestMatrix() const x1 += Si[j] * E1[j]; x2 += Si[j] * E2[j]; } - d = fabs((x1 - lambda*E1[i])/lengthE1); + d = bSmoothOrDartTwoFaceCase ? 0.0 : fabs((x1 - lambda*E1[i])/lengthE1); rc = TestMatrixReturnValue(d,rc); if (!(rc >= 0.0)) break; @@ -2823,7 +2838,7 @@ double ON_SubDMatrix::TestMatrix() const y2 += m_S[j][i] * L2[j]; } - d = fabs((y1 - lambda*L1[i])/lengthL1); + d = (bDartTwoFaceCase && (2u == i || 4u == i)) ? 0.0 : fabs((y1 - lambda*L1[i])/lengthL1); rc = TestMatrixReturnValue(d,rc); if (!(rc >= 0.0)) break; @@ -2870,7 +2885,7 @@ double ON_SubDMatrix::TestMatrix() const // = lambda * (Transpose(LP) * E ) // = lambda * LPoE // If LPoE != 0, then lamba = 1, which is not the case. - rc = TestMatrixReturnValue(fabs(E1oLP) / lengthE1, rc); + rc = bSmoothOrDartTwoFaceCase ? 0.0 : TestMatrixReturnValue(fabs(E1oLP) / lengthE1, rc); if (!(rc >= 0.0)) break; rc = TestMatrixReturnValue(fabs(E2oLP) / lengthE2, rc); diff --git a/opennurbs_subd_ring.cpp b/opennurbs_subd_ring.cpp index 9558b123..793095bd 100644 --- a/opennurbs_subd_ring.cpp +++ b/opennurbs_subd_ring.cpp @@ -23,692 +23,199 @@ #include "opennurbs_subd_data.h" -static void Internal_PointRingSharpSubdivision( - const ON_SubDVertexTag center_vertex_tag, - double sector_coefficient, - const ON_SubDEdgePtr* edges, - size_t edges_stride, - const unsigned int N, - const unsigned int F, - ON_SubDEdgeSharpness* edge_sharpness, - double* point_ring, - size_t point_ring_stride -) -{ - // All ring vertices are either smooth or creases. - // - // The center vertex can be smooth, crease or dart. - // - // In all cases, the sector coefficient is passed in. - // - // All ring faces are quads. - // - // edges[] is used only to get the edge tag. - // In some cases, point_ring[] is the result - // of 1 subdivision from the elements in edges[]. - // // - // This function subdivides the point ring until all sharpness - // values become zero. At that point we can apply the usual - // sector limit surface matrix to calculate limit surface - // point and tangent plane normal. - - if (N < 2) - { - ON_SUBD_ERROR("Invalid input. N must be at least 2."); - return; - } - if (ON_SubDVertexTag::Smooth == center_vertex_tag || ON_SubDVertexTag::Dart == center_vertex_tag) - { - // These checks insure the pointer arithmetic below never dereferences an invalid - // pointer due to invalid input. - if (N != F) - { - ON_SUBD_ERROR("Invalid input. At least on of the center vertex tag, N, or F is wrong."); - return; - } - } - else if(ON_SubDVertexTag::Crease == center_vertex_tag) - { - // These checks insure the pointer arithmetic below never dereferences an invalid - // pointer due to invalid input. - if (N != F + 1) - { - ON_SUBD_ERROR("Invalid input. At least on of the center vertex tag, N, or F is wrong."); - return; - } - if (ON_SubDEdgeTag::Crease != ON_SUBD_EDGE_POINTER(edges->m_ptr)->m_edge_tag) - { - ON_SUBD_ERROR("Invalid input. In a crease sector the first edge must be a crease."); - return; - } - if (ON_SubDEdgeTag::Crease != ON_SUBD_EDGE_POINTER((edges + ((N - 1)* edges_stride) )->m_ptr)->m_edge_tag) - { - ON_SUBD_ERROR("Invalid input. In a crease sector the last edge must be a crease."); - return; - } - } - else - { - ON_SUBD_ERROR("Invalid input. Center vertex tag must be smooth, dart, or crease."); - return; - } - - const unsigned expected_vertex_C_count - = (ON_SubDVertexTag::Crease == center_vertex_tag) - ? 2U - : (ON_SubDVertexTag::Dart == center_vertex_tag ? 1U : 0U); - - if (ON_SubDVertexTag::Smooth == center_vertex_tag) - sector_coefficient = 0.5; - else if (false == (sector_coefficient > 0.0 && sector_coefficient < 1.0)) - { - // Input edges passed to ON_SubD::GetQuadSectorPointRing() - // had incorrect sector coefficients. Using 1/2 will be good enough - // to get some sort of answer. - ON_SUBD_ERROR("An input edges to ON_SubD::GetQuadSectorPointRing() had incorrect sector coeffient value."); - sector_coefficient = 0.5; - } - - const unsigned R = 1U + N + F; - ON_SimpleArray R1_buffer; - ON_3dPoint* R1 = R1_buffer.Reserve(R); - const size_t AQstride = 2 * point_ring_stride; - - // (V0[0],V0[1],V0[2]) = control net position of center vertex - const double* V0 = point_ring; - - // A0 is used to get the ends of the radial edges on the input point ring. - // In this function, "start of edge" means the central vertex control point (V0) - // and "end of edge" means the edge control points on the point ring. - // The stride for the A0 array is AQstride = 2*point_ring_stride. - // (A0[0],A0[1],A0[2]) = control net position of end of 1st edge - const double* A0 = V0 + point_ring_stride; - - // Q0 is used to get the quad points that are diagonally opposite from - // the center vertex on the input point ring. - // The stride for the Q0 array is AQstride = 2*point_ring_stride. - const double* Q0 = A0 + point_ring_stride; - - for (;;) - { - bool bSubdividePointRingAgain = false; - - // Face subdivision points = quad centroids. - for (unsigned i = 0; i < F; ++i) - { - R1 += 2; - const double* A0next = (i+1x = 0.25 * (V0[0] + A0[0] + Q0[0] + A0next[0]); - R1->y = 0.25 * (V0[1] + A0[1] + Q0[1] + A0next[1]); - R1->z = 0.25 * (V0[2] + A0[2] + Q0[2] + A0next[2]); - A0 += AQstride; - Q0 += AQstride; - } - - // reset pointers - R1 = R1_buffer.Array(); - A0 = V0 + point_ring_stride; - Q0 = A0 + point_ring_stride; - - // Edge subdivision points - unsigned vertex_A_count = 0; // number of crease and sharp edges. - unsigned vertex_C_count = 0; // number of crease edges. - ON_3dPoint vertex_A[2] = {}; // end points of the 1st two crease and/or sharp edges - ON_3dPoint vertex_C[2] = {}; // end points of the crease edges (at most two per sector) - double min_vertex_s = 0.0; // "min" means 2nd largest - double max_vertex_s = 0.0; - unsigned vertex_s_count = 0; - - ++R1; // Move P1 to point at end of 1st subdivided edge - const double* A0prev = A0 + (N - 1) * AQstride; - - // If a "lint" detector complains about Q0prev being a nullptr, it's wrong. - // When N != F, it is certain the first edge has a crease tag and Q0prev - // is not dereferenced. By the time Q0 is dereferenced, it is not nullptr. - const double* Q0prev = (N==F) ? (Q0 + (N - 1) * AQstride) : nullptr; - - for (unsigned i = 0; i < N; ++i) - { - if (ON_SubDEdgeTag::Crease == ON_SUBD_EDGE_POINTER(edges->m_ptr)->m_edge_tag) - { - // crease edge - if (vertex_C_count >= 2) - { - ON_SUBD_ERROR("Invalid input. A sector has at most 2 creased edges."); - return; - } - vertex_C[vertex_C_count] = ON_3dPoint(A0); - ++vertex_C_count; - if (vertex_A_count < 2) - vertex_A[vertex_A_count] = ON_3dPoint(A0); - ++vertex_A_count; - - R1->x = 0.5 * (V0[0] + A0[0]); - R1->y = 0.5 * (V0[1] + A0[1]); - R1->z = 0.5 * (V0[2] + A0[2]); - } - else - { - // smooth edge - - const double edge_s = edge_sharpness[i].Average(); - - if (edge_s >= 1.0) - { - // we don't need the ordinary edge subdivision point. - R1->x = 0.0; - R1->y = 0.0; - R1->z = 0.0; - } - else - { - // set R1 = ordinary edge subdivision point - - if (0.5 == sector_coefficient) - { - // smooth vertex or tagged vertex with "ordinary" number of edges. - R1->x = 0.375 * (V0[0] + A0[0]); - R1->y = 0.375 * (V0[1] + A0[1]); - R1->z = 0.375 * (V0[2] + A0[2]); - } - else - { - // tagged vertex with extraordinary number of edges. - const double w1 = 1.0 - sector_coefficient; - R1->x = 0.75 * (sector_coefficient * V0[0] + w1 * A0[0]); - R1->y = 0.75 * (sector_coefficient * V0[1] + w1 * A0[1]); - R1->z = 0.75 * (sector_coefficient * V0[2] + w1 * A0[2]); - } - - if (nullptr != Q0prev) - { - // The "if (nullptr != Q0prev)" check is always true. Dale Lear put the - // "if (nullptr != Q0prev)" check here to suppress incorrect compiler / lint warnings. - // The 2023 lint detectors are not sophisticated enought to figure out - // that if we get here, then Q0prev is not nullptr. - const double* A0next = (i + 1 < N) ? (A0 + AQstride) : (V0 + point_ring_stride); - R1->x += 0.0625 * (A0next[0] + Q0[0] + A0prev[0] + Q0prev[0]); - R1->y += 0.0625 * (A0next[1] + Q0[1] + A0prev[1] + Q0prev[1]); - R1->z += 0.0625 * (A0next[2] + Q0[2] + A0prev[2] + Q0prev[2]); - } - } - - - if (edge_s > 0.0) - { - // This edge is sharp, modify P1 to take account of sharpness. - if (vertex_A_count < 2) - vertex_A[vertex_A_count] = ON_3dPoint(A0); - ++vertex_A_count; - const double vertex_s = edge_sharpness[i][0]; - if (vertex_s > 0.0) - { - // NOTE: "min_vertex_s" is the 2nd largest value, not the actual minimum value - // The value is used only when there are exactly 2 nonzero vertex_s values. - if (0 == vertex_s_count) - { - min_vertex_s = vertex_s; - max_vertex_s = vertex_s; - } - else if (vertex_s > max_vertex_s) - { - min_vertex_s = max_vertex_s; - max_vertex_s = vertex_s; - } - else if (vertex_s < min_vertex_s) - { - min_vertex_s = vertex_s; - } - ++vertex_s_count; - } - - const double M[3] = { - 0.5 * (V0[0] + A0[0]), - 0.5 * (V0[1] + A0[1]), - 0.5 * (V0[2] + A0[2]) - }; - if (edge_s >= 1.0) - { - // crease subdivision point. - R1->x = M[0]; - R1->y = M[1]; - R1->z = M[2]; - } - else - { - // blend of smooth and crease edge subdivision points. - const double r = 1.0 - edge_s; - R1->x = r * R1->x + edge_s * M[0]; - R1->y = r * R1->y + edge_s * M[1]; - R1->z = r * R1->z + edge_s * M[2]; - } - - // sibdivide sharpness - edge_sharpness[i] = edge_sharpness[i].Subdivided(0); - - if (false == bSubdividePointRingAgain) - { - // When bSubdividePointRingAgain is true, we need to subdivide - // the point_ring at least one more time after finishing the - // current subdivision. - bSubdividePointRingAgain = edge_sharpness[i].IsNotZero(); - } - } - } - - // increment pointers for the next edge. - A0prev = A0; - A0 += AQstride; - Q0prev = Q0; - Q0 += AQstride; - edges += edges_stride; - R1 += 2; - } - - if (expected_vertex_C_count != vertex_C_count) - { - ON_SUBD_ERROR("Invalid input. Sector tag and number of crease edges are incompatible."); - return; - } - - // reset pointers - R1 = R1_buffer.Array(); - A0 = V0 + point_ring_stride; - Q0 = A0 + point_ring_stride; - edges -= N * edges_stride; - - const double vertex_s - = (2 == vertex_s_count && min_vertex_s < max_vertex_s) - ? 0.5 * (min_vertex_s + max_vertex_s) - : (vertex_A_count >= 2 ? max_vertex_s : 0.0); - - if (vertex_s >= 1.0) - { - // we don't need the ordinary vertex subdivision point. - R1->x = 0.0; - R1->y = 0.0; - R1->z = 0.0; - } - else - { - // set R1 = ordinary vertex subdivision point - if (2 == vertex_C_count) - { - // crease vertex - R1->x = 0.125 * (6.0 * V0[0] + vertex_C[0].x + vertex_C[1].x); - R1->y = 0.125 * (6.0 * V0[1] + vertex_C[0].y + vertex_C[1].y); - R1->z = 0.125 * (6.0 * V0[2] + vertex_C[0].z + vertex_C[1].z); - } - else - { - // smooth or dart vertex. (We know N = F) - double Asum[3] = {}; // Asum = sum of edge ring points - double Qsum[3] = {}; // Qsum = sum of quad face ring points - for (unsigned i = 0; i < N; ++i) - { - Asum[0] += A0[0]; - Asum[1] += A0[1]; - Asum[2] += A0[2]; - A0 += AQstride; - Qsum[0] += Q0[0]; - Qsum[1] += Q0[1]; - Qsum[2] += Q0[2]; - Q0 += AQstride; - } - A0 -= N * AQstride; - Q0 -= N * AQstride; - const double v = 1.0 - 1.75 / ((double)N); - const double nn = N * N; - const double a = 1.5 / nn; - const double q = 0.25 / nn; - R1->x = v * V0[0] + a * Asum[0] + q * Qsum[0]; - R1->y = v * V0[1] + a * Asum[1] + q * Qsum[1]; - R1->z = v * V0[2] + a * Asum[2] + q * Qsum[2]; - } - } - - if (vertex_s > 0.0) - { - double S[3]; - if (vertex_A_count == 2) - { - // Exactly 2 edges are creases or sharp - S[0] = 0.125 * (6.0 * V0[0] + vertex_A[0].x + vertex_A[1].x); - S[1] = 0.125 * (6.0 * V0[1] + vertex_A[0].y + vertex_A[1].y); - S[2] = 0.125 * (6.0 * V0[2] + vertex_A[0].z + vertex_A[1].z); - } - else - { - // 3 or more edges are creases or sharp (vertex_A_count > 2) - S[0] = V0[0]; - S[1] = V0[1]; - S[2] = V0[2]; - } - if (vertex_s >= 1.0) - { - R1->x = S[0]; - R1->y = S[1]; - R1->z = S[2]; - } - else - { - const double r = 1.0 - vertex_s; - R1->x = r * R1->x + vertex_s * S[0]; - R1->y = r * R1->y + vertex_s * S[1]; - R1->z = r * R1->z + vertex_s * S[2]; - } - } - - // R1 = subdivided point ring with sharpness taken into account - // Copy R1 to point_ring. - for (unsigned i = 0; i < R; ++i) - { - point_ring[0] = R1->x; - point_ring[1] = R1->y; - point_ring[2] = R1->z; - ++R1; - point_ring += point_ring_stride; - } - - if (false == bSubdividePointRingAgain) - { - // The point ring subdivisions have eliminated sharpness - // and the sector limit surface matrix can be applied to - // the point ring to calculate the limit surface point - // and tangent plane normal. - break; - } - - // reset pointers for next subdivision - // Each value in edge_sharpness has been reduced by 1 or is zero. - R1 -= R; - point_ring -= R * point_ring_stride; - } -} - - unsigned int ON_SubD::GetQuadSectorPointRing( - bool bFirstPass, - bool bSecondPass, - const ON_SubDVertex* center_vertex, + bool bPermitNoSubdivisions, + bool bObsoleteAndIgnoredParameter, + const ON_SubDVertex* obsolete_and_ignored_parameter, const ON_SubDComponentPtr* component_ring, size_t component_ring_count, double* point_ring, size_t point_ring_stride ) { - //// NO VALIDATION CHECKS - //// CALLER INSURES INPUT HAS NON-nullptr POINTERS AND CORRECT COUNTS + // MINIMAL VALIDATION CHECKS TO PREVENT CRASHES + // CALLER MUST INSURE INPUT IS CORRECT - // Except for internal testing functions, bSecondPass is always true. - - double subP[3]; - const double* Q = nullptr; - - const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); - const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); - const unsigned int point_ring_count = 1 + N + F; - - const double* point_ring1 = point_ring + (point_ring_count*point_ring_stride); - - - const ON_SubDVertex* vertex0 = center_vertex; - const size_t element_stride = (nullptr != vertex0) ? 1 : 2; + const ON_SubDVertex* vertex0 = (nullptr != component_ring) ? ON_SUBD_VERTEX_POINTER(component_ring[0].m_ptr) : nullptr; if (nullptr == vertex0) + return ON_SUBD_RETURN_ERROR(0); + + const ON_SubDVertexTag center_vertex_tag = vertex0->m_vertex_tag; + const bool bCreaseOrCornerSector = (ON_SubDVertexTag::Crease == center_vertex_tag || ON_SubDVertexTag::Corner == center_vertex_tag); + const bool bDartOrCreaseOrCornerSector = bCreaseOrCornerSector || ON_SubDVertexTag::Dart == center_vertex_tag; + if (false == bDartOrCreaseOrCornerSector && ON_SubDVertexTag::Smooth != center_vertex_tag) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned N = ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned F = ON_SubD::ComponentRingFaceCount(component_ring_count); + if (N < 2U) + return ON_SUBD_RETURN_ERROR(0); + if (F + (bCreaseOrCornerSector ? 1u : 0u) != N) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned point_ring_count = 1u + N + F; + if ((size_t)point_ring_count != component_ring_count) + return ON_SUBD_RETURN_ERROR(0); + + // If final subdivision_count = 0, then no subdivision is needed. + // If final subdivision_count = 1, then exactly 1 subdivision is needed. + // If final subdivision_count = 2, then at least two subdivisions are needed. + unsigned subdivision_count = bPermitNoSubdivisions ? 0u : 1u; + + for (unsigned eptr_dex = 1u; eptr_dex < point_ring_count; eptr_dex += 2u) { - vertex0 = component_ring[0].Vertex(); - if (nullptr == vertex0) + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(component_ring[eptr_dex].m_ptr); + if (nullptr == e) return ON_SUBD_RETURN_ERROR(0); - } - - const ON_SubDEdgePtr* edges; - - // We need to count sharp edges, crease edges, and save edge sharpnesses. - // edge_sharpness[i].[0] = sharpeness at vertex0 end. - ON_SubDEdgeSharpness* edge_sharpness = nullptr; - double maxs = 0.0; - double sector_coefficient = (vertex0->IsDartOrCreaseOrCorner()) ? 0.0 : 0.5; - - edges = (1 == element_stride) ? vertex0->m_edges : (const ON_SubDEdgePtr*)(component_ring + 1); - for (unsigned i = 0; i < N; ++i, edges += element_stride) - { - const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges->m_ptr); - if (nullptr == edge) + const unsigned end0 = e->VertexArrayIndex(vertex0); + if (end0 > 1u) return ON_SUBD_RETURN_ERROR(0); - const ON_SubDEdgeTag etag = edge->m_edge_tag; - if (ON_SubDEdgeTag::Smooth == etag || ON_SubDEdgeTag::SmoothX == etag) - { - if (0.0 == sector_coefficient) - { - // The sector coefficient for the central vertex is a property - // of the sector and constant on all smooth edges in the sector. - // So, it suffices to harvest it from any smooth edge in the sector. - sector_coefficient = edge->m_sector_coefficient[ON_SUBD_EDGE_DIRECTION(edges->m_ptr)]; - } - ON_SubDEdgeSharpness s = edge->Sharpness(); - if ( s.IsNotZero() ) - { - if (nullptr == edge_sharpness) - edge_sharpness = (ON_SubDEdgeSharpness*)calloc(N, sizeof(edge_sharpness[0])); - if (1 == ON_SUBD_EDGE_DIRECTION(edges->m_ptr)) - s = s.Reversed(); - edge_sharpness[i] = s; - if (maxs < s[0]) - maxs = s[0]; - if (maxs < s[1]) - maxs = s[1]; - } - } - } - if (nullptr != edge_sharpness && bSecondPass) - { - // Let the subdivision in the 2nd pass do some (or all) of the work. - bFirstPass = false; - } + const ON_SubDEdgeTag etag = e->m_edge_tag; + if (ON_SubDEdgeTag::Crease == etag) + continue; - // Except for internal testing functions, bSecondPass is always true. - // Sometimes bFirstPass is false when it is already known that - // an ring edge vertex is tagged. - for (unsigned int pass = (bFirstPass ? 0U : 1U); pass < (bSecondPass ? 2U : 1U); pass++) - { - if (1 == pass && nullptr != edge_sharpness) + const ON_SubDEdgeSharpness s0 = e->Sharpness(false); + const double x0 = s0.MaximumEndSharpness(); + if (x0 > 0.0) { - if (maxs <= 1.0) + if (subdivision_count < 1u) { - // The single subdivision step that occurs during this - // pass will remove all sharpness from the point ring - // and Internal_PointRingSharpSubdivision() doesn't need to be called. - onfree(edge_sharpness); - edge_sharpness = nullptr; - maxs = 0.0; + // at least one subdivision is required to remove sharpness + subdivision_count = 1u; } - else + const double x1 = s0.Subdivided(end0).MaximumEndSharpness(); + if (x1 > 0.0) { - // we need to subdivide the sharpness values that will get passed - // to Internal_PointRingSharpSubdivision(). - maxs = 0.0; - for (unsigned k = 0; k < N; ++k) - { - const ON_SubDEdgeSharpness s1 = edge_sharpness[k].Subdivided(0); - edge_sharpness[k] = s1; - if (maxs < s1[0]) - maxs = s1[0]; - if (maxs < s1[1]) - maxs = s1[1]; - } - if (0.0 == maxs) - { - // subdivision removed sharpness. - onfree(edge_sharpness); - edge_sharpness = nullptr; - } + // at least two subdivisions are required to remove sharpness. + subdivision_count = 2u; + break; } } - if (0 == pass) - Q = vertex0->m_P; + if (subdivision_count >= 1u) + continue; + + if (ON_SubDEdgeTag::SmoothX == etag) + { + // one subdivision is reqired to handle SmoothX edges + subdivision_count = 1u; + } else { - if ( false == bSecondPass) - return ON_SUBD_RETURN_ERROR(0); // subdivision not permitted - - if (false == vertex0->GetSubdivisionPoint(subP)) + const ON_SubDVertex* v1 = e->m_vertex[1u - end0]; + if (nullptr == v1 || v1 == vertex0) return ON_SUBD_RETURN_ERROR(0); - - Q = subP; + if (v1->IsDartOrCreaseOrCorner() && 0.5 != e->m_sector_coefficient[1u - end0]) + { + // one subdivision is reqired to handle extraordinary sector coeffient at v1. + subdivision_count = 1u; + } } + } - double* P = point_ring; + if (subdivision_count > 1u) + { + // we need to subdivide at least twice to get the point ring + + ON_SubDVertexQuadSector vqs; + if (false == vqs.InitializeFromSubdividedSectorComponents(component_ring, component_ring_count)) + return ON_SUBD_RETURN_ERROR(0); + if (N != vqs.CenterVertexEdgeCount()) + return ON_SUBD_RETURN_ERROR(0); + if (F != vqs.SectorFaceCount()) + return ON_SUBD_RETURN_ERROR(0); + if (point_ring_count != vqs.SectorVertexCount()) + return ON_SUBD_RETURN_ERROR(0); + if (false == vqs.SubdivideUntilEdgeSharpnessIsZero()) + return ON_SUBD_RETURN_ERROR(0); + + // harvest the ring points from vqs. + for (unsigned i = 0; i < point_ring_count; ++i) + { + const ON_3dPoint P = vqs.m_v[i].ControlNetPoint(); + point_ring[0] = P.x; + point_ring[1] = P.y; + point_ring[2] = P.z; + point_ring += point_ring_stride; + } + return point_ring_count; + } + + if (0u == subdivision_count) + { + for (unsigned fptr_dex = 2u; fptr_dex < point_ring_count; fptr_dex += 2u) + { + const ON_SubDFace* f = ON_SUBD_FACE_POINTER(component_ring[fptr_dex].m_ptr); + if (nullptr == f) + return ON_SUBD_RETURN_ERROR(0); + if (4 == f->m_edge_count) + continue; + + // a subdivision is required to handle non-quad faces + subdivision_count = 1u; + break; + } + } + + // get ring points with 0 or 1 subdivision + double subP[3]; + const double* Q = nullptr; + if (0 == subdivision_count) + Q = vertex0->m_P; + else + { + if (false == vertex0->GetSubdivisionPoint(subP)) + return ON_SUBD_RETURN_ERROR(0); + Q = subP; + } + + double* P = point_ring; + P[0] = Q[0]; + P[1] = Q[1]; + P[2] = Q[2]; + P += point_ring_stride; + + for (unsigned eptr_dex = 1u; eptr_dex < point_ring_count; eptr_dex += 2u) + { + // Get edge point + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(component_ring[eptr_dex].m_ptr); + if (nullptr == e) + return ON_SUBD_RETURN_ERROR(0); + + if (0u == subdivision_count) + { + const ON_SubDVertex* vertexQ = e->OtherEndVertex(vertex0); + if (nullptr == vertexQ) + return ON_SUBD_RETURN_ERROR(0); + Q = vertexQ->m_P; + } + else + { + if (false == e->GetSubdivisionPoint(subP)) + return ON_SUBD_RETURN_ERROR(0); + // Q = subP set above when vertex0 was subdivided. + } P[0] = Q[0]; P[1] = Q[1]; P[2] = Q[2]; P += point_ring_stride; - - const ON_SubDEdgePtr* edges0 = (1 == element_stride) ? vertex0->m_edges : (const ON_SubDEdgePtr*)(component_ring + 1); - edges = edges0; - const ON_SubDFacePtr* faces = (1 == element_stride) ? ((const ON_SubDFacePtr * )vertex0->m_faces) : (const ON_SubDFacePtr*)(component_ring + 2); - for ( unsigned int i = 0; i < N; ++i, edges += element_stride, faces += element_stride ) - { - // Get edge point - ON__UINT_PTR eptr = edges->m_ptr; - const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr); - if (nullptr == edge) - return ON_SUBD_RETURN_ERROR(0); - eptr = 1 - ON_SUBD_EDGE_DIRECTION(eptr); - const ON_SubDVertex* vertex1 = edge->m_vertex[eptr]; - if ( nullptr == vertex1) - return ON_SUBD_RETURN_ERROR(0); - - if (0 == pass) - { - if (ON_SubDEdgeTag::SmoothX == edge->m_edge_tag) - break; // need to use subdivision point in 2nd pass - - if (ON_SubDVertexTag::Smooth == vertex1->m_vertex_tag - || ON_SubDEdgeTag::Crease == edge->m_edge_tag - || 0.5 == edge->m_sector_coefficient[eptr] - ) - { - // We have one of these cases. - // 1) edge is smooth, vertex1 is smooth. - // 2) edge is smooth, vertex1 is creas/corner/dart with an ordinary. - // configuration (0.5 == edge->m_sector_coefficient[eptr]). - // 3) edge is crease. - // - // If the edge is not sharp, then the conditions - // needed to use a matrix to calculate the limit - // surface from the point ring are valid for this edge. - // - // If the edge is sharp, a later call to - // Internal_PointRingSharpSubdivision() will subdivide - // the sharpness away. - Q = vertex1->m_P; - } - else - { - // The edge is smooth, vertex1 is a crease/corner/dart, - // and there is a subdivision bias introduced by - // an extraordinary configuration of edges at - // vertex1. The subdivision in the 2nd pass will - // remove these complications so the conditions - // needed to use a matrix to calculate the limit - // surface from the point ring will be valid. - break; - } - } - else - { - // The 2nd pass subdivides the elements in the initial ring - // to insure the edge from the center to the ring point - // is either a smooth edge going to a smooth ring vertex - // or a crease edge going to a crease ring vertex. - // - // If the edge is sharp, a later call to - // Internal_PointRingSharpSubdivision() will subdivide - // the sharpness away. - if (false == edge->GetSubdivisionPoint(subP)) - return ON_SUBD_RETURN_ERROR(0); - // Q = subP set above when vertex0 was subdivided. - } - P[0] = Q[0]; - P[1] = Q[1]; - P[2] = Q[2]; - P += point_ring_stride; - if (point_ring1 == P) + if (eptr_dex + 1u < point_ring_count) + { + const ON_SubDFace* f = ON_SUBD_FACE_POINTER(component_ring[eptr_dex+1u].m_ptr); + if (nullptr == f) + return ON_SUBD_RETURN_ERROR(0); + if (0u == subdivision_count) { - // success on a sector with crease boundary - if (nullptr != edge_sharpness) - { - // At least one of the smooth edges is sharp. - // Internal_PointRingSharpSubdivision() subdivides - // the ring enough to remove sharpness so the conditions - // needed to use a matrix to calculate the limit - // surface from the point ring will be valid. - Internal_PointRingSharpSubdivision( - vertex0->m_vertex_tag, - sector_coefficient, - edges - (N - 1) * element_stride, - element_stride, - N, - F, - edge_sharpness, - point_ring, - point_ring_stride - ); - onfree(edge_sharpness); - } - return point_ring_count; - } - - const ON_SubDFace* face = ON_SUBD_FACE_POINTER(faces->m_ptr); - if (0 == pass) - { - if (4 != face->m_edge_count) - { - // face is not a quad. - // We need 2nd pass to subdivide this face so the center vertex is surrounded by quads. - break; - } - - // find the vertex opposite vertex0 - eptr = face->m_edge4[0].m_ptr; - edge = ON_SUBD_EDGE_POINTER(eptr); - if ( nullptr == edge) - return ON_SUBD_RETURN_ERROR(0); - eptr = ON_SUBD_EDGE_DIRECTION(eptr); - if (vertex0 == edge->m_vertex[eptr]) - eptr = 2; // vertex0 = face->Vertex(0), set vertexQ = face->vertex(2) - else if (vertex0 == edge->m_vertex[1-eptr]) - eptr = 3; // vertex0 = face->Vertex(1), set vertexQ = face->vertex(3) - else - { - eptr = face->m_edge4[2].m_ptr; - edge = ON_SUBD_EDGE_POINTER(eptr); - if ( nullptr == edge) - return ON_SUBD_RETURN_ERROR(0); - eptr = ON_SUBD_EDGE_DIRECTION(eptr); - if (vertex0 == edge->m_vertex[eptr]) - eptr = 0; // vertex0 = face->Vertex(2), set vertexQ = face->vertex(0) - else if (vertex0 == edge->m_vertex[1-eptr]) - eptr = 1; // vertex0 = face->Vertex(3), set vertexQ = face->vertex(1) - else - return ON_SUBD_RETURN_ERROR(0); - } - eptr = face->m_edge4[eptr].m_ptr; - edge = ON_SUBD_EDGE_POINTER(eptr); - if ( nullptr == edge) - return ON_SUBD_RETURN_ERROR(0); - eptr = ON_SUBD_EDGE_DIRECTION(eptr); - const ON_SubDVertex* vertexQ = edge->m_vertex[eptr]; - if ( nullptr == vertexQ) + const ON_SubDVertex* vertexQ = f->QuadOppositeVertex(vertex0); + if (nullptr == vertexQ) return ON_SUBD_RETURN_ERROR(0); Q = vertexQ->m_P; } else { - if (false == face->GetSubdivisionPoint(subP)) + if (false == f->GetSubdivisionPoint(subP)) return ON_SUBD_RETURN_ERROR(0); // Q = subP set above when vertex0 was subdivided. } @@ -717,35 +224,9 @@ unsigned int ON_SubD::GetQuadSectorPointRing( P[2] = Q[2]; P += point_ring_stride; } - - if (point_ring1 == P) - { - // success on a smooth sector - if (nullptr != edge_sharpness) - { - // At least one of the smooth edges is sharp. - // Internal_PointRingSharpSubdivision() subdivides - // the ring enough to remove sharpness so the conditions - // needed to use a matrix to calculate the limit - // surface from the point ring will be valid. - Internal_PointRingSharpSubdivision( - vertex0->m_vertex_tag, - sector_coefficient, - edges - N * element_stride, - element_stride, - N, - F, - edge_sharpness, - point_ring, - point_ring_stride - ); - onfree(edge_sharpness); - } - return point_ring_count; - } } - return ON_SUBD_RETURN_ERROR(0); + return point_ring_count; } unsigned int ON_SubD::ComponentRingEdgeCount(size_t component_ring_count) @@ -888,9 +369,15 @@ unsigned int ON_SubD::GetSectorSubdivsionPointRing( if ( point_ring_count > subd_point_ring_capacity || nullptr == subd_point_ring) return ON_SUBD_RETURN_ERROR(0); - const bool bFirstPass = false; - const bool bSecondPass = true; // returned ring will be at subdivision level 1 (or greater if there are edges with sharpness > 1) - unsigned int rc = GetQuadSectorPointRing(bFirstPass,bSecondPass,nullptr,component_ring,component_ring_count, subd_point_ring, subd_point_ring_stride); + const bool bObsoleteAndIgnoredParameter = false; + unsigned int rc = GetQuadSectorPointRing( + false, + bObsoleteAndIgnoredParameter, + nullptr, + component_ring, + component_ring_count, + subd_point_ring, + subd_point_ring_stride); if (0 == rc) return ON_SUBD_RETURN_ERROR(0); @@ -942,10 +429,16 @@ unsigned int ON_SubD::GetSectorPointRing( if ( point_ring_count > point_ring_capacity || nullptr == point_ring) return ON_SUBD_RETURN_ERROR(0); - const bool bFirstPass = true; // Except for internal testing functions, bSubdivideIfNeeded is always true. - const bool bSecondPass = bSubdivideIfNeeded; - unsigned int rc = GetQuadSectorPointRing(bFirstPass,bSecondPass,nullptr, component_ring,component_ring_count, point_ring,point_ring_stride); + const bool bObsoleteAndIgnoredParameter = false; + unsigned int rc = GetQuadSectorPointRing( + bSubdivideIfNeeded ? false : true, + bObsoleteAndIgnoredParameter, + nullptr, + component_ring, + component_ring_count, + point_ring,point_ring_stride + ); if (0 == rc) return ON_SUBD_RETURN_ERROR(0); @@ -982,9 +475,15 @@ unsigned int ON_SubD::GetSectorPointRing( unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit, component_ring,component_ring_capacity); if (component_ring_count > 0) { - const bool bFirstPass = true; - const bool bSecondPass = bSubdivideIfNeeded; - point_ring_count = ON_SubD::GetQuadSectorPointRing( bFirstPass, bSecondPass, nullptr, component_ring, component_ring_count, point_ring, point_ring_stride); + const bool bObsoleteAndIgnoredParameter = false; + point_ring_count = ON_SubD::GetQuadSectorPointRing( + bSubdivideIfNeeded ? false : true, + bObsoleteAndIgnoredParameter, + nullptr, + component_ring, + component_ring_count, + point_ring, point_ring_stride + ); } if ( component_ring != stack_component_ring) diff --git a/opennurbs_sun.cpp b/opennurbs_sun.cpp index 86595508..74197424 100644 --- a/opennurbs_sun.cpp +++ b/opennurbs_sun.cpp @@ -41,8 +41,10 @@ #define ON_RDK_SUN_SHADOW_INTENSITY L"shadow-intensity" #define ON_RDK_SUN_INTENSITY L"intensity" -static double Sin(double deg) { return sin(ON_RadiansFromDegrees(deg)); } -static double Cos(double deg) { return cos(ON_RadiansFromDegrees(deg)); } +static double Sin(double deg) { return sin(ON_RadiansFromDegrees(deg)); } +static double Cos(double deg) { return cos(ON_RadiansFromDegrees(deg)); } +static double Tan(double deg) { return tan(ON_RadiansFromDegrees(deg)); } + static double ArcSin(double sine) { return ON_DegreesFromRadians(asin(sine)); } static double ArcTan2(double dy, double dx) { return ON_DegreesFromRadians(atan2(dy, dx)); } static double WorldToCompass(double d) { return 90.0 - d; } @@ -64,18 +66,6 @@ static bool IsLeapYear(int year) return true; } -static int DaysInMonth(int month, int year) -{ - month = std::max(1, std::min(12, month)); - - if ((2 == month) && IsLeapYear(year)) - return 29; - - static const int tab[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - - return tab[month]; -} - static double Planck(double lambda, double temp) { static const double E = 2.7182818284590452354; @@ -124,23 +114,401 @@ static ON__UINT32 UpdateCRC(ON__UINT32 crc, T x) return ON_CRC32(crc, sizeof(x), &x); } -static void ConvertSolarVectorToHorizonCoords(const double* vector, double& azimuth, double& altitude) +inline static double Int(double x) { - altitude = ArcSin(-vector[2]); - - const double dY = vector[0] / -Cos(altitude); - const double dX = vector[1] / -Cos(altitude); - - azimuth = ArcTan2(dY, dX); - if (azimuth < 0.0) - azimuth += 360.0; + return (x < 0.0) ? ceil(x) : floor(x); } -void ConvertHorizonCoordsToSolarVector(double azimuth, double altitude, double* vector) +inline static double Frac(double x) { - vector[0] = -Cos(altitude) * Sin(azimuth); - vector[1] = -Cos(altitude) * Cos(azimuth); - vector[2] = -Sin(altitude); + return x - Int(x); +} + +inline static double Unwind(double dDegrees) +{ + dDegrees = Frac(dDegrees / 360.0) * 360.0; + if (dDegrees < 0.0) + dDegrees += 360.0; + + return dDegrees; +} + +// Reference: Jean Meeus - 'Astronomical Algorithms', second edition. + +class ON_SunEngine::CImpl final +{ +public: + void UpdateIfModified(void); + + double JulianDay(void) const + { + return _local_julian_day - (_local_tz_hours + (_local_daylight_mins / 60.0)) / 24.0; + } + +public: + double _azimuth = 0.0; + double _altitude = 0.0; + double _latitude = 0.0; + double _longitude = 0.0; + double _local_julian_day = 0.0; + double _local_tz_hours = 0.0; + int _local_daylight_mins = 0; + double _cache_right_ascension = 0.0; + double _cache_sin_declination = 0.0; + double _cache_cos_declination = 0.0; + double _cache_tan_declination = 0.0; + double _cache_sin_latitude = Sin(0.0); + double _cache_cos_latitude = Cos(0.0); + double _cache_greenwich_sidereal_time = 0.0; + bool _modified = true; + bool _julian_date_changed = true; + Accuracy _accuracy = Accuracy::Minimum; + + // The obliquity of the ecliptic (the tilt of the earth's axis) is usually considered to be about 23.5 + // degrees, but it actually changes very slowly with the passing centuries. These rough values are used + // when the accuracy is set to minimum. + const double _rough_cos_obliquity = 0.91747714052291862; + const double _rough_sin_obliquity = 0.39778850739794974; +}; + +void ON_SunEngine::CImpl::UpdateIfModified(void) +{ + if (!_modified) + return; + + if (_julian_date_changed) + { + const auto dJulianDayUT = JulianDay(); + + const auto dJulianDayUT2000 = dJulianDayUT - 2451545.0; + const auto dJulianCenturies = dJulianDayUT2000 / 36525.0; + const auto dJulianCenturies2 = dJulianCenturies * dJulianCenturies; + const auto dJulianCenturies3 = dJulianCenturies * dJulianCenturies2; + + // Calculate the sun's true and apparent ecliptic longitude. + const auto dL0 = 280.46646 + 36000.76983 * dJulianCenturies + 0.0003032 * dJulianCenturies2; + + auto dC = 0.0; + + if ((Accuracy::Maximum == _accuracy)) + { + const auto dMeanAnomaly = 357.52911 + 35999.05029 * dJulianCenturies - 0.0001537 * dJulianCenturies2; + + dC = Sin(dMeanAnomaly * 3.0) * 0.000289 + + Sin(dMeanAnomaly * 2.0) * (0.019993 - 0.000101 * dJulianCenturies) + + Sin(dMeanAnomaly ) * (1.914602 - 0.004817 * dJulianCenturies - + 0.000014 * dJulianCenturies2); + } + + const auto dTrueLongitude = Unwind(dL0 + dC); + + auto dApparentLongitude = dTrueLongitude - 0.00569; + + auto dSinObliquity = _rough_sin_obliquity; + auto dCosObliquity = _rough_cos_obliquity; + + if ((Accuracy::Maximum == _accuracy)) + { + // Include the effect of the moon. + const auto dOmega = 125.04 - 1934.136 * dJulianCenturies; + dApparentLongitude -= 0.00478 * Sin(dOmega); + + // Calculate the obliquity of the ecliptic (the tilt of the earth's axis). + const auto dObliquity = 23.439291111 - (46.81500 * dJulianCenturies - + 0.000590 * dJulianCenturies2 + + 0.001813 * dJulianCenturies3) / 3600.0 + + 0.002560 * Cos(dOmega); + dSinObliquity = Sin(dObliquity); + dCosObliquity = Cos(dObliquity); + } + + // Calculate the sun's equatorial coordinates (right ascension and declination). + const auto dSinApparentLongitude = Sin(dApparentLongitude); + const auto dCosApparentLongitude = Cos(dApparentLongitude); + + const auto dDeclination = ArcSin(dSinApparentLongitude * dSinObliquity); + + _cache_right_ascension = Unwind(ArcTan2(dSinApparentLongitude * dCosObliquity, dCosApparentLongitude)); + _cache_sin_declination = Sin(dDeclination); + _cache_cos_declination = Cos(dDeclination); + _cache_tan_declination = Tan(dDeclination); // Declination is between -24 and +24 so no problem with Tan(90). + + // Calculate the sidereal time at Greenwich, expressed in degrees. + const auto dTheta0 = 280.46061837 + (360.98564736629 * dJulianDayUT2000) + + (0.000387933 * dJulianCenturies2) - (dJulianCenturies3 / 38710000.0); + + _cache_greenwich_sidereal_time = Unwind(dTheta0); + + _julian_date_changed = false; + } + + // Calculate the sun's local hour angle, expressed in degrees. + const auto dLocalHourAngle = _cache_greenwich_sidereal_time + _longitude - _cache_right_ascension; + + // Calculate the sun's horizontal coordinates (azimuth and altitude). + const auto dSinLocalHourAngle = Sin(dLocalHourAngle); + const auto dCosLocalHourAngle = Cos(dLocalHourAngle); + + _azimuth = Unwind(180.0 + ArcTan2(dSinLocalHourAngle, + dCosLocalHourAngle * _cache_sin_latitude - + _cache_tan_declination * _cache_cos_latitude)); + + _altitude = ArcSin(_cache_sin_latitude * _cache_sin_declination + + _cache_cos_latitude * _cache_cos_declination * dCosLocalHourAngle); + + _modified = false; +} + +ON_SunEngine::ON_SunEngine(Accuracy a) +{ + _impl = new CImpl; + _impl->_accuracy = a; +} + +ON_SunEngine::~ON_SunEngine() +{ + delete _impl; + _impl = nullptr; +} + +ON_SunEngine::ON_SunEngine(const ON_SunEngine& e) +{ + _impl = new CImpl; + operator = (e); +} + +const ON_SunEngine& ON_SunEngine::operator = (const ON_SunEngine& e) +{ + _impl->_accuracy = e._impl->_accuracy; + _impl->_azimuth = e._impl->_azimuth; + _impl->_altitude = e._impl->_altitude; + _impl->_latitude = e._impl->_latitude; + _impl->_longitude = e._impl->_longitude; + _impl->_local_julian_day = e._impl->_local_julian_day; + _impl->_local_tz_hours = e._impl->_local_tz_hours; + _impl->_local_daylight_mins = e._impl->_local_daylight_mins; + _impl->_cache_right_ascension = e._impl->_cache_right_ascension; + _impl->_cache_sin_declination = e._impl->_cache_sin_declination; + _impl->_cache_cos_declination = e._impl->_cache_cos_declination; + _impl->_cache_tan_declination = e._impl->_cache_tan_declination; + _impl->_cache_sin_latitude = e._impl->_cache_sin_latitude; + _impl->_cache_cos_latitude = e._impl->_cache_cos_latitude; + _impl->_cache_greenwich_sidereal_time = e._impl->_cache_greenwich_sidereal_time; + _impl->_modified = e._impl->_modified; + _impl->_julian_date_changed = e._impl->_julian_date_changed; + + return *this; +} + +bool ON_SunEngine::operator == (const ON_SunEngine& e) +{ + if (_impl->_azimuth != e._impl->_azimuth) return false; + if (_impl->_altitude != e._impl->_altitude) return false; + if (_impl->_latitude != e._impl->_latitude) return false; + if (_impl->_longitude != e._impl->_longitude) return false; + if (_impl->_local_julian_day != e._impl->_local_julian_day) return false; + if (_impl->_local_tz_hours != e._impl->_local_tz_hours) return false; + if (_impl->_local_daylight_mins != e._impl->_local_daylight_mins) return false; + + return true; +} + +bool ON_SunEngine::operator != (const ON_SunEngine& e) +{ + return !(operator == (e)); +} + +int ON_SunEngine::DaysInMonth(int month, int year) // Static. +{ + month = std::max(1, std::min(12, month)); + + if ((2 == month) && IsLeapYear(year)) + return 29; + + static const int tab[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + return tab[month]; +} + +double ON_SunEngine::Latitude(void) const +{ + return _impl->_latitude; +} + +bool ON_SunEngine::SetLatitude(double lat) +{ + if ((lat < -90.0) || (lat > 90.0)) + return false; + + if (_impl->_latitude != lat) + { + _impl->_latitude = lat; + _impl->_cache_sin_latitude = Sin(lat); + _impl->_cache_cos_latitude = Cos(lat); + _impl->_modified = true; + } + + return true; +} + +double ON_SunEngine::Longitude(void) const +{ + return _impl->_longitude; +} + +bool ON_SunEngine::SetLongitude(double dLong) +{ + if ((dLong < -180.0) || (dLong > 180.0)) + return false; + + if (_impl->_longitude != dLong) + { + _impl->_longitude = dLong; + _impl->_modified = true; + } + + return true; +} + +bool ON_SunEngine::SetTimeZoneHours(double dHours) +{ + if ((dHours < -12.0) || (dHours > 13.0)) + return false; + + const double dLocalTimeZoneHours = dHours; + if (_impl->_local_tz_hours != dLocalTimeZoneHours) + { + _impl->_local_tz_hours = dLocalTimeZoneHours; + _impl->_modified = true; + _impl->_julian_date_changed = true; + } + + return true; +} + +bool ON_SunEngine::SetDaylightSavingMinutes(int iMinutes) +{ + if ((iMinutes < 0) || (iMinutes > 120)) + return false; + + const int iLocalDaylightMinutes = iMinutes; + if (_impl->_local_daylight_mins != iLocalDaylightMinutes) + { + _impl->_local_daylight_mins = iLocalDaylightMinutes; + _impl->_modified = true; + _impl->_julian_date_changed = true; + } + + return true; +} + +bool ON_SunEngine::SetLocalDateTime(int iYear, int iMonth, int iDay, double dHours) +{ + if ((iYear < 1800) || (iYear > 2199) || (iMonth < 1) || (iMonth > 12)) + return false; + + if ((iDay < 1) || (iDay > DaysInMonth(iMonth, iYear))) + return false; + + if ((dHours < 0.0) || (dHours > 24.0)) + return false; + + if (iMonth < 3) + { + iMonth += 12; + iYear--; + } + + const int a = iYear / 100; + const int b = 2 - a + (a / 4); + const int iJulianDay = (36525 * (iYear + 4716)) / 100 + (306 * (iMonth + 1)) / 10 + iDay + b - 1524; + + const double dJulianDay = iJulianDay + dHours / 24.0 - 0.5; + + return SetLocalJulianDay(dJulianDay); +} + +void ON_SunEngine::LocalDateTime(int& iYear, int& iMonth, int& iDay, double& dHours) const +{ + const double jd = _impl->_local_julian_day + 0.5; + int b = (int)Int(jd); + const int a = (b * 100 - 186721625) / 3652425; + b += 1 + a - (a / 4) + 1524; + const int c = (b * 100 - 12210) / 36525; + const int d = (365 * c) + (c / 4); + const int e = (10000 * (b - d)) / 306001; + iDay = (int)(b - d - ((306001 * e) / 10000)); + iMonth = (int)((e < 14) ? e - 1 : e - 13); + iYear = (int)((iMonth > 2) ? c - 4716 : c - 4715); + dHours = Frac(jd) * 24.0 + 1e-8; +} + +bool ON_SunEngine::SetLocalJulianDay(double dLocalJulianDay) +{ + if ((dLocalJulianDay < 2378496.5) || (dLocalJulianDay > 2524593.499999999)) + return false; + + if (_impl->_local_julian_day != dLocalJulianDay) + { + _impl->_local_julian_day = dLocalJulianDay; + _impl->_julian_date_changed = true; + _impl->_modified = true; + } + + return true; +} + +double ON_SunEngine::Azimuth(void) const +{ + _impl->UpdateIfModified(); + + return _impl->_azimuth; +} + +double ON_SunEngine::Altitude(void) const +{ + _impl->UpdateIfModified(); + + return _impl->_altitude; +} + +double ON_SunEngine::JulianDay(void) const +{ + return _impl->JulianDay(); +} + +double ON_SunEngine::LocalJulianDay(void) const +{ + return _impl->_local_julian_day; +} + +double ON_SunEngine::TimeZoneHours(void) const +{ + return _impl->_local_tz_hours; +} + +int ON_SunEngine::DaylightSavingMinutes(void) const +{ + return _impl->_local_daylight_mins; +} + +void ON_SunEngine::ConvertHorizonCoordsToSolarVector(double dAzimuth, double dAltitude, double* dVector) // Static. +{ + dVector[0] = -Cos(dAltitude) * Sin(dAzimuth); + dVector[1] = -Cos(dAltitude) * Cos(dAzimuth); + dVector[2] = -Sin(dAltitude); +} + +void ON_SunEngine::ConvertSolarVectorToHorizonCoords(const double* dVector, double& dAzimuth, double& dAltitude) // Static. +{ + dAltitude = ArcSin(-dVector[2]); + + const double dY = dVector[0] / -Cos(dAltitude); + const double dX = dVector[1] / -Cos(dAltitude); + + dAzimuth = ArcTan2(dY, dX); + if (dAzimuth < 0.0) + dAzimuth += 360.0; } class ON_Sun::CImpl : public ON_InternalXMLImpl @@ -157,24 +525,24 @@ static const wchar_t* XMLPath_Sun(void) ON_Sun::ON_Sun() { - m_impl = new CImpl; + _impl = new CImpl; } ON_Sun::ON_Sun(ON_XMLNode& model_node) { - m_impl = new CImpl(model_node); + _impl = new CImpl(model_node); } ON_Sun::ON_Sun(const ON_Sun& sun) { - m_impl = new CImpl; + _impl = new CImpl; operator = (sun); } ON_Sun::~ON_Sun() { - delete m_impl; - m_impl = nullptr; + delete _impl; + _impl = nullptr; } int ON_Sun::MinYear(void) @@ -191,225 +559,223 @@ const ON_Sun& ON_Sun::operator = (const ON_Sun& sun) { if (this != &sun) { - SetEnableOn (sun.EnableOn()); - SetEnableAllowed (sun.EnableAllowed()); - SetEnableOn (sun.EnableOn()); - SetManualControlAllowed (sun.ManualControlAllowed()); - SetManualControlOn (sun.ManualControlOn()); - SetNorth (sun.North()); - SetDaylightSavingOn (sun.DaylightSavingOn()); - SetDaylightSavingMinutes(sun.DaylightSavingMinutes()); - SetAzimuth (sun.Azimuth()); - SetAltitude (sun.Altitude()); - SetLatitude (sun.Latitude()); - SetLongitude (sun.Longitude()); - SetTimeZone (sun.TimeZone()); + // When copying the object, we need to directly copy the underlying XML. So we can't allow + // virtual overrides to execute because they might shadow the real values we want to copy. + ON_Sun::SetEnableOn (sun.ON_Sun::EnableOn()); + ON_Sun::SetEnableAllowed (sun.ON_Sun::EnableAllowed()); + ON_Sun::SetEnableOn (sun.ON_Sun::EnableOn()); + ON_Sun::SetManualControlAllowed (sun.ON_Sun::ManualControlAllowed()); + ON_Sun::SetManualControlOn (sun.ON_Sun::ManualControlOn()); + ON_Sun::SetNorth (sun.ON_Sun::North()); + ON_Sun::SetDaylightSavingOn (sun.ON_Sun::DaylightSavingOn()); + ON_Sun::SetDaylightSavingMinutes(sun.ON_Sun::DaylightSavingMinutes()); + ON_Sun::SetAzimuth (sun.ON_Sun::Azimuth()); + ON_Sun::SetAltitude (sun.ON_Sun::Altitude()); + ON_Sun::SetLatitude (sun.ON_Sun::Latitude()); + ON_Sun::SetLongitude (sun.ON_Sun::Longitude()); + ON_Sun::SetTimeZone (sun.ON_Sun::TimeZone()); + ON_Sun::SetIntensity (sun.ON_Sun::Intensity()); + ON_Sun::SetShadowIntensity (sun.ON_Sun::ShadowIntensity()); int y = 0, m = 0, d = 0; double h = 0.0; - sun.LocalDateTime(y, m, d, h); - SetLocalDateTime(y, m, d, h); + sun.ON_Sun::LocalDateTime(y, m, d, h); + ON_Sun::SetLocalDateTime(y, m, d, h); } return *this; } -bool ON_Sun::operator == (const ON_Sun& sun) +bool ON_Sun::operator == (const ON_Sun& sun) const { - if (EnableAllowed() != sun.EnableAllowed()) return false; - if (EnableOn() != sun.EnableOn()) return false; - if (ManualControlAllowed() != sun.ManualControlAllowed()) return false; - if (ManualControlOn() != sun.ManualControlOn()) return false; - if (North() != sun.North()) return false; - if (DaylightSavingOn() != sun.DaylightSavingOn()) return false; - if (DaylightSavingMinutes() != sun.DaylightSavingMinutes()) return false; + // When checking equality, we need to directly check the underlying XML. So we can't allow + // virtual overrides to execute because they might shadow the real values we want to copy. + if (ON_Sun::EnableAllowed() != sun.ON_Sun::EnableAllowed()) return false; + if (ON_Sun::EnableOn() != sun.ON_Sun::EnableOn()) return false; + if (ON_Sun::ManualControlAllowed() != sun.ON_Sun::ManualControlAllowed()) return false; + if (ON_Sun::ManualControlOn() != sun.ON_Sun::ManualControlOn()) return false; + if (ON_Sun::DaylightSavingOn() != sun.ON_Sun::DaylightSavingOn()) return false; + if (ON_Sun::DaylightSavingMinutes() != sun.ON_Sun::DaylightSavingMinutes()) return false; - if (!IsDoubleEqual(Azimuth() , sun.Azimuth())) return false; - if (!IsDoubleEqual(Altitude() , sun.Altitude())) return false; - if (!IsDoubleEqual(Latitude() , sun.Latitude())) return false; - if (!IsDoubleEqual(Longitude(), sun.Longitude())) return false; - if (!IsDoubleEqual(TimeZone() , sun.TimeZone())) return false; + if (!IsDoubleEqual(ON_Sun::North() , sun.ON_Sun::North())) return false; + if (!IsDoubleEqual(ON_Sun::Latitude() , sun.ON_Sun::Latitude())) return false; + if (!IsDoubleEqual(ON_Sun::Longitude() , sun.ON_Sun::Longitude())) return false; + if (!IsDoubleEqual(ON_Sun::Azimuth() , sun.ON_Sun::Azimuth())) return false; + if (!IsDoubleEqual(ON_Sun::Altitude() , sun.ON_Sun::Altitude())) return false; + if (!IsDoubleEqual(ON_Sun::TimeZone() , sun.ON_Sun::TimeZone())) return false; + if (!IsDoubleEqual(ON_Sun::Intensity() , sun.ON_Sun::Intensity())) return false; + if (!IsDoubleEqual(ON_Sun::ShadowIntensity(), sun.ON_Sun::ShadowIntensity())) return false; - int y1, m1, d1, y2, m2, d2; double h1, h2; - LocalDateTime(y1, m1, d1, h1); - sun.LocalDateTime(y2, m2, d2, h2); + int y1, m1, d1, y2, m2, d2; double h1, h2; + ON_Sun::LocalDateTime(y1, m1, d1, h1); + sun.ON_Sun::LocalDateTime(y2, m2, d2, h2); - if ((y1 != y2) || (m1 != m2) || (d1 != d2)) - return false; + if ((y1 != y2) || (m1 != m2) || (d1 != d2)) + return false; + + if (!IsDoubleEqual(h1, h2)) + return false; - if (!IsDoubleEqual(h1, h2)) - return false; - - return true; + return true; } -bool ON_Sun::operator != (const ON_Sun& sun) +bool ON_Sun::operator != (const ON_Sun& sun) const { return !(operator == (sun)); } bool ON_Sun::EnableAllowed(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ALLOWED, false).AsBool(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ALLOWED, false).AsBool(); } bool ON_Sun::EnableOn(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ON, false).AsBool(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ON, false).AsBool(); } bool ON_Sun::ManualControlAllowed(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ALLOWED, false).AsBool(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ALLOWED, false).AsBool(); } bool ON_Sun::ManualControlOn(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ON, false).AsBool(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ON, false).AsBool(); } double ON_Sun::North(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_NORTH, 90.0).AsDouble(); -} - -ON_3dVector ON_Sun::Vector(void) const -{ - const double azimuth = Azimuth() + WorldToCompass(North()); - - double d[3] = { 0 }; - ConvertHorizonCoordsToSolarVector(azimuth, Altitude(), d); - - return ON_3dVector(d); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_NORTH, 90.0).AsDouble(); } double ON_Sun::Azimuth(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_AZIMUTH, 0.0).AsDouble(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_AZIMUTH, 0.0).AsDouble(); } double ON_Sun::Altitude(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_ALTITUDE, 0.0).AsDouble(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_ALTITUDE, 0.0).AsDouble(); } double ON_Sun::Latitude(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LATITUDE, 0.0).AsDouble(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LATITUDE, 0.0).AsDouble(); } double ON_Sun::Longitude(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LONGITUDE, 0.0).AsDouble(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LONGITUDE, 0.0).AsDouble(); } double ON_Sun::TimeZone(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_TIMEZONE, 0.0).AsDouble(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_TIMEZONE, 0.0).AsDouble(); } bool ON_Sun::DaylightSavingOn(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_ON, false).AsBool(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_ON, false).AsBool(); } int ON_Sun::DaylightSavingMinutes(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, 0).AsInteger(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, 0).AsInteger(); } void ON_Sun::LocalDateTime(int& year, int& month, int& day, double& hours) const { const wchar_t* s = XMLPath_Sun(); - year = m_impl->GetParameter(s, ON_RDK_SUN_DATE_YEAR, 2000); - month = m_impl->GetParameter(s, ON_RDK_SUN_DATE_MONTH, 1); - day = m_impl->GetParameter(s, ON_RDK_SUN_DATE_DAY, 1); - hours = m_impl->GetParameter(s, ON_RDK_SUN_TIME_HOURS, 0.0); + year = _impl->GetParameter(s, ON_RDK_SUN_DATE_YEAR, 2000); + month = _impl->GetParameter(s, ON_RDK_SUN_DATE_MONTH, 1); + day = _impl->GetParameter(s, ON_RDK_SUN_DATE_DAY, 1); + hours = _impl->GetParameter(s, ON_RDK_SUN_TIME_HOURS, 0.0); +} + +void ON_Sun::UTCDateTime(int& year, int& month, int& day, double& hours) const +{ + // Get the local date and time. + LocalDateTime(year, month, day, hours); + + // Set the local time, timezone and daylight into a helper 'sun'. + ON_SunEngine engine; + engine.SetLocalDateTime(year, month, day, hours); + engine.SetTimeZoneHours(TimeZone()); + engine.SetDaylightSavingMinutes(DaylightSavingOn() ? DaylightSavingMinutes() : 0); + + // Get the UTC JD and set it as 'local', then get the 'local' (Greenwich) time. + engine.SetLocalJulianDay(engine.JulianDay()); + engine.LocalDateTime(year, month, day, hours); } double ON_Sun::Intensity(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_INTENSITY, 1.0).AsDouble(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_INTENSITY, 1.0).AsDouble(); } double ON_Sun::ShadowIntensity(void) const { - return m_impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_SHADOW_INTENSITY, 1.0).AsDouble(); + return _impl->GetParameter(XMLPath_Sun(), ON_RDK_SUN_SHADOW_INTENSITY, 1.0).AsDouble(); } void ON_Sun::SetEnableAllowed(bool allowed) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ALLOWED, allowed); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ALLOWED, allowed); } void ON_Sun::SetEnableOn(bool on) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ON, on); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_ENABLE_ON, on); } void ON_Sun::SetManualControlAllowed(bool allowed) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ALLOWED, allowed); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ALLOWED, allowed); } void ON_Sun::SetManualControlOn(bool manual) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ON, manual); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_MANUAL_CONTROL_ON, manual); } void ON_Sun::SetNorth(double north) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_NORTH, north); -} - -void ON_Sun::SetVector(const ON_3dVector& v) -{ - ON_3dVector vec = v; - vec.Unitize(); - - const double d[3] = { vec.x, vec.y, vec.z }; - - double azimuth = 0.0, altitude = 0.0; - ConvertSolarVectorToHorizonCoords(d, azimuth, altitude); - - azimuth -= WorldToCompass(North()); - SetAzimuth(azimuth); - - SetAltitude(altitude); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_NORTH, north); } void ON_Sun::SetAzimuth(double azimuth) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_AZIMUTH, azimuth); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_AZIMUTH, azimuth); } void ON_Sun::SetAltitude(double altitude) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_ALTITUDE, altitude); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_ALTITUDE, altitude); } void ON_Sun::SetLatitude(double latitude) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LATITUDE, latitude); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LATITUDE, latitude); } void ON_Sun::SetLongitude(double longitude) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LONGITUDE, longitude); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_LONGITUDE, longitude); } void ON_Sun::SetTimeZone(double hours) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_TIMEZONE, hours); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_OBSERVER_TIMEZONE, hours); } void ON_Sun::SetDaylightSavingOn(bool on) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_ON, on); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_ON, on); } void ON_Sun::SetDaylightSavingMinutes(int minutes) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, minutes); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, minutes); } bool ON_Sun::SetLocalDateTime(int year, int month, int day, double hours) @@ -418,97 +784,162 @@ bool ON_Sun::SetLocalDateTime(int year, int month, int day, double hours) return false; const wchar_t* s = XMLPath_Sun(); - m_impl->SetParameter(s, ON_RDK_SUN_DATE_YEAR, year); - m_impl->SetParameter(s, ON_RDK_SUN_DATE_MONTH, month); - m_impl->SetParameter(s, ON_RDK_SUN_DATE_DAY, day); - m_impl->SetParameter(s, ON_RDK_SUN_TIME_HOURS, hours); + _impl->SetParameter(s, ON_RDK_SUN_DATE_YEAR, year); + _impl->SetParameter(s, ON_RDK_SUN_DATE_MONTH, month); + _impl->SetParameter(s, ON_RDK_SUN_DATE_DAY, day); + _impl->SetParameter(s, ON_RDK_SUN_TIME_HOURS, hours); return true; } +bool ON_Sun::SetUTCDateTime(int year, int month, int day, double hours) +{ + // Set the UTC date and time into a helper 'sun' assuming 'local' means 'Greenwich' and get the UTC JD. + ON_SunEngine engine; + engine.SetLocalDateTime(year, month, day, hours); + + // Convert the UTC JD to local JD by adding the timezone and daylight offset, + // and use the helper 'sun' to get the local date and time. + const int dsm = DaylightSavingOn() ? DaylightSavingMinutes() : 0; + engine.SetLocalJulianDay(engine.JulianDay() + (TimeZone() + (dsm / 60.0)) / 24.0); + engine.LocalDateTime(year, month, day, hours); + + // Set the local date and time. + return SetLocalDateTime(year, month, day, hours); +} + void ON_Sun::SetShadowIntensity(double intensity) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_SHADOW_INTENSITY, std::max(0.0, std::min(1.0, intensity))); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_SHADOW_INTENSITY, std::max(0.0, std::min(1.0, intensity))); } void ON_Sun::SetIntensity(double intensity) { - m_impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_INTENSITY, std::max(0.0, intensity)); + _impl->SetParameter(XMLPath_Sun(), ON_RDK_SUN_INTENSITY, std::max(0.0, intensity)); } ON__UINT32 ON_Sun::DataCRC(ON__UINT32 crc) const { - if (ManualControlAllowed() && ManualControlOn()) - { - crc = UpdateCRC(crc, Integerize(Azimuth())); - crc = UpdateCRC(crc, Integerize(Altitude())); - } - else - { - crc = UpdateCRC(crc, EnableOn()); - crc = UpdateCRC(crc, Integerize(North())); - crc = UpdateCRC(crc, Integerize(Latitude())); - crc = UpdateCRC(crc, Integerize(Longitude())); - crc = UpdateCRC(crc, Integerize(TimeZone())); - crc = UpdateCRC(crc, DaylightSavingOn()); - crc = UpdateCRC(crc, DaylightSavingMinutes()); + // We need to calculate the CRC of the underlying XML. So we can't allow virtual overrides to + // execute because they might shadow the real values we want to use. - int y = 0, m = 0, d = 0; double h = 0.0; - LocalDateTime(y, m, d, h); - crc = UpdateCRC(crc, y); - crc = UpdateCRC(crc, m); - crc = UpdateCRC(crc, d); - crc = UpdateCRC(crc, Integerize(h)); - } + crc = UpdateCRC(crc, ON_Sun::EnableAllowed()); + crc = UpdateCRC(crc, ON_Sun::EnableOn()); + crc = UpdateCRC(crc, ON_Sun::ManualControlAllowed()); + crc = UpdateCRC(crc, ON_Sun::ManualControlOn()); + crc = UpdateCRC(crc, ON_Sun::DaylightSavingOn()); + crc = UpdateCRC(crc, ON_Sun::DaylightSavingMinutes()); + crc = UpdateCRC(crc, Integerize(ON_Sun::Azimuth())); + crc = UpdateCRC(crc, Integerize(ON_Sun::Altitude())); + crc = UpdateCRC(crc, Integerize(ON_Sun::North())); + crc = UpdateCRC(crc, Integerize(ON_Sun::Latitude())); + crc = UpdateCRC(crc, Integerize(ON_Sun::Longitude())); + crc = UpdateCRC(crc, Integerize(ON_Sun::TimeZone())); + crc = UpdateCRC(crc, Integerize(ON_Sun::ShadowIntensity())); + crc = UpdateCRC(crc, Integerize(ON_Sun::Intensity())); - crc = UpdateCRC(crc, Integerize(ShadowIntensity())); - crc = UpdateCRC(crc, Integerize(Intensity())); + int y = 0, m = 0, d = 0; double h = 0.0; + ON_Sun::LocalDateTime(y, m, d, h); + crc = UpdateCRC(crc, y); + crc = UpdateCRC(crc, m); + crc = UpdateCRC(crc, d); + crc = UpdateCRC(crc, Integerize(h)); return crc; } -#define SUNASSERT(x) ON_ASSERT(x); if (!(x)) return false; +#define SUN_ASSERT(x) ON_ASSERT(x); if (!(x)) return false; bool ON_Sun::IsValid(void) const { - // Returns false if SUNASSERT fails. - - SUNASSERT(Azimuth() >= 0.0); - SUNASSERT(Azimuth() <= 360.0) - SUNASSERT(Altitude() >= -90.0); - SUNASSERT(Altitude() <= +90.0); + // Returns false if SUN_ASSERT fails. double hours = 0.0; int year = 0, month = 0, day = 0; LocalDateTime(year, month, day, hours); - SUNASSERT(year >= MinYear()); - SUNASSERT(year <= MaxYear()); - SUNASSERT(month >= 1); - SUNASSERT(month <= 12); - SUNASSERT(day >= 1); - SUNASSERT(day <= DaysInMonth(month, year)); - SUNASSERT(hours >= 0.0); - SUNASSERT(hours <= 24.0); + SUN_ASSERT(year >= MinYear()); + SUN_ASSERT(year <= MaxYear()); + SUN_ASSERT(month >= 1); + SUN_ASSERT(month <= 12); + SUN_ASSERT(day >= 1); + SUN_ASSERT(day <= ON_SunEngine::DaysInMonth(month, year)); + SUN_ASSERT(hours >= 0.0); + SUN_ASSERT(hours <= 24.0); - SUNASSERT(North() >= 0.0); - SUNASSERT(North() <= 360.0) - SUNASSERT(Latitude() >= -90.0); - SUNASSERT(Latitude() <= +90.0); - SUNASSERT(Longitude() >= -180.0); - SUNASSERT(Longitude() <= +180.0); - SUNASSERT(TimeZone() >= -12.0); - SUNASSERT(TimeZone() <= +13.0); - SUNASSERT(DaylightSavingMinutes() >= 0); - SUNASSERT(DaylightSavingMinutes() <= 120); - - SUNASSERT(ShadowIntensity() >= 0.0); - SUNASSERT(ShadowIntensity() <= 1.0); - - SUNASSERT(Intensity() >= 0.0); + SUN_ASSERT(Azimuth() >= 0.0); + SUN_ASSERT(Azimuth() <= 360.0) + SUN_ASSERT(Altitude() >= -90.0); + SUN_ASSERT(Altitude() <= +90.0); + SUN_ASSERT(North() >= 0.0); + SUN_ASSERT(North() <= 360.0) + SUN_ASSERT(Latitude() >= -90.0); + SUN_ASSERT(Latitude() <= +90.0); + SUN_ASSERT(Longitude() >= -180.0); + SUN_ASSERT(Longitude() <= +180.0); + SUN_ASSERT(TimeZone() >= -12.0); + SUN_ASSERT(TimeZone() <= +13.0); + SUN_ASSERT(DaylightSavingMinutes() >= 0); + SUN_ASSERT(DaylightSavingMinutes() <= 120); + SUN_ASSERT(Intensity() >= 0.0); + SUN_ASSERT(ShadowIntensity() >= 0.0); + SUN_ASSERT(ShadowIntensity() <= 1.0); return true; } +void ON_Sun::LoadFromXMLNode(const ON_XMLNode& node) +{ + ON_XMLParameters p(node); + + const auto y = p.GetParam(ON_RDK_SUN_DATE_YEAR, 2000).AsInteger(); + const auto m = p.GetParam(ON_RDK_SUN_DATE_MONTH, 1).AsInteger(); + const auto d = p.GetParam(ON_RDK_SUN_DATE_DAY, 1).AsInteger(); + const auto h = p.GetParam(ON_RDK_SUN_TIME_HOURS, 12.0).AsDouble(); + SetLocalDateTime(y, m, d, h); + + SetEnableAllowed (p.GetParam(ON_RDK_SUN_ENABLE_ALLOWED, false)); + SetEnableOn (p.GetParam(ON_RDK_SUN_ENABLE_ON, false)); + SetManualControlAllowed (p.GetParam(ON_RDK_SUN_MANUAL_CONTROL_ALLOWED, false)); + SetManualControlOn (p.GetParam(ON_RDK_SUN_MANUAL_CONTROL_ON, false)); + SetNorth (p.GetParam(ON_RDK_SUN_NORTH, 90.0)); + SetAzimuth (p.GetParam(ON_RDK_SUN_AZIMUTH, 0.0)); + SetAltitude (p.GetParam(ON_RDK_SUN_ALTITUDE, 0.0)); + SetDaylightSavingOn (p.GetParam(ON_RDK_SUN_DAYLIGHT_SAVING_ON, false)); + SetDaylightSavingMinutes(p.GetParam(ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, 0)); + SetLatitude (p.GetParam(ON_RDK_SUN_OBSERVER_LATITUDE, 0.0)); + SetLongitude (p.GetParam(ON_RDK_SUN_OBSERVER_LONGITUDE, 0.0)); + SetTimeZone (p.GetParam(ON_RDK_SUN_OBSERVER_TIMEZONE, 0)); + SetShadowIntensity (p.GetParam(ON_RDK_SUN_SHADOW_INTENSITY, 1.0)); + SetIntensity (p.GetParam(ON_RDK_SUN_INTENSITY, 1.0)); +} + +void ON_Sun::SaveToXMLNode(ON_XMLNode& node) const +{ + ON_XMLParameters p(node); + + int y = 0, m = 0, d = 0; double h = 0.0; + LocalDateTime(y, m, d, h); + + p.SetParam(ON_RDK_SUN_DATE_YEAR , y); + p.SetParam(ON_RDK_SUN_DATE_MONTH, m); + p.SetParam(ON_RDK_SUN_DATE_DAY , d); + p.SetParam(ON_RDK_SUN_TIME_HOURS, h); + p.SetParam(ON_RDK_SUN_ENABLE_ALLOWED , EnableAllowed()); + p.SetParam(ON_RDK_SUN_ENABLE_ON , EnableOn()); + p.SetParam(ON_RDK_SUN_MANUAL_CONTROL_ALLOWED , ManualControlAllowed()); + p.SetParam(ON_RDK_SUN_MANUAL_CONTROL_ON , ManualControlOn()); + p.SetParam(ON_RDK_SUN_NORTH , North()); + p.SetParam(ON_RDK_SUN_AZIMUTH , Azimuth()); + p.SetParam(ON_RDK_SUN_ALTITUDE , Altitude()); + p.SetParam(ON_RDK_SUN_DAYLIGHT_SAVING_ON , DaylightSavingOn()); + p.SetParam(ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, DaylightSavingMinutes()); + p.SetParam(ON_RDK_SUN_OBSERVER_LATITUDE , Latitude()); + p.SetParam(ON_RDK_SUN_OBSERVER_LONGITUDE , Longitude()); + p.SetParam(ON_RDK_SUN_OBSERVER_TIMEZONE , TimeZone()); + p.SetParam(ON_RDK_SUN_SHADOW_INTENSITY , ShadowIntensity()); + p.SetParam(ON_RDK_SUN_INTENSITY , Intensity()); +} + ON_4fColor ON_Sun::SunColorFromAltitude(double altitude) // Static. { ON_4fColor colDark(0, 0, 64, 255); @@ -530,6 +961,23 @@ ON_4fColor ON_Sun::SunColorFromAltitude(double altitude) // Static. return col; } +bool ON_Sun::IsValidDateTime(int y, int m, int d, int h, int n, int s) // Static. +{ + if ((h < 0) || (n < 0) || (s < 0)) + return false; + + if ((h > 23) || (n > 59) || (s > 59)) + return false; + + if ((y < ON_Sun::MinYear()) || (m < 1) || (d < 1)) + return false; + + if ((y > ON_Sun::MaxYear()) || (m > 12) || (d > ON_SunEngine::DaysInMonth(m, y))) + return false; + + return true; +} + static ON_UUID uuidFeatureSun = { 0x62ee2cf6, 0xb855, 0x4549, { 0xa2, 0x77, 0xe2, 0xbb, 0xf6, 0x09, 0xf3, 0x28 } }; ON_Light ON_Sun::Light(void) const @@ -538,18 +986,18 @@ ON_Light ON_Sun::Light(void) const light.Enable(false); light.SetStyle(ON::world_directional_light); light.SetLocation(ON_3dPoint(0.0, 0.0, 0.0)); - - light.SetShadowIntensity(ShadowIntensity()); light.SetIntensity(Intensity()); + light.SetShadowIntensity(ShadowIntensity()); if (IsValid()) { - light.SetDirection(Vector()); + const auto vec = CalculateVectorFromAzimuthAndAltitude(); + light.SetDirection(vec); - const bool bOn = EnableOn(); - light.Enable(bOn); + const bool on = EnableOn(); + light.Enable(on); - const ON_Color col = SunColorFromAltitude(Altitude()); + const auto col = SunColorFromAltitude(Altitude()); light.SetDiffuse(col); } @@ -557,3 +1005,176 @@ ON_Light ON_Sun::Light(void) const return light; } + +ON_3dVector ON_Sun::CalculateVectorFromAzimuthAndAltitude(void) const +{ + const double azimuth = Azimuth() + WorldToCompass(North()); + + double d[3] = { 0 }; + ON_SunEngine::ConvertHorizonCoordsToSolarVector(azimuth, Altitude(), d); + + return ON_3dVector(d); +} + +void ON_Sun::SetAzimuthAndAltitudeFromVector(const ON_3dVector& v) +{ + ON_3dVector vec = v; + vec.Unitize(); + + double azimuth = 0.0, altitude = 0.0; + + const double d[3] = { vec.x, vec.y, vec.z }; + ON_SunEngine::ConvertSolarVectorToHorizonCoords(d, azimuth, altitude); + + azimuth -= WorldToCompass(North()); + SetAzimuth(azimuth); + + SetAltitude(altitude); +} + +void ON_Sun::SetXMLNode(ON_XMLNode& node) const +{ + _impl->SetModelNode(node); +} + +void* ON_Sun::EVF(const wchar_t* func, void* data) +{ + return nullptr; +} + +// ON_SunEx + +class ON_SunEx::CImpl +{ +public: + void UpdateAziAlt(const ON_Sun& sun); + + double _azimuth = 0.0; + double _altitude = 0.0; + ON__UINT32 _azi_alt_crc = 0; + mutable std::recursive_mutex m_mutex; +}; + +ON_SunEx::ON_SunEx() +{ + _impl = new CImpl; + _impl->_azimuth = ON_Sun::Azimuth(); + _impl->_altitude = ON_Sun::Altitude(); +} + +ON_SunEx::ON_SunEx(ON_XMLNode& model_node) + : + ON_Sun(model_node) +{ + _impl = new CImpl; + _impl->_azimuth = ON_Sun::Azimuth(); + _impl->_altitude = ON_Sun::Altitude(); +} + +ON_SunEx::ON_SunEx(const ON_SunEx& sun) +{ + _impl = new CImpl; + + operator = (sun); +} + +ON_SunEx::~ON_SunEx() +{ + delete _impl; + _impl = nullptr; +} + +const ON_Sun& ON_SunEx::operator = (const ON_Sun& sun) +{ + ON_Sun::operator = (sun); + + return *this; +} + +const ON_SunEx& ON_SunEx::operator = (const ON_SunEx& sun) +{ + ON_Sun::operator = (sun); + + _impl->_azimuth = sun._impl->_azimuth; + _impl->_altitude = sun._impl->_altitude; + _impl->_azi_alt_crc = sun._impl->_azi_alt_crc; + + return *this; +} + +static ON__UINT32 AziAltCRC(const ON_Sun& sun) +{ + ON__UINT32 crc = 0; + + const double latitude = sun.Latitude(); + const double longitude = sun.Longitude(); + const double time_zone = sun.TimeZone(); + const int dsm = sun.DaylightSavingOn() ? sun.DaylightSavingMinutes() : 0; + + int y = 0, m = 0, d = 0; double h = 0.0; + sun.LocalDateTime(y, m, d, h); + + crc = ON_CRC32(crc, sizeof(latitude) , &latitude); + crc = ON_CRC32(crc, sizeof(longitude), &longitude); + crc = ON_CRC32(crc, sizeof(time_zone), &time_zone); + crc = ON_CRC32(crc, sizeof(dsm) , &dsm); + + crc = ON_CRC32(crc, sizeof(y), &y); + crc = ON_CRC32(crc, sizeof(m), &m); + crc = ON_CRC32(crc, sizeof(d), &d); + crc = ON_CRC32(crc, sizeof(h), &h); + + return crc; +} + +void ON_SunEx::CImpl::UpdateAziAlt(const ON_Sun& sun) +{ + const auto crc = AziAltCRC(sun); + if (_azi_alt_crc != crc) + { + _azi_alt_crc = crc; + + ON_SunEngine engine; + engine.SetLatitude (sun.Latitude()); + engine.SetLongitude(sun.Longitude()); + engine.SetTimeZoneHours(sun.TimeZone()); + + const int dsm = sun.DaylightSavingOn() ? sun.DaylightSavingMinutes() : 0; + engine.SetDaylightSavingMinutes(dsm); + + int y = 0, m = 0, d = 0; double h = 0.0; + sun.LocalDateTime(y, m, d, h); + engine.SetLocalDateTime(y, m, d, h); + + _azimuth = engine.Azimuth(); + _altitude = engine.Altitude(); + } +} + +double ON_SunEx::Azimuth(void) const +{ + std::lock_guard lg(_impl->m_mutex); + + if (ManualControlAllowed() && ManualControlOn()) + return ON_Sun::Azimuth(); + + _impl->UpdateAziAlt(*this); + + return _impl->_azimuth; +} + +double ON_SunEx::Altitude(void) const +{ + std::lock_guard lg(_impl->m_mutex); + + if (ManualControlAllowed() && ManualControlOn()) + return ON_Sun::Altitude(); + + _impl->UpdateAziAlt(*this); + + return _impl->_altitude; +} + +void ON_SunEx::InvalidateCache(void) +{ +} diff --git a/opennurbs_sun.h b/opennurbs_sun.h index 91ced153..a3141adc 100644 --- a/opennurbs_sun.h +++ b/opennurbs_sun.h @@ -14,13 +14,113 @@ #if !defined(ON_SUN_INC_) #define ON_SUN_INC_ -class ON_CLASS ON_Sun final +// Class ON_SunEngine is a sun calculation engine. It's responsible for doing the astronomical calculations +// used by the sun. Note that most ON_Sun methods don't use this class. Only the utility methods use +// this because generally, ON_Sun is a simple sun object that merely gets and sets stored values. +// If you want to do sun calculations with OpenNURBS you should use this class or ON_SunEx instead of ON_Sun. + +class ON_CLASS ON_SunEngine +{ +public: + enum class Accuracy + { + Minimum, // Suitable for doing many calculations when you just need rough results quickly. + Maximum, // Suitable for generating accurate tables of sun positions (e.g., an ephemeris). + }; + + ON_SunEngine(Accuracy a = Accuracy::Minimum); + ON_SunEngine(const ON_SunEngine&); + virtual ~ON_SunEngine(); + + const ON_SunEngine& operator = (const ON_SunEngine&); + + bool operator == (const ON_SunEngine& e); + bool operator != (const ON_SunEngine& e); + + // Return the latitude of the observer in degrees. + virtual double Latitude(void) const; + + // Return the longitude of the observer in degrees. + virtual double Longitude(void) const; + + // Set the latitude of the observer in degrees. Must be in the range -90 to +90. + // This value is used by the calculation of the sun's azimuth and altitude. + // Returns true if latitude is valid, else false. + virtual bool SetLatitude(double dLat); + + // Set the longitude of the observer in degrees. Must be in the range -180 to +180. + // This value is used by the calculation of the sun's azimuth and altitude. + // Returns true if longitude is valid, else false. + virtual bool SetLongitude(double dLong); + + // Set the time zone of the observer in hours. Must be in the range -12 to +13. + // Returns true if hours is valid, else false. + virtual bool SetTimeZoneHours(double dHours); + + // Set the daylight saving of the observer in minutes. Must be in the range 0 to 120. + // Returns true if minutes is valid, else false. + virtual bool SetDaylightSavingMinutes(int iMinutes); + + // Set the local date and time of the observer. The date must lie between 1800 and 2199 inclusive. + // The time is supplied as decimal hours which must be in the range 0 to 24. + // Returns true if date and time are valid, else false. + virtual bool SetLocalDateTime(int iYear, int iMonth, int iDay, double dHours); + + // Set the local Julian Day of the observer. Optional alternative to calling SetLocalDateTime(). + // Returns true if Julian Day is valid, else false. + virtual bool SetLocalJulianDay(double dLocalJulianDay); + + // Returns The azimuth of the sun in degrees. + virtual double Azimuth(void) const; + + // Returns The altitude of the sun in degrees. + virtual double Altitude(void) const; + + // Returns The Julian Day corresponding to Universal Time (UT ~ GMT). + virtual double JulianDay(void) const; + + // Returns The Julian Day corresponding to local time. + virtual double LocalJulianDay(void) const; + + // Returns The time zone of the observer in hours. + virtual double TimeZoneHours(void) const; + + // Returns The daylight saving of the observer in minutes. + virtual int DaylightSavingMinutes(void) const; + + // Gets the local date and time of the observer. + // Param year accepts the year in the range 1800 to 2199. + // Param month accepts the month in the range 1 to 12. + // Param day accepts the day in the range 1 to 31. + // Param hours accepts the hour in the range 0 to 24. + virtual void LocalDateTime(int& year, int& month, int& day, double& hours) const; + + // Helper function; returns the number of days in a month for a specific year. + static int DaysInMonth(int month, int year); + + // Helper function; converts azimuth and altitude to unit vector. + static void ConvertHorizonCoordsToSolarVector(double dAzimuth, double dAltitude, double* dVector); + + // Helper function; converts unit vector to azimuth and altitude. + static void ConvertSolarVectorToHorizonCoords(const double* dVector, double& dAzimuth, double& dAltitude); + +private: + class CImpl; + CImpl* _impl; +}; + +// Class ON_Sun represents a 'sun'. Note that generally it does not perform any astronomical calculations; +// it merely allows the programmer to get and set the various sun properties. Only the utility methods +// at the end of the class perform calculations (by using ON_SunEngine). If you want to do general sun +// calculations with OpenNURBS you should use ON_SunEngine or ON_SunEx instead of this class. + +class ON_CLASS ON_Sun { public: ON_Sun(); ON_Sun(ON_XMLNode& model_node); ON_Sun(const ON_Sun& sun); - ~ON_Sun(); + virtual ~ON_Sun(); // Returns the minimum allowed year for sun methods. static int MinYear(void); @@ -28,147 +128,216 @@ public: // Returns the maximum allowed year for sun methods. static int MaxYear(void); - const ON_Sun& operator = (const ON_Sun& sun); + virtual const ON_Sun& operator = (const ON_Sun& sun); - bool operator == (const ON_Sun& sun); - bool operator != (const ON_Sun& sun); + virtual bool operator == (const ON_Sun& sun) const; + virtual bool operator != (const ON_Sun& sun) const; // Returns true if enabling/disabling the sun is allowed, else false. - bool EnableAllowed(void) const; + virtual bool EnableAllowed(void) const; // Returns true if the sun is enabled, else false. - bool EnableOn(void) const; + virtual bool EnableOn(void) const; // Returns true if manual control of the sun position is allowed, else false. - bool ManualControlAllowed(void) const; + virtual bool ManualControlAllowed(void) const; // Returns true if manual control of the sun position is in effect, else false. - bool ManualControlOn(void) const; + virtual bool ManualControlOn(void) const; // Returns The world angle corresponding to North in degrees. // This angle is zero along the x-axis and increases anticlockwise. - double North(void) const; - - // Returns The sun's direction vector in world space, taking into account the sun's azimuth & - // altitude and the direction of north. See Azimuth(), Altitude(), North(). - // Note that this does not actually calculate the azimuth or altitude from the place and time; - // it merely returns the values that were stored in the model. - ON_3dVector Vector(void) const; + virtual double North(void) const; // Returns the azimuth of the sun in degrees. The value increases Eastwards with North as zero. - // Note: This value is not affected by the direction of north. See North() - double Azimuth(void) const; + // Note: This value is not affected by the direction of north. See North(). + virtual double Azimuth(void) const; // Returns the altitude of the sun in degrees. - double Altitude(void) const; + virtual double Altitude(void) const; // Returns the latitude of the observer. - double Latitude(void) const; + virtual double Latitude(void) const; // Returns the longitude of the observer. - double Longitude(void) const; + virtual double Longitude(void) const; // Returns the time zone of the observer in decimal hours. - double TimeZone(void) const; + virtual double TimeZone(void) const; // Returns true if daylight saving is on, else false. - bool DaylightSavingOn(void) const; + virtual bool DaylightSavingOn(void) const; // Returns the daylight saving offset of the observer in minutes. - int DaylightSavingMinutes(void) const; + virtual int DaylightSavingMinutes(void) const; // Retrieves the local date and time of the observer. - // year accepts the year in the range MinYear() to MaxYear(); - // month accepts the month in the range 1 to 12. - // day accepts the day in the range 1 to 31. - // hours accepts the time expressed as decimal hours in the range 0 to 24. + // Param year accepts the year in the range MinYear() to MaxYear(). + // Param month accepts the month in the range 1 to 12. + // Param day accepts the day in the range 1 to 31. + // Param hours accepts the time expressed as decimal hours in the range 0 to 24. // Returns The local date and time of the observer. - void LocalDateTime(int& year, int& month, int& day, double& hours) const; + virtual void LocalDateTime(int& year, int& month, int& day, double& hours) const; // Returns the intensity to be used for the sun. This is 1.0 by default. - double Intensity(void) const; + virtual double Intensity(void) const; // Returns the shadow intensity to be used for the sun. This is 1.0 by default. 0.0 turns off all shadows. - double ShadowIntensity(void) const; + virtual double ShadowIntensity(void) const; // Set whether or not enabling/disabling the sun is allowed. - void SetEnableAllowed(bool allowed); + virtual void SetEnableAllowed(bool allowed); // Set whether or not the sun is enabled. - void SetEnableOn(bool on); + virtual void SetEnableOn(bool on); // Set whether or not manual control of the sun position is allowed. - void SetManualControlAllowed(bool allowed); + virtual void SetManualControlAllowed(bool allowed); // Set whether or not manual control of the sun position is in effect. - void SetManualControlOn(bool manual); + virtual void SetManualControlOn(bool manual); // Set the azimuth corresponding to North. // north is the world angle corresponding to North in degrees in the range 0 to 360. // This angle is zero along the x-axis and increases anticlockwise. - void SetNorth(double north); - - // Set the sun's direction vector when manual control is in effect. This calculates and sets - // the sun's azimuth & altitude and takes into account the direction of north. - // See SetAzimuth(), SetAltitude(), North(). - void SetVector(const ON_3dVector& v); + virtual void SetNorth(double north); // Set the azimuth of the sun when manual control is in effect. - // - azimuth is the sun's azimuth in degrees. The value increases Eastwards with North as zero. + // Param azimuth is the sun's azimuth in degrees. The value increases Eastwards with North as zero. // Note: This value is not affected by the direction of north. - void SetAzimuth(double azimuth); + virtual void SetAzimuth(double azimuth); // Set the sun's altitude when manual control is in effect. - // - altitude is the sun's altitude in degrees in the range -90 to +90. - void SetAltitude(double altitude); + // Param altitude is the sun's altitude in degrees in the range -90 to +90. + virtual void SetAltitude(double altitude); // Set the latitude of the observer. - // - latitude is the observer's latitude in degrees in the range -90 to +90. - void SetLatitude(double latitude); + // Param latitude is the observer's latitude in degrees in the range -90 to +90. + virtual void SetLatitude(double latitude); // Set the longitude of the observer. - // - longitude is the observer's longitude in degrees in the range -180 to +180. - void SetLongitude(double longitude); + // Param longitude is the observer's longitude in degrees in the range -180 to +180. + virtual void SetLongitude(double longitude); // Set the time zone of the observer in hours, in the range -12 to +13. - void SetTimeZone(double hours); + virtual void SetTimeZone(double hours); // Set whether or not the observer is using daylight saving time. - void SetDaylightSavingOn(bool on); + virtual void SetDaylightSavingOn(bool on); // Set the daylight saving of the observer in minutes, in the range 0 to 120. - void SetDaylightSavingMinutes(int minutes); + virtual void SetDaylightSavingMinutes(int minutes); // Set the local date and time of the observer. - // - year is the year which must lie between MinYear() to MaxYear() inclusive. - // - month is the month in the range 1 to 12. - // - day is the day in the range 1 to 31. - // - hours is the time expressed as decimal hours in the range 0 to 24. + // Param year is the year which must lie between MinYear() to MaxYear() inclusive. + // Param month is the month in the range 1 to 12. + // Param day is the day in the range 1 to 31. + // Param hours is the time expressed as decimal hours in the range 0 to 24. // Returns true if successful, false if the date is out of range. - bool SetLocalDateTime(int year, int month, int day, double hours); + virtual bool SetLocalDateTime(int year, int month, int day, double hours); // Set the shadow intensity to be used for the sun. This is 1.0 by default. 0.0 turns off all shadows. - void SetShadowIntensity(double intensity); + virtual void SetShadowIntensity(double intensity); // Set the intensity to be used for the sun. This is 1.0 by default. - void SetIntensity(double intensity); + virtual void SetIntensity(double intensity); // Returns the CRC of the sun state. - ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; // Returns true if all the sun parameters are valid, or false if any value is invalid. - bool IsValid(void) const; + virtual bool IsValid(void) const; + + // Load the sun properties from a 'sun' XML node. + virtual void LoadFromXMLNode(const ON_XMLNode& node); + + // Save the sun properties to a 'sun' XML node. + virtual void SaveToXMLNode(ON_XMLNode& node) const; + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); + +public: // These utility methods are provided for convenience and perform calculations by using ON_SunEngine. // Get an ON_Light which represents the sun. Note that this does not actually calculate the sun's - // azimuth or altitude from the place and time. It merely uses the values that were stored in the model. - ON_Light Light(void) const; + // azimuth or altitude from the place and time. It merely uses the stored azimuth and altitude values. + virtual ON_Light Light(void) const; + + // Retrieves the date and time of the observer expressed in Coordinated Universal Time. This is the same + // as the local date and time adjusted for the observer's time zone and daylight saving. + // Param year accepts the year in the range MinYear() to MaxYear(). + // Param month accepts the month in the range 1 to 12. + // Param day accepts the day in the range 1 to 31. + // Param hours accepts the time expressed as decimal hours in the range 0 to 24. + // Returns The UTC date and time of the observer. + virtual void UTCDateTime(int& year, int& month, int& day, double& hours) const; + + // Set the date and time of the observer expressed in Coordinated Universal Time. This is the same + // as the local date and time adjusted for the observer's time zone and daylight saving. + // Param year is the year which must lie between MinYear() to MaxYear() inclusive. + // Param month is the month in the range 1 to 12. + // Param day is the day in the range 1 to 31. + // Param hours is the time expressed as decimal hours in the range 0 to 24. + // Returns true if successful, false if the date is out of range. + virtual bool SetUTCDateTime(int year, int month, int day, double hours); + + // Returns The sun's direction vector in world space, taking into account the sun's azimuth and + // altitude and the direction of north. Note that this does not actually calculate the azimuth or + // altitude from the place and time; it merely uses the stored azimuth and altitude values. + virtual ON_3dVector CalculateVectorFromAzimuthAndAltitude(void) const; + + // Calculates and sets the sun's azimuth and altitude taking into account the direction of north. + virtual void SetAzimuthAndAltitudeFromVector(const ON_3dVector& v); // Get a color for rendering a sun light when the sun is at a particular altitude in the sky. static ON_4fColor SunColorFromAltitude(double altitude); + // Returns true if the specified time is within the range supported by ON_Sun. + static bool IsValidDateTime(int year, int month, int day, int hour, int min, int sec); + +private: + // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + void SetXMLNode(ON_XMLNode& node) const; + private: class CImpl; - CImpl* m_impl; + CImpl* _impl; +}; + +// Class ON_SunEx is the same as ON_Sun except that it overrides Azimuth() and Altitude() to calculate +// the sun's azimuth / altitude. +class ON_CLASS ON_SunEx : public ON_Sun +{ +public: + ON_SunEx(); + ON_SunEx(ON_XMLNode& model_node); + ON_SunEx(const ON_SunEx& sun); + virtual ~ON_SunEx(); + + virtual const ON_Sun& operator = (const ON_Sun& sun) override; + virtual const ON_SunEx& operator = (const ON_SunEx& sun); + + // Returns the azimuth of the sun in the sky (in degrees) as viewed by an observer on Earth. The value + // increases Eastwards with North as zero. This value is not affected by the sun's 'north' setting. + // If manual control is in effect, the stored value is returned. Otherwise the value is computed from the + // values stored in the local date and time, latitude, longitude, time zone and daylight saving settings. + // The stored value is not modified by this method (i.e., it is truly const). + virtual double Azimuth(void) const override; + + // Returns the altitude of the sun in the sky (in degrees) as viewed by an observer on Earth. + // If manual control is in effect, the stored value is returned. Otherwise the value is computed from the + // values stored in the local date and time, latitude, longitude, time zone and daylight saving settings. + // The stored value is not modified by this method (i.e., it is truly const). + virtual double Altitude(void) const override; + +private: // For internal use only. + friend class ON_3dmRenderSettingsPrivate; + virtual void InvalidateCache(void); + +private: + class CImpl; + CImpl* _impl; }; #endif diff --git a/opennurbs_system_runtime.h b/opennurbs_system_runtime.h index a3c43796..25e1d403 100644 --- a/opennurbs_system_runtime.h +++ b/opennurbs_system_runtime.h @@ -144,24 +144,9 @@ #elif defined(ON_RUNTIME_ANDROID) // Android is Linux #define ON_RUNTIME_LINUX - -#if defined(__x86_64__) || defined(__LP64__) || defined(__ppc64__) -#define ON_64BIT_RUNTIME -#else -#define ON_32BIT_RUNTIME #endif -#if !defined(ON_SIZEOF_WCHAR_T) -#define ON_SIZEOF_WCHAR_T 4 -#endif - -// It looks like all android compiles are little endian. -// If there is a better test, please add it here -#if !defined(ON_LITTLE_ENDIAN) -#define ON_LITTLE_ENDIAN -#endif - -#elif defined(ON_RUNTIME_LINUX) +#if defined(ON_RUNTIME_LINUX) #if !defined(ON_SIZEOF_WCHAR_T) #define ON_SIZEOF_WCHAR_T 4 diff --git a/opennurbs_unicode.cpp b/opennurbs_unicode.cpp index fc3b6ec9..eaed98f1 100644 --- a/opennurbs_unicode.cpp +++ b/opennurbs_unicode.cpp @@ -118,10 +118,55 @@ int ON_IsUnicodeSpaceCodePoint( ON_UnicodeCodePoint::ON_Space == u || ON_UnicodeCodePoint::ON_NoBreakSpace == u || ON_UnicodeCodePoint::ON_NarrowNoBreakSpace == u - || ON_UnicodeCodePoint::ON_ZeroWidthSpace == u + || (u >= ON_UnicodeCodePoint::ON_EnQuad && u <= ON_UnicodeCodePoint::ON_ZeroWidthSpace) ; } +int ON_IsUnicodeSpaceOrControlCodePoint( + ON__UINT32 u +) +{ + // Additional space code points may be added in the future. + // The goal is to detect code points that should be trimmed + // when isolating text tokens in a string. + + if (u > 0 && u <= ON_UnicodeCodePoint::ON_Space) + return true; + + if (u < ON_UnicodeCodePoint::ON_Delete) + return false; + + if (u == ON_UnicodeCodePoint::ON_Delete) + return true; + + if (ON_IsUnicodeSpaceCodePoint(u)) + return true; + + if (ON_IsUnicodeC1ControlCodePoint(u)) + return true; + + if (u < 0x2000) + return false; + + // Bidirectional text control + if (u >= 0x200E && u <= 0x200F) + return true; + + // Separators, Format characters, and NNBSP + if (u >= 0x2028 && u <= 0x202F) + return true; + + // Bidirectional text control + if (u >= 0x202A && u <= 0x202E) + return true; + + // Bidirectional text control + if (u >= 0x2066 && u <= 0x2069) + return true; + + return false; +} + int ON_IsUnicodeC1ControlCodePoint( ON__UINT32 u ) diff --git a/opennurbs_unicode.h b/opennurbs_unicode.h index a42e7977..23a1af29 100644 --- a/opennurbs_unicode.h +++ b/opennurbs_unicode.h @@ -72,6 +72,9 @@ enum ON_UnicodeCodePoint /// SPACE U+0020 ON_Space = 0x20, + /// DELETE U+007F + ON_Delete = 0x7F, + /// NO-BREAK SPACE (NBSP) U+00A0 ON_NoBreakSpace = 0x00A0, @@ -99,9 +102,6 @@ enum ON_UnicodeCodePoint /// 6 per EM SPACE U+2006 (1/6 EM SPACE) ON_SixPerEmSpace = 0x2006, - /// ZERO WIDTH SPACE (ZWSP) U+200B - ON_ZeroWidthSpace = 0x200B, - /// FIGURE SPACE U+2007 Average digit width. ON_FigureSpace = 0x2007, @@ -114,6 +114,9 @@ enum ON_UnicodeCodePoint /// HAIR SPACE U+200A (Narrower than THIN SPACE) ON_HairSpace = 0x200A, + /// ZERO WIDTH SPACE (ZWSP) U+200B + ON_ZeroWidthSpace = 0x200B, + /// MEDIUM MATHEMATICAL SPACE U+2025 (about 2/9 EM SPACE) ON_MediumMathematicalSpace = 0x205F, @@ -440,23 +443,42 @@ int ON_IsValidUnicodeCodePoint( /* Returns: - True if u is one of ON_UnicodeCodePoint::ON_Space, ON_UnicodeCodePoint::ON_NoBreakSpace, - ON_UnicodeCodePoint::ON_NarrowNoBreakSpace, or ON_UnicodeCodePoint::ON_ZeroWidthSpace. + True if u is ON_UnicodeCodePoint::ON_Space, or a space character U+2000 - u+200B. Remarks: - Additional space code points may be added in the future. The goal is to - detect code points that separate words. + Additional space code points may be added in the future. + The goal is to detect code points that separate words. */ ON_DECL int ON_IsUnicodeSpaceCodePoint( ON__UINT32 u ); + /* Returns: - True if u >= 0x80 and y <= 0x9F. + True if u is some type of space or control code point. Examples include + C0 code points ( <= u+001F ), ON_UnicodeCodePoint::ON_Space, ON_UnicodeCodePoint::ON_Delete, + ON_UnicodeCodePoint::ON_NoBreakSpace, ON_UnicodeCodePoint::ON_NarrowNoBreakSpace, + ON_UnicodeCodePoint::ON_ZeroWidthSpace, and C1 code points. Remarks: - Additional space code points may be added in the future. The goal is to - detect code points that separate words. + Additional space code points may be added in the future. + The goal is to detect code points that should be used as + separators when isolating text tokens in a string. + + Ligatures (ZWNJ U+200C, ZWJ U+200D) return false because they control formatting in a token. +*/ +ON_DECL +int ON_IsUnicodeSpaceOrControlCodePoint( + ON__UINT32 u +); + + +/* +Returns: + True if u >= U+0080 and u <= U+009F. +Remarks: + Additional space code points may be added in the future. + The goal is to detect code points that separate words. */ ON_DECL int ON_IsUnicodeC1ControlCodePoint( diff --git a/opennurbs_wstring.cpp b/opennurbs_wstring.cpp index 41f30eee..453a1aec 100644 --- a/opennurbs_wstring.cpp +++ b/opennurbs_wstring.cpp @@ -2456,7 +2456,9 @@ void ON_wString::TrimLeft(const wchar_t* s) { for (i = 0; 0 != (c = m_s[i]); i++) { - if ( c < 0 || c > ON_wString::Space ) + // All postive code points in ON_IsUnicodeSpaceOrControlCodePoint() + // are UTF-16 singltons so it's ok to cast c as a Unicode code point. + if ( c < 0 || 0 == ON_IsUnicodeSpaceOrControlCodePoint((ON__UINT32)c) ) break; } } @@ -2496,7 +2498,9 @@ void ON_wString::TrimRight(const wchar_t* s) { for (i--; i >= 0 && 0 != (c = m_s[i]); i--) { - if ( c < 0 || c > ON_wString::Space ) + // All postive code points in ON_IsUnicodeSpaceOrControlCodePoint() + // are UTF-16 singltons so it's ok to cast c as a Unicode code point. + if ( c < 0 || 0 == ON_IsUnicodeSpaceOrControlCodePoint((ON__UINT32)c) ) break; } } diff --git a/opennurbs_xform.h b/opennurbs_xform.h index 34245efc..fd055cdb 100644 --- a/opennurbs_xform.h +++ b/opennurbs_xform.h @@ -1793,7 +1793,7 @@ public: bool IsNotSet() const; ON_3dPoint m_point = ON_3dPoint::UnsetPoint; - double m_t[4]; // parameters (unused values are set to ON_UNSET_VALUE) + double m_t[4] = {}; // parameters (When m_point is set, unused values are set to ON_UNSET_VALUE.) double m_depth = ON_UNSET_VALUE; // larger values are in front of smaller values. double m_distance = 1.0e300; // smaller values are closer to pick ray. }; diff --git a/opennurbs_xml.cpp b/opennurbs_xml.cpp index f3d8ea48..f7605381 100644 --- a/opennurbs_xml.cpp +++ b/opennurbs_xml.cpp @@ -26,8 +26,8 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif -#pragma warning (push) -#pragma warning (disable:4127) +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4127) ON__INT64 Integerize(float dirty) { @@ -215,15 +215,6 @@ static bool GetTimeComponents(const ON_wString& sTime, int& y, int& m, int& d, i return true; } -//static bool IsValidTime(const ON_wString& sTime) -//{ -// int y = 0, m = 0, d = 0, h = 0, n = 0, s = 0; -// if (!GetTimeComponents(sTime, y, m, d, h, n, s)) -// return false; -// -// return true; -//} - static ON_wString TimeToString(time_t time) { tm stm = { 0 }; @@ -818,8 +809,6 @@ void ON_XMLVariant::SetValue(const ON_Xform& xform) bool ON_XMLVariant::AsBool(void) const { - DoAutoTyping(Types::Bool); - switch (m_impl->m_type) { case Types::Bool: return m_impl->m_bVal; @@ -839,8 +828,6 @@ bool ON_XMLVariant::AsBool(void) const int ON_XMLVariant::AsInteger(void) const { - DoAutoTyping(Types::Integer); - switch (m_impl->m_type) { case Types::Bool: return m_impl->m_bVal ? 1 : 0; @@ -860,8 +847,6 @@ int ON_XMLVariant::AsInteger(void) const double ON_XMLVariant::AsDouble(void) const { - DoAutoTyping(Types::Double); - switch (m_impl->m_type) { case Types::Bool: return m_impl->m_bVal ? 1.0 : 0.0; @@ -877,8 +862,6 @@ double ON_XMLVariant::AsDouble(void) const float ON_XMLVariant::AsFloat(void) const { - DoAutoTyping(Types::Float); - switch (m_impl->m_type) { case Types::Bool: return m_impl->m_bVal ? 1.0f : 0.0f; @@ -986,8 +969,6 @@ ON_Xform ON_XMLVariant::AsXform(void) const ON_4fColor ON_XMLVariant::AsColor(void) const { - DoAutoTyping(Types::DoubleColor4); - ON_4fColor col(ON_Color(0, 0, 0, 0)); switch (m_impl->m_type) @@ -1008,8 +989,6 @@ ON_4fColor ON_XMLVariant::AsColor(void) const ON_UUID ON_XMLVariant::AsUuid(void) const { - DoAutoTyping(Types::Uuid); - switch (m_impl->m_type) { case Types::String: @@ -1025,8 +1004,6 @@ ON_UUID ON_XMLVariant::AsUuid(void) const time_t ON_XMLVariant::AsTime(void) const { - DoAutoTyping(Types::Time); - switch (m_impl->m_type) { case Types::String: @@ -1041,8 +1018,6 @@ time_t ON_XMLVariant::AsTime(void) const ON_Buffer ON_XMLVariant::AsBuffer(void) const { - DoAutoTyping(Types::Buffer); - ON_Buffer buf; #if defined(ON_RUNTIME_APPLE) #pragma clang diagnostic push @@ -1090,8 +1065,6 @@ void* ON_XMLVariant::AsBuffer(size_t& size_out) const ON_wString ON_XMLVariant::AsString(void) const { - DoAutoTyping(Types::String); - switch (m_impl->m_type) { case Types::Integer: @@ -1448,110 +1421,6 @@ void ON_XMLVariant::Format(ON_wString& sOut) const sType.Array(), AsString().Array(), StringFromUnits(Units()), DataCRC(0)); } -#if 1 -void AutoTypeVariant(ON_XMLVariant&) { } -void ON_XMLVariant::DoAutoTyping(Types) const { } -#else -void ON_XMLVariant::DoAutoTyping(Types type) const -{ - // Only do automatic typing if the flag is set. - if (!m_impl->m_bTypePending) - return; - - m_impl->m_bTypePending = false; // Must be first. - - // This is only meant to work if the value is a string - i.e, it has just been read out of the XML stream. - if (m_impl->m_type != Types::String) - return; - - // This requires a pretty big const hack - since we are changing the variant - // pretty dramatically, but we aren't actually changing its value. - - auto* pThis = const_cast(this); - switch (type) - { - case Types::String: break; - case Types::Bool: pThis->SetValue(AsBool()); break; - case Types::Integer: pThis->SetValue(AsInteger()); break; - case Types::Float: pThis->SetValue(AsFloat()); break; - case Types::Double: pThis->SetValue(AsDouble()); break; - case Types::Uuid: pThis->SetValue(AsUuid()); break; - case Types::Time: pThis->SetValue(AsTime()); break; - case Types::Buffer: pThis->SetValue(AsBuffer()); break; - case Types::DoubleColor4: pThis->SetValue(AsColor()); break; - case Types::DoubleArray2: pThis->SetValue(As2dPoint(), ArrayTypes::Array2); break; - case Types::DoubleArray3: pThis->SetValue(As3dPoint(), ArrayTypes::Array3); break; - case Types::DoubleArray4: pThis->SetValue(As4dPoint(), ArrayTypes::Array4); break; - case Types::Matrix: pThis->SetValue(AsXform(), ArrayTypes::Array16); break; - } - - // Otherwise we assume the conversion is not supported and just go on with life. -} - -void AutoTypeVariant(ON_XMLVariant& v) -{ - // Used by the XML reader to try to invent sensible types for variants read in from the stream. - if (v.Type() != ON_XMLVariant::Types::String) - return; // The variant already has a type. - - ON_wString s = v.AsString(); - v.SetTypePendingFlag(true); - - if ((s == L"true") || (s == L"false")) - { - v.AsBool(); - } - else - if (s.StartsWithNoCase(wszBase64Prefix)) - { - v.AsBuffer(); - } - else - if (IsValidTime(s)) - { - v.AsTime(); - } - else - if (s.IsValidIntegerNumber()) - { - v.AsInteger(); - } - else - if (s.IsValidMatrix()) - { - v.AsMatrix(); - } - else - if (s.IsValid4dPoint()) - { - v.As4dPoint(); - } - else - if (s.IsValid3dPoint()) - { - v.As3dPoint(); - } - else - if (s.IsValid2dPoint()) - { - v.As2dPoint(); - } - else - if (s.IsValidRealNumber()) - { - v.AsDouble(); - } - else - { - const auto uuid = ON_UuidFromString(s); - if (ON_UuidIsNotNil(uuid)) - { - v.AsUuid(); - } - } -} -#endif - // ON_XMLProperty class CPropertyData final @@ -1751,6 +1620,11 @@ ON_XMLProperty* ON_XMLProperty::Next(void) const return m_impl->m_pNext; } +void* ON_XMLProperty::EVF(const wchar_t*, void*) +{ + return nullptr; +} + // ON_XMLSegmentedStream class ON_XMLSegmentedStream::CImpl @@ -1833,7 +1707,7 @@ public: bool RemoveProperty(const wchar_t* name); ON_XMLNode* DetachChild(ON_XMLNode& child); void RemoveAllProperties(void); - const ON_XMLNode& TopmostParent(void) const; + const ON_XMLNode& TopLevel(void) const; ON_XMLNode* AttachChildNode(ON_XMLNode* pNode); void RemoveAllChildren(void); const ON_wString& TagName(void) const; @@ -1898,26 +1772,7 @@ static const wchar_t* StringFromPropType(ON_XMLVariant::Types vt) } } -static ON_XMLVariant::Types PropTypeFromString(const ON_wString& s) -{ - if (L"int" == s) return ON_XMLVariant::Types::Integer; - if (L"float" == s) return ON_XMLVariant::Types::Float; - if (L"double" == s) return ON_XMLVariant::Types::Double; - if (L"string" == s) return ON_XMLVariant::Types::String; - if (L"bool" == s) return ON_XMLVariant::Types::Bool; - if (L"matrix" == s) return ON_XMLVariant::Types::Matrix; - if (L"uuid" == s) return ON_XMLVariant::Types::Uuid; - if (L"time" == s) return ON_XMLVariant::Types::Time; - if (L"buffer" == s) return ON_XMLVariant::Types::Buffer; - if (L"color" == s) return ON_XMLVariant::Types::DoubleColor4; - if (L"2da" == s) return ON_XMLVariant::Types::DoubleArray2; - if (L"3da" == s) return ON_XMLVariant::Types::DoubleArray3; - if (L"4da" == s) return ON_XMLVariant::Types::DoubleArray4; - - return ON_XMLVariant::Types::Null; -} - -ON__UINT32 ON_XMLNode::CImpl::DataCRC(ON__UINT32 crc, int depth) const +ON__UINT32 ON_XMLNode::CImpl::DataCRC(ON__UINT32 crc, int depth) const // [MARKER] This is probably wrong. { crc = TagName().DataCRCLower(crc); @@ -1970,7 +1825,7 @@ void ON_XMLNode::CImpl::SetTagName(const wchar_t* name) m_name.TrimRight(); } -const ON_XMLNode& ON_XMLNode::CImpl::TopmostParent(void) const +const ON_XMLNode& ON_XMLNode::CImpl::TopLevel(void) const { std::lock_guard lg(m_mutex); @@ -2181,28 +2036,28 @@ ON_XMLProperty* ON_XMLNode::CImpl::AddProperty(const ON_XMLProperty& prop) { std::lock_guard lg(m_mutex); - auto* pProp = new ON_XMLProperty(prop); - pProp->Impl().m_owner = &m_node; - pProp->Impl().m_pNext = m_first_property; - m_first_property = pProp; + auto* prop_copy = new ON_XMLProperty(prop); + prop_copy->Impl().m_owner = &m_node; + prop_copy->Impl().m_pNext = m_first_property; + m_first_property = prop_copy; - return pProp; + return prop_copy; } -ON_XMLProperty* ON_XMLNode::CImpl::AttachProperty(ON_XMLProperty* pProp) +ON_XMLProperty* ON_XMLNode::CImpl::AttachProperty(ON_XMLProperty* prop) { - if (nullptr == pProp) + if (nullptr == prop) return nullptr; std::lock_guard lg(m_mutex); - RemoveProperty(pProp->Name()); + RemoveProperty(prop->Name()); - pProp->Impl().m_pNext = m_first_property; - m_first_property = pProp; + prop->Impl().m_pNext = m_first_property; + m_first_property = prop; m_first_property->Impl().m_owner = &m_node; - return pProp; + return prop; } bool ON_XMLNode::CImpl::RemoveProperty(const wchar_t* name) @@ -2569,7 +2424,7 @@ const ON_XMLNode& ON_XMLNode::operator = (const ON_XMLNode& src) auto pi = src.GetPropertyIterator(); while (nullptr != (pProperty = pi.GetNextProperty())) { - m_impl->AddProperty(*pProperty); // This does a copy anyway - no need to call the copy constructor + m_impl->AddProperty(*pProperty); } // Copy in the children. @@ -2583,6 +2438,40 @@ const ON_XMLNode& ON_XMLNode::operator = (const ON_XMLNode& src) return *this; } +bool ON_XMLNode::operator == (const ON_XMLNode& other) const +{ + ON_XMLProperty* prop_other = nullptr; + auto pio = other.GetPropertyIterator(); + while (nullptr != (prop_other = pio.GetNextProperty())) + { + auto* prop_this = GetNamedProperty(prop_other->Name()); + if (nullptr == prop_this) + return false; + + if (!(prop_this->GetValue() == prop_other->GetValue())) + return false; + } + + ON_XMLNode* child_other = nullptr; + auto cio = other.GetChildIterator(); + while (nullptr != (child_other = cio.GetNextChild())) + { + auto* child_this = GetNamedChild(child_other->TagName()); + if (nullptr == child_this) + return false; + + if (!(*child_this == *child_other)) + return false; + } + + return true; +} + +bool ON_XMLNode::operator != (const ON_XMLNode& node) const +{ + return !(operator == (node)); +} + bool ON_XMLNode::MergeFrom(const ON_XMLNode& src) { std::lock_guard lg1(m_impl->m_mutex); @@ -2598,7 +2487,7 @@ bool ON_XMLNode::MergeFrom(const ON_XMLNode& src) while (nullptr != (pProperty = pi.GetNextProperty())) { // Replaces any that are already there. - AddProperty(*pProperty); + SetProperty(*pProperty); } // Copy in the children. @@ -2642,7 +2531,7 @@ void ON_XMLNode::RemoveAllProperties(void) m_impl->AddEmptyDefaultProperty(); } -ON_XMLProperty* ON_XMLNode::AddProperty(const ON_XMLProperty& prop) +ON_XMLProperty* ON_XMLNode::SetProperty(const ON_XMLProperty& prop) { std::lock_guard lg(m_impl->m_mutex); @@ -2749,14 +2638,14 @@ bool ON_XMLNode::IsValidXMLName(const wchar_t* wszTagName) // Static. return true; } -ON_XMLNode* ON_XMLNode::GetParent(void) const +ON_XMLNode* ON_XMLNode::Parent(void) const { return m_impl->m_parent; } -const ON_XMLNode& ON_XMLNode::TopmostParent(void) const +const ON_XMLNode& ON_XMLNode::TopLevel(void) const { - return m_impl->TopmostParent(); + return m_impl->TopLevel(); } ON_XMLNode* ON_XMLNode::AttachChildNode(ON_XMLNode* pNode) @@ -2764,11 +2653,6 @@ ON_XMLNode* ON_XMLNode::AttachChildNode(ON_XMLNode* pNode) return m_impl->AttachChildNode(pNode); } -ON_XMLNode* ON_XMLNode::AddChildNode(ON_XMLNode* pNode) -{ - return AttachChildNode(pNode); -} - ON_XMLProperty* ON_XMLNode::AttachProperty(ON_XMLProperty* pProp) { return m_impl->AttachProperty(pProp); @@ -2783,7 +2667,7 @@ bool ON_XMLNode::RemoveProperty(const wchar_t* wszPropertyName) void ON_XMLNode::Remove(void) { - auto* pParent = GetParent(); + auto* pParent = Parent(); if (nullptr != pParent) { pParent->RemoveChild(this); @@ -2875,7 +2759,7 @@ static bool PrependNodeToStringAndRecurseParents(const ON_XMLNode* pNode, ON_wSt if (nullptr == pNode) return false; - auto* pParent = pNode->GetParent(); + auto* pParent = pNode->Parent(); if (nullptr == pParent) return false; @@ -2892,7 +2776,7 @@ ON_wString ON_XMLNode::GetPathFromRoot(void) const std::lock_guard lg(m_impl->m_mutex); ON_wString sPath = TagName(); - PrependNodeToStringAndRecurseParents(GetParent(), sPath); + PrependNodeToStringAndRecurseParents(Parent(), sPath); return sPath; } @@ -2988,11 +2872,8 @@ bool ON_XMLNode::CImpl::GetPropertiesFromTag(const ON_wString& sTag) const int pos2 = m_bAutoTypePropValue ? sPropertyValue.Find(L':') : -1; if (pos2 > 0) { - // The type is encoded in the value. - const auto type = PropTypeFromString(sPropertyValue.Left(pos2)); vValue = sPropertyValue.Mid(pos2 + 1); vValue.SetTypePendingFlag(true); - vValue.DoAutoTyping(type); } else { @@ -3551,12 +3432,9 @@ ON__UINT32 ON_XMLNode::ReadFromStream(const wchar_t* stream, bool bWarningsAsErr const int pos2 = CImpl::m_bAutoTypePropValue ? sDefaultProperty.Find(L":") : -1; if (pos2 > 0) { - // The type is encoded in the value. - const auto type = PropTypeFromString(sDefaultProperty.Left(pos2)); auto& v = pProp->GetNonConstValue(); v = sDefaultProperty.Mid(pos2 + 1); v.SetTypePendingFlag(true); - v.DoAutoTyping(type); } else { @@ -3566,7 +3444,7 @@ ON__UINT32 ON_XMLNode::ReadFromStream(const wchar_t* stream, bool bWarningsAsErr } } - TopmostParent().OnNodeReadFromStream(this); + TopLevel().OnNodeReadFromStream(this); while ((*pBuffer == L'\r') || (*pBuffer == L'\n')) pBuffer++; @@ -3746,6 +3624,11 @@ ON_XMLProperty* ON_XMLNode::PropertyIterator::GetNextProperty(void) return pProp; } +void* ON_XMLNode::PropertyIterator::EVF(const wchar_t*, void*) +{ + return nullptr; +} + // ON_XMLRootNode // TODO: Somehow I managed to port the non-rc version of the root node. @@ -3765,6 +3648,15 @@ ON_XMLRootNode::ON_XMLRootNode() m_impl = new (m_Impl) CImpl; IMPL_CHECK; } +ON_XMLRootNode::ON_XMLRootNode(const ON_XMLNode& src) + : + ON_XMLNode(sXMLRootNodeName) +{ + m_impl = new (m_Impl) CImpl; IMPL_CHECK; + + *this = src; +} + ON_XMLRootNode::ON_XMLRootNode(const ON_XMLRootNode& src) : ON_XMLNode(sXMLRootNodeName) @@ -3780,6 +3672,13 @@ ON_XMLRootNode::~ON_XMLRootNode() m_impl = nullptr; } +const ON_XMLRootNode& ON_XMLRootNode::operator = (const ON_XMLNode& src) +{ + *static_cast(this) = src; + + return *this; +} + const ON_XMLRootNode& ON_XMLRootNode::operator = (const ON_XMLRootNode& src) { *static_cast(this) = src; @@ -3984,11 +3883,6 @@ int ON_XMLUserData::Version(void) const return 2; } -void ON_XMLUserData::_Dump(const wchar_t* wszFileName) const -{ - m_impl->m_XMLRoot.WriteToFile(wszFileName); -} - ON_XMLProperty* ON_XMLUserData::InternalProperty(const wchar_t* wszXMLPath, const wchar_t* wszPropertyName) const { const auto* pNode = m_impl->m_XMLRoot.NodeForRead().GetNodeAtPath(wszXMLPath); @@ -4092,6 +3986,11 @@ bool ON_XMLUserData::Write(ON_BinaryArchive& archive) const return true; } +void ON_XMLUserData::_Dump(const wchar_t* wszFileName) const +{ + m_impl->m_XMLRoot.WriteToFile(wszFileName); +} + void* ON_XMLUserData::EVF(const wchar_t*, void*) { return nullptr; @@ -4186,6 +4085,15 @@ ON_XMLNode* ON_XMLParameters::SetParam(const wchar_t* wszParamName, const ON_XML return SetParamNode(m_impl->m_node, wszParamName, value); } +ON_XMLVariant ON_XMLParameters::GetParam(const wchar_t* param_name, const ON_XMLVariant& default_value) const +{ + ON_XMLVariant value; + if (GetParam(param_name, value)) + return value; + + return default_value; +} + ON_XMLNode* ON_XMLParameters::SetParamNode(ON_XMLNode& node, const wchar_t* wszParamName, const ON_XMLVariant& vValue) { auto* pChildNode = ObtainChildNodeForWrite(node, wszParamName); @@ -4216,7 +4124,7 @@ ON_XMLNode* ON_XMLParameters::SetParamNode(ON_XMLNode& node, const wchar_t* wszP // Default property is the actual value. ON_XMLProperty prop; prop.SetValue(vValue); - pChildNode->AddProperty(prop); + pChildNode->SetProperty(prop); // Set units (if any). if (ON::LengthUnitSystem::None != vValue.Units()) @@ -4224,7 +4132,7 @@ ON_XMLNode* ON_XMLParameters::SetParamNode(ON_XMLNode& node, const wchar_t* wszP prop.SetName(L"units"); const auto* wsz = StringFromUnits(vValue.Units()); prop.SetValue(wsz); - pChildNode->AddProperty(prop); + pChildNode->SetProperty(prop); } if (m_impl->m_bWriteTypeProperty) @@ -4232,12 +4140,17 @@ ON_XMLNode* ON_XMLParameters::SetParamNode(ON_XMLNode& node, const wchar_t* wszP // Set type. prop.SetName(L"type"); prop.SetValue(wszType); - pChildNode->AddProperty(prop); + pChildNode->SetProperty(prop); } return pChildNode; } +ON_XMLNode& ON_XMLParameters::Node(void) +{ + return m_impl->m_node; +} + const ON_XMLNode& ON_XMLParameters::Node(void) const { return m_impl->m_node; @@ -4474,7 +4387,7 @@ ON_XMLNode* ON_XMLParametersV8::ObtainChildNodeForWrite(ON_XMLNode& node, const ON_XMLProperty prop; prop.SetName(ON_NAME); prop.SetValue(wszParamName); - pChildNode->AddProperty(prop); + pChildNode->SetProperty(prop); } return pChildNode; @@ -4515,6 +4428,9 @@ bool ON_XMLParametersV8::GetParam(const wchar_t* wszParamName, ON_XMLVariant& vV static const ON_UUID uuidUniversalRenderEngine = { 0x99999999, 0x9999, 0x9999, { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99 } }; +ON_UUID ON_UuidDefaultMaterialInstance = { 0xdefadefa, 0xdefa, 0xdefa, { 0xde, 0xfa, 0xde, 0xfa, 0xde, 0xfa, 0xde, 0xfa } }; +ON_UUID ON_UuidDefaultEnvironmentInstance = { 0xdefaeeee, 0xdefa, 0xeeee, { 0xde, 0xfa, 0xee, 0xee, 0xde, 0xfa, 0xee, 0xee } }; + ON_OBJECT_IMPLEMENT(ON_RdkUserData, ON_UserData, "AFA82772-1525-43dd-A63C-C84AC5806911"); static ON_RdkUserData::ReadCallback g_RdkUserDataReadCallback = nullptr; @@ -4977,10 +4893,10 @@ bool ON_RunXMLTests(const wchar_t* test_folder) const auto* szPropName2 = L"time"; ON_XMLNode node(L"parent"); ON_XMLProperty prop1(szPropName1, 25); - node.AddProperty(prop1); + node.SetProperty(prop1); const auto time = time_t(617283945); ON_XMLProperty prop2(szPropName2, time); - node.AddProperty(prop2); + node.SetProperty(prop2); auto s = node.String(); s.RemoveWhiteSpace(); Validate(s == L""); @@ -5000,7 +4916,7 @@ bool ON_RunXMLTests(const wchar_t* test_folder) s = node.String(); s.RemoveWhiteSpace(); Validate(s == L""); - node.AddProperty(prop1); + node.SetProperty(prop1); pProp = node.GetNamedProperty(szPropName1); Validate(pProp != nullptr); @@ -5065,10 +4981,10 @@ bool ON_RunXMLTests(const wchar_t* test_folder) const auto* szPropName2 = L"time"; ON_XMLRootNode root; ON_XMLProperty prop1(szPropName1, 25); - root.AddProperty(prop1); + root.SetProperty(prop1); const auto time = time_t(617283945); ON_XMLProperty prop2(szPropName2, time); - root.AddProperty(prop2); + root.SetProperty(prop2); auto s = root.String(); s.RemoveWhiteSpace(); Validate(s == L""); @@ -5152,17 +5068,17 @@ bool ON_RunXMLTests(const wchar_t* test_folder) return g_bXMLTestsOK; } -#pragma warning (pop) +#pragma ON_PRAGMA_WARNING_POP //------------------------------------------------------------------------------------------------------------------- -ON_UUID uuidPostEffect_ToneMapper_Clamp = { 0xacb8d258, 0xc1d6, 0x499d, { 0xaa, 0x23, 0x02, 0xdc, 0xde, 0xa2, 0xb0, 0xa2 } }; -ON_UUID uuidPostEffect_Gamma = { 0x84c0798d, 0xc43a, 0x4402, { 0x88, 0x91, 0xe0, 0xc8, 0x08, 0x8e, 0x67, 0xca } }; -ON_UUID chanRGBA = { 0x453a9a1c, 0x9307, 0x4976, { 0xb2, 0x82, 0x4e, 0xad, 0x4d, 0x53, 0x98, 0x79 } }; -ON_UUID chanDistanceFromCamera = { 0xb752ce0b, 0xc219, 0x4bdd, { 0xb1, 0x34, 0x26, 0x42, 0x5e, 0x1c, 0x43, 0x31 } }; -ON_UUID uuidRenderSettingsPreset_Studio = { 0x5898cc05, 0x4202, 0x4dfb, { 0x83, 0xfe, 0x8f, 0xa8, 0x8f, 0x91, 0xc7, 0xd6 } }; -ON_UUID uuidRenderSettingsPreset_Custom = { 0xc89a74fb, 0x1451, 0x4a9b, { 0xb8, 0x7d, 0xe3, 0x0f, 0xf3, 0x51, 0x0f, 0x96 } }; -ON_UUID uuidRenderSettingsPreset_Exterior = { 0x1346FE79, 0xBF49, 0x4BB6, { 0x86, 0xF4, 0xF2, 0xC2, 0x81, 0xD1, 0xD5, 0x5A } }; -ON_UUID uuidRenderSettingsPreset_Interior = { 0x14A1D7E9, 0xC75D, 0x464D, { 0xBB, 0x81, 0x38, 0x1C, 0xA2, 0xF1, 0xC9, 0x58 } }; +ON_UUID uuidPostEffect_ToneMapper_Clamp = { 0xacb8d258, 0xc1d6, 0x499d, { 0xaa, 0x23, 0x02, 0xdc, 0xde, 0xa2, 0xb0, 0xa2 } }; +ON_UUID uuidPostEffect_Gamma = { 0x84c0798d, 0xc43a, 0x4402, { 0x88, 0x91, 0xe0, 0xc8, 0x08, 0x8e, 0x67, 0xca } }; +ON_UUID chanRGBA = { 0x453a9a1c, 0x9307, 0x4976, { 0xb2, 0x82, 0x4e, 0xad, 0x4d, 0x53, 0x98, 0x79 } }; +ON_UUID chanDistanceFromCamera = { 0xb752ce0b, 0xc219, 0x4bdd, { 0xb1, 0x34, 0x26, 0x42, 0x5e, 0x1c, 0x43, 0x31 } }; +ON_UUID uuidRenderPreset_Studio = { 0x5898cc05, 0x4202, 0x4dfb, { 0x83, 0xfe, 0x8f, 0xa8, 0x8f, 0x91, 0xc7, 0xd6 } }; +ON_UUID uuidRenderPreset_Custom = { 0xc89a74fb, 0x1451, 0x4a9b, { 0xb8, 0x7d, 0xe3, 0x0f, 0xf3, 0x51, 0x0f, 0x96 } }; +ON_UUID uuidRenderPreset_Exterior = { 0x1346FE79, 0xBF49, 0x4BB6, { 0x86, 0xF4, 0xF2, 0xC2, 0x81, 0xD1, 0xD5, 0x5A } }; +ON_UUID uuidRenderPreset_Interior = { 0x14A1D7E9, 0xC75D, 0x464D, { 0xBB, 0x81, 0x38, 0x1C, 0xA2, 0xF1, 0xC9, 0x58 } }; //------------------------------------------------------------------------------------------------------------------- @@ -5204,7 +5120,7 @@ void ON_RdkDocumentDefaults::CreateXML(void) { if (ValueSets::All == _vs) { - Create(doc, ON_RDK_CURRENT_CONTENT).CreateNodeAtPath(ON_RDK_ENVIRONMENT); + Create(doc, ON_RDK_CURRENT_CONTENT).CreateNodeAtPath(ON_RDK_BACKGROUND_ENVIRONMENT); Create(doc, ON_RDK_DEFAULT_CONTENT_SECTION); } @@ -5244,13 +5160,8 @@ void ON_RdkDocumentDefaults::CreateXML(void) // 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_TONE_MAPPING); Create(peps, ON_RDK_PEP_TYPE_LATE); } } @@ -5275,15 +5186,15 @@ void ON_RdkDocumentDefaults::CreateXML(void) // 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, (_major_version < 6) ? 1.0f : 2.2f); - p.SetParam(ON_RDK_USE_POST_PROCESS_GAMMA, true); - p.SetParam(ON_RDK_USE_LINEAR_WORKFLOW, (_major_version < 6) ? false : true); + p.SetParam(ON_RDK_DITHERING_ON, false); + p.SetParam(ON_RDK_DITHERING_METHOD, ON_RDK_DITHERING_METHOD_FLOYD_STEINBERG); p.SetParam(ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT, ON_nil_uuid); p.SetParam(ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT_ON, (_major_version < 6) ? false : true); - p.SetParam(ON_RDK_CURRENT_PRESET, (_major_version < 8) ? uuidRenderSettingsPreset_Custom - : uuidRenderSettingsPreset_Studio); + p.SetParam(ON_RDK_CURRENT_RENDER_PRESET, (_major_version < 8) ? uuidRenderPreset_Custom + : uuidRenderPreset_Studio); + p.SetParam(ON_RDK_POST_PROCESS_GAMMA_ON, true); + p.SetParam(ON_RDK_POST_PROCESS_GAMMA, (_major_version < 6) ? 1.0f : 2.2f); + p.SetParam(ON_RDK_PRE_PROCESS_GAMMA_ON, (_major_version < 6) ? false : true); } else { @@ -5296,37 +5207,44 @@ void ON_RdkDocumentDefaults::CreateXML(void) if (_major_version < 8) { - p.SetParam(ON_RDK_CURRENT_PRESET, uuidRenderSettingsPreset_Custom); + p.SetParam(ON_RDK_CURRENT_RENDER_PRESET, uuidRenderPreset_Custom); } } } if (ValueSets::All == _vs) { - // Sun. + // Sun and Skylight. auto& sun = Create(settings, ON_RDK_SUN); { ON_XMLParameters p(sun); + p.SetParam(ON_RDK_SUN_ENABLE_ALLOWED, true); 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_MANUAL_CONTROL_ON, false); + p.SetParam(ON_RDK_SUN_INTENSITY, 1.0); + p.SetParam(ON_RDK_SUN_SHADOW_INTENSITY, 1.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); + + ON_SunEngine e; + e.SetLocalDateTime(2000, 1, 1, 12.0); + int y = 0, m = 0, d = 0; double h = 0.0; + e.LocalDateTime(y, m, d, h); + p.SetParam(ON_RDK_SUN_DATE_YEAR, y); + p.SetParam(ON_RDK_SUN_DATE_MONTH, m); + p.SetParam(ON_RDK_SUN_DATE_DAY, d); + p.SetParam(ON_RDK_SUN_TIME_HOURS, h); + p.SetParam(ON_RDK_SUN_AZIMUTH, e.Azimuth()); + p.SetParam(ON_RDK_SUN_ALTITUDE, e.Altitude()); + p.SetParam(ON_RDK_SUN_OBSERVER_LATITUDE, e.Latitude()); + p.SetParam(ON_RDK_SUN_OBSERVER_LONGITUDE, e.Longitude()); + p.SetParam(ON_RDK_SUN_OBSERVER_TIMEZONE, e.TimeZoneHours()); + p.SetParam(ON_RDK_SUN_SKYLIGHT_ON, (_major_version < 6) ? false : true); p.SetParam(ON_RDK_SUN_SKYLIGHT_SHADOW_INTENSITY, 1.0); - p.SetParam(ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON, (_major_version < 6) ? false : true); - p.SetParam(ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT, ON_nil_uuid); + p.SetParam(ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_OVERRIDE, (_major_version < 6) ? false : true); + p.SetParam(ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_ID, ON_nil_uuid); } // Safe frame. @@ -5335,7 +5253,7 @@ void ON_RdkDocumentDefaults::CreateXML(void) 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); + p.SetParam(ON_RDK_SF_4x3_FIELD_GRID_ON, false); auto& live_frame = Create(safe_frame, ON_RDK_SF_LIVE_FRAME); { @@ -5374,12 +5292,12 @@ void ON_RdkDocumentDefaults::CreateXML(void) p.SetParam(ON_RDK_GP_ALTITUDE, 0.0); p.SetParam(ON_RDK_GP_AUTO_ALTITUDE, true); p.SetParam(ON_RDK_GP_SHADOW_ONLY, (_major_version < 6) ? false : true); - p.SetParam(ON_RDK_GP_MATERIAL, L""); + p.SetParam(ON_RDK_GP_MATERIAL_ID, 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); + p.SetParam(ON_RDK_GP_TEXTURE_OFFSET_LOCKED, false); + p.SetParam(ON_RDK_GP_TEXTURE_SIZE_LOCKED, true); } } else @@ -5418,4 +5336,9 @@ ON_RdkDocumentDefaults::~ON_RdkDocumentDefaults() { } +void* ON_RdkDocumentDefaults::EVF(const wchar_t*, void*) +{ + return nullptr; +} + #pragma ON_PRAGMA_WARNING_POP diff --git a/opennurbs_xml.h b/opennurbs_xml.h index 6f6b2cce..fb1f5a4c 100644 --- a/opennurbs_xml.h +++ b/opennurbs_xml.h @@ -21,7 +21,7 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*); #define ON_RDK_DOCUMENT L"render-content-manager-document" #define ON_RDK_CURRENT_CONTENT L"content" - #define ON_RDK_ENVIRONMENT L"environment" + #define ON_RDK_BACKGROUND_ENVIRONMENT L"environment" #define ON_RDK_DEFAULT_CONTENT_SECTION L"default-content-section" #define ON_RDK_SETTINGS L"settings" @@ -53,13 +53,15 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*); #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_TONE_MAPPING L"tone-mapping" #define ON_RDK_PEP_TYPE_LATE L"late" + #define ON_RDK_PEP_PLUG_IN L"plug-in" + #define ON_RDK_PEP_LOCAL_NAME L"name" + #define ON_RDK_PEP_ON L"on" + #define ON_RDK_PEP_ID L"id" + #define ON_RDK_PEP_SHOWN L"shown" + #define ON_RDK_PEP_PARAMS L"state" // This should really be 'params'. #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" @@ -69,20 +71,21 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*); #define ON_RDK_RCH_MODE_AUTOMATIC L"automatic" #define ON_RDK_RCH_MODE_CUSTOM L"custom" - #define ON_RDK_CURRENT_PRESET L"current-preset" + #define ON_RDK_CURRENT_RENDER_PRESET L"current-preset" #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_DITHERING_ON L"use-dithering" + #define ON_RDK_DITHERING_METHOD L"dithering" + #define ON_RDK_DITHERING_METHOD_FLOYD_STEINBERG L"floyd-steinberg" + #define ON_RDK_DITHERING_METHOD_SIMPLE_NOISE L"simple-noise" + + #define ON_RDK_PRE_PROCESS_GAMMA_ON L"use-linear-workflow" + #define ON_RDK_POST_PROCESS_GAMMA_ON L"use-post-process-gamma" + #define ON_RDK_POST_PROCESS_GAMMA L"gamma" // Keep original XML tag. #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_SUN L"sun" #define ON_RDK_SUN_ENABLE_ALLOWED L"enable-allowed" #define ON_RDK_SUN_ENABLE_ON L"enable-on" @@ -102,15 +105,15 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*); #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_SKYLIGHT_ENVIRONMENT_OVERRIDE L"skylight-custom-environment-on" + #define ON_RDK_SUN_SKYLIGHT_ENVIRONMENT_ID 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_4x3_FIELD_DISPLAY_ON L"field-display-on" + #define ON_RDK_SF_4x3_FIELD_GRID_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" @@ -120,22 +123,22 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*); #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_GP_ON L"on" + #define ON_RDK_GP_ALTITUDE L"altitude" + #define ON_RDK_GP_AUTO_ALTITUDE L"auto-altitude" + #define ON_RDK_GP_SHOW_UNDERSIDE L"show-underside" + #define ON_RDK_GP_SHADOW_ONLY L"shadow-only" + #define ON_RDK_GP_MATERIAL_ID L"material" + #define ON_RDK_GP_TEXTURE_OFFSET L"texture-offset" + #define ON_RDK_GP_TEXTURE_OFFSET_LOCKED L"offset-lock" // Keep old string. + #define ON_RDK_GP_TEXTURE_SIZE L"texture-size" + #define ON_RDK_GP_TEXTURE_SIZE_LOCKED L"repeat-lock" // Keep old string. + #define ON_RDK_GP_TEXTURE_ROTATION L"texture-rotation" #define ON_RDK_POSTFIX_SECTION L"-section" #define ON_RDK_SLASH L"/" -class ON_CLASS ON_XMLVariant final +class ON_CLASS ON_XMLVariant { public: enum class Types : unsigned int @@ -170,65 +173,65 @@ public: ON_XMLVariant(const void* buffer, size_t size); ON_XMLVariant(const ON_Buffer& buffer); ON_XMLVariant(const ON_XMLVariant& src); - ~ON_XMLVariant(); + virtual ~ON_XMLVariant(); const ON_XMLVariant& operator = (const ON_XMLVariant& src); bool operator == (const ON_XMLVariant& v) const; bool operator != (const ON_XMLVariant& v) const; - ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; public: - Types Type(void) const; - ON_wString TypeAsString(void) const; - bool IsEmpty(void) const; - bool IsNull(void) const; + virtual Types Type(void) const; + virtual ON_wString TypeAsString(void) const; + virtual bool IsEmpty(void) const; + virtual bool IsNull(void) const; - ON::LengthUnitSystem Units(void) const; - void SetUnits(ON::LengthUnitSystem units); + virtual ON::LengthUnitSystem Units(void) const; + virtual void SetUnits(ON::LengthUnitSystem units); // Optimized version of ON_wString::NeedsXMLEncode(). Always use this if possible // because it knows from the type of the variant whether entity ref encoded output is even possible. - bool NeedsXMLEncode(void) const; + virtual bool NeedsXMLEncode(void) const; - ON_Buffer& GetBuffer(void) const; - void ClearBuffers(void); + virtual ON_Buffer& GetBuffer(void) const; + virtual void ClearBuffers(void); public: - void SetNull(void); - void SetValue(int v); - void SetValue(double v); - void SetValue(float v); - void SetValue(const wchar_t* s); - void SetValue(const ON_wString& s); - void SetValue(const double* p, ArrayTypes at); - void SetValue(const float* p, ArrayTypes at); - void SetValue(const ON_2dPoint& p); - void SetValue(const ON_3dPoint& p); - void SetValue(const ON_4dPoint& p); - void SetValue(const ON_4fColor& c); - void SetValue(bool b); - void SetValue(time_t time); - void SetValue(const ON_UUID& uuid); - void SetValue(const ON_Xform& xform); - void SetValue(const void* buffer, size_t size); - void SetValue(const ON_Buffer& buffer); + virtual void SetNull(void); + virtual void SetValue(int v); + virtual void SetValue(double v); + virtual void SetValue(float v); + virtual void SetValue(const wchar_t* s); + virtual void SetValue(const ON_wString& s); + virtual void SetValue(const double* p, ArrayTypes at); + virtual void SetValue(const float* p, ArrayTypes at); + virtual void SetValue(const ON_2dPoint& p); + virtual void SetValue(const ON_3dPoint& p); + virtual void SetValue(const ON_4dPoint& p); + virtual void SetValue(const ON_4fColor& c); + virtual void SetValue(bool b); + virtual void SetValue(time_t time); + virtual void SetValue(const ON_UUID& uuid); + virtual void SetValue(const ON_Xform& xform); + virtual void SetValue(const void* buffer, size_t size); + virtual void SetValue(const ON_Buffer& buffer); - bool AsBool(void) const; - int AsInteger(void) const; - double AsDouble(void) const; - float AsFloat(void) const; - ON_2dPoint As2dPoint(void) const; - ON_3dPoint As3dPoint(void) const; - ON_4dPoint As4dPoint(void) const; - ON_UUID AsUuid(void) const; - ON_Xform AsXform(void) const; - time_t AsTime(void) const; - ON_4fColor AsColor(void) const; - ON_Buffer AsBuffer(void) const; - void* AsBuffer(size_t& size_out) const; - ON_wString AsString(void) const; + virtual bool AsBool(void) const; + virtual int AsInteger(void) const; + virtual double AsDouble(void) const; + virtual float AsFloat(void) const; + virtual ON_2dPoint As2dPoint(void) const; + virtual ON_3dPoint As3dPoint(void) const; + virtual ON_4dPoint As4dPoint(void) const; + virtual ON_UUID AsUuid(void) const; + virtual ON_Xform AsXform(void) const; + virtual time_t AsTime(void) const; + virtual ON_4fColor AsColor(void) const; + virtual ON_Buffer AsBuffer(void) const; + virtual void* AsBuffer(size_t& size_out) const; + virtual ON_wString AsString(void) const; operator bool() const; operator double() const; @@ -243,17 +246,15 @@ public: operator ON_Buffer() const; operator ON_wString() const; - void DoAutoTyping(Types t) const; - public: - bool TypePending(void) const; - void SetTypePendingFlag(bool bTypePending) const; + virtual bool TypePending(void) const; + virtual void SetTypePendingFlag(bool bTypePending) const; // Format string as type and value. Useful for debugging. - void Format(ON_wString& s) const; + virtual void Format(ON_wString& s) const; protected: - void StringToPoint(int iValues) const; + virtual void StringToPoint(int iValues) const; private: class CImpl; @@ -261,34 +262,36 @@ private: ON__UINT8 m_Impl[176]; }; -class ON_CLASS ON_XMLProperty final +class ON_CLASS ON_XMLProperty { public: ON_XMLProperty(); ON_XMLProperty(const ON_XMLVariant& value); ON_XMLProperty(const ON_wString& sName, const ON_XMLVariant& value); ON_XMLProperty(const ON_XMLProperty& prop); - ~ON_XMLProperty(); + virtual ~ON_XMLProperty(); const ON_XMLProperty& operator = (const ON_XMLProperty& prop); - const ON_wString& Name(void) const; - void SetName(const wchar_t* name); + virtual const ON_wString& Name(void) const; + virtual void SetName(const wchar_t* name); - ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; - ON_XMLProperty* Next(void) const; + virtual ON_XMLProperty* Next(void) const; - bool IsDefaultProperty(void) const; + virtual bool IsDefaultProperty(void) const; - const ON_XMLVariant& GetValue(void) const; - void SetValue(const ON_XMLVariant& value); + virtual const ON_XMLVariant& GetValue(void) const; + virtual void SetValue(const ON_XMLVariant& value); bool operator < (const ON_XMLProperty& prop) const; - ON_XMLVariant& GetNonConstValue(void); + virtual ON_XMLVariant& GetNonConstValue(void); - void SetHugeStringValue(const ON_wString& s); + virtual void SetHugeStringValue(const ON_wString& s); + + virtual void* EVF(const wchar_t* func, void* data); private: class CImpl; @@ -305,9 +308,9 @@ public: ON_XMLSegmentedStream(); virtual ~ON_XMLSegmentedStream(); - int Count(void) const; - void Append(wchar_t* s); - wchar_t* Segment(int index) const; + virtual int Count(void) const; + virtual void Append(wchar_t* s); + virtual wchar_t* Segment(int index) const; protected: // Emergency virtual function for future expansion. @@ -332,10 +335,13 @@ public: const ON_XMLNode& operator = (const ON_XMLNode&); - bool MergeFrom(const ON_XMLNode& src); // src node must have the same name + bool operator == (const ON_XMLNode&) const; + bool operator != (const ON_XMLNode&) const; - const ON_wString& TagName(void) const; - void SetTagName(const wchar_t* name); + virtual bool MergeFrom(const ON_XMLNode& src); // src node must have the same name + + virtual const ON_wString& TagName(void) const; + virtual void SetTagName(const wchar_t* name); static bool IsValidXMLNameWithDebugging(const wchar_t* name); static bool IsValidXMLName(const wchar_t* name); @@ -343,81 +349,80 @@ public: public: // Hierarchy. // Get the first child of this node or null if none. - ON_XMLNode* FirstChild(void) const; + virtual ON_XMLNode* FirstChild(void) const; // Get the previous sibling of this node or null if none. - ON_XMLNode* PrevSibling(void) const; + virtual ON_XMLNode* PrevSibling(void) const; // Get the next sibling of this node or null if none. - ON_XMLNode* NextSibling(void) const; + virtual ON_XMLNode* NextSibling(void) const; // Get the parent of this node or null if none. - ON_XMLNode* GetParent(void) const; + virtual ON_XMLNode* Parent(void) const; - // Get the topmost parent of this node or null if none. - const ON_XMLNode& TopmostParent(void) const; + // Get the top-level parent of this node. + virtual const ON_XMLNode& TopLevel(void) const; // Recursively iterate over the children of this node, calling the supplied callback function for each child. // If the callback function returns false, this function will fail. // Returns true if successful, false on failure. - bool RecurseChildren(ON_XMLRecurseChildrenCallback callback, void* data) const; + virtual bool RecurseChildren(ON_XMLRecurseChildrenCallback callback, void* data) const; public: // Change data. // Adds a node as a child of this node. Takes ownership of node. // Returns a pointer to node for convenience. - ON_XMLNode* AttachChildNode(ON_XMLNode* node); - - // DEPRECATED, use AttachChildNode() - ON_DEPRECATED ON_XMLNode* AddChildNode(ON_XMLNode* node); + virtual ON_XMLNode* AttachChildNode(ON_XMLNode* node); // Attaches a property directly to the node. Takes ownership of the property. - // Returns a pointer to the copy of the property. - ON_XMLProperty* AttachProperty(ON_XMLProperty* prop); + // Any existing property with the same name is first deleted. + // For convenience, returns a pointer to the input property. + virtual ON_XMLProperty* AttachProperty(ON_XMLProperty* prop); - // Adds a property to the node. The property object is copied and ownership remains with the caller. + // Sets a property on the node. The property is copied and ownership of it remains with the caller. + // Any existing property with the same name is first deleted. // Returns a pointer to the copy of the property. - ON_XMLProperty* AddProperty(const ON_XMLProperty& prop); + virtual ON_XMLProperty* SetProperty(const ON_XMLProperty& prop); // Removes and deletes this node. - void Remove(void); + virtual void Remove(void); // Removes and deletes the child node, if possible. // Returns true if successful, else false. - bool RemoveChild(ON_XMLNode* child); + virtual bool RemoveChild(ON_XMLNode* child); // Removes and deletes the named property, if possible. // Returns true if successful, else false. - bool RemoveProperty(const wchar_t* prop_name); + virtual bool RemoveProperty(const wchar_t* prop_name); // Removes the child node and passes ownership to the caller. // Returns the detached node or null on failure. - ON_XMLNode* DetachChild(ON_XMLNode& child); + virtual ON_XMLNode* DetachChild(ON_XMLNode& child); // Removes and deletes all child nodes. - void RemoveAllChildren(void); + virtual void RemoveAllChildren(void); // Removes and deletes all properties and adds an empty default property. - void RemoveAllProperties(void); + virtual void RemoveAllProperties(void); // Removes and deletes all child nodes and properties, and clears the tag name. virtual void Clear(void); // Moves this node before another node. - void MoveBefore(ON_XMLNode& other); + virtual void MoveBefore(ON_XMLNode& other); // Moves this node after another node. - void MoveAfter(ON_XMLNode& other); + virtual void MoveAfter(ON_XMLNode& other); public: // Serialization. static constexpr ON__UINT32 ReadError = 0xFFFFFFFF; // Indicates ReadFromStream() failure. - ON__UINT32 ReadFromStream(const wchar_t* buf, bool warnings_as_errors=false, bool validate_tags=true); + virtual ON__UINT32 ReadFromStream(const wchar_t* buf, bool warnings_as_errors=false, bool validate_tags=true); - void* LastReadBufferPointer(void) const; + virtual void* LastReadBufferPointer(void) const; // This function is called on the top-most node during the reading process. virtual void OnNodeReadFromStream(const ON_XMLNode* node) const; - ON_wString String(bool include_formatting=true, bool force_long_format=false, bool sorted_props=false) const; + virtual ON_wString String(bool include_formatting=true, bool force_long_format=false, bool sorted_props=false) const; operator ON_wString() const; // Writes the node to a wide buffer (AKA 'stream') as XML text. @@ -425,7 +430,7 @@ public: // Serialization. // To find out how much space is needed without actually writing anything, pass zero. // When writing to the buffer, a terminator is written if there is room for it, but the // terminator is not included in the returned number of characters. - ON__UINT32 WriteToStream(wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; + virtual ON__UINT32 WriteToStream(wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; class ON_CLASS CharacterCounts { @@ -444,35 +449,35 @@ public: // Serialization. ON__UINT64 m_reserved = 0; }; - CharacterCounts WriteToStreamEx (wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; - CharacterCounts WriteHeaderToStream (wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; - CharacterCounts WriteChildrenToStream(wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; - CharacterCounts WriteFooterToStream (wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false) const; + virtual CharacterCounts WriteToStreamEx (wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; + virtual CharacterCounts WriteHeaderToStream (wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; + virtual CharacterCounts WriteChildrenToStream(wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false, bool sorted_props = false) const; + virtual CharacterCounts WriteFooterToStream (wchar_t* stream, ON__UINT32 max_chars, bool include_formatting = true, bool force_long_format = false) const; - bool WriteToSegmentedStream(ON_XMLSegmentedStream& stream, bool include_formatting=true, bool force_long_format=false, bool sorted_props=false) const; + virtual bool WriteToSegmentedStream(ON_XMLSegmentedStream& stream, bool include_formatting=true, bool force_long_format=false, bool sorted_props=false) const; public: // Utilities. - int PropertyCount(void) const; - int ChildCount(void) const; + virtual int PropertyCount(void) const; + virtual int ChildCount(void) const; - int GetNestedDepth(void) const; + virtual int GetNestedDepth(void) const; // Get the CRC of this node. This assumes the node has the following format: // // 1.23456 // ... // - ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + virtual ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; // Use this when you are looking for a node that is only one child below - non-recursive, but fast. - ON_XMLNode* GetNamedChild(const wchar_t* name) const; + virtual ON_XMLNode* GetNamedChild(const wchar_t* name) const; - ON_XMLProperty* GetNamedProperty(const wchar_t* name) const; + virtual ON_XMLProperty* GetNamedProperty(const wchar_t* name) const; - ON_XMLProperty& GetDefaultProperty(void) const; + virtual ON_XMLProperty& GetDefaultProperty(void) const; // Returns the full path to this node. - ON_wString GetPathFromRoot(void) const; + virtual ON_wString GetPathFromRoot(void) const; ///////////////////////////////////////////////////////////////////////////////////////////////////////// // Gets at nodes deep into the tree using a slash-delimited path, i.e., "child/grandchild/great-grandchild". @@ -482,12 +487,12 @@ public: // Utilities. // Gets a child node given the relative path from the current node. If the node does not exist, the method // returns null. The returned pointer should not be deleted by the caller. The child node is owned by its // immediate parent at that position in the node hierarchy. - ON_XMLNode* GetNodeAtPath(const wchar_t* path) const; + virtual ON_XMLNode* GetNodeAtPath(const wchar_t* path) const; // Gets a child node given the relative path from the current node. If the node does not exist, it is // created. This method should therefore never return null. The returned pointer should not be deleted // by the caller. The child node is owned by its immediate parent at that position in the node hierarchy. - ON_XMLNode* CreateNodeAtPath(const wchar_t* path); + virtual ON_XMLNode* CreateNodeAtPath(const wchar_t* path); ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -506,9 +511,6 @@ public: // Iteration. const ChildIterator& operator = (const ChildIterator& other); virtual ON_XMLNode* GetNextChild(void); - - protected: - // Emergency virtual function for future expansion. virtual void* EVF(const wchar_t* func, void* data); private: @@ -517,31 +519,32 @@ public: // Iteration. ON__UINT8 m_Impl[24]; }; - class ON_CLASS PropertyIterator final + class ON_CLASS PropertyIterator { public: PropertyIterator(const ON_XMLNode* parent, bool sorted = false); PropertyIterator(const PropertyIterator& other); - ~PropertyIterator(); - - ON_XMLProperty* GetNextProperty(void); + virtual ~PropertyIterator(); const PropertyIterator& operator = (const PropertyIterator& other); + virtual ON_XMLProperty* GetNextProperty(void); + virtual void* EVF(const wchar_t* func, void* data); + private: class CImpl; CImpl* m_impl; ON__UINT8 m_Impl[64]; }; - ChildIterator GetChildIterator(void) const; - PropertyIterator GetPropertyIterator(bool alphabetized = false) const; + virtual ChildIterator GetChildIterator(void) const; + virtual PropertyIterator GetPropertyIterator(bool alphabetized = false) const; static bool AutoTypePropValue(void); static void SetAutoTypePropValue(bool b=true); // For internal use only. - void SetInternalDebuggingFlags(ON__UINT64); + virtual void SetInternalDebuggingFlags(ON__UINT64); private: class CImpl; @@ -554,16 +557,18 @@ class ON_CLASS ON_XMLRootNode : public ON_XMLNode { public: ON_XMLRootNode(); - ON_XMLRootNode(const ON_XMLRootNode&); + ON_XMLRootNode(const ON_XMLNode& node); + ON_XMLRootNode(const ON_XMLRootNode& root); virtual ~ON_XMLRootNode(); - const ON_XMLRootNode& operator = (const ON_XMLRootNode&); + const ON_XMLRootNode& operator = (const ON_XMLNode& node); + const ON_XMLRootNode& operator = (const ON_XMLRootNode& root); - const ON_XMLRootNode& NodeForRead(void) const; - ON_XMLRootNode& NodeForWrite(void); + virtual const ON_XMLRootNode& NodeForRead(void) const; + virtual ON_XMLRootNode& NodeForWrite(void); - 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 bool ReadFromFile(const wchar_t* path, bool warnings_as_errors=false, bool validate_tags=true); + virtual bool WriteToFile (const wchar_t* path, bool include_formatting=true, bool utf8=false, bool sorted_properties=false) const; virtual void Clear(void) override; @@ -583,19 +588,18 @@ public: const ON_XMLUserData& operator = (const ON_XMLUserData& ud); - const ON_XMLRootNode& XMLRootForRead(void) const; - ON_XMLRootNode& XMLRootForWrite(void) const; + virtual const ON_XMLRootNode& XMLRootForRead(void) const; + virtual ON_XMLRootNode& XMLRootForWrite(void) const; - ON_XMLProperty* Property(const wchar_t* xml_path, const wchar_t* prop_name) const; + virtual ON_XMLProperty* Property(const wchar_t* xml_path, const wchar_t* prop_name) const; - ON_XMLVariant Value(const wchar_t* xml_path, const wchar_t* prop_name = L"") const; - bool SetValue(const wchar_t* xml_path, const wchar_t* prop_name, const ON_XMLVariant& value); - void SetValue(const wchar_t* xml_path, const ON_XMLVariant& value); + virtual ON_XMLVariant Value(const wchar_t* xml_path, const wchar_t* prop_name = L"") const; + virtual bool SetValue(const wchar_t* xml_path, const wchar_t* prop_name, const ON_XMLVariant& value); + virtual void SetValue(const wchar_t* xml_path, const ON_XMLVariant& value); - void Clear(void) const; + virtual void Clear(void) const; - int Version(void) const; - void _Dump(const wchar_t* wszFileName) const; + virtual int Version(void) const; virtual bool Archive(void) const override; @@ -605,15 +609,16 @@ public: virtual bool Write(ON_BinaryArchive&) const override; virtual bool Read(ON_BinaryArchive&) override; -protected: + virtual void _Dump(const wchar_t* wszFileName) const; + // Emergency virtual function for future expansion. - virtual void* EVF(const wchar_t* wszFunc, void* pvData); + virtual void* EVF(const wchar_t* func, void* data); protected: ON_XMLProperty* InternalProperty(const wchar_t* xml_path, const wchar_t* prop_name) const; public: - void SetToDefaultsImpl(int) const; + virtual void SetToDefaultsImpl(int) const; private: class CImpl; @@ -632,14 +637,21 @@ public: const ON_XMLParameters& operator = (const ON_XMLParameters&) = delete; - void SetWriteTypeProperty(bool b); - void SetDefaultReadType(const wchar_t* type); + virtual void SetWriteTypeProperty(bool b); + virtual void SetDefaultReadType(const wchar_t* type); virtual ON_wString AsString(void) const; virtual void SetAsString(const wchar_t* s); - virtual ON_XMLNode* SetParam(const wchar_t* param_name, const ON_XMLVariant& param_value); virtual bool GetParam(const wchar_t* param_name, ON_XMLVariant& param_value_out) const; + virtual ON_XMLNode* SetParam(const wchar_t* param_name, const ON_XMLVariant& param_value); + + // Easy-to-use helper function. Returns 'default_value' if param is not found. + ON_XMLVariant GetParam(const wchar_t* param_name, const ON_XMLVariant& default_value) const; + + // Expert access to the underlying XML node. + ON_XMLNode& Node(void); + const ON_XMLNode& Node(void) const; class ON_CLASS CIterator { @@ -659,7 +671,6 @@ public: CIterator* NewIterator(void) const; protected: - const ON_XMLNode& Node(void) const; virtual bool GetParamNode(const ON_XMLNode& node, ON_XMLVariant& param_value) const; virtual void* EVF(const wchar_t*, void*); virtual ON_XMLNode* SetParamNode(ON_XMLNode& node, const wchar_t* param_name, const ON_XMLVariant& param_value); @@ -675,15 +686,15 @@ class ON_CLASS ON_XMLParametersV8 : public ON_XMLParameters public: ON_XMLParametersV8(ON_XMLNode& node); ON_XMLParametersV8(const ON_XMLNode& node); + ON_XMLParametersV8(const ON_XMLParametersV8&) = delete; + + const ON_XMLParametersV8& operator = (const ON_XMLParametersV8&) = delete; virtual bool GetParam(const wchar_t* param_name, ON_XMLVariant& param_value_out) const override; - ON_XMLParametersV8(const ON_XMLParametersV8&) = delete; - const ON_XMLParametersV8& operator = (const ON_XMLParametersV8&) = delete; - protected: virtual ON_XMLNode* ObtainChildNodeForWrite(ON_XMLNode& node, const wchar_t* param_name) const override; - ON_XMLNode* FindNodeByNameProperty(const wchar_t* param_name) const; + virtual ON_XMLNode* FindNodeByNameProperty(const wchar_t* param_name) const; }; ON_DECL bool ON_RunXMLTests(const wchar_t* test_folder); @@ -737,12 +748,15 @@ public: const ON_RdkDocumentDefaults& operator = (const ON_RdkDocumentDefaults&) = delete; - const ON_XMLNode& Node(void) const; + virtual const ON_XMLNode& Node(void) const; - void CopyDefaultsTo(ON_XMLNode& dest) const; + virtual void CopyDefaultsTo(ON_XMLNode& dest) const; + + // Emergency virtual function for future expansion. + virtual void* EVF(const wchar_t* func, void* data); private: - void CreateXML(void); + virtual void CreateXML(void); private: ON_XMLRootNode _root; @@ -752,4 +766,7 @@ private: }; /////////////////////////////////////////////////////////////////////////////////////////////////////// +extern ON_DECL ON_UUID ON_UuidDefaultMaterialInstance; +extern ON_DECL ON_UUID ON_UuidDefaultEnvironmentInstance; + #endif