From 488533eb7a511d12b0cb18d8c76b681092ef22de Mon Sep 17 00:00:00 2001 From: Bozo The Builder Date: Wed, 16 Dec 2020 05:49:44 -0800 Subject: [PATCH] Sync changes from upstream repository Co-authored-by: Andrew le Bihan Co-authored-by: Brian Gillespie Co-authored-by: Dale Lear Co-authored-by: Giulio Piacentino Co-authored-by: Greg Arden Co-authored-by: Lars Co-authored-by: Lowell Co-authored-by: Nathan Letwory Co-authored-by: Pierre Cuvilliers Co-authored-by: Tim Hemmelman --- opennurbs_3dm_settings.cpp | 155 ++++-- opennurbs_annotationbase.cpp | 22 +- opennurbs_annotationbase.h | 29 +- opennurbs_archive.cpp | 17 +- opennurbs_archive.h | 23 +- opennurbs_archive_manifest.cpp | 13 +- opennurbs_brep.cpp | 18 + opennurbs_brep.h | 16 + opennurbs_crc.cpp | 256 ++++++++-- opennurbs_defines.cpp | 8 +- opennurbs_defines.h | 17 +- opennurbs_dimensionstyle.cpp | 203 +++++++- opennurbs_dimensionstyle.h | 99 +++- opennurbs_font.cpp | 60 ++- opennurbs_font.h | 8 + opennurbs_hatch.cpp | 2 +- opennurbs_instance.cpp | 2 +- opennurbs_internal_Vx_annotation.cpp | 4 +- opennurbs_line.cpp | 82 +-- opennurbs_line.h | 19 + opennurbs_material.cpp | 56 +- opennurbs_material.h | 12 + opennurbs_math.cpp | 112 ++++ opennurbs_math.h | 27 +- opennurbs_mesh.cpp | 218 +++++--- opennurbs_mesh.h | 376 +++++++++++--- opennurbs_model_component.cpp | 85 ++-- opennurbs_model_component.h | 39 +- opennurbs_nurbscurve.cpp | 12 +- opennurbs_nurbssurface.cpp | 18 +- opennurbs_nurbssurface.h | 8 + opennurbs_point.cpp | 10 + opennurbs_public_version.h | 22 +- opennurbs_public_version.rc | 2 +- opennurbs_sha1.cpp | 4 +- opennurbs_sha1.h | 65 +++ opennurbs_statics.cpp | 736 ++++++++++++++++++--------- opennurbs_string.h | 75 ++- opennurbs_string_values.cpp | 2 +- opennurbs_subd.cpp | 685 +++++++++++++++++++++++-- opennurbs_subd.h | 557 +++++++++++++++++++- opennurbs_subd_archive.cpp | 86 +++- opennurbs_subd_copy.cpp | 43 +- opennurbs_subd_data.h | 67 ++- opennurbs_subd_fragment.cpp | 304 +++++++---- opennurbs_subd_heap.cpp | 139 ++--- opennurbs_subd_iter.cpp | 4 +- opennurbs_subd_texture.cpp | 25 + opennurbs_textiterator.cpp | 22 +- opennurbs_textiterator.h | 6 +- opennurbs_texture.h | 5 +- opennurbs_unicode.cpp | 22 + opennurbs_unicode.h | 25 + opennurbs_xform.h | 2 +- 54 files changed, 4026 insertions(+), 898 deletions(-) diff --git a/opennurbs_3dm_settings.cpp b/opennurbs_3dm_settings.cpp index d8cdce74..860cf2fb 100644 --- a/opennurbs_3dm_settings.cpp +++ b/opennurbs_3dm_settings.cpp @@ -37,7 +37,7 @@ static double ON_Internal_UnitSystemCtorMetersPerUnit( switch (length_unit_system) { case ON::LengthUnitSystem::None: - meters_per_unit = 0.0; + meters_per_unit = 1.0; break; case ON::LengthUnitSystem::Angstroms: case ON::LengthUnitSystem::Nanometers: @@ -63,7 +63,7 @@ static double ON_Internal_UnitSystemCtorMetersPerUnit( case ON::LengthUnitSystem::AstronomicalUnits: case ON::LengthUnitSystem::LightYears: case ON::LengthUnitSystem::Parsecs: - meters_per_unit = ON::UnitScale(ON::LengthUnitSystem::Meters, length_unit_system); + meters_per_unit = ON::UnitScale(length_unit_system, ON::LengthUnitSystem::Meters); break; case ON::LengthUnitSystem::CustomUnits: meters_per_unit = 1.0; @@ -81,7 +81,7 @@ static double ON_Internal_UnitSystemCtorMetersPerUnit( ON_UnitSystem::ON_UnitSystem(ON::LengthUnitSystem length_unit_system) : m_unit_system(ON::LengthUnitSystemFromUnsigned(static_cast(length_unit_system))) -, m_meters_per_unit(ON_Internal_UnitSystemCtorMetersPerUnit(m_unit_system)) +, m_meters_per_custom_unit(ON_Internal_UnitSystemCtorMetersPerUnit(m_unit_system)) {} ON_UnitSystem& ON_UnitSystem::operator=( @@ -100,7 +100,7 @@ bool ON_UnitSystem::operator==(const ON_UnitSystem& other) const if ( ON::LengthUnitSystem::CustomUnits == m_unit_system ) { - if ( !(m_meters_per_unit == other.m_meters_per_unit) ) + if ( !(m_meters_per_custom_unit == other.m_meters_per_custom_unit) ) return false; if ( false == m_custom_unit_name.EqualOrdinal(other.m_custom_unit_name,false) ) return false; @@ -116,7 +116,7 @@ bool ON_UnitSystem::operator!=(const ON_UnitSystem& other) const if ( ON::LengthUnitSystem::CustomUnits == m_unit_system ) { - if ( m_meters_per_unit != other.m_meters_per_unit ) + if (m_meters_per_custom_unit != other.m_meters_per_custom_unit) return true; if ( false == m_custom_unit_name.EqualOrdinal(other.m_custom_unit_name,false) ) return true; @@ -129,28 +129,17 @@ bool ON_UnitSystem::IsValid() const { if ( m_unit_system != ON::LengthUnitSystemFromUnsigned(static_cast(m_unit_system)) ) { - // bogus enum value + // invalid enum value return false; } - if (ON::LengthUnitSystem::None == m_unit_system) - { - if (!(0.0 == m_meters_per_unit)) - return false; - } - else - { - if (!(m_meters_per_unit > 0.0 && m_meters_per_unit < ON_UNSET_POSITIVE_VALUE)) - { - // m_meters_per_unit should be > 0.0 and a valid double - return false; - } + if (ON::LengthUnitSystem::Unset == m_unit_system) + return false; - if (ON::LengthUnitSystem::CustomUnits != m_unit_system) - { - if (m_meters_per_unit != ON::UnitScale(ON::LengthUnitSystem::Meters, m_unit_system)) - return false; - } + if (ON::LengthUnitSystem::CustomUnits == m_unit_system) + { + if (false == ON_IsValidPositiveNumber(m_meters_per_custom_unit)) + return false; } return true; @@ -158,7 +147,7 @@ bool ON_UnitSystem::IsValid() const bool ON_UnitSystem::IsSet() const { - return ( ON::LengthUnitSystem::None != m_unit_system && IsValid() ); + return (ON::LengthUnitSystem::Unset != m_unit_system && ON::LengthUnitSystem::None != m_unit_system && IsValid() ); } bool ON_UnitSystem::IsCustomUnitSystem() const @@ -188,17 +177,18 @@ void ON_UnitSystem::SetCustomUnitSystem( double meters_per_custom_unit ) { + ON_wString local_str(custom_unit_name); + local_str.TrimLeftAndRight(); m_unit_system = ON::LengthUnitSystem::CustomUnits; - m_custom_unit_name = custom_unit_name; - m_custom_unit_name.TrimLeftAndRight(); - if (meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE) + m_custom_unit_name = local_str; + if ( ON_IsValidPositiveNumber(meters_per_custom_unit) ) { - m_meters_per_unit = meters_per_custom_unit; + m_meters_per_custom_unit = meters_per_custom_unit; } else { ON_ERROR("Invalid meters_per_custom_unit parameter"); - m_meters_per_unit = 1.0; // must be > 0.0 and < ON_UNSET_POSITIVE_VALUE + m_meters_per_custom_unit = 1.0; // must be > 0.0 and < ON_UNSET_POSITIVE_VALUE } } @@ -213,7 +203,7 @@ void ON_UnitSystem::SetCustomUnitSystemName( { const double meters_per_custom_unit = bIsCustomUnitSystem - ? m_meters_per_unit + ? m_meters_per_custom_unit : 1.0; SetCustomUnitSystem(local_name, meters_per_custom_unit); } @@ -223,24 +213,82 @@ void ON_UnitSystem::SetCustomUnitSystemScale( double meters_per_custom_unit ) { - const bool bIsCustomUnitSystem = (ON::LengthUnitSystem::CustomUnits == m_unit_system); - if (meters_per_custom_unit != m_meters_per_unit || bIsCustomUnitSystem) + if (ON_IsValidPositiveNumber(meters_per_custom_unit)) { - if (meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE) + const bool bIsCustomUnitSystem = (ON::LengthUnitSystem::CustomUnits == m_unit_system); + if (false == (meters_per_custom_unit == m_meters_per_custom_unit) || bIsCustomUnitSystem) { - ON_wString unit_system_name + const ON_wString unit_system_name = (ON::LengthUnitSystem::CustomUnits == m_unit_system) - ? unit_system_name + ? m_custom_unit_name : ON_wString::EmptyString; SetCustomUnitSystem(unit_system_name, meters_per_custom_unit); } } } +double ON_UnitSystem::MetersPerUnit( + double unset_return_value +) const +{ + switch (m_unit_system) + { + case ON::LengthUnitSystem::None: + return 1.0; + break; + case ON::LengthUnitSystem::CustomUnits: + return m_meters_per_custom_unit; + break; + case ON::LengthUnitSystem::Unset: + return unset_return_value; + break; + default: + break; + } + return ON::UnitScale(m_unit_system, ON::LengthUnitSystem::Meters); +} + +double ON_UnitSystem::MillimetersPerUnit( + double unset_return_value +) const +{ + switch (m_unit_system) + { + case ON::LengthUnitSystem::None: + return 1.0; + break; + case ON::LengthUnitSystem::CustomUnits: + return 1000.0*m_meters_per_custom_unit; + break; + case ON::LengthUnitSystem::Unset: + return unset_return_value; + break; + default: + break; + } + return ON::UnitScale(m_unit_system, ON::LengthUnitSystem::Millimeters); +} double ON_UnitSystem::MetersPerUnit() const { - return m_meters_per_unit; + // NOTE WELL: + // https://mcneel.myjetbrains.com/youtrack/issue/RH-60700 + // For standard units, this function returns the WRONG value (inverse of the correct value). + // The reason is the Rhino 6 VRay plug-in assumes the incorrect value is returned + // and V6 VRay does not work correctly in Rhino 7 if the correct value is returned. + // After some discussion (see the bug above), we will leave the invers bug in + // ON_UnitSystem::MetersPerUnit(), deprecate ON_UnitSystem::MetersPerUnit(), + // and add a new function that returns the correct answer. + if (ON::LengthUnitSystem::CustomUnits == m_unit_system) + { + // correct answer for custome units - V6 behavior. + return m_meters_per_custom_unit; // + } + + + // For standard units, the inverse of the correct answer is returned + // to preserve V6 bug so VRay works in Rhino 7. + return 1.0/ON_UnitSystem::MetersPerUnit(ON_DBL_QNAN); } ON::LengthUnitSystem ON_UnitSystem::UnitSystem() const @@ -438,12 +486,20 @@ const ON_wString& ON_UnitSystem::UnitSystemName() const break; case ON::LengthUnitSystem::CustomUnits: + if (m_custom_unit_name.IsEmpty()) + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"custom units", s_name); + return s_name; + } return m_custom_unit_name; break; case ON::LengthUnitSystem::Unset: { - return ON_wString::EmptyString; + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"unset", s_name); + return s_name; } break; } @@ -493,7 +549,7 @@ bool ON_UnitSystem::Read( ON_BinaryArchive& file ) { m_unit_system = us; m_custom_unit_name = custom_unit_name; - m_meters_per_unit = meters_per_unit; + m_meters_per_custom_unit = meters_per_unit; } else { @@ -529,9 +585,9 @@ bool ON_UnitSystem::Write( ON_BinaryArchive& file ) const { if (!file.WriteInt(static_cast(m_unit_system))) break; - if (!file.WriteDouble(m_meters_per_unit)) + if (!file.WriteDouble(ON::LengthUnitSystem::CustomUnits == m_unit_system ? m_meters_per_custom_unit : ON::UnitScale(m_unit_system, ON::LengthUnitSystem::Meters))) break; - if (!file.WriteString(m_custom_unit_name)) + if (!file.WriteString(ON::LengthUnitSystem::CustomUnits == m_unit_system ? m_custom_unit_name : ON_wString::EmptyString)) break; rc = true; break; @@ -543,15 +599,22 @@ bool ON_UnitSystem::Write( ON_BinaryArchive& file ) const return rc; } -void ON_UnitSystem::Dump( ON_TextLog& dump ) const +const ON_wString ON_UnitSystem::ToString() const { - ON_wString sUnitSystem(UnitSystemName()); - if ( ON::LengthUnitSystem::CustomUnits == m_unit_system) + ON_wString str(UnitSystemName()); + if (ON::LengthUnitSystem::CustomUnits == m_unit_system) { ON_wString meters_per_unit; - meters_per_unit.Format(L" (= %g meters )", m_meters_per_unit); - sUnitSystem += meters_per_unit; + meters_per_unit.Format(L" (= %g meters )", m_meters_per_custom_unit); + str += meters_per_unit; } + return str; + +} + +void ON_UnitSystem::Dump( ON_TextLog& dump ) const +{ + const ON_wString sUnitSystem(ToString()); dump.Print("Unit system: %ls\n",static_cast(sUnitSystem)); } @@ -580,7 +643,7 @@ bool ON_3dmUnitsAndTolerances::Write( ON_BinaryArchive& file ) const if ( rc ) rc = file.WriteInt( i ); // added in version 102 - if ( rc ) rc = file.WriteDouble( m_unit_system.MetersPerUnit() ); + if ( rc ) rc = file.WriteDouble( m_unit_system.MetersPerUnit(ON_DBL_QNAN)); if ( rc ) rc = file.WriteString( (ON::LengthUnitSystem::CustomUnits == m_unit_system.UnitSystem() ? m_unit_system.UnitSystemName() : ON_wString::EmptyString) ); return rc; } diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp index c05b58f2..a57850d0 100644 --- a/opennurbs_annotationbase.cpp +++ b/opennurbs_annotationbase.cpp @@ -158,8 +158,16 @@ static bool Internal_UpdateOverrideCandidateParentId( { for (;;) { - if (0 == archive.ReferenceModelSerialNumber() && 0 == archive.InstanceDefinitionModelSerialNumber()) + if ( + false == archive.CheckForRemappedIds() + && 0 == archive.ReferenceModelSerialNumber() + && 0 == archive.InstanceDefinitionModelSerialNumber() + ) + { return false; // common situation - no change reqired + } + + if (nullptr == override_candidate) break; const ON_UUID archive_parent_id = override_candidate->ParentId(); @@ -812,11 +820,23 @@ bool ON_Annotation::IsOverrideStylePointer( bool ON_Annotation::AllowTextScaling() const { + // These functions are being added to continue the V5 behavior of + // per-object text scaling. There is no user interface + // in V6 or V7 that shows this setting or that allows a user + // to change this setting. + // AllowTextScaling() = false means the effective dimstyle value + // of DimScale() (model space scale factor) is ignored (treated as if it were 1). return m_allow_text_scaling; } void ON_Annotation::SetAllowTextScaling(bool scale) { + // These functions are being added to continue the V5 behavior of + // per-object text scaling. There is no user interface + // in V6 or V7 that shows this setting or that allows a user + // to change this setting. + // AllowTextScaling() = false means the effective dimstyle value + // of DimScale() (model space scale factor) is ignored (treated as if it were 1). if (scale != m_allow_text_scaling) { m_allow_text_scaling = scale ? true : false; diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h index 1fb38864..a192536e 100644 --- a/opennurbs_annotationbase.h +++ b/opennurbs_annotationbase.h @@ -346,6 +346,29 @@ public: bool bRequireSetOverrides ) const; + + /* + Description: + Conceptually, calling this function applies ON_DimsStyle(scale) to the + dimstyle information used for this annotation. + + When an annotation object is in **layout/page space**, this is + the only way top get properties like TextHeight() to scale properly. + + When an annotation object is in **model space** and + **model space scaling is enabled**, + then calling this->SetDimScale(this->DimScale()*scale) + will work as well. + + Parameters: + parent_dimstyle - [in] + scale - [in] + */ + void ScaleOverrideDimstyle( + const ON_DimStyle* parent_dimstyle, + double scale + ); + protected: static bool Internal_IsOverrideDimStyleCandidate( const ON_DimStyle* override_style_candidate, @@ -432,7 +455,11 @@ public: ) const; // These functions are being added to continue the V5 behavior of - // per-object text scaling + // per-object text scaling. There is no user interface + // in V6 or V7 that shows this setting or that allows a user + // to change this setting. + // AllowTextScaling() = false means the effective dimstyle value + // of DimScale() (model space scale factor) is ignored (treated as if it were 1). bool AllowTextScaling() const; void SetAllowTextScaling(bool scale); diff --git a/opennurbs_archive.cpp b/opennurbs_archive.cpp index a5f8f3ac..f08c2fe3 100644 --- a/opennurbs_archive.cpp +++ b/opennurbs_archive.cpp @@ -7199,6 +7199,8 @@ void ON_BinaryArchive::SetModelSerialNumber( m_model_serial_number = model_serial_number; m_reference_model_serial_number = reference_model_serial_number; m_instance_definition_model_serial_number = instance_definition_model_serial_number; + if (0 != m_reference_model_serial_number || 0 != m_instance_definition_model_serial_number) + m_bCheckForRemappedIds = true; } void ON_BinaryArchive::ClearModelSerialNumber() @@ -7209,6 +7211,19 @@ void ON_BinaryArchive::ClearModelSerialNumber() m_instance_definition_model_serial_number = 0; } +void ON_BinaryArchive::SetCheckForRemappedIds( + bool bCheckForRemappedIds +) +{ + this->m_bCheckForRemappedIds = bCheckForRemappedIds ? true : false; +} + +bool ON_BinaryArchive::CheckForRemappedIds() const +{ + return this->m_bCheckForRemappedIds; +} + + unsigned int ON_BinaryArchive::ModelSerialNumber() const { return m_model_serial_number; @@ -11181,7 +11196,7 @@ int ON_BinaryArchive::Internal_Read3dmDimStyle( // That's why we are checking for a matching system dimstyle before using the unit // system from the file. V6_dimstyle->SetUnitSystem(ON::LengthUnitSystem::None); - V6_dimstyle->SetUnitSystemFromContext(true,Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(),ON::LengthUnitSystem::None); + V6_dimstyle->SetUnitSystemFromContext(true,Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(),ON::LengthUnitSystem::None); delete V5_dimstyle; } else diff --git a/opennurbs_archive.h b/opennurbs_archive.h index 5b2f4d27..32b43426 100644 --- a/opennurbs_archive.h +++ b/opennurbs_archive.h @@ -4840,12 +4840,30 @@ public: unsigned int reference_model_serial_number, unsigned int instance_definition_model_serial_number ); + /* Description: Clear() information set by SetModelSerialNumber() do not modify ON_ModelComponent model serial number information when the classes are read. - */ void ClearModelSerialNumber(); + */ + void ClearModelSerialNumber(); + + /* + Parameters: + bCheckForRemappedIds - [in] + true if the archive is reading in a situation where component ids may get remapped. + */ + void SetCheckForRemappedIds( + bool bCheckForRemappedIds + ); + + /* + Returns: + True if the archive is reading in a situation where component ids may get remapped. + */ + bool CheckForRemappedIds() const; + unsigned int ModelSerialNumber() const; unsigned int ReferenceModelSerialNumber() const; unsigned int InstanceDefinitionModelSerialNumber() const; @@ -5200,6 +5218,9 @@ public: private: bool m_SetModelComponentSerialNumbers = false; + bool m_bCheckForRemappedIds = false; + bool m_reservedA = false; + bool m_reservedB = false; unsigned int m_model_serial_number = 0; unsigned int m_reference_model_serial_number = 0; unsigned int m_instance_definition_model_serial_number = 0; diff --git a/opennurbs_archive_manifest.cpp b/opennurbs_archive_manifest.cpp index fdf2e3e4..a66f24f3 100644 --- a/opennurbs_archive_manifest.cpp +++ b/opennurbs_archive_manifest.cpp @@ -698,7 +698,18 @@ ON_NameHash ON_NameHash::Create( { for (int i = 0; i < UTF32_count; i++) { - sUTF32[i] = ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalType::MinimumOrdinal, sUTF32[i]); + if (ON_IsUnicodeSpaceCodePoint(sUTF32[i])) + sUTF32[i] = ON_UnicodeCodePoint::ON_Space; + else + sUTF32[i] = ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalType::MinimumOrdinal, sUTF32[i]); + } + } + else + { + for (int i = 0; i < UTF32_count; i++) + { + if (ON_IsUnicodeSpaceCodePoint(sUTF32[i])) + sUTF32[i] = ON_UnicodeCodePoint::ON_Space; } } diff --git a/opennurbs_brep.cpp b/opennurbs_brep.cpp index af4dbb17..410ee2fb 100644 --- a/opennurbs_brep.cpp +++ b/opennurbs_brep.cpp @@ -6726,6 +6726,24 @@ ON_Brep::IsSolid() const return (bIsManifold && bIsOriented && !bHasBoundary) ? true : false; } +void ON_Brep::SetSolidOrientationForExperts(int solid_orientation) +{ + switch (solid_orientation) + { + case 0: // not a solid + m_is_solid = 3; + break; + case 1: // solid with normals pointing out + m_is_solid = 1; + break; + case -1: // solid with normals pointing in + m_is_solid = 2; + break; + default: + break; + } +} + int ON_Brep::SolidOrientation() const { // m_is_solid values: diff --git a/opennurbs_brep.h b/opennurbs_brep.h index 47c31ce3..4419e721 100644 --- a/opennurbs_brep.h +++ b/opennurbs_brep.h @@ -2940,6 +2940,22 @@ public: bool* pbHasBoundary = nullptr ) const; + + /* + Description: + When an expert is 100% certain of a brep's solid orientation, this function + can be used to set the SolidOrientation() property. + Parameters: + solid_orientation - [in] + 0: not solid, + 1: oriented manifold solid (no boundary) with outward facing normals. + -1: oriented manifold solid (no boundary) with inward facing normals. + */ + void SetSolidOrientationForExperts( + int solid_orientation + ); + + /* Description: Determine if P is inside Brep. This question only makes sense diff --git a/opennurbs_crc.cpp b/opennurbs_crc.cpp index c8758e16..a9a4d8ed 100644 --- a/opennurbs_crc.cpp +++ b/opennurbs_crc.cpp @@ -75,35 +75,42 @@ ON__UINT16 ON_CRC16( ON__UINT16 current_remainder, size_t count, const void* p ) { ON__UINT16 r1; // update crc remainder - while (count >= 8) - { - // while() loop unrolled for speed - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; - current_remainder = (current_remainder << 8) ^ (*b++); - current_remainder ^= r1; - count -= 8; - } + + // Dale Lear September 2020 + // I was comparing 16-bit crc, 32-bit crc, MAD5 and SHA-1 hash calcualation speeds. + // It turns out, that compilers and optimizers have improved a fair bit since 1994 when this code was written. + // Manual loop unrolling doesn't help (this used to make a measurable difference in 1994 when we used Watcom C on Win 3.1.) + // The TestHashSpeed command tests hashing speeds. Short story - use either ON_CRC32 or ON_SHA1. + + //while (count >= 8) + // { + // // while() loop unrolled for speed + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + // current_remainder = (current_remainder << 8) ^ (*b++); + // current_remainder ^= r1; + // count -= 8; + //} while (count--) { r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; @@ -267,4 +274,195 @@ ON__UINT32 ON_CRC32( ON__UINT32 current_remainder, size_t count, const void* p ) } +/* +Description: + Test speeds of various hash algoritmhs. +Parameters: + byte_count - [in] + Number of bytes to hash. This number is rounded up to the nearest multiple of 1024. + crc16 - [in/out] + If crc16 is not nullptr, then 16 bit CRC hashing is tested using function ON_CRC16(). + crc32 - [in/out] + If crc32 is not nullptr, then 32 bit CRC hashing is tested using function ON_CRC32(). + md5_hash - [in/out] + If md5_hash is not nullptr, then MD5 hashing is tested using class ON_MD5. + sha1_hash - [in/out] + If sha1_hash is not nullptr, then SHA-1 hashing is tested class ON_SHA1. + elapsed_time_in_seconds - [out] + elapsed_time_in_seconds[0] = 16 bit CRC hash time in seconds. + elapsed_time_in_seconds[1] = 32 bit CRC hash time in seconds. + elapsed_time_in_seconds[2] = MD5 hash time in seconds. + elapsed_time_in_seconds[3] = SHA-1 hash time in seconds. + If a hash was tested, then number of seconds it took to compute the hash is returned. + Otherwise ON_DBL_QNAN is returned. + +*/ +void ON_TestHashSpeed( + size_t byte_count, + ON__UINT16* crc16, + ON__UINT32* crc32, + ON_MD5_Hash* md5_hash, + ON_SHA1_Hash* sha1_hash, + double elapsed_time_in_seconds[4] +) +{ + for (int i = 0; i < 4; ++i) + elapsed_time_in_seconds[i] = ON_DBL_QNAN; + + ON_RandomNumberGenerator rng; + ON_SimpleArray buffer_array(1024); + for (int i = 0; i < buffer_array.Capacity(); ++i) + buffer_array.Append(rng.RandomNumber()); + + const ON__UINT32* buffer = buffer_array.Array(); + const size_t sizeof_buffer = buffer_array.UnsignedCount() * sizeof(buffer[0]); + + ON_StopWatch sw; + + if (nullptr != crc16) + { + sw.Start(); + ON__UINT16 h16 = 0; + for (size_t count = 0; count < byte_count; count += sizeof_buffer) + h16 = ON_CRC16(h16, sizeof_buffer, buffer); + *crc16 = h16; + sw.Stop(); + elapsed_time_in_seconds[0] = sw.ElapsedTime(); + } + + if (nullptr != crc32) + { + sw.Start(); + ON__UINT32 h32 = 0; + for (size_t count = 0; count < byte_count; count += sizeof_buffer) + h32 = ON_CRC32(h32, sizeof_buffer, buffer); + *crc32 = h32; + sw.Stop(); + elapsed_time_in_seconds[1] = sw.ElapsedTime(); + } + + if (nullptr != md5_hash) + { + sw.Start(); + ON_MD5 md5; + for (size_t count = 0; count < byte_count; count += sizeof_buffer) + md5.AccumulateBytes(buffer, sizeof_buffer); + *md5_hash = md5.Hash(); + sw.Stop(); + elapsed_time_in_seconds[2] = sw.ElapsedTime(); + } + + if (nullptr != sha1_hash) + { + sw.Start(); + ON_SHA1 sha1; + for (size_t count = 0; count < byte_count; count += sizeof_buffer) + sha1.AccumulateBytes(buffer, sizeof_buffer); + *sha1_hash = sha1.Hash(); + sw.Stop(); + elapsed_time_in_seconds[3] = sw.ElapsedTime(); + } + + return; +} + +void ON_TestHashSpeed( + size_t byte_count, + bool bTestCRC16, + bool bTestCRC32, + bool bTestMD5, + bool bTestSHA1, + ON_TextLog& text_log +) +{ + ON__UINT16 crc16 = 0; + ON__UINT32 crc32 = 0; + ON_MD5_Hash md5_hash = ON_MD5_Hash::ZeroDigest; + ON_SHA1_Hash sha1_hash = ON_SHA1_Hash::ZeroDigest; + double elapsed_time_in_seconds[4] = {}; + + byte_count = (byte_count / 1024) * 1024; + + ON_TestHashSpeed( + byte_count, + bTestCRC16 ? &crc16 : nullptr, + bTestCRC32 ? &crc32 : nullptr, + bTestMD5 ? &md5_hash : nullptr, + bTestSHA1 ? &sha1_hash : nullptr, + elapsed_time_in_seconds + ); + +#if defined(ON_DEBUG) + const char* str = "Debug opennurbs hashing times for "; +#else + const char* str = "Release opennurbs hashing times for "; +#endif + text_log.Print(str); + + const size_t one_kb = 1024; + const size_t one_mb = one_kb * one_kb; + const size_t one_gb = one_kb * one_kb * one_kb; + if (byte_count >= one_gb && 0 == byte_count % one_gb) + text_log.Print("%zu GB:\n", byte_count / one_gb); + else if (byte_count >= one_mb && 0 == byte_count % one_mb) + text_log.Print("%zu MB:\n", byte_count / one_mb); + else if (byte_count >= one_kb && 0 == byte_count % one_kb) + text_log.Print("%zu KB:\n", byte_count / one_kb); + else + text_log.Print("%zu bytes:\n", byte_count); + + const ON_TextLogIndent indent1(text_log); + + const ON_String hashname[4] = { + "16 bit CRC", + "32 bit crc", + "MD5", + "SHA-1" + }; + const bool bTested[4] = { bTestCRC16 ,bTestCRC32 ,bTestMD5 ,bTestSHA1 }; + + // Set i0 = index of the fastest hash test + int i0 = -1; + for (int i = 0; i < 4; i++) + { + if (bTested[i] && elapsed_time_in_seconds[i] > 0.0) + { + if (i0 < 0 || elapsed_time_in_seconds[i] < elapsed_time_in_seconds[i0]) + i0 = i; + } + } + + for (int i = 0; i < 4; ++i) + { + if (bTested[i]) + { + const int test_dex = 0; + text_log.Print("%s: %g seconds.", static_cast(hashname[i]), elapsed_time_in_seconds[i]); + if (elapsed_time_in_seconds[i] > 0.0 && i0 >= 0 && i0 != i) + text_log.Print(" (%g x %s)", elapsed_time_in_seconds[i] / elapsed_time_in_seconds[i0], static_cast(hashname[i0])); + text_log.PrintNewLine(); + } + } +} + +void ON_TestHashSpeed( + size_t byte_count, + ON_TextLog& text_log +) +{ + const bool bTestCRC16 = true; + const bool bTestCRC32 = true; + const bool bTestMD5 = true; + const bool bTestSHA1 = true; + ON_TestHashSpeed( + byte_count, + bTestCRC16, + bTestCRC32, + bTestMD5, + bTestSHA1, + text_log + ); +} + + diff --git a/opennurbs_defines.cpp b/opennurbs_defines.cpp index be404ed9..04a56c3f 100644 --- a/opennurbs_defines.cpp +++ b/opennurbs_defines.cpp @@ -1050,7 +1050,7 @@ double ON::UnitScale( && ON::LengthUnitSystem::CustomUnits != us_from ) { - const double meters_per_custom_unit = us_to.MetersPerUnit(); + const double meters_per_custom_unit = us_to.MetersPerUnit(ON_DBL_QNAN); if ( meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE ) { scale *= meters_per_custom_unit; @@ -1096,7 +1096,7 @@ double ON::UnitScale( && ON::LengthUnitSystem::CustomUnits != us_to ) { - const double meters_per_custom_unit = us_from.MetersPerUnit(); + const double meters_per_custom_unit = us_from.MetersPerUnit(ON_DBL_QNAN); if ( meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE ) { scale /= meters_per_custom_unit; @@ -1132,8 +1132,8 @@ double ON::UnitScale( return ON::UnitScale( us_from, us_to ); // uncommon custom units case - const double meters_per_unit_from = u_and_t_from.MetersPerUnit(); - const double meters_per_unit_to = u_and_t_to.MetersPerUnit(); + const double meters_per_unit_from = u_and_t_from.MetersPerUnit(ON_DBL_QNAN); + const double meters_per_unit_to = u_and_t_to.MetersPerUnit(ON_DBL_QNAN); if (meters_per_unit_from == meters_per_unit_to) return 1.0; double scale = 1.0; diff --git a/opennurbs_defines.h b/opennurbs_defines.h index dbd83ca2..e362b4be 100644 --- a/opennurbs_defines.h +++ b/opennurbs_defines.h @@ -133,14 +133,23 @@ #endif #if defined(PI) +/* double precision ON_PI = 3.141592653589793238462643. ON_PI radians = 180 degrees */ #define ON_PI PI #else +/* double precision ON_PI = 3.141592653589793238462643. ON_PI radians = 180 degrees */ #define ON_PI 3.141592653589793238462643 #endif +/* double precision ON_2PI = 2.0*ON_PI. ON_2PI radians = 360 degrees. */ #define ON_2PI (2.0*ON_PI) +/* double precision ON_HALFPI = 0.5*ON_PI. ON_HALFPI radians = 90 degrees. */ +#define ON_HALFPI (0.5*ON_PI) + +/* angle_in_degrees = ON_DEGREES_TO_RADIANS*angle_in_radians */ #define ON_DEGREES_TO_RADIANS (ON_PI/180.0) + +/* angle_in_radians = ON_RADIANS_TO_DEGREES*angle_in_degrees */ #define ON_RADIANS_TO_DEGREES (180.0/ON_PI) /* @@ -1384,9 +1393,11 @@ public: 12.0 = ON::UnitScale( ON::LengthUnitSystem::Feet, ON::LengthUnitSystem::Inches ) Remarks: - If you are using custom unit systems, use the version - that takes ON_UnitSystem or ON_3dmUnitsAndTolerances - parameters. + If you are using custom unit systems, use the version that takes ON_UnitSystem + or ON_3dmUnitsAndTolerances parameters. + If either parameter is ON::LengthUnitSystem::Unset, then ON_DBL_QNAN is returned. + If either parameter is ON::LengthUnitSystem::None, then 1.0 is returned. + If either parameter is ON::LengthUnitSystem::CustomUnits, then 1.0 is returned. */ static double UnitScale( ON::LengthUnitSystem us_from, diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp index 745a9cd3..42b7f5d7 100644 --- a/opennurbs_dimensionstyle.cpp +++ b/opennurbs_dimensionstyle.cpp @@ -2175,7 +2175,7 @@ bool ON_DimStyle::CompareFields(const ON_DimStyle& style) const m_signed_ordinate == style.m_signed_ordinate && m_scale_value.LeftToRightScale() == style.m_scale_value.LeftToRightScale() && - m_unitsystem == style.m_unitsystem && + m_dimstyle_unitsystem == style.m_dimstyle_unitsystem && m_text_orientation == style.m_text_orientation && m_leader_text_orientation == style.m_leader_text_orientation && m_dim_text_orientation == style.m_dim_text_orientation && @@ -2478,7 +2478,7 @@ bool ON_DimStyle::Write( if (!m_scale_value.Write(file)) break; - u = static_cast(m_unitsystem); + u = static_cast(this->UnitSystem()); if (!file.WriteInt(u)) break; // END chunk version 1.1 information @@ -2966,9 +2966,11 @@ bool ON_DimStyle::Read( if (!m_scale_value.Read(file)) break; - u = static_cast(m_unitsystem); + u = static_cast(this->UnitSystem()); if (!file.ReadInt(&u)) break; - m_unitsystem = ON::LengthUnitSystemFromUnsigned(u); + m_dimstyle_unitsystem = ON::LengthUnitSystemFromUnsigned(u); + if (ON::LengthUnitSystem::Unset == m_dimstyle_unitsystem || ON::LengthUnitSystem::CustomUnits == m_dimstyle_unitsystem) + m_dimstyle_unitsystem = ON::LengthUnitSystem::None; // END chunk version 1.1 information if (minor_version <= 1) @@ -3627,7 +3629,7 @@ const class ON_SHA1_Hash ON_DimStyle::TextPositionPropertiesHash() const sha1.AccumulateUnsigned32(static_cast(m_leader_text_horizontal_alignment)); sha1.AccumulateDouble(m_scale_value.LeftToRightScale()); - sha1.AccumulateUnsigned32(static_cast(m_unitsystem)); + sha1.AccumulateUnsigned32(static_cast(this->UnitSystem())); sha1.AccumulateUnsigned32(static_cast(m_text_orientation)); sha1.AccumulateUnsigned32(static_cast(m_leader_text_orientation)); @@ -4555,24 +4557,66 @@ void ON_DimStyle::SetSignedOrdinate(bool allowsigned) ON::LengthUnitSystem ON_DimStyle::UnitSystem() const { - return m_unitsystem; + /// NOTE WELL: A dimstyle unit system was added in V6, but has never been fully used. + /// The idea was this would make it easier to figure out what text height/ arrow size, + /// ... actually meant. Especially in situations where model space and page space have + /// different unit systems, and in more complex cases like text in instance definitions + /// and inserting annotation from models with mismatched unit systems. + /// It is used internally to get some scales properly set and use in limited + /// merging contexts. + /// + /// From a user's perspective, in Rhino 6 and Rhino 7 ON_DimStyle lengths like TextHeight(), ArrowSize(), ... + /// are with respect to the context the annotation resides in. For example, if TextHeight() = 3.5, + /// model units = meters, page units = millimters, and DimScale() = 1, then + /// text created in model space will be 3.5 meters high and + /// text created in page space will be 3.5 millimeters high. + /// + /// Ideally, ON_DimStyle::UnitSystem() would specify the text height units + /// and ON_DimStyle::DimScale() cound be adjusted as model space extents require. + /// Text in instance definitions would have a well defined height and references + /// to those instance defintions would display predictably in both model space and page space. + + // It is critical that this function never return Unset or CustomUnits. + // returning None insures unknown scal values are 1 instead of ON_DBL_QNAN + if (ON::LengthUnitSystem::Unset == m_dimstyle_unitsystem || ON::LengthUnitSystem::CustomUnits == m_dimstyle_unitsystem) + return ON::LengthUnitSystem::None; + + return m_dimstyle_unitsystem; } void ON_DimStyle::SetUnitSystem(ON::LengthUnitSystem us) { - if (ON::LengthUnitSystem::CustomUnits == us) + /// NOTE WELL: A dimstyle unit system was added in V6, but has never been fully used. + /// The idea was this would make it easier to figure out what text height/ arrow size, + /// ... actually meant. Especially in situations where model space and page space have + /// different unit systems, and in more complex cases like text in instance definitions + /// and inserting annotation from models with mismatched unit systems. + /// It is used internally to get some scales properly set and use in limited + /// merging contexts. + /// + /// From a user's perspective, in Rhino 6 and Rhino 7 ON_DimStyle lengths like TextHeight(), ArrowSize(), ... + /// are with respect to the context the annotation resides in. For example, if TextHeight() = 3.5, + /// model units = meters, page units = millimters, and DimScale() = 1, then + /// text created in model space will be 3.5 meters high and + /// text created in page space will be 3.5 millimeters high. + /// + /// Ideally, ON_DimStyle::UnitSystem() would specify the text height units + /// and ON_DimStyle::DimScale() cound be adjusted as model space extents require. + /// Text in instance definitions would have a well defined height and references + /// to those instance defintions would display predictably in both model space and page space. + if (ON::LengthUnitSystem::CustomUnits == us || ON::LengthUnitSystem::Unset == us) { - ON_ERROR("Annotation styles cannot have custom length units."); + ON_ERROR("Annotation styles cannot have unset or custom length units."); + us = ON::LengthUnitSystem::Millimeters; // Using None insures scale values are 1 instead of ON_DBL_QNAN } - else + + if (m_dimstyle_unitsystem != us) { - if (m_unitsystem != us) - { - m_unitsystem = us; - Internal_ContentChange(); - } - Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::UnitSystem); + m_dimstyle_unitsystem = us; + Internal_ContentChange(); } + + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::UnitSystem); } void ON_DimStyle::SetUnitSystemFromContext( @@ -4581,6 +4625,26 @@ void ON_DimStyle::SetUnitSystemFromContext( ON::LengthUnitSystem destination_unit_system ) { + /// NOTE WELL: A dimstyle unit system was added in V6, but has never been fully used. + /// The idea was this would make it easier to figure out what text height/ arrow size, + /// ... actually meant. Especially in situations where model space and page space have + /// different unit systems, and in more complex cases like text in instance definitions + /// and inserting annotation from models with mismatched unit systems. + /// It is used internally to get some scales properly set and use in limited + /// merging contexts. + /// + /// From a user's perspective, in Rhino 6 and Rhino 7 ON_DimStyle lengths like TextHeight(), ArrowSize(), ... + /// are with respect to the context the annotation resides in. For example, if TextHeight() = 3.5, + /// model units = meters, page units = millimters, and DimScale() = 1, then + /// text created in model space will be 3.5 meters high and + /// text created in page space will be 3.5 millimeters high. + /// + /// Ideally, ON_DimStyle::UnitSystem() would specify the text height units + /// and ON_DimStyle::DimScale() cound be adjusted as model space extents require. + /// Text in instance definitions would have a well defined height and references + /// to those instance defintions would display predictably in both model space and page space. + + ON::LengthUnitSystem dim_style_units = ON::LengthUnitSystemFromUnsigned(static_cast(UnitSystem())); switch (dim_style_units) @@ -4608,7 +4672,7 @@ void ON_DimStyle::SetUnitSystemFromContext( const ON_DimStyle& from_name = ON_DimStyle::SystemDimstyleFromName(name_hash); if (name_hash == from_name.NameHash() && name_hash != ON_DimStyle::Default.NameHash()) { - dim_style_units = from_name.m_unitsystem; + dim_style_units = from_name.UnitSystem(); continue; } } @@ -4619,7 +4683,7 @@ void ON_DimStyle::SetUnitSystemFromContext( const ON_DimStyle& from_id = ON_DimStyle::SystemDimstyleFromId(id); if (id == from_id.Id() && id != ON_DimStyle::Default.Id() ) { - dim_style_units = from_id.m_unitsystem; + dim_style_units = from_id.UnitSystem(); continue; } } @@ -4724,7 +4788,7 @@ static bool Internal_IsUnsetDimstyleUnitSystem( bool ON_DimStyle::UnitSystemIsSet() const { - return false == Internal_IsUnsetDimstyleUnitSystem(m_unitsystem); + return false == Internal_IsUnsetDimstyleUnitSystem(this->UnitSystem()); } void ON_DimStyle::SetDimScale( @@ -5671,9 +5735,17 @@ double ON_DimStyle::BaselineSpacing() const } //------------------- + +static bool Internal_IsValidDimStyleScale(double scale) +{ + return ON_IsValidPositiveNumber(scale) && fabs(scale - 1.0) > ON_ZERO_TOLERANCE; +} + void ON_DimStyle::Scale(double scale) { - if (ON_IsValid(scale) && scale > ON_SQRT_EPSILON && 1.0 != scale) + // If you modify this code, be sure to update ON_Annotation::ScaleOverrideDimstyle(). + + if (Internal_IsValidDimStyleScale(scale)) { m_extextension *= scale; m_extoffset *= scale; @@ -5691,6 +5763,99 @@ void ON_DimStyle::Scale(double scale) } } + + +double Internal_ScaleOverrideLength( + double parent_dimstyle_length, + double current_dimstyle_length, + double scale +) +{ + // returns scaled dimstyle length + const double x = current_dimstyle_length * scale; + const double rel_tol = 1.0e-6 * fabs(parent_dimstyle_length); + const double abs_tol = ON_ZERO_TOLERANCE; + const double delta = fabs(x - parent_dimstyle_length); + return (delta <= rel_tol || delta <= abs_tol) ? parent_dimstyle_length : x; +} + +void ON_Annotation::ScaleOverrideDimstyle( + const ON_DimStyle* parent_dimstyle, + double scale +) +{ + // Dale Lear Setp 2020 https://mcneel.myjetbrains.com/youtrack/issue/RH-60536 + // I added this function to scale the appearance of annotation + // in layout/page views. Using DimScale() does not work in layout/page + // views because it only applies to annotation in model space views. + + if (Internal_IsValidDimStyleScale(scale)) + { + // The list of properties here must exactly match what is in void ON_DimStyle::Scale(double scale). + const ON_DimStyle d0 = ON_DimStyle::DimStyleOrDefault(parent_dimstyle); + + // x0 = current value (may already be an override and different from the d0 value) + // x1 = scaled value (may end up being the d0 value) + double x0, x1; + + // m_extextension *= scale; + x0 = this->ExtensionLineExtension(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.ExtExtension(), x0, scale); + this->SetExtensionLineExtension(parent_dimstyle, x1); + + // m_extoffset *= scale + x0 = this->ExtensionLineOffset(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.ExtOffset(), x0, scale); + this->SetExtensionLineOffset(parent_dimstyle, x1); + + // m_arrowsize *= scale; + x0 = this->ArrowSize(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.ArrowSize(), x0, scale); + this->SetArrowSize(parent_dimstyle, x1); + + // m_centermark *= scale; + x0 = this->CenterMarkSize(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.CenterMark(), x0, scale); + this->SetCenterMarkSize(parent_dimstyle, x1); + + // m_textgap *= scale; + x0 = this->TextGap(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.TextGap(), x0, scale); + this->SetTextGap(parent_dimstyle, x1); + + // m_textheight *= scale + x0 = this->TextHeight(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.TextHeight(), x0, scale); + this->SetTextHeight(parent_dimstyle, x1); + + // m_dimextension *= scale; + x0 = this->DimExtension(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.DimExtension(), x0, scale); + this->SetDimExtension(parent_dimstyle, x1); + + // m_baseline_spacing *= scale; + x0 = this->BaselineSpacing(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.BaselineSpacing(), x0, scale); + this->SetBaselineSpacing(parent_dimstyle, x1); + + // m_fixed_extension_len *= scale; + x0 = this->FixedExtensionLength(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.FixedExtensionLen(), x0, scale); + this->SetFixedExtensionLength(parent_dimstyle, x1); + + // m_leaderarrowsize *= scale; + x0 = this->LeaderArrowSize(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.LeaderArrowSize(), x0, scale); + this->SetLeaderArrowSize(parent_dimstyle, x1); + + // m_leader_landing_length *= scale; + x0 = this->LeaderLandingLength(parent_dimstyle); + x1 = Internal_ScaleOverrideLength(d0.LeaderLandingLength(), x0, scale); + this->SetLeaderLandingLength(parent_dimstyle, x1); + } + +} + void ON_DimStyle::SetToleranceFormat(ON_DimStyle::tolerance_format format) { if (m_tolerance_format != format) diff --git a/opennurbs_dimensionstyle.h b/opennurbs_dimensionstyle.h index c242a801..0bdd145e 100644 --- a/opennurbs_dimensionstyle.h +++ b/opennurbs_dimensionstyle.h @@ -262,6 +262,11 @@ public: static const ON_DimStyle DefaultMillimeterSmall; // index = -5, unique and persistent id. static const ON_DimStyle DefaultMillimeterLarge; // index = -6, unique and persistent id. static const ON_DimStyle DefaultMillimeterArchitecture; // index = -7, unique and persistent id. + static const ON_DimStyle DefaultFeetDecimal; // index = -8, unique and persistent id. + static const ON_DimStyle DefaultFeetEngrave; // index = -9, unique and persistent id. + static const ON_DimStyle DefaultMillimeterEngrave; // index = -10, unique and persistent id. + static const ON_DimStyle DefaultModelUnitsDecimal; // index = -11, unique and persistent id. + static const ON_DimStyle DefaultModelUnitsEngrave; // index = -12, unique and persistent id. public: /* @@ -1586,7 +1591,7 @@ public: In Rhino, use CRhinoDoc.DimStyleContext() to get an ON_DimStyleContext. In an ONX_Model, use ONX_Model.DimStyleContext() to get an ON_DimStyleContext. In other situations, you can pass on of the system dimstyles like - ON_DimStyle::DefaultMillimeterSmall or ON_DimStyle::DefaultMillimeterArchitecture. + ON_DimStyle::DefaultMillimeterSmall or ON_DimStyle::DefaultMillimeterArchitectural. The worst possible choices are ON_DimStyle::Default or ON_DimStyle::Unset. annotation_type - [in] ON::AnnotationType::Unset if style will be used for multiple types of annotation @@ -1965,16 +1970,46 @@ public: void SetSignedOrdinate(bool allowsigned); /// - /// Unit system for dimension rendering sizes like TextHeight, TextGap, ArrowSize, ExtOffset, - /// and dozens of other properties that control the appearance and placement of the components - /// used to render a dimension. + /// NOTE WELL: A dimstyle unit system was added in V6, but has never been fully used. + /// The idea was this would make it easier to figure out what text height/ arrow size, + /// ... actually meant. Especially in situations where model space and page space have + /// different unit systems, and in more complex cases like text in instance definitions + /// and inserting annotation from models with mismatched unit systems. + /// It is used internally to get some scales properly set and use in limited + /// merging contexts. + /// + /// From a user's perspective, in Rhino 6 and Rhino 7 ON_DimStyle lengths like TextHeight(), ArrowSize(), ... + /// are with respect to the context the annotation resides in. For example, if TextHeight() = 3.5, + /// model units = meters, page units = millimters, and DimScale() = 1, then + /// text created in model space will be 3.5 meters high and + /// text created in page space will be 3.5 millimeters high. + /// + /// Ideally, ON_DimStyle::UnitSystem() would specify the text height units + /// and ON_DimStyle::DimScale() cound be adjusted as model space extents require. + /// Text in instance definitions would have a well defined height and references + /// to those instance defintions would display predictably in both model space and page space. /// ON::LengthUnitSystem UnitSystem() const; /// - /// Unit system for dimension rendering sizes like TextHeight, TextGap, ArrowSize, ExtOffset, - /// and dozens of other properties that control the appearance and placement of the components - /// used to render a dimension. + /// NOTE WELL: A dimstyle unit system was added in V6, but has never been fully used. + /// The idea was this would make it easier to figure out what text height/ arrow size, + /// ... actually meant. Especially in situations where model space and page space have + /// different unit systems, and in more complex cases like text in instance definitions + /// and inserting annotation from models with mismatched unit systems. + /// It is used internally to get some scales properly set and use in limited + /// merging contexts. + /// + /// From a user's perspective, in Rhino 6 and Rhino 7 ON_DimStyle lengths like TextHeight(), ArrowSize(), ... + /// are with respect to the context the annotation resides in. For example, if TextHeight() = 3.5, + /// model units = meters, page units = millimters, and DimScale() = 1, then + /// text created in model space will be 3.5 meters high and + /// text created in page space will be 3.5 millimeters high. + /// + /// Ideally, ON_DimStyle::UnitSystem() would specify the text height units + /// and ON_DimStyle::DimScale() cound be adjusted as model space extents require. + /// Text in instance definitions would have a well defined height and references + /// to those instance defintions would display predictably in both model space and page space. /// void SetUnitSystem(ON::LengthUnitSystem us); @@ -1983,17 +2018,17 @@ public: When a dimension style unit system is not set, this function examines the context the dimension style is being used in and sets the unit system. + Ideally, both source_unit_system and destination_unit_system are page space units. + Less ideally, both source_unit_system and destination_unit_system are model space units. Parameters: bUseName - [in] Consider the name when assinging a unit system. For example, a dimension style name "Millimters Small" would be assinged a unit system of millimeters. source_unit_system - [in] - unit system in the model or file where the dimension - style originated. + unit system in the context where the dimension style originated. destination_unit_system - [in] - unit system in the model or file where the dimension - style will be used. + unit system in the context where the dimension style will be used. */ void SetUnitSystemFromContext( bool bUseName, @@ -2002,6 +2037,24 @@ public: ); /* + /// NOTE WELL: A dimstyle unit system was added in V6, but has never been fully used. + /// The idea was this would make it easier to figure out what text height/ arrow size, + /// ... actually meant. Especially in situations where model space and page space have + /// different unit systems, and in more complex cases like text in instance definitions + /// and inserting annotation from models with mismatched unit systems. + /// It is used internally to get some scales properly set and use in limited + /// merging contexts. + /// + /// From a user's perspective, in Rhino 6 and Rhino 7 ON_DimStyle lengths like TextHeight(), ArrowSize(), ... + /// are with respect to the context the annotation resides in. For example, if TextHeight() = 3.5, + /// model units = meters, page units = millimters, and DimScale() = 1, then + /// text created in model space will be 3.5 meters high and + /// text created in page space will be 3.5 millimeters high. + /// + /// Ideally, ON_DimStyle::UnitSystem() would specify the text height units + /// and ON_DimStyle::DimScale() cound be adjusted as model space extents require. + /// Text in instance definitions would have a well defined height and references + /// to those instance defintions would display predictably in both model space and page space. Returns: true if the unit system is set to an explicit valid length unit. */ @@ -2335,11 +2388,25 @@ private: ON_ScaleValue m_scale_value = ON_ScaleValue::OneToOne; - // Unit system for dimension rendering sizes like text height, and arrow head length. - // m_textheight, m_textgap, m_arrowsize, m_extoffset, and dozens of other properties - // that control the appearance and placement of the components used to render - // a dimension. - ON::LengthUnitSystem m_unitsystem = ON::LengthUnitSystem::None; + /// NOTE WELL: A dimstyle unit system was added in V6, but has never been fully used. + /// The idea was this would make it easier to figure out what text height/ arrow size, + /// ... actually meant. Especially in situations where model space and page space have + /// different unit systems, and in more complex cases like text in instance definitions + /// and inserting annotation from models with mismatched unit systems. + /// It is used internally to get some scales properly set and use in limited + /// merging contexts. + /// + /// From a user's perspective, in Rhino 6 and Rhino 7 ON_DimStyle lengths like TextHeight(), ArrowSize(), ... + /// are with respect to the context the annotation resides in. For example, if TextHeight() = 3.5, + /// model units = meters, page units = millimters, and DimScale() = 1, then + /// text created in model space will be 3.5 meters high and + /// text created in page space will be 3.5 millimeters high. + /// + /// Ideally, ON_DimStyle::UnitSystem() would specify the text height units + /// and ON_DimStyle::DimScale() cound be adjusted as model space extents require. + /// Text in instance definitions would have a well defined height and references + /// to those instance defintions would display predictably in both model space and page space. + ON::LengthUnitSystem m_dimstyle_unitsystem = ON::LengthUnitSystem::None; ON::TextOrientation m_text_orientation = ON::TextOrientation::InPlane; ON::TextOrientation m_leader_text_orientation = ON::TextOrientation::InPlane; diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp index b2cf58d1..53031897 100644 --- a/opennurbs_font.cpp +++ b/opennurbs_font.cpp @@ -925,9 +925,16 @@ const ON_Font* ON_ManagedFonts::GetFromFontCharacteristics( managed_font ); + // Lowell - Added additional check for matching underline and strikethrough settings RH-60947 + unsigned int managed_font_us_dev = ON_Font::UnderlinedStrikethroughDeviation( + set_font_characteristics, + managed_font + ); + if ( nullptr != managed_font && 0 == managed_font_wss_dev + && 0 == managed_font_us_dev && point_size == managed_font->PointSize() ) { @@ -981,7 +988,18 @@ const ON_Font* ON_ManagedFonts::GetFromFontCharacteristics( managed_font ); - if (nullptr != managed_font && 0 == managed_font_wss_dev) + // RH - 60947 + managed_font_us_dev = ON_Font::UnderlinedStrikethroughDeviation( + set_font_characteristics->IsUnderlined(), + set_font_characteristics->IsStrikethrough(), + managed_font + ); + + + if (nullptr != managed_font + && 0 == managed_font_wss_dev + && 0 == managed_font_us_dev + ) return managed_font; break; @@ -1958,6 +1976,46 @@ unsigned int ON_FontFaceQuartet::FaceCount() const return face_count; } +const ON_wString ON_FontFaceQuartet::MemberToString( + ON_FontFaceQuartet::Member member +) +{ + switch (member) + { + case ON_FontFaceQuartet::Member::Unset: + return ON_wString(L"Unset"); + break; + case ON_FontFaceQuartet::Member::Regular: + return ON_wString(L"Regular"); + break; + case ON_FontFaceQuartet::Member::Bold: + return ON_wString(L"Bold"); + break; + case ON_FontFaceQuartet::Member::Italic: + return ON_wString(L"Italic"); + break; + case ON_FontFaceQuartet::Member::BoldItalic: + return ON_wString(L"Bold-Italic"); + break; + } + return ON_wString::EmptyString; +} + +ON_FontFaceQuartet::Member ON_FontFaceQuartet::MemberFromUnsigned( + unsigned int member_as_unsigned +) +{ + switch (member_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontFaceQuartet::Member::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontFaceQuartet::Member::Regular); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontFaceQuartet::Member::Bold); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontFaceQuartet::Member::Italic); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontFaceQuartet::Member::BoldItalic); + } + return ON_FontFaceQuartet::Member::Unset; +} + void ON_FontFaceQuartet::Dump(ON_TextLog& text_log) const { text_log.Print(L"Quartet Name: %ls\n", static_cast(m_quartet_name)); diff --git a/opennurbs_font.h b/opennurbs_font.h index b323ac4d..8b90a2ba 100644 --- a/opennurbs_font.h +++ b/opennurbs_font.h @@ -2237,6 +2237,14 @@ public: BoldItalic = 4 }; + static const ON_wString MemberToString( + ON_FontFaceQuartet::Member member + ); + + static ON_FontFaceQuartet::Member MemberFromUnsigned( + unsigned int member_as_unsigned + ); + ON_FontFaceQuartet() = default; ~ON_FontFaceQuartet() = default; ON_FontFaceQuartet(const ON_FontFaceQuartet&) = default; diff --git a/opennurbs_hatch.cpp b/opennurbs_hatch.cpp index 2e5b6235..b94a4581 100644 --- a/opennurbs_hatch.cpp +++ b/opennurbs_hatch.cpp @@ -1726,7 +1726,7 @@ static void UnrotateHatch(ON_Hatch* hatch) base2.Rotate(a, ON_2dPoint::Origin); hatch->SetBasePoint(base2); - //hatch->SetPatternRotation(hatch->PatternRotation()+a); + hatch->SetPatternRotation(hatch->PatternRotation()+a); } // Project world origin to hatch plane and set hatch plane origin to the result // Translate hatch 2d curves to get back to the right position diff --git a/opennurbs_instance.cpp b/opennurbs_instance.cpp index b88de29a..a387abc5 100644 --- a/opennurbs_instance.cpp +++ b/opennurbs_instance.cpp @@ -2392,7 +2392,7 @@ bool ON_InstanceDefinition::Internal_WriteV5( break; // version 1.3 fields - added 6 March 2006 - if (!binary_archive.WriteDouble(m_us.MetersPerUnit())) + if (!binary_archive.WriteDouble(m_us.MetersPerUnit(ON_DBL_QNAN))) break; const bool bLegacyBoolThatIsAlwasyFalse = false; diff --git a/opennurbs_internal_Vx_annotation.cpp b/opennurbs_internal_Vx_annotation.cpp index 03a999ee..a3c5ecac 100644 --- a/opennurbs_internal_Vx_annotation.cpp +++ b/opennurbs_internal_Vx_annotation.cpp @@ -308,9 +308,7 @@ void ON_Text::Internal_SetObsoleteV5TextObjectInformation( break; const double unit_scale = ON::UnitScale(V5_model_space_object_unit_system, dim_style.UnitSystem()); - if (!ON_IsValid(unit_scale)) - break; - if (!(unit_scale > 0.0)) + if (false == ON_IsValidPositiveNumber(unit_scale)) break; const double V6_object_text_height = unit_scale*obsolete_V5_text_object_height; diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp index 1b9faea7..d46102b8 100644 --- a/opennurbs_line.cpp +++ b/opennurbs_line.cpp @@ -589,6 +589,13 @@ ON_Triangle::ON_Triangle(double x) m_V[0] = m_V[1] = m_V[2] = p; } +ON_Triangle::ON_Triangle(const double vertices[9]) +{ + m_V[0] = ON_3dPoint(vertices); + m_V[1] = ON_3dPoint(vertices ? vertices+3 : vertices); + m_V[2] = ON_3dPoint(vertices ? vertices+6 : vertices); +} + ON_Triangle::operator ON_3dPoint*() { return m_V; @@ -734,7 +741,11 @@ ON_3dPoint ON_Triangle::Centroid() const return PointAt(1.0/3.0, 1.0/3.0); } -bool ON_Triangle::ClosestPointTo(const ON_3dPoint & P, double * s1, double * s2) const +bool ON_Triangle::GetBarycentricCoordinates( + const ON_3dPoint& P, + bool constrainInside, + double* s1, double* s2 +) const { bool rc = false; // Choose base vertex v[i0] is closest to P @@ -761,53 +772,53 @@ bool ON_Triangle::ClosestPointTo(const ON_3dPoint & P, double * s1, double * s2) // use decomposition s[i0] = 1 - s[(i0 + 1) % 3] - s[(i0 + 2) % 3]; - for (int i = 0; i < 3; i++) - { - if (s[i] < 0) - { - double t; - if (Edge(i).ClosestPointTo(P, &t)) - { - s = ON_3dPoint( 0,0,0 ); - if (t < 0) - { - s[(i + 1) % 3] = 1.0; - } + if (constrainInside) + for (int i = 0; i < 3; i++) + if (s[i] < 0) + { + double t; + if (Edge(i).ClosestPointTo(P, &t)) + { + s = ON_3dPoint( 0,0,0 ); + if (t < 0) + { + s[(i + 1) % 3] = 1.0; + } - else if (t >= 1.0) - { - s[(i + 2) % 3] = 1.0; - } - else - { - s[(i + 1) % 3] = 1 - t; - s[(i + 2) % 3] = t; - } - } - break; - } - } + else if (t >= 1.0) + { + s[(i + 2) % 3] = 1.0; + } + else + { + s[(i + 1) % 3] = 1 - t; + s[(i + 2) % 3] = t; + } + } + break; + } rc = true; } else { // decomposition failed: // Find closest point to longest edge i0 - double max = -1; - for (int i = 0; i < 3; i++) + double max = Edge(0).Direction().LengthSquared(); + i0 = 0; + for (int i = 1; i < 3; i++) { - double len = Edge(i).Length(); - if (len > max) + double lensq = Edge(i).Direction().LengthSquared(); + if (max < lensq) { - i0 = 0; - max = len; + i0 = i; + max = lensq; } } double t; if (Edge(i0).ClosestPointTo(P, &t)) { s[(i0 + 1) % 3] = (1 - t); - s[(i0 + 1) % 3] = t; + s[(i0 + 2) % 3] = t; rc = true; } @@ -819,6 +830,11 @@ bool ON_Triangle::ClosestPointTo(const ON_3dPoint & P, double * s1, double * s2) return rc; } +bool ON_Triangle::ClosestPointTo(const ON_3dPoint & P, double * s1, double * s2) const +{ + return GetBarycentricCoordinates(P, true, s1, s2); +} + ON_3dPoint ON_Triangle::ClosestPointTo(const ON_3dPoint& P) const { diff --git a/opennurbs_line.h b/opennurbs_line.h index 84b0cb77..016fa8e8 100644 --- a/opennurbs_line.h +++ b/opennurbs_line.h @@ -321,6 +321,7 @@ public: ON_Triangle(const ON_3dPoint vertices[3]); ON_Triangle(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c); ON_Triangle(double x); // Allows Triangle(0.0) ZeroTriangle + ON_Triangle(const double vertices[9]); ON_Triangle(const ON_Triangle& tri) = default; ON_Triangle& operator=(const ON_Triangle& tri) = default; @@ -475,6 +476,24 @@ public: double* s1, double *s2 ) const; + /* + Description: + Find the point that is closest to the test_point. + Parameters: + test_point - [in] + constrainInside[in] - if true, variable are inside triangle + s1, s2 - [out] PointAt( *s1, *s2) is the point on the + triangle closest to test_point. + + Returns: + true if successful. + */ + bool GetBarycentricCoordinates( + const ON_3dPoint& test_point, + bool constrainInside, + double* s1, double *s2 + ) const; + /* Description: Find the point on the triangle that is diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp index a4031680..e12a6b18 100644 --- a/opennurbs_material.cpp +++ b/opennurbs_material.cpp @@ -1469,6 +1469,9 @@ int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material rc = CompareDouble(a_pbr->OpacityRoughness(), b_pbr->OpacityRoughness()); if (0 != rc) return rc; + rc = CompareDouble(a_pbr->Alpha(), b_pbr->Alpha()); + if (0 != rc) return rc; + rc = a_pbr->Emission().Compare(b_pbr->Emission()); return rc; } @@ -2042,10 +2045,6 @@ ON_Color ON_Material::PreviewColor(void) const bool ON_Material::UseDiffuseTextureAlphaForObjectTransparencyTexture() const { - //Physically based materials do not support alpha transparency (at the moment). - if (IsPhysicallyBased()) - return false; - return m_bUseDiffuseTextureAlphaForObjectTransparencyTexture; } @@ -2332,12 +2331,13 @@ ON_Texture::TYPE ON_Texture::TypeFromUnsigned(unsigned int type_as_unsigned) ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_sheen_tint_texture); ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_texture); ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_roughness_texture); - ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_bump_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_bump_texture); ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_ior_texture); ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_roughness_texture); ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_emission_texture); ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_ambient_occlusion_texture); ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_displacement_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_alpha_texture); } ON_ERROR("Invalid type_as_unsigned value."); @@ -6650,11 +6650,11 @@ private: { ON_ASSERT(IsValid()); - const int chunk_version = 1; + const int chunk_version = 2; if (false == archive.BeginWrite3dmAnonymousChunk(chunk_version)) return false; - bool rc = m_parameters.Write(archive); + bool rc = m_parameters.Write(archive, chunk_version); if (!archive.EndWrite3dmChunk()) { @@ -6672,9 +6672,9 @@ private: bool rc = false; - if (chunk_version == 1) + if (chunk_version == 1 || chunk_version == 2) { - rc = m_parameters.Read(archive); + rc = m_parameters.Read(archive, chunk_version); } if (!archive.EndRead3dmChunk()) @@ -6709,7 +6709,7 @@ public: struct Parameters { #if defined SUPPORT_PBR_USERDATA_SERIALIZATION - bool Write(ON_BinaryArchive& binary_archive) const + bool Write(ON_BinaryArchive& binary_archive, int version) const { if (!binary_archive.WriteColor(base_color)) return false; if (!binary_archive.WriteInt((int)brdf)) return false; @@ -6730,11 +6730,16 @@ public: if (!binary_archive.WriteDouble(opacity)) return false; if (!binary_archive.WriteDouble(opacity_roughness)) return false; if (!binary_archive.WriteColor(emission)) return false; + + if (version >= 2) + { + if (!binary_archive.WriteDouble(alpha)) return false; + } return true; } - bool Read(ON_BinaryArchive& binary_archive) + bool Read(ON_BinaryArchive& binary_archive, int version) { if (!binary_archive.ReadColor(base_color)) return false; if (!binary_archive.ReadInt((int*)&brdf)) return false; @@ -6755,6 +6760,11 @@ public: if (!binary_archive.ReadDouble(&opacity)) return false; if (!binary_archive.ReadDouble(&opacity_roughness)) return false; if (!binary_archive.ReadColor(emission)) return false; + + if (version >= 2) + { + if (!binary_archive.ReadDouble(&alpha)) return false; + } return true; } @@ -6781,6 +6791,7 @@ public: if (ON_IS_UNSET_DOUBLE(opacity)) return false; if (ON_IS_UNSET_DOUBLE(opacity_roughness)) return false; if (!emission.IsValid()) return false; + if (ON_IS_UNSET_DOUBLE(alpha)) return false; return true; } @@ -6804,6 +6815,7 @@ public: double opacity_roughness = 0.0; ON_4fColor emission = ON_Color::Black; ON_PhysicallyBasedMaterial::BRDFs brdf = ON_PhysicallyBasedMaterial::BRDFs::GGX; + double alpha = 1.0; } m_parameters; }; @@ -7107,6 +7119,16 @@ void ON_PhysicallyBasedMaterial::SetEmission(ON_4fColor d) Implementation().UserData().m_parameters.emission = d; } +double ON_PhysicallyBasedMaterial::Alpha(void) const +{ + return Implementation().UserData().m_parameters.alpha; +} + +void ON_PhysicallyBasedMaterial::SetAlpha(double d) +{ + Implementation().UserData().m_parameters.alpha = d; +} + ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_Material& src) { //Placement new - create the impl in the stack allocated space. @@ -7196,6 +7218,18 @@ bool ON_PhysicallyBasedMaterial_Supported(const ON_PhysicallyBasedMaterial& mate return material.BaseColor().IsValid(); } +bool ON_PhysicallyBasedMaterial::UseBaseColorTextureAlphaForObjectAlphaTransparencyTexture() const +{ + ON_Material& mat = *Implementation().material; + return mat.UseDiffuseTextureAlphaForObjectTransparencyTexture(); +} + +void ON_PhysicallyBasedMaterial::SetUseBaseColorTextureAlphaForObjectAlphaTransparencyTexture(bool b) +{ + ON_Material& mat = *Implementation().material; + mat.SetUseDiffuseTextureAlphaForObjectTransparencyTexture(b); +} + void ON_PhysicallyBasedMaterial::ToLegacy(void) { ON_Material& mat = *Implementation().material; diff --git a/opennurbs_material.h b/opennurbs_material.h index 53c49412..6dca0867 100644 --- a/opennurbs_material.h +++ b/opennurbs_material.h @@ -722,6 +722,10 @@ public: virtual ON_4fColor Emission(void) const; virtual void SetEmission(ON_4fColor); + //Controls Alpha transparency - 1.0 is fully opaque, 0.0 is non-visible. Use opacity for refraction - this overrides all other shading. + /*virtual*/ double Alpha(void) const; + /*virtual*/ void SetAlpha(double); + //Texture access functions - exactly the same as ON_Material. Provided for ease of use. virtual int FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0 = -1) const; virtual int AddTexture(const ON_Texture& tx); @@ -738,6 +742,14 @@ public: //Expert function to remove all PBR data from a material virtual void ToLegacy(void); + //These function just route through to ON_Material - PBR materials need to support the same functionality. + // + //If UseBaseColorTextureAlphaForObjectAlphaTransparencyTexture returns true, the alpha channel + //of the texture in m_textures with m_type=pbr_base_color is used in addition to any + //textures with m_type=pbr_alpha_texture. + bool UseBaseColorTextureAlphaForObjectAlphaTransparencyTexture() const; + void SetUseBaseColorTextureAlphaForObjectAlphaTransparencyTexture(bool); + public: class ON_CLASS ParametersNames { diff --git a/opennurbs_math.cpp b/opennurbs_math.cpp index 4219e4e2..9d2158d6 100644 --- a/opennurbs_math.cpp +++ b/opennurbs_math.cpp @@ -157,6 +157,118 @@ double ON_TestMathFunction( int function_index, double x, double y ) return z; } +bool ON_PassesNanTest() +{ + + bool bPassesAllNanTests = false; + for (;;) + { + const double nan1 = ON_DBL_QNAN; + const double nan2 = ON_DBL_QNAN; + const double zero = 0.0; + const double one = 1.0; + + // nan != * and * != nan should alwasy be true + const bool b_NE_test + = nan1 != nan1 + && nan1 != nan2 + && nan1 != zero + && nan1 != one + && zero != nan2 + && one != nan2 + ; + + // nan op * and * op nan when op is ==, < > <= >= should all be false + const bool b_EQ_test + = nan1 == nan1 + || nan1 == nan2 + || nan1 == zero + || nan1 == one + || zero == nan2 + || one == nan2 + ; + + const bool b_LT_test + = nan1 < nan1 + || nan1 < nan2 + || nan1 < zero + || nan1 < one + || zero < nan2 + || one < nan2 + ; + + const bool b_GT_test + = nan1 > nan1 + || nan1 > nan2 + || nan1 > zero + || nan1 > one + || zero > nan2 + || one > nan2 + ; + + const bool b_LE_test + = nan1 <= nan1 + || nan1 <= nan2 + || nan1 <= zero + || nan1 <= one + || zero <= nan2 + || one <= nan2 + ; + + const bool b_GE_test + = nan1 >= nan1 + || nan1 >= nan2 + || nan1 >= zero + || nan1 >= one + || zero >= nan2 + || one >= nan2 + ; + + const bool bPassesIEE754NanCompareTests + = b_NE_test + && false == b_EQ_test + && false == b_LT_test + && false == b_GT_test + && false == b_LE_test + && false == b_GE_test + ; + + if (false == bPassesIEE754NanCompareTests) + { + // some opennurbs code will fail. + ON_ERROR("This compiler does not conform to the IEEE-754 nan compare specification. Some opennurbs code will fail."); + break; + } + + const double x[] = { + nan1 + one, one + nan1, + nan1 - one, one - nan1, + nan1 * one, one * nan1, + nan1 / one, one / nan1 + }; + + const size_t xcount = sizeof(x) / sizeof(x[0]); + bool bPassesNanAritmeticTest = true; + for (size_t i = 0; i < xcount && bPassesNanAritmeticTest; ++i) + { + bPassesNanAritmeticTest = x[i] != x[i]; + } + + if (false == bPassesNanAritmeticTest) + { + // some opennurbs code will fail. + ON_ERROR("This compiler does not conform to the IEEE-754 nan arithmetic specification. Some opennurbs code will fail."); + break; + } + + bPassesAllNanTests = true; + break; + } + + + return bPassesAllNanTests; +} + double ON_DegreesFromRadians( double angle_in_radians ) diff --git a/opennurbs_math.h b/opennurbs_math.h index 9aa94b22..2b3c6abd 100644 --- a/opennurbs_math.h +++ b/opennurbs_math.h @@ -17,6 +17,17 @@ #if !defined(ON_MATH_INC_) #define ON_MATH_INC_ +/* +Returns: + True (1) if nan compares and arithmetic agree with IEEE-754. + (nan != nan) is true. + (nan op x) and (x op nan) is false for op ==, <, >, <=, and >=. + (nan op x) and (x op nan) is nan for op == +, -, *, and /. + False (0) otherwise. +*/ +ON_DECL +bool ON_PassesNanTest(); + class ON_3dVector; class ON_Interval; class ON_Line; @@ -334,11 +345,25 @@ private: Description: Test a double to make sure it is a valid number. Returns: - True if x != ON_UNSET_VALUE and _finite(x) is true. + (x > ON_UNSET_VALUE && x < ON_UNSET_POSITIVE_VALUE) */ ON_DECL bool ON_IsValid( double x ); +/* +Returns: + (x > 0.0 && x < ON_UNSET_POSITIVE_VALUE); +*/ +ON_DECL +bool ON_IsValidPositiveNumber(double x); + +/* +Returns: + (x > ON_UNSET_VALUE && x < 0.0)); +*/ +ON_DECL +bool ON_IsValidNegativeNumber(double x); + /* Returns: -1: a < b or a is not a nan and b is a nan diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp index 98e73490..1d3533f8 100644 --- a/opennurbs_mesh.cpp +++ b/opennurbs_mesh.cpp @@ -3077,23 +3077,31 @@ void ON_Mesh::SetSolidOrientation(int solid_orientation) static -int ON_MeshIsManifold_CompareV( const void* a, const void* b ) +int ON_MeshIsManifold_Compare3floats( const void* a, const void* b ) { - return memcmp(a,b,sizeof(ON_3fPoint)); - /* - float d; + // NOPE // memcmp thinks -0.0f and 0.0f are different numbers // return memcmp(a,b,sizeof(ON_3fPoint)); + const float* fa = (const float*)a; const float* fb = (const float*)b; - if ( 0.0f == (d = (*fa++ - *fb++)) ) + for (const float* fa3 = fa + 3; fa < fa3; ++fa, ++fb) { - if ( 0.0f == (d = (*fa++ - *fb++)) ) - { - if ( 0.0f == (d = (*fa++ - *fb++)) ) - return 0; - } + const float x = *fa; + const float y = *fb; + if (x < y) + return -1; + if (x > y) + return 1; + if (x == y) + continue; // neither x nor y is a nan + + // at least one of x and y is a nan + // use conventaion (not a nan) < (nan) because this code must use a well ordered compare for all values. + if (x == x) + return -1; // x is not a nan, y is a nan + if (y == y) + return 1; // x is a nan, y is not a nan } - return ( d < 0.0f ) ? -1 : 1; - */ + return 0; } static @@ -3417,7 +3425,7 @@ bool ON_Mesh::IsManifold( if ( bTopologicalTest ) { // coincident vertices are assigned the same vertex id - ON_Sort(ON::sort_algorithm::quick_sort,vid,m_V.Array(),vcount,sizeof(m_V[0]),ON_MeshIsManifold_CompareV); + ON_Sort(ON::sort_algorithm::quick_sort,vid,m_V.Array(),vcount,sizeof(m_V[0]), ON_MeshIsManifold_Compare3floats); ecount = 0; v = m_V.Array(); ecount = 0; @@ -3428,7 +3436,7 @@ bool ON_Mesh::IsManifold( vid[i] = ecount; for ( j = i+1; j < vcount; j++ ) { - if ( ON_MeshIsManifold_CompareV(&v,v+vid[j]) ) + if (ON_MeshIsManifold_Compare3floats(v0,v+vid[j]) ) { ecount++; break; @@ -6168,7 +6176,7 @@ void ON_MeshParameters::SetFaceType( ON_MeshParameters::ON_MeshParameters( - double density, + double normalized_mesh_density, double min_edge_length ) { @@ -6179,17 +6187,17 @@ ON_MeshParameters::ON_MeshParameters( SetRefineAngleRadians(0.0); SetMinimumEdgeLength(min_edge_length); - if ( ON_IsValid(density) ) + if ( ON_IsValid(normalized_mesh_density) ) { - if ( density < 0.0 ) - density = 0.0; - else if ( density > 1.0 ) - density = 1.0; - SetRelativeTolerance(density); - SetRefine((density < 0.65)); - SetSimplePlanes((0.0 == density)); + if (normalized_mesh_density < 0.0 ) + normalized_mesh_density = 0.0; + else if (normalized_mesh_density > 1.0 ) + normalized_mesh_density = 1.0; + SetRelativeTolerance(normalized_mesh_density); + SetRefine((normalized_mesh_density < 0.65)); + SetSimplePlanes((0.0 == normalized_mesh_density)); - ON_SubDDisplayParameters subd_parameters = ON_SubDDisplayParameters::CreateFromMeshDensity(density); + ON_SubDDisplayParameters subd_parameters = ON_SubDDisplayParameters::CreateFromMeshDensity(normalized_mesh_density); SetSubDDisplayParameters(subd_parameters); } } @@ -6241,22 +6249,22 @@ const ON_wString ON_MeshParameters::Description() const return description; } -const ON_MeshParameters ON_MeshParameters::CreateFromMeshDensity(double slider_value) +const ON_MeshParameters ON_MeshParameters::CreateFromMeshDensity(double normalized_mesh_density) { - return ON_MeshParameters(ON_MeshParameters::ClampMeshDensityValue(slider_value)); + return ON_MeshParameters(ON_MeshParameters::ClampMeshDensityValue(normalized_mesh_density)); } -double ON_MeshParameters::MeshDensityAsPercentage(double slider_value) +double ON_MeshParameters::MeshDensityAsPercentage(double normalized_mesh_density) { - if (slider_value >= 0.0 && slider_value <= 1.0) + if (normalized_mesh_density >= 0.0 && normalized_mesh_density <= 1.0) { const double percent_fuzz_tol = 1.0e-4; - const double slider_percent = slider_value * 100.0; // percent = slider_value as a percentage. + const double slider_percent = normalized_mesh_density * 100.0; // percent = slider_value as a percentage. const double n = floor(slider_percent + 0.25); if (fabs(n - slider_percent) <= percent_fuzz_tol) return n; // slider_percent is within fuzz of being an integer - return the integer - const double p = 100.0*(floor(1024.0 * slider_value + 0.25) / 1024.0); + const double p = 100.0*(floor(1024.0 * normalized_mesh_density + 0.25) / 1024.0); if (fabs(p - slider_percent) <= percent_fuzz_tol) return p; // slider_percent is within fuzz of 100.0*(N/1024.0). Return 100.0*(N/1024.0). @@ -6282,44 +6290,24 @@ double ON_MeshParameters::MeshDensity() const break; if (false == (this->m_refine_angle_radians == 0.0)) break; - if (this->SubDDisplayParameters().DisplayDensity() != ON_SubDDisplayParameters::CreateFromMeshDensity(candidate_density).DisplayDensity()) + const ON_SubDDisplayParameters subdp = this->SubDDisplayParameters(); + if ( subdp.DisplayDensityIsAbsolute() ) + break; // mesh dialog "slider" UI is always controls adaptive subd display density + if (subdp.DisplayDensity(ON_SubD::Empty) != ON_SubDDisplayParameters::CreateFromMeshDensity(candidate_density).DisplayDensity(ON_SubD::Empty)) break; // Now build one with the candidate_density slider value - ON_MeshParameters candidate_mp = ON_MeshParameters::CreateFromMeshDensity(candidate_density); + const ON_MeshParameters candidate_mp = ON_MeshParameters::CreateFromMeshDensity(candidate_density); -#define ON_COPY_MESH_PARAMETERS_MEMBER(M) candidate_mp.M = this->M - // ignore these paramters do not control the mesh density. - ON_COPY_MESH_PARAMETERS_MEMBER(m_bCustomSettings); - ON_COPY_MESH_PARAMETERS_MEMBER(m_bCustomSettingsEnabled); - ON_COPY_MESH_PARAMETERS_MEMBER(m_bComputeCurvature); - ON_COPY_MESH_PARAMETERS_MEMBER(m_bDoublePrecision); - ON_COPY_MESH_PARAMETERS_MEMBER(m_bClosedObjectPostProcess); - ON_COPY_MESH_PARAMETERS_MEMBER(m_texture_range); - if (ON_nil_uuid == this->m_mesher_id) - { - // Pangolin parameters do not apply - ON_COPY_MESH_PARAMETERS_MEMBER(m_mesher_id); - ON_COPY_MESH_PARAMETERS_MEMBER(m_bEvaluatorBasedTessellation); - ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_min_num_segments); - ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_angle_tol_in_degrees); - ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_max_dist_between_points); - ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_min_parametric_ratio); - ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_angle_tol_in_degrees); - ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_max_edge_length); - ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_min_edge_length); - ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_min_edge_length_ratio_uv); - ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_max_aspect_ratio); - ON_COPY_MESH_PARAMETERS_MEMBER(m_smoothing_passes); - } -#undef ON_COPY_MESH_PARAMETERS_MEMBER - - if (0 != ON_MeshParameters::Compare(candidate_mp, *this)) + if (candidate_mp.RelativeTolerance() != candidate_density) + break; + if (candidate_mp.GeometrySettingsHash() != GeometrySettingsHash()) break; // These mesh parameters will create the same mesh geometry as ON_MeshParameters::CreateFromMeshDensity(). return candidate_density; } + return ON_DBL_QNAN; } @@ -6697,7 +6685,7 @@ bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const int mft = m_face_type; if ( mft < 0 || mft > 2 ) { - ON_ERROR("ON_MeshParameters::Read() - m_face_type out of bounds."); + ON_ERROR("ON_MeshParameters::Write() - m_face_type out of bounds."); mft = 0; } if (rc) rc = file.WriteInt(mft); @@ -6719,12 +6707,94 @@ bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const if (rc) { // added for chunk version 1.5 - June 19, 2020 - SubDDisplayParameters().Write(file); + const ON_SubDDisplayParameters subdp = SubDDisplayParameters(); + rc = subdp.Write(file); } } return rc; } +enum ON_MeshParameters::Type ON_MeshParameters::GeometrySettingsType() const +{ + const ON_SHA1_Hash mp_hash = GeometrySettingsHash(); + + if ( mp_hash == ON_MeshParameters::DefaultMesh.GeometrySettingsHash()) + return ON_MeshParameters::Type::Default; + if ( mp_hash == ON_MeshParameters::FastRenderMesh.GeometrySettingsHash()) + return ON_MeshParameters::Type::FastRender; + if ( mp_hash == ON_MeshParameters::QualityRenderMesh.GeometrySettingsHash()) + return ON_MeshParameters::Type::QualityRender; + if ( mp_hash == ON_MeshParameters::DefaultAnalysisMesh.GeometrySettingsHash()) + return ON_MeshParameters::Type::DefaultAnalysis; + + const double mesh_density = MeshDensity(); + if (mesh_density == RelativeTolerance()) + return ON_MeshParameters::Type::FromMeshDensity; + + return ON_MeshParameters::Type::Custom; +} + +static bool Internal_MeshParametersRead_UpdateSubDParameters( + const unsigned archive_opennurbs_version, + ON_MeshParameters& archive_mp +) +{ + // Returns true if subd settings get updated. + + if (ON_MeshParameters::Type::Custom != archive_mp.GeometrySettingsType()) + return false; // a current built-in type - common and leave the as is. + + /* + The adaptive SubD stuff appeared in early Nov 2020. The Dec 1 2020 date gives the changes + time to work through the build and disitrubution processes. + */ + const unsigned before_adaptive_subd_display = ON_VersionNumberConstruct(7, 1, 2020, 12, 1, 0); + if (archive_opennurbs_version >= before_adaptive_subd_display) + return false; + + // Adaptive SubD meshing was added Nov 11 and the way m_relative_tolerance set the subd density changed. + // Version check padded to Dec 1, 2020 to give time for this code to get committed and built into daily distribution. + const ON_SHA1_Hash archive_mp_hash = archive_mp.GeometrySettingsHash(); + const ON_SubDDisplayParameters archive_subdp = archive_mp.SubDDisplayParameters(); + + ON_MeshParameters mp[] = { + ON_MeshParameters::DefaultMesh, + ON_MeshParameters::FastRenderMesh, + ON_MeshParameters::QualityRenderMesh, + ON_MeshParameters::DefaultAnalysisMesh, + ON_MeshParameters::CreateFromMeshDensity(archive_mp.RelativeTolerance()) + }; + const ON_MeshParameters::Type mp_type[] = + { + ON_MeshParameters::Type::Default, + ON_MeshParameters::Type::FastRender, + ON_MeshParameters::Type::QualityRender, + ON_MeshParameters::Type::DefaultAnalysis, + ON_MeshParameters::Type::FromMeshDensity + }; + + const size_t mp_count = sizeof(mp) / sizeof(mp[0]); + for (size_t mp_dex = 0; mp_dex < mp_count; ++mp_dex) + { + // If the only geometry setting differnce bewteen a built-in type and archive_mp + // is the subd meshing parameters, update archive_mp to use the built-in's subd + // meshing parameters. + const ON_SubDDisplayParameters mp_subdp = mp[mp_dex].SubDDisplayParameters(); + mp[mp_dex].SetSubDDisplayParameters(archive_subdp); + if (archive_mp_hash == mp[mp_dex].GeometrySettingsHash()) + { + // Update archive_mp to use new SubD defaults in mp_subdp. + // This prevents "this" from being treated as customized instead + // of one the the defaults and things will work as expected + // going forward in time with this model. + archive_mp.SetSubDDisplayParameters(mp_subdp); + return true; + } + } + + return false; +} + bool ON_MeshParameters::Read( ON_BinaryArchive& file ) { *this = ON_MeshParameters::DefaultMesh; @@ -6796,24 +6866,28 @@ bool ON_MeshParameters::Read( ON_BinaryArchive& file ) ON_SubDDisplayParameters subdp = ON_SubDDisplayParameters::Default; rc = subdp.Read(file); if (rc) - SetSubDDisplayParameters(subdp); + { + this->SetSubDDisplayParameters(subdp); + Internal_MeshParametersRead_UpdateSubDParameters(file.ArchiveOpenNURBSVersion(), *this); + } } } } } } } + return rc; } bool ON_SubDDisplayParameters::Write(class ON_BinaryArchive& archive) const { - if (false == archive.BeginWrite3dmAnonymousChunk(1)) + if (false == archive.BeginWrite3dmAnonymousChunk(2)) return false; bool rc = false; for(;;) { - const unsigned int display_density = this->DisplayDensity(); + const unsigned int display_density = this->m_display_density; if (false == archive.WriteInt(display_density)) break; @@ -6822,6 +6896,9 @@ bool ON_SubDDisplayParameters::Write(class ON_BinaryArchive& archive) const if (false == archive.WriteInt(loc_as_unsigned)) break; + if (false == archive.WriteBool(this->m_bDisplayDensityIsAbsolute)) + break; + rc = true; break; } @@ -6842,10 +6919,10 @@ bool ON_SubDDisplayParameters::Read(class ON_BinaryArchive& archive) if (chunk_version <= 0) break; - unsigned int display_density = this->DisplayDensity(); + unsigned int display_density = this->DisplayDensity(ON_SubD::Empty); if (false == archive.ReadInt(&display_density)) break; - SetDisplayDensity(display_density); + SetAdaptiveDisplayDensity(display_density); unsigned int loc_as_unsigned = static_cast(this->MeshLocation()); if (false == archive.ReadInt(&loc_as_unsigned)) @@ -6853,6 +6930,15 @@ bool ON_SubDDisplayParameters::Read(class ON_BinaryArchive& archive) const ON_SubDComponentLocation loc = ON_SubDComponentLocationFromUnsigned(loc_as_unsigned); SetMeshLocation(loc); + if (chunk_version >= 2) + { + bool bDisplayDensityIsAbsolute = false; + if (false == archive.ReadBool(&bDisplayDensityIsAbsolute)) + break; + if (bDisplayDensityIsAbsolute) + this->SetAbsoluteDisplayDensity(display_density); + } + rc = true; break; } diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h index a0ba1024..bc872948 100644 --- a/opennurbs_mesh.h +++ b/opennurbs_mesh.h @@ -73,96 +73,200 @@ public: enum : unsigned int { /// - /// Each SubD quad will generate 4 mesh quads in a 2x2 grid. - /// A simple meshing density of 0% corresponds to ExtraCoarseDensity. - /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// Indicates the SubD display mesh density has not be set. /// - ExtraCoarseDensity = 1, - MinimumDensity = 1, + UnsetDensity = 0, /// - /// Each SubD quad will generate 16 mesh quads in a 4x4 grid. - /// A simple meshing density of 1% to 19% corresponds to CoarseDensity. - /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// The minimum SubD display density that can be se in Rhino user interface is ExtraCoarseDensity (1). + /// + MinimumUserInterfaceDensity = 1, + + /// + /// The maximum SubD display density that can be se in Rhino user interface is ExtraFineDensity (5). + /// + MaximumUserInterfaceDensity = 5, + + /// + /// SubD display density values <= MinimumAdaptiveDensity will never be adaptively reduced. + /// SubD display density values > MinimumAdaptiveDensity may be adaptively reduced to a value >= MinimumAdaptiveDensity. + /// + MinimumAdaptiveDensity = 1, + + /// + /// Each SubD quad will generate 1 display mesh quads in a 1x1 grid. + /// This density can only be used with SubDs where every face is a quad. + /// User interface code never returns this density. + /// + MinimumDensity = 0, + + /// + /// When interpreted as an absolute SubD display density, each SubD quad will generate + /// 4 display mesh quads in a 2x2 grid and each SubD N-gon will generate N display mesh quads. + /// Adaptive reductions do not apply to this density. + /// This is the minimum SubD display density. + /// + ExtraCoarseDensity = 1, + + /// + /// When interpreted as an absolute SubD display density, each SubD quad will generate + /// 16 display mesh quads in a 4x4 grid and each SubD N-gon will generate N*4 display mesh quads. + /// Adaptive reductions do not apply to this density. /// CoarseDensity = 2, /// - /// Each SubD quad will generate 64 mesh quads in an 8x8 grid. - /// A simple meshing density of 20% to 34% corresponds to MediumDensity. - /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. - /// This density is too low to produce acceptable results on the SubD Rhino Logo and many other models. + /// When interpreted as an absolute SubD display density, each SubD quad will generate + /// 64 display mesh quads in an 8x8 grid and each SubD N-gon will generate N*8 display mesh quads. + /// When a SubD has more than 8000 faces, adaptive MediumDensity is reduced to CoarseDensity. /// MediumDensity = 3, /// - /// Each SubD quad will generate 256 mesh quads in a 16x16 grid. - /// A simple meshing density of 35% to 75% corresponds to FineDensity. - /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. - /// This is the default value for creating mesh approximations of SubD surface. - /// It produces acceptable results on the SubD Rhino Logo and most other models. + /// When interpreted as an absolute SubD display density, each SubD quad will generate + /// 256 display mesh quads in a 16x16 grid and each SubD N-gon will generate N*16 display mesh quads. + /// When a SubD has more than 2000 faces, adaptive FineDensity is reduced to adaptive MediumDensity. /// FineDensity = 4, + + /// + /// When interpreted as an absolute SubD display density, each SubD quad will generate + /// 256 display mesh quads in a 16x16 grid and each SubD N-gon will generate N*16 display mesh quads. + /// When a SubD has more than 2000 faces, adaptive DefaultDensity is reduced to adaptive MediumDensity. + /// This is the default value for creating mesh approximations of SubD surface. + /// When treadted as an adaptive setting, it produces acceptable results for most SubDs. + /// DefaultDensity = 4, /// - /// Each SubD quad will generate 1024 mesh quads in a 32x32 grid. - /// A simple meshing density of 76% to 99% corresponds to ExtraFineDensity. - /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// When interpreted as an absolute SubD display density, each SubD quad will generate + /// 1024 display mesh quads in a 32x32 grid and each SubD N-gon will generate N*32 display mesh quads. + /// When a SubD has more than 500 faces, adaptive ExtraFineDensity is reduced to adaptive FineDensity. /// ExtraFineDensity = 5, /// - /// Each SubD quad will generate 4096 mesh quads in a 64x64 grid. - /// A simple meshing density of 100% corresponds to MaximumDensity. - /// SubD display density values may never exceed 6 and core code assumes these values are <= 6. - /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. - /// This value creates ridiculously dense meshes and should generally be avoided. + /// When interpreted as an absolute SubD display density, each SubD quad will generate + /// 4096 display mesh quads in a 64x64 grid and each SubD N-gon will generate N*64 display mesh quads. + /// ON_SubDDisplayParameters.AdaptiveDensity() determines if the subd display density is + /// treated adaptively or absolutely. + /// This value creates ridiculously dense display meshes and should generally be avoided. + /// No Rhino user interface will create this value. /// - MaximumDensity = 6 + MaximumDensity = 6, + }; + + enum : unsigned int + { + /// + /// When the SubD display density is adaptive (default), AdaptiveMeshQuadMaximum + /// specifies the approximate number of display mesh quads to permit before + /// reducing the SubD display mesh density. + /// Approximate display mesh quad count = subd.FaceCount()*(4^subd_display_density). + /// This enum value may change from release to release as rendering technology improves. + /// Make sure your code works for values between 1024 and 134217728. + /// + AdaptiveDisplayMeshQuadMaximum = 512000 }; public: static const ON_SubDDisplayParameters Empty; // Parameters for a course limit surface display mesh. - // m_display_density = ON_SubDDisplayParameters::ExtraCoarseDensity + // SubD display density = adaptive ON_SubDDisplayParameters::ExtraCoarseDensity static const ON_SubDDisplayParameters ExtraCoarse; // Parameters for a course limit surface display mesh. - // m_display_density = ON_SubDDisplayParameters::CoarseDensity + // SubD display density = adaptive ON_SubDDisplayParameters::CoarseDensity static const ON_SubDDisplayParameters Coarse; // Parameters for a medium limit surface display mesh. - // To crude for a quality rendering of the SubD Rhino logo. - // m_display_density = ON_SubDDisplayParameters::MediumDensity + // SubD display density = adaptive ON_SubDDisplayParameters::MediumDensity + // Too crude for a quality rendering of the SubD Rhino logo. static const ON_SubDDisplayParameters Medium; // Parameters for the default limit surface display mesh. // Produces and acceptable rendering of the SubD Rhino logo. - // m_display_density = ON_SubDDisplayParameters::FineDensity (default) + // SubD display density = adaptive ON_SubDDisplayParameters::FineDensity (default) static const ON_SubDDisplayParameters Fine; // Parameters for an extra fine limit surface display mesh. - // m_display_density = ON_SubDDisplayParameters::ExtraFineDensity + // SubD display density = adaptive ON_SubDDisplayParameters::ExtraFineDensity static const ON_SubDDisplayParameters ExtraFine; // Parameters for the default limit surface display mesh. - // m_display_density = ON_SubDDisplayParameters::DefaultDensity + // SubD display density = adaptive ON_SubDDisplayParameters::DefaultDensity static const ON_SubDDisplayParameters Default; + /* + Parameters: + adaptive_subd_display_density - [in] + A value <= ON_SubDDisplayParameters::MaximumDensity. + When in doubt, pass ON_SubDDisplayParameters::DefaultDensity. + Invalid input values are treated as ON_SubDDisplayParameters::DefaultDensity. + subd_face_count - [in] + Number of SubD faces. + When subd_face_count = 0, adaptive_subd_display_density is returned. + Returns: + The absolute SubD display density for SubD with subd_face_count faces. + The absolute SubD display density is <= adaptive_subd_display_density and <= ON_SubDDisplayParameters::MaximumDensity. + */ + static unsigned int AbsoluteDisplayDensityFromSubDFaceCount( + unsigned adaptive_subd_display_density, + unsigned subd_face_count + ); + + /* + Parameters: + adaptive_subd_display_density - [in] + A value <= ON_SubDDisplayParameters::MaximumDensity. + When in doubt, pass ON_SubDDisplayParameters::DefaultDensity. + Invalid input values are treated as ON_SubDDisplayParameters::DefaultDensity. + subd - [in] + In the cases when the subd in question is not available, like user interface code that applies in general + and to unknown SubDs, pass ON_SubD::Empty. + Returns: + The absolute SubD display density for subd. + The absolute SubD display density is <= adaptive_subd_display_density and <= ON_SubDDisplayParameters::MaximumDensity. + */ + static unsigned int AbsoluteDisplayDensityFromSubD( + unsigned adaptive_subd_display_density, + const class ON_SubD& subd + ); + /* Description: In most applications, the caller sets the mesh_density and leaves the other parameters set to the default values. Parameters: - subd_display_density - [in] - A value between ON_SubDDisplayParameters::MinimumDensity and ON_SubDDisplayParameters::MaximumDensity. + adaptive_subd_display_density - [in] + A value <= ON_SubDDisplayParameters::MaximumDensity. + When in doubt, pass ON_SubDDisplayParameters::DefaultDensity. + Values < ON_SubDDisplayParameters::MinimumAdaptiveDensity are treated as N_SubDDisplayParameters::MinimumAdaptiveDensity. + All other invalid input values are treated as ON_SubDDisplayParameters::DefaultDensity. + Returns: + A ON_SubDDisplayParameters with adaptive SubD display density. */ static const ON_SubDDisplayParameters CreateFromDisplayDensity( - unsigned int subd_display_density - ); + unsigned int adaptive_subd_display_density + ); + + /* + Description: + Use of absolute display density is strongly discouraged. + SubDs can have a single face or millions of faces. + Adaptive display meshing produces more desirable results in almost all cases. + Parameters: + absolute_subd_display_density - [in] + A value <= ON_SubDDisplayParameters::MaximumDensity. + When in doubt, pass ON_SubDDisplayParameters::DefaultDensity. + Returns: + A ON_SubDDisplayParameters that treats the display density value as a constant for all SubDs. + */ + static const ON_SubDDisplayParameters CreateFromAbsoluteDisplayDensity( + unsigned int absolute_subd_display_density + ); /* Description: @@ -175,14 +279,14 @@ public: The table below shows the correpondence between normalized_density and subd display density. Mesh density percentage / normalized_mesh_density / subd display density - 0% -> [0.0, ON_ZERO_TOLERANCE] -> 1 = MinimumDensity - 0% to 19% -> (ON_ZERO_TOLERANCE, 0.20) -> 2 = CoarseDensity - 20% to 34% -> [0.20, 0.35) -> 3 = MediumDensity - 35% to 75% -> [0.35, 0.75] -> 4 = FineDensity - 76% to 99% -> (0.75, 1 - ON_ZERO_TOLERANCE) -> 5 = ExtraFineDensity - 100% -> [1 - ON_ZERO_TOLERANCE, 1.0] -> 6 = MaximumDensity + 0% -> [0.0, ON_ZERO_TOLERANCE] -> 1 = adaptive MinimumUserInterfaceDensity + 0% to 19% -> (ON_ZERO_TOLERANCE, 0.20) -> 2 = adaptive CoarseDensity + 20% to 34% -> [0.20, 0.35) -> 3 = adaptive MediumDensity + 35% to 75% -> [0.35, 0.75] -> 4 = adaptive FineDensity + 76% to 99% -> (0.75, 1 - ON_ZERO_TOLERANCE) -> 5 = adaptive ExtraFineDensity + 100% -> [1 - ON_ZERO_TOLERANCE, 1.0] -> 5 = adaptive MaximumUserInterfaceDensity - Invalid input -> ON_SubDDisplayParameters::DefaultDensity; + Invalid input -> adaptive DefaultDensity; Returns: A valid ON_SubDDisplayParameters with the specified subd display denstity. @@ -192,23 +296,92 @@ public: ); public: - // 0 <= m_display_density <= ON_SubDDisplayParameters::MaximumDensity - // If n = m_display_density, then each SubD quad face will have - // a grid of 2^n x 2^n mesh quads for a total of 2^(2n) mesh quads. - // n grid size total number of mesh quad faces per SubD quad face - // 0 1 x 1 1 (avoid 0 - it does not work for n-gons n != 4) - // 1 2 x 2 4 - // 2 4 x 4 16 - // 3 8 x 8 64 - // 4 16 x 16 128 - // 5 32 x 32 1,024 - // 6 64 x 64 4,096 + + ON_DEPRECATED_MSG("Use DisplayDensity(subd)") unsigned int DisplayDensity() const; + /* + Returns: + True if the SubD display density setting is adaptive and approximate display + mesh quad count is capped at ON_SubDDisplayParameters::AdaptiveDisplayMeshQuadMaximum. + Remarks: + this->DensityIsAdaptive() and this->DensityIsAbsolute() always return opposite bool values. + Use the one that makes your code easiest to read and understand. + */ + bool DisplayDensityIsAdaptive() const; + + /* + Returns: + True if the SubD display density setting is absolute. + Remarks: + this->DensityIsAdaptive() and this->DensityIsAbsolute() always return opposite bool values. + Use the one that makes your code easiest to read and understand. + */ + bool DisplayDensityIsAbsolute() const; + + /* + Parameters: + subd - [in] + Used when the display density is adaptive and subd.FaceCount() > 0. + Ignored when the display density is absolute or subd.FaceCount() = 0. + + Returns: + The absolute display density to use when creating display meshes for subd. + When adaptive reduction is enabled, subd.FaceCount() is used to determine + the appropriate display density. + + Remarks: + The chart below shows the relationship between the returned value and the + number of display mesh quads a generated by 1 SubD quad. + + return display mesh + value quads + 0 1 = 1x1 + 1 4 = 2x2 + 2 16 = 4x4 + 3 64 = 8x8 + 4 128 = 16x16 + 5 1,024 = 32x32 + 6 4,096 = 64x64 + */ + unsigned int DisplayDensity( + const class ON_SubD& subd + ) const; + + ON_DEPRECATED_MSG("Use SetAdaptiveDisplayDensity()") void SetDisplayDensity( - unsigned int display_density + unsigned int adaptive_display_density ); + /* + Description: + Set an adaptive SubD display density that caps display mesh quad count at ON_SubDDisplayParameters::AdaptiveDisplayMeshQuadMaximum. + Parameters: + adaptive_display_density - [in] + adaptive_display_density <= ON_SubDDisplayParameters::MaximumDensity + Values <= ON_SubDDisplayParameters::MinimumAdaptiveDensity will never be adaptively reduced during display mesh creation. + Remarks: + The use of this setting + */ + void SetAdaptiveDisplayDensity( + unsigned int adaptive_display_density + ); + + /* + Description: + In almast all cases, you are better off using SetAdaptiveDisplayDensity(). + Parameters: + absolute_display_density - [in] + absolute_display_density <= ON_SubDDisplayParameters::MaximumDensity + Remarks: + The use of this setting + */ + void SetAbsoluteDisplayDensity( + unsigned int absolute_display_density + ); + +public: + /* Description: @@ -238,24 +411,29 @@ private: // and the value of m_subd_mesh_parameters is saved in 3dm archives. subd_mesh_density_mask = 0x07, subd_mesh_location_bit = 0x08, + subd_mesh_absolute_density_bit = 0x10, // If this bit set, then the settings are not current defaults. subd_mesh_nondefault_bit = 0x80 }; private: - // 0 <= m_display_density <= ON_SubDDisplayParameters::MaximumDensity - // If n = m_display_density, then each SubD quad face will have - // a grid of 2^n x 2^n mesh quads for a total of 2^(2n) mesh quads. + // If n = absolute_display_density, then each SubD quad face will have + // a grid of 2^n x 2^n mesh quads for a total of 4^) mesh quads. // n grid size total number of mesh faces per SubD quad // 0 1 x 1 1 // 1 2 x 2 4 // 2 4 x 4 16 // 3 8 x 8 64 - // 4 16 x 16 128 + // 4 16 x 16 256 // 5 32 x 32 1,024 // 6 64 x 64 4,096 - unsigned int m_display_density = 0; + + // m_bAbsoluteDisplayDensity determines if m_display_density is adaptive or absolute. + bool m_bDisplayDensityIsAbsolute = false; // default must be false so 7.0 to 7.1 transition works correctly + unsigned char m_display_density = 0; // SubD display density (0,1,2,3,4,5,6) + + unsigned short m_reserved = 0; // If m_bControlNetMesh is false, a mesh of the limit surface is produced. // If m_bControlNetMesh is true, a mesh of the subdivided control net is produced. @@ -439,6 +617,58 @@ public: unsigned int mesh_parameter_id_as_unsigned ); + /// + /// ON_MeshParameter::Type identifies the type of mesh creation settings. + /// + enum class Type : unsigned char + { + /// + /// Not set. + /// + Unset = 0, + + /// + /// Default mesh creation settings from ON_MeshParameters::DefaultMesh. + /// + Default = 1, + + /// + /// Fast render mesh creation settings from ON_MeshParameters::FastRenderMesh. + /// + FastRender = 2, + + /// + /// Quality render mesh creation settings from ON_MeshParameters::QualityRenderMesh. + /// + QualityRender = 3, + + /// + /// Default analysis mesh creation settings from ON_MeshParameters::DefaultAnalysisMesh + /// + DefaultAnalysis = 4, + + /// + /// Mesh density settings from ON_MeshParameters::CreateFromMeshDensity(normalized_mesh_density). + /// The value of normalized_mesh_density is returned by the MeshDensity() property. + /// + FromMeshDensity = 5, + + /// + /// Mesh creation settings are set and are not from one of the cases listed above. + /// + Custom = 15 + }; + + /* + Returns: + The type of geometry settings. + Remarks: + This function will never return ON_MeshParameters::Type::Unset. + In particular, if the return value is not ON_MeshParameters::Type::Custom, + then the settings come from one of the built-in mesh creation settings. + */ + ON_MeshParameters::Type GeometrySettingsType() const; + /* Description: Mesh creationg parameters to create the default render mesh. @@ -486,7 +716,7 @@ public: This function creates ON_MeshParameters from a user interface "slider" like Rhino's simple mesh controls. Parameters: - normalized_density - [in] + normalized_mesh_density - [in] A double between 0.0 and 1.0. 0.0 creates meshes with fewer faces than 1.0. @@ -495,7 +725,7 @@ public: A valid ON_MeshParameters with the specified subd display denstity. */ static const ON_MeshParameters CreateFromMeshDensity( - double normalized_density + double normalized_mesh_density ); /* @@ -514,13 +744,13 @@ public: Description: Convert a mesh density value to a percentage with finite precision fuzz removed. Parameters: - normalized_density - [in] - valid input is 0.0 <= normalized_density <= 1.0 + normalized_mesh_density - [in] + valid input is 0.0 <= normalized_mesh_density <= 1.0 Returns: If normalized_density is valid, 100*normalized_density with fuzz cleaned up is returned. Otherwise ON_DBL_QNAN is returned. */ - static double MeshDensityAsPercentage(double normalized_density); + static double MeshDensityAsPercentage(double normalized_mesh_density); /* Description: @@ -528,7 +758,7 @@ public: ON_MeshParameters::CreateFromMeshDensity() and ON_SubDDisplayParameters CreateFromMeshDensity(). Parameters: - normalized_density - [in] + normalized_mesh_density - [in] should be close to being between 0 and 1. Returns: if normalized_density is between 0.0 and 1.0, that value is returned. @@ -536,7 +766,7 @@ public: If normalized_density is a hair bigger than 1.0, then 1.0 is returned. Otherwise 0.5 is returned. */ - static double ClampMeshDensityValue(double normalized_density); + static double ClampMeshDensityValue(double normalized_mesh_density); /* @@ -581,9 +811,9 @@ public: Description: Tool for provding a simple "slider" interface. Parameters: - mesh_density - [in] 0.0 <= density <= 1.0 - 0 quickly creates coarse meshes. - 1 slowly creates dense meshes. + normalized_mesh_density - [in] 0.0 <= normalized_mesh_density <= 1.0 + 0 quickly creates extremely coarse meshes. + 1 slowly creates extremely dense meshes. min_edge_length - [in] > 0.0 custom value ON_UNSET_VALUE: for default (0.0001) @@ -595,7 +825,7 @@ public: in a predictable way and is easier to search for when examining code. */ ON_MeshParameters( - double mesh_density, + double normalized_mesh_density, double min_edge_length = ON_UNSET_VALUE ); @@ -991,7 +1221,7 @@ private: // Uses ON_SubDDisplayParameters::EncodeAsUnsignedChar() / ON_SubDDisplayParameters::DecodeFromUnsignedChar() // to save ON_SubDDisplayParameters settings in this class. - // (Done this way to avoid breaking the C++ public SDK.) + // (Done this way to avoid breaking the version 6.0 C++ public SDK because ON_SubDDisplayParameters is to big to add to this class.) unsigned char m_subd_mesh_parameters_as_char = 0; int m_grid_min_count = 0; diff --git a/opennurbs_model_component.cpp b/opennurbs_model_component.cpp index 691795ac..1e709fb8 100644 --- a/opennurbs_model_component.cpp +++ b/opennurbs_model_component.cpp @@ -1748,7 +1748,8 @@ bool ON_ModelComponent::IsValidComponentName( { return (length > 0 - && length == (size_t)ON_wString::Length(candidate_component_name) + && length <= 2147483645 + && length == (size_t)ON_wString::Length(candidate_component_name,length+1) && ON_ModelComponent::IsValidComponentName(candidate_component_name) ); } @@ -1861,15 +1862,22 @@ bool ON_ModelComponent::IsValidComponentName( return rc; } +static bool Internal_IsValidComponentNameCodePoint(ON__UINT32 unicode_code_point) +{ + return + unicode_code_point >= ON_UnicodeCodePoint::ON_Space + && 0 != ON_IsValidUnicodeCodePoint(unicode_code_point) + && 0 == ON_IsUnicodeC1ControlCodePoint(unicode_code_point) + ; +} bool ON_ModelComponent::IsValidComponentNameFirstCodePoint( ON__UINT32 unicode_code_point ) { return - ON_IsValidUnicodeCodePoint(unicode_code_point) - && unicode_code_point > ON_wString::Space - && 127 != unicode_code_point + Internal_IsValidComponentNameCodePoint(unicode_code_point) + && 0 == ON_IsUnicodeSpaceCodePoint(unicode_code_point) && unicode_code_point != '(' && unicode_code_point != ')' && unicode_code_point != '[' @@ -1906,50 +1914,45 @@ bool ON_ModelComponent::IsValidComponentName( const wchar_t* candidate_component_name ) { - bool bPermitInternalSpaces = true; - bool rc = false; - bool bLastCharWasSpace = false; + const bool bPermitInternalSpaces = true; + + + if (nullptr == candidate_component_name || 0 == candidate_component_name[0]) + return false; + // the first character in a component name cannot be a bracket, space or below - if ( nullptr != candidate_component_name - && *candidate_component_name > ON_wString::Space - && *candidate_component_name != '(' - && *candidate_component_name != ')' - && *candidate_component_name != '[' - && *candidate_component_name != ']' - && *candidate_component_name != '{' - && *candidate_component_name != '}' - ) + const ON__UINT32 c2[2] = { (ON__UINT32)(candidate_component_name[0]), (ON__UINT32)(candidate_component_name[1]) }; + const ON__UINT32 first_code_point + = ON_IsValidUTF32Value(c2[0]) + ? c2[0] + : (ON_IsValidUTF16SurrogatePair(c2[0], c2[1]) ? ON_DecodeUTF16SurrogatePair(c2[0], c2[1],0): 0) + ; + bool rc = IsValidComponentNameFirstCodePoint(first_code_point); + + bool bLastCharWasSpace = false; + for ( const wchar_t* name = candidate_component_name; 0 != *name && rc; name++ ) { - rc = true; - for ( const wchar_t* name = candidate_component_name; 0 != *name && rc; name++ ) + const ON__UINT32 c = (ON__UINT32)(name[0]); + if (0 == Internal_IsValidComponentNameCodePoint(c)) { -#if (2 == ON_SIZEOF_WCHAR_T) - if (0 == ON_IsValidUTF16Singleton((ON__UINT32)(name[0]))) + if (ON_IsValidUTF16SurrogatePair(c, (ON__UINT32)(name[1]))) { - if (ON_IsValidUTF16SurrogatePair((ON__UINT32)(name[0]), (ON__UINT32)(name[1]))) - { - name++; - continue; - } - return false; - } -#elif (4 == ON_SIZEOF_WCHAR_T) - if (0 == ON_IsValidUTF32Value((ON__UINT32)(name[0]))) - { - return false; - } -#endif - if ( ON_wString::Space == *name ) - { - rc = bPermitInternalSpaces; - bLastCharWasSpace = true; - } - else if ( 127 == *name || *name < ON_wString::Space ) - rc = false; - else + // all code points that come from surrogate pairs are permitted bLastCharWasSpace = false; + name++; + continue; + } + return false; } + if (ON_IsUnicodeSpaceCodePoint(c)) + { + rc = bPermitInternalSpaces; + bLastCharWasSpace = true; + } + else + bLastCharWasSpace = false; } + return (rc && !bLastCharWasSpace) ? true : false; } diff --git a/opennurbs_model_component.h b/opennurbs_model_component.h index 284dd9a4..1faaeffb 100644 --- a/opennurbs_model_component.h +++ b/opennurbs_model_component.h @@ -204,6 +204,20 @@ public: const wchar_t* candidate_component_name ); + /* + Parameters: + candidate_component_name - [in] + A null terminated string that is UTF-16 or UTF-32 encoded. + When sizeof(wchar_t) >= 4, both UTF-16 surrogate pairs + and UTF-32 code point values > 0xFFFF are valid and can + appear in the same string. + Returns: + True if candidate_component_name is a valid component name. + Remarks: + Component names cannot begin with a (, ), [, ], {, }, or space. + These brackets can be the second or later code points. + A space can be an interior code point. + */ static bool IsValidComponentName( const wchar_t* candidate_component_name ); @@ -212,6 +226,27 @@ public: const ON_wString& candidate_component_name ); + /* + Parameters: + length - [in] + > 0. + The expected number of elements before the null terminator in candidate_component_name[]. + candidate_component_name - [in] + A null terminated string that is UTF-16 or UTF-32 encoded. + When sizeof(wchar_t) >= 4, both UTF-16 surrogate pairs + and UTF-32 code point values > 0xFFFF are valid and can + appear in the same string. + The capacity of candidate_component_name[] must be >= length+1. + This validity test fails if candidate_component_name[length] is not zero + or if a null terminator appears before candidate_component_name[length]. + Returns: + True if candidate_component_name is a valid component name. + Remarks: + Component names cannot begin with a (, ), [, ], {, }, or space. + These brackets can be the second or later code points. + A space can be an interior code point. + */ + static bool IsValidComponentName( size_t length, const wchar_t* candidate_component_name @@ -1392,10 +1427,10 @@ public: ON_DimStyle::Default ON_DimStyle::DefaultInchDecimal ON_DimStyle::DefaultInchFractional - ON_DimStyle::DefaultFootInchArchitecture + ON_DimStyle::DefaultFootInchArchitectural ON_DimStyle::DefaultMillimeterSmall ON_DimStyle::DefaultMillimeterLarge - ON_DimStyle::DefaultMillimeterArchitecture + ON_DimStyle::DefaultMillimeterArchitectural */ bool IsSystemComponent() const; diff --git a/opennurbs_nurbscurve.cpp b/opennurbs_nurbscurve.cpp index 3332e792..ec611045 100644 --- a/opennurbs_nurbscurve.cpp +++ b/opennurbs_nurbscurve.cpp @@ -964,9 +964,8 @@ static ON_NurbsCurve* MoveSeamPeriodicKnot(const ON_NurbsCurve& crv, int knot_in int cv_id = knot_index-dg+1; for (int i=0; iCVCount(); i++){ - ON_4dPoint cv; - crv.GetCV(cv_id%distinct_cvc, cv); - pNC->SetCV(i, cv); + double* pcv = crv.CV(cv_id%distinct_cvc); + pNC->SetCV(i, ON::intrinsic_point_style, pcv); cv_id++; } @@ -1012,13 +1011,13 @@ static ON_NurbsCurve* MoveSeamPeriodic(const ON_Curve& crv, double t) double d1 = k1-s; bool bInsert = true; if (d0<=d1){ - if (d0 < ON_ZERO_TOLERANCE*k0){ + if (d0 < ON_ZERO_TOLERANCE*fabs(k0)){ knot_index--; bInsert = false; } } else { - if (d1 < ON_ZERO_TOLERANCE*k1) + if (d1 < ON_ZERO_TOLERANCE*fabs(k1)) bInsert = false; } @@ -1051,13 +1050,14 @@ bool ON_NurbsCurve::ChangeClosedCurveSeam( double t ) if ( old_dom.Includes(k,true) ) { ON_NurbsCurve left, right; - // TODO - if periodic - dont' put multi knot in middle. + // if periodic - dont' put fully multiple knot in middle. bool bGotIt = false; if ( IsPeriodic() ){//This only works if the curve has only simple knots ON_NurbsCurve* pMovedNC = MoveSeamPeriodic(*this, t); if (pMovedNC){ *this = *pMovedNC; delete pMovedNC; + bGotIt = true; } } if (!bGotIt) diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp index cabb73ed..9a3ad9d9 100644 --- a/opennurbs_nurbssurface.cpp +++ b/opennurbs_nurbssurface.cpp @@ -218,9 +218,25 @@ int ON_NurbsSurface::KnotCount( int dir ) const double* ON_NurbsSurface::CV( int i, int j ) const { - return (m_cv) ? (m_cv + i*m_cv_stride[0] + j*m_cv_stride[1]) : nullptr; + const int offset = (i * m_cv_stride[0] + j * m_cv_stride[1]); + return (m_cv && offset >= 0) ? (m_cv + offset) : nullptr; } +double* ON_NurbsSurface::CV( + ON_2dex cvdex +) const +{ + return (cvdex.i >= 0 && cvdex.j >= 0) ? CV(cvdex.i, cvdex.j) : nullptr; +} + +double* ON_NurbsSurface::CV( + ON_2udex cvdex +) const +{ + return (cvdex.i < 0x7FFFFFFFU && cvdex.j < 0x7FFFFFFFU) ? CV(cvdex.i, cvdex.j) : nullptr; +} + + const ON_4dPoint ON_NurbsSurface::ControlPoint( int i, int j diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h index b989715e..6cf60aa7 100644 --- a/opennurbs_nurbssurface.h +++ b/opennurbs_nurbssurface.h @@ -725,6 +725,14 @@ public: int j ) const; + double* CV( + ON_2dex cvdex + ) const; + + double* CV( + ON_2udex cvdex + ) const; + /* Parameters: i - [in] diff --git a/opennurbs_point.cpp b/opennurbs_point.cpp index 2712fdc7..a3a31249 100644 --- a/opennurbs_point.cpp +++ b/opennurbs_point.cpp @@ -29,6 +29,16 @@ bool ON_IsValid(double x) return ON_IS_VALID(x); } +bool ON_IsValidPositiveNumber(double x) +{ + return (x > 0.0 && x < ON_UNSET_POSITIVE_VALUE); +} + +bool ON_IsValidNegativeNumber(double x) +{ + return (x > ON_UNSET_VALUE && x < 0.0); +} + int ON_CompareDouble( double a, double b diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 26289a3c..13281484 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -6,7 +6,7 @@ // To update version numbers, edit ..\build\build_dates.msbuild #define RMA_VERSION_MAJOR 7 -#define RMA_VERSION_MINOR 0 +#define RMA_VERSION_MINOR 1 //////////////////////////////////////////////////////////////// // @@ -14,10 +14,10 @@ // first step in each build. // #define RMA_VERSION_YEAR 2020 -#define RMA_VERSION_MONTH 9 -#define RMA_VERSION_DATE 11 -#define RMA_VERSION_HOUR 14 -#define RMA_VERSION_MINUTE 20 +#define RMA_VERSION_MONTH 12 +#define RMA_VERSION_DATE 8 +#define RMA_VERSION_HOUR 9 +#define RMA_VERSION_MINUTE 49 //////////////////////////////////////////////////////////////// // @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 7,0,20255,14200 -#define VERSION_WITH_PERIODS 7.0.20255.14200 +#define VERSION_WITH_COMMAS 7,1,20343,9490 +#define VERSION_WITH_PERIODS 7.1.20343.09490 #define COPYRIGHT "Copyright (C) 1993-2020, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." @@ -44,11 +44,11 @@ #define RMA_VERSION_NUMBER_MAJOR_WSTRING L"7" #define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"6" -#define RMA_VERSION_NUMBER_SR_STRING "SR0" -#define RMA_VERSION_NUMBER_SR_WSTRING L"SR0" +#define RMA_VERSION_NUMBER_SR_STRING "SR1" +#define RMA_VERSION_NUMBER_SR_WSTRING L"SR1" -#define RMA_VERSION_WITH_PERIODS_STRING "7.0.20255.14200" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.20255.14200" +#define RMA_VERSION_WITH_PERIODS_STRING "7.1.20343.09490" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.1.20343.09490" diff --git a/opennurbs_public_version.rc b/opennurbs_public_version.rc index 05a82ebc..afeb16a1 100644 --- a/opennurbs_public_version.rc +++ b/opennurbs_public_version.rc @@ -22,7 +22,7 @@ BEGIN BEGIN VALUE "CompanyName", "" VALUE "FileDescription", "Public openNURBS Dynamic Link Library" - VALUE "FileVersion", VERSION_WITH_PERIODS + VALUE "FileVersion", RMA_VERSION_WITH_PERIODS_STRING VALUE "InternalName", "Public openNURBS" VALUE "LegalCopyright", COPYRIGHT VALUE "LegalTrademarks", "openNURBS is a trademark of Robert McNeel & Associates." diff --git a/opennurbs_sha1.cpp b/opennurbs_sha1.cpp index dca2853d..886f1a92 100644 --- a/opennurbs_sha1.cpp +++ b/opennurbs_sha1.cpp @@ -875,7 +875,7 @@ void ON_SHA1::AccumulateUnitSystem AccumulateLengthUnitSystem(length_unit_system); if (ON::LengthUnitSystem::CustomUnits == length_unit_system) { - AccumulateDouble(unit_system.MetersPerUnit()); + AccumulateDouble(unit_system.MetersPerUnit(ON_DBL_QNAN)); AccumulateString(unit_system.UnitSystemName()); } } @@ -1158,4 +1158,4 @@ bool ON_SHA1::Validate() return false; return true; -} \ No newline at end of file +} diff --git a/opennurbs_sha1.h b/opennurbs_sha1.h index 70252dbc..ac999156 100644 --- a/opennurbs_sha1.h +++ b/opennurbs_sha1.h @@ -545,4 +545,69 @@ private: mutable ON_SHA1_Hash m_sha1_hash; }; +/* +Description: + Test speeds of various hash algoritmhs. +Parameters: + byte_count - [in] + Number of bytes to hash. This number is rounded up to the nearest multiple of 1024. + crc16 - [in/out] + If crc16 is not nullptr, then 16 bit CRC hashing is tested using function ON_CRC16(). + crc32 - [in/out] + If crc32 is not nullptr, then 32 bit CRC hashing is tested using function ON_CRC32(). + md5_hash - [in/out] + If md5_hash is not nullptr, then MD5 hashing is tested using class ON_MD5. + sha1_hash - [in/out] + If sha1_hash is not nullptr, then SHA-1 hashing is tested class ON_SHA1. + elapsed_time_in_seconds - [out] + elapsed_time_in_seconds[0] = 16 bit CRC hash time in seconds. + elapsed_time_in_seconds[1] = 32 bit CRC hash time in seconds. + elapsed_time_in_seconds[2] = MD5 hash time in seconds. + elapsed_time_in_seconds[3] = SHA-1 hash time in seconds. + If a hash was tested, then number of seconds it took to compute the hash is returned. + Otherwise ON_DBL_QNAN is returned. +*/ +ON_DECL +void ON_TestHashSpeed( + size_t byte_count, + ON__UINT16* crc16, + ON__UINT32* crc32, + ON_MD5_Hash* md5_hash, + ON_SHA1_Hash* sha1_hash, + double elapsed_time_in_seconds[4] +); + +/* +Description: + Test speeds of various hash algoritmhs and use text_log to print the results. +Parameters: + byte_count - [in] + Number of bytes to hash. This number is rounded up to the nearest multiple of 1024. + bCRC16 - [in] + True to test 16 bit CRC hashing speed. + bCRC32 - [in] + True to test 32 bit CRC hashing speed. + bMD5 - [in] + True to test MD5 hashing speed. + bSHA1 - [in] + True to test SHA-1 hashing speed. + text_log - [in] + Test results are printed using text_log. +*/ +ON_DECL +void ON_TestHashSpeed( + size_t byte_count, + bool bTestCRC16, + bool bTestCRC32, + bool bTestMD5, + bool bTestSHA1, + ON_TextLog& text_log +); + +ON_DECL +void ON_TestHashSpeed( + size_t byte_count, + ON_TextLog& text_log +); + #endif diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp index 66928336..2b3e790d 100644 --- a/opennurbs_statics.cpp +++ b/opennurbs_statics.cpp @@ -121,6 +121,24 @@ const double ON_SubDSectorType::MaximumCornerAngleRadians = 2.0*ON_PI - ON_SubDS const ON_SubDSectorId ON_SubDSectorId::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDSectorId); const ON_SubDSectorId ON_SubDSectorId::Invalid = ON_SubDSectorId::Create(nullptr, nullptr); +const ON_SubDHash ON_SubDHash::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDHash); + + +// {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; @@ -944,6 +962,11 @@ static ON_MeshParameters Internal_ON_MeshParameters_Constants( // the change in a comment by the changed value. Include the previous value in // your comment. This is crtically important so we can keep track of what we are // trying to accomplish. + // + // You must also update the mesh parameters file reading code so that settings with old defaults + // are replaced with setting that have the new defaults AND old defaults get saved in earlier version + // 3dm files. This requires somebody with a solid understanding of how ON_MeshParameters::Read()/Write() + // works, how saving earlier versions of 3dm fiels works, and how reading old version files works. switch (selector) { @@ -1043,10 +1066,22 @@ static ON_MeshParameters Internal_ON_MeshParameters_Constants( break; case 3: // ON_MeshParameters::DefaultAnalysisMesh - // 7 July 2006 Dale Lear - // I switched from the default constructor to the density=0.5 constructor to fix RR 10330. - mp = ON_MeshParameters(0.5, ON_MeshParameters::DefaultMesh.MinimumEdgeLength()); + // 7 July 2006 Dale Lear Rhino 5 + // I switched from the default constructor to the density=0.5 constructor to fix RR 10330. + //mp = ON_MeshParameters(0.5, ON_MeshParameters::DefaultMesh.MinimumEdgeLength()); + //mp.SetTextureRange(1); // m_texture_range must be 1. Do not change this setting. + + // Rhino 6, 7 defaults; + mp = ON_MeshParameters(0.8, ON_MeshParameters::DefaultMesh.MinimumEdgeLength()); mp.SetTextureRange(1); // m_texture_range must be 1. Do not change this setting. + mp.SetGridAspectRatio(0.0); + mp.SetGridAngleRadians(20.0*ON_DEGREES_TO_RADIANS); + mp.SetGridAmplification(1.0); + mp.SetRefineAngleRadians(20.0 * ON_DEGREES_TO_RADIANS); + mp.SetComputeCurvature(true); + mp.SetGridMinCount(16); + mp.SetSubDDisplayParameters(ON_SubDDisplayParameters::Default); + mp.SetRefine(true); break; } @@ -1071,6 +1106,52 @@ const ON_MeshParameters ON_MeshParameters::FastRenderMesh = Internal_ON_MeshPara const ON_MeshParameters ON_MeshParameters::QualityRenderMesh = Internal_ON_MeshParameters_Constants(2); const ON_MeshParameters ON_MeshParameters::DefaultAnalysisMesh = Internal_ON_MeshParameters_Constants(3); +bool ON_MeshParameters_AreValid() +{ + // This is a validation test to insure the code that sets default mesh parameters + // and the code that detects default mesh parameters works correctly. + // This validation test passes as of November 2020. If ON_ERROR() is called in this function + // it means new bugs have been introduced. These need to be fixed immediately in order to + // keep the code that generates display meshes working properly. + if (ON_MeshParameters::Type::Default != ON_MeshParameters::DefaultMesh.GeometrySettingsType()) + { + ON_ERROR("ON_MeshParameters::DefaultMesh.GeometrySettingsType() returned an unexpected value."); + return false; + } + if (ON_MeshParameters::Type::FastRender != ON_MeshParameters::FastRenderMesh.GeometrySettingsType()) + { + ON_ERROR("ON_MeshParameters::FastRenderMesh.GeometrySettingsType() returned an unexpected value."); + return false; + } + if (ON_MeshParameters::Type::QualityRender != ON_MeshParameters::QualityRenderMesh.GeometrySettingsType()) + { + ON_ERROR("ON_MeshParameters::QualityRenderMesh.GeometrySettingsType() returned an unexpected value."); + return false; + } + if (ON_MeshParameters::Type::DefaultAnalysis != ON_MeshParameters::DefaultAnalysisMesh.GeometrySettingsType()) + { + ON_ERROR("ON_MeshParameters::DefaultAnalysisMesh.GeometrySettingsType() returned an unexpected value."); + return false; + } + for (double normalized_mesh_density = 0.0; normalized_mesh_density <= 1.0; normalized_mesh_density += 0.125) + { + const ON_MeshParameters mp = ON_MeshParameters::CreateFromMeshDensity(normalized_mesh_density); + if (ON_MeshParameters::Type::FromMeshDensity != mp.GeometrySettingsType()) + { + ON_ERROR("ON_MeshParameters::ON_MeshParameters::CreateFromMeshDensity(...).GeometrySettingsType() returned an unexpected value."); + return false; + } + if (normalized_mesh_density != mp.MeshDensity()) + { + ON_ERROR("ON_MeshParameters::ON_MeshParameters::CreateFromMeshDensity(...).MeshDensity() returned an unexpected value."); + return false; + } + } + return true; +} + +const static bool ON_MeshParameters_AreValid_ = ON_MeshParameters_AreValid(); + const ON_3dmUnitsAndTolerances ON_3dmUnitsAndTolerances::Millimeters ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmUnitsAndTolerances); const ON_Circle ON_Circle::UnitCircle ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Circle); @@ -1846,315 +1927,479 @@ static void Internal_SystemDimStyleFinalize( dimension_style.ContentHash(); } +// Static DimStyle definitions + +static void DimStyleDefaultInit(ON_DimStyle& ds) +{ + ds.SetExtExtension (0.5); + ds.SetExtOffset (0.5); + ds.SetArrowSize (1.0); + ds.SetLeaderArrowSize (1.0); + ds.SetCenterMark (0.5); + ds.SetCenterMarkStyle (ON_DimStyle::centermark_style::Mark); + ds.SetTextGap (0.25); + ds.SetTextHeight (1.0); + ds.SetDimTextLocation (ON_DimStyle::TextLocation::AboveDimLine); + ds.SetDimRadialTextLocation (ON_DimStyle::TextLocation::InDimLine); + ds.SetAngleFormat (ON_DimStyle::angle_format::DecimalDegrees); + ds.SetAngleResolution (2); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::ModelUnits); + ds.SetAlternateDimensionLengthDisplay (ON_DimStyle::LengthDisplay::ModelUnits); + ds.SetLengthResolution (2); + ds.SetAlternateLengthResolution (2); + ds.SetLengthFactor (1.0); + ds.SetAlternateLengthFactor (1.0); + ds.SetAlternate (false); + ds.SetForceDimLine (true); + ds.SetDecimalSeparator (L'.'); + ds.SetPrefix (L""); + ds.SetSuffix (L""); + ds.SetAlternatePrefix (L" ["); + ds.SetAlternateSuffix (L"]"); + ds.SetDimExtension (0.0); + ds.SetSuppressExtension1 (false); + ds.SetSuppressExtension2 (false); + ds.SetToleranceFormat (ON_DimStyle::tolerance_format::None); + ds.SetToleranceResolution (4); + ds.SetToleranceUpperValue (0.0); + ds.SetToleranceLowerValue (0.0); + ds.SetToleranceHeightScale (1.0); + ds.SetBaselineSpacing (3.0); + ON_TextMask tm; + ds.SetTextMask (tm); + ds.SetDimScaleSource (0); + ds.SetExtensionLineColorSource (ON::object_color_source::color_from_layer); + ds.SetDimensionLineColorSource (ON::object_color_source::color_from_layer); + ds.SetArrowColorSource (ON::object_color_source::color_from_layer); + ds.SetTextColorSource (ON::object_color_source::color_from_layer); + ds.SetExtensionLineColor (ON_Color(0)); + ds.SetDimensionLineColor (ON_Color(0)); + ds.SetArrowColor (ON_Color(0)); + ds.SetTextColor (ON_Color(0)); + ds.SetExtensionLinePlotColorSource (ON::plot_color_source::plot_color_from_layer); + ds.SetDimensionLinePlotColorSource (ON::plot_color_source::plot_color_from_layer); + ds.SetArrowPlotColorSource (ON::plot_color_source::plot_color_from_layer); + ds.SetTextPlotColorSource (ON::object_color_source::color_from_layer); + ds.SetExtensionLinePlotColor (ON_Color(0)); + ds.SetDimensionLinePlotColor (ON_Color(0)); + ds.SetArrowPlotColor (ON_Color(0)); + ds.SetTextPlotColor (ON_Color(0)); + ds.SetExtensionLinePlotWeightSource (ON::plot_weight_source::plot_weight_from_layer); + ds.SetDimensionLinePlotWeightSource (ON::plot_weight_source::plot_weight_from_layer); + ds.SetExtensionLinePlotWeight (0.0); + ds.SetDimensionLinePlotWeight (0.0); + ds.SetFixedExtensionLen (1.0); + ds.SetFixedExtensionLenOn (false); + ds.SetTextRotation (0.0); + ds.SetAlternateLengthResolution (4); + ds.SetToleranceHeightScale (0.6); + ds.SetSuppressArrow1 (false); + ds.SetSuppressArrow2 (false); + ds.SetTextMoveLeader (0); + ds.SetArcLengthSymbol (L'\0'); + ds.SetStackHeightScale (0.7); + ds.SetStackFractionFormat (ON_DimStyle::stack_format::StackHorizontal); + ds.SetAlternateRoundOff (0.0); + ds.SetRoundOff (0.0); + ds.SetAngleRoundOff (0.0); + ds.SetZeroSuppress (ON_DimStyle::suppress_zero::None); + ds.SetAlternateZeroSuppress (ON_DimStyle::suppress_zero::None); + ds.SetAngleZeroSuppress (ON_DimStyle::suppress_zero::None); + ds.SetAlternateBelow (false); + ds.SetArrowType1 (ON_Arrowhead::arrow_type::SolidTriangle); + ds.SetArrowType2 (ON_Arrowhead::arrow_type::SolidTriangle); + ds.SetLeaderArrowType (ON_Arrowhead::arrow_type::SolidTriangle); + ds.SetTextVerticalAlignment (ON::TextVerticalAlignment::Top); + ds.SetTextHorizontalAlignment (ON::TextHorizontalAlignment::Left); + ds.SetLeaderTextVerticalAlignment (ON::TextVerticalAlignment::MiddleOfTop); + ds.SetLeaderTextHorizontalAlignment (ON::TextHorizontalAlignment::Left); + ds.SetLeaderContentAngleStyle (ON_DimStyle::ContentAngleStyle::Horizontal); + ds.SetLeaderCurveType (ON_DimStyle::leader_curve_type::Polyline); + ds.SetLeaderContentAngleRadians (0.0); + ds.SetLeaderHasLanding (false); + ds.SetLeaderLandingLength (1.0); + ds.SetDrawForward (true); + ds.SetSignedOrdinate (true); + ds.SetDimScale (1.0); + ds.SetUnitSystem (ON::LengthUnitSystem::None); + ds.SetTextOrientation (ON::TextOrientation::InPlane); + ds.SetLeaderTextOrientation (ON::TextOrientation::InPlane); + ds.SetDimTextOrientation (ON::TextOrientation::InPlane); + ds.SetDimRadialTextOrientation (ON::TextOrientation::InPlane); + ds.SetDimTextAngleStyle (ON_DimStyle::ContentAngleStyle::Aligned); + ds.SetDimRadialTextAngleStyle (ON_DimStyle::ContentAngleStyle::Horizontal); + ds.SetTextUnderlined (false); +} + +static void DimStyleMillimeterArchitecturalInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (1.0); + ds.SetArrowSize (3.0); + ds.SetLeaderArrowSize (3.0); + ds.SetCenterMark (3.0); + ds.SetTextGap (1.0); + ds.SetTextHeight (3.0); + ds.SetAngleResolution (0); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::Millmeters); + ds.SetAlternateDimensionLengthDisplay (ON_DimStyle::LengthDisplay::InchesDecimal); + ds.SetLengthResolution (0); + ds.SetBaselineSpacing (9.0); + ds.SetArrowType1 (ON_Arrowhead::arrow_type::Tick); + ds.SetArrowType2 (ON_Arrowhead::arrow_type::Tick); + ds.SetLeaderArrowType (ON_Arrowhead::arrow_type::OpenArrow); + ds.SetDimScale (100.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Millimeters); +} + +static void DimStyleMillimeterLargeInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (1.0); + ds.SetArrowSize (3.5); + ds.SetLeaderArrowSize (3.5); + ds.SetCenterMark (3.5); + ds.SetTextGap (1.0); + ds.SetTextHeight (3.5); + ds.SetAngleResolution (0); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::Millmeters); + ds.SetAlternateDimensionLengthDisplay (ON_DimStyle::LengthDisplay::InchesDecimal); + ds.SetLengthResolution (1); + ds.SetToleranceHeightScale (1.0); + ds.SetBaselineSpacing (10.5); + ds.SetLeaderLandingLength (3.5); + ds.SetDimScale (100.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Millimeters); +} + +static void DimStyleMillimeterSmallInit (ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (1.0); + ds.SetArrowSize (3.0); + ds.SetLeaderArrowSize (3.0); + ds.SetCenterMark (2.5); + ds.SetTextGap (0.8); + ds.SetTextHeight (2.5); + ds.SetAngleResolution (1); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::Millmeters); + ds.SetAlternateDimensionLengthDisplay (ON_DimStyle::LengthDisplay::InchesDecimal); + ds.SetBaselineSpacing (7.5); + ds.SetLeaderLandingLength (2.5); + ds.SetDimScale (10.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Millimeters); +} + +static void DimStyleInchDecimalInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (0.125); + ds.SetExtOffset (0.0625); + ds.SetArrowSize (0.125); + ds.SetLeaderArrowSize (0.125); + ds.SetCenterMark (0.25); + ds.SetTextGap (0.0625); + ds.SetTextHeight (0.125); + ds.SetAngleResolution (0); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::InchesDecimal); + ds.SetToleranceHeightScale (1.0); + ds.SetBaselineSpacing (0.38); + ds.SetLeaderLandingLength (0.125); + ds.SetDimScale (10.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Inches); +} + +static void DimStyleInchFractionalInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (0.125); + ds.SetExtOffset (0.0625); + ds.SetArrowSize (0.1); + ds.SetLeaderArrowSize (0.1); + ds.SetCenterMark (0.25); + ds.SetTextGap (0.0625); + ds.SetTextHeight (0.125); + ds.SetAngleResolution (1); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::InchesFractional); + ds.SetLengthResolution (4); + ds.SetDimExtension (0.1); + ds.SetBaselineSpacing (0.38); + ds.SetArrowType1 (ON_Arrowhead::arrow_type::Tick); + ds.SetArrowType2 (ON_Arrowhead::arrow_type::Tick); + ds.SetLeaderArrowType (ON_Arrowhead::arrow_type::OpenArrow); + ds.SetLeaderLandingLength (0.125); + ds.SetDimScale (12.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Inches); +} + +static void DimStyleFootInchArchitecturalInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (0.125); + ds.SetExtOffset (0.0625); + ds.SetArrowSize (0.1); + ds.SetLeaderArrowSize (0.1); + ds.SetCenterMark (0.25); + ds.SetTextGap (0.0625); + ds.SetTextHeight (0.125); + ds.SetAngleResolution (0); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::FeetAndInches); + ds.SetAlternateLengthResolution (1); + ds.SetBaselineSpacing (0.38); + ds.SetZeroSuppress (ON_DimStyle::suppress_zero::SuppressZeroFeet); + ds.SetArrowType1 (ON_Arrowhead::arrow_type::Tick); + ds.SetArrowType2 (ON_Arrowhead::arrow_type::Tick); + ds.SetLeaderArrowType (ON_Arrowhead::arrow_type::OpenArrow); + ds.SetLeaderLandingLength (0.125); + ds.SetDimScale (96.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Inches); +} + +static void DimStyleFeetDecimalInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (0.125); + ds.SetExtOffset (0.0625); + ds.SetArrowSize (0.125); + ds.SetLeaderArrowSize (0.125); + ds.SetCenterMark (0.25); + ds.SetTextGap (0.0625); + ds.SetTextHeight (0.125); + ds.SetAngleResolution (0); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::FeetDecimal); + ds.SetBaselineSpacing (0.38); + ds.SetLeaderLandingLength (0.125); + ds.SetDimScale (12.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Inches); +} + +static void DimStyleModelUnitsDecimalInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (0.125); + ds.SetExtOffset (0.0625); + ds.SetArrowSize (0.125); + ds.SetLeaderArrowSize (0.125); + ds.SetCenterMark (0.25); + ds.SetTextGap (0.0625); + ds.SetTextHeight (0.125); + ds.SetAngleResolution (0); + ds.SetBaselineSpacing (0.38); + ds.SetLeaderLandingLength (0.125); + ds.SetDimScale (10.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Inches); +} + +static void DimStyleFeetEngraveInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::FeetDecimal); + ds.SetTextVerticalAlignment (ON::TextVerticalAlignment::Bottom); + ds.SetDimScale (12.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Inches); +} + +static void DimStyleMillimeterEngraveInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetExtExtension (1.5); + ds.SetExtOffset (1.5); + ds.SetArrowSize (3.0); + ds.SetLeaderArrowSize (3.0); + ds.SetCenterMark (1.5); + ds.SetTextGap (0.75); + ds.SetTextHeight (3.0); + ds.SetDimensionLengthDisplay (ON_DimStyle::LengthDisplay::Millmeters); + ds.SetBaselineSpacing (9.0); + ds.SetFixedExtensionLen (3.0); + ds.SetLeaderLandingLength (3.0); + ds.SetDimScale (10.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Millimeters); +} + +static void DimStyleModelUnitsEngraveInit(ON_DimStyle& ds) +{ + DimStyleDefaultInit(ds); + ds.SetAlternateDimensionLengthDisplay (ON_DimStyle::LengthDisplay::Millmeters); + ds.SetToleranceHeightScale (1.0); + ds.SetTextVerticalAlignment (ON::TextVerticalAlignment::Bottom); + ds.SetDimScale (10.0); + ds.SetUnitSystem (ON::LengthUnitSystem::Inches); +} + + static ON_DimStyle DimStyleDefault() { - // {25B90869-0022-4E04-B498-98B4175F65FD} const ON_UUID id = { 0x25b90869, 0x22, 0x4e04,{ 0xb4, 0x98, 0x98, 0xb4, 0x17, 0x5f, 0x65, 0xfd } }; ON_DimStyle dimstyle; DimStyleInit(L"Default", -1, id, dimstyle); - + DimStyleDefaultInit(dimstyle); Internal_SystemDimStyleFinalize(dimstyle); return dimstyle; } static ON_DimStyle DimStyleInchDecimal() { - const ON_UUID id = { 0x2105610c, 0xcfc7, 0x4473,{ 0xa5, 0x80, 0xc3, 0xd9, 0xc, 0xe8, 0xc7, 0xa3 } }; + const ON_UUID id = + { 0x2105610c, 0xcfc7, 0x4473,{ 0xa5, 0x80, 0xc3, 0xd9, 0xc, 0xe8, 0xc7, 0xa3 } }; ON_DimStyle dimstyle; DimStyleInit(L"Inch Decimal", -2, id, dimstyle); - - //dimstyle.SetDimScale(10.0); - dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Inches, 10.0, ON::LengthUnitSystem::Inches); - dimstyle.SetUnitSystem(ON::LengthUnitSystem::Inches); - - dimstyle.SetExtExtension(0.125); - dimstyle.SetExtOffset(0.0625); - dimstyle.SetArrowSize(0.125); - dimstyle.SetCenterMark(0.25); - dimstyle.SetTextGap(0.0625); - dimstyle.SetTextHeight(0.125); - dimstyle.SetBaselineSpacing(0.375); - - - dimstyle.SetDimTextLocation(ON_DimStyle::TextLocation::AboveDimLine); - - dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::SolidTriangle); - dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::SolidTriangle); - - dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::InchesDecimal); - dimstyle.SetLengthResolution(2); - - dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); - dimstyle.SetAngleResolution(0); - - dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::InchesDecimal); - dimstyle.SetAlternateLengthResolution(2); - dimstyle.SetLengthFactor(1.0); - dimstyle.SetAlternate(false); - dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); - dimstyle.SetAlternateLengthResolution(2); - dimstyle.SetAlternateLengthFactor(1.0); - - dimstyle.SetDimExtension(0.0); - - dimstyle.SetLeaderArrowSize(0.125); - dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::SolidTriangle); - dimstyle.SetLeaderLandingLength(0.125); - - //ValidateCpyStuff(dimstyle); - + DimStyleInchDecimalInit(dimstyle); Internal_SystemDimStyleFinalize(dimstyle); return dimstyle; } - static ON_DimStyle DimStyleInchFractional() { - const ON_UUID id = { 0x6bcb1506, 0x699f, 0x445d,{ 0xa1, 0x22, 0x4f, 0xc7, 0x78, 0x2b, 0xc4, 0x86 } }; + const ON_UUID id = + { 0x6bcb1506, 0x699f, 0x445d,{ 0xa1, 0x22, 0x4f, 0xc7, 0x78, 0x2b, 0xc4, 0x86 } }; ON_DimStyle dimstyle; DimStyleInit(L"Inch Fractional", -3, id, dimstyle); - - //dimstyle.SetDimScale(12.0); - dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Inches, 1.0, ON::LengthUnitSystem::Feet); - dimstyle.SetUnitSystem(ON::LengthUnitSystem::Inches); - - dimstyle.SetExtExtension(0.125); - dimstyle.SetExtOffset(0.0625); - dimstyle.SetArrowSize(0.1); - dimstyle.SetCenterMark(0.25); - dimstyle.SetTextGap(0.0625); - dimstyle.SetTextHeight(0.125); - dimstyle.SetBaselineSpacing(0.375); - - dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::Tick); - dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::Tick); - - - dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); - dimstyle.SetAngleResolution(1); - - dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::InchesFractional); - dimstyle.SetLengthResolution(4); - dimstyle.SetLengthFactor(1.0); - dimstyle.SetAlternate(false); - dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); - dimstyle.SetAlternateLengthResolution(2); - dimstyle.SetAlternateLengthFactor(1.0); - - dimstyle.SetDimExtension(0.1); - - dimstyle.SetLeaderArrowSize(0.1); - dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::SolidTriangle); - dimstyle.SetLeaderLandingLength(0.125); - - //ValidateCpyStuff(dimstyle); - + DimStyleInchFractionalInit(dimstyle); Internal_SystemDimStyleFinalize(dimstyle); return dimstyle; } - -static ON_DimStyle DimStyleFootInchArchitecture() +static ON_DimStyle DimStyleFootInchArchitectural() { - const ON_UUID id = { 0x50d6ef1b, 0xd1d0, 0x408a,{ 0x86, 0xc0, 0xee, 0x8b, 0x36, 0x8, 0x88, 0x3e } }; + const ON_UUID id = + { 0x50d6ef1b, 0xd1d0, 0x408a,{ 0x86, 0xc0, 0xee, 0x8b, 0x36, 0x8, 0x88, 0x3e } }; ON_DimStyle dimstyle; DimStyleInit(L"Foot-Inch Architectural", -4, id, dimstyle); - - //dimstyle.SetDimScale(96.0); - dimstyle.SetDimScale(0.125, ON::LengthUnitSystem::Inches, 1.0, ON::LengthUnitSystem::Feet); - dimstyle.SetUnitSystem(ON::LengthUnitSystem::Inches); - - dimstyle.SetExtExtension(0.125); - dimstyle.SetExtOffset(0.0625); - dimstyle.SetArrowSize(0.1); - dimstyle.SetCenterMark(0.25); - dimstyle.SetTextGap(0.0625); - dimstyle.SetTextHeight(0.125); - dimstyle.SetBaselineSpacing(0.375); - - dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::Tick); - dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::Tick); - - - dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); - dimstyle.SetAngleResolution(0); - - dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::FeetAndInches); - dimstyle.SetLengthResolution(3); - dimstyle.SetLengthFactor(1.0); - dimstyle.SetAlternate(false); - dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); - dimstyle.SetAlternateLengthResolution(2); - dimstyle.SetAlternateLengthFactor(1.0); - - dimstyle.SetDimExtension(0.1); - - dimstyle.SetLeaderArrowSize(0.1); - dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::SolidTriangle); - dimstyle.SetLeaderLandingLength(0.125); - - //ValidateCpyStuff(dimstyle); - + DimStyleFootInchArchitecturalInit(dimstyle); Internal_SystemDimStyleFinalize(dimstyle); return dimstyle; } - static ON_DimStyle DimStyleMillimeterSmall() { - const ON_UUID id = { 0xdbe22573, 0x8cad, 0x4ced,{ 0x89, 0x47, 0x3, 0xa0, 0x48, 0xed, 0xde, 0x56 } }; + const ON_UUID id = + { 0xdbe22573, 0x8cad, 0x4ced,{ 0x89, 0x47, 0x3, 0xa0, 0x48, 0xed, 0xde, 0x56 } }; ON_DimStyle dimstyle; DimStyleInit(L"Millimeter Small", -5, id, dimstyle); - - //dimstyle.SetDimScale(10.0); - dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Millimeters, 10.0, ON::LengthUnitSystem::Millimeters); - dimstyle.SetUnitSystem(ON::LengthUnitSystem::Millimeters); - - dimstyle.SetExtExtension(1.0); - dimstyle.SetExtOffset(0.5); - dimstyle.SetArrowSize(3.0); - dimstyle.SetCenterMark(2.5); - dimstyle.SetTextGap(0.8); - dimstyle.SetTextHeight(2.5); - dimstyle.SetBaselineSpacing(7.5); - - dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::OpenArrow); - dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::OpenArrow); - - - dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); - dimstyle.SetAngleResolution(1); - - dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::Millmeters); - dimstyle.SetLengthResolution(2); - dimstyle.SetLengthFactor(1.0); - dimstyle.SetAlternate(false); - dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); - dimstyle.SetAlternateLengthResolution(2); - dimstyle.SetAlternateLengthFactor(1.0); - - dimstyle.SetDimExtension(0.0); - - dimstyle.SetLeaderArrowSize(3.0); - dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::OpenArrow); - dimstyle.SetLeaderLandingLength(3.0); - - //ValidateCpyStuff(dimstyle); - + DimStyleMillimeterSmallInit(dimstyle); Internal_SystemDimStyleFinalize(dimstyle); return dimstyle; } - static ON_DimStyle DimStyleMillimeterLarge() { - const ON_UUID id = { 0xf7b30534, 0x773e, 0x45bc,{ 0x9d, 0x87, 0x9d, 0x14, 0x80, 0x9c, 0x96, 0x44 } }; + const ON_UUID id = + { 0xf7b30534, 0x773e, 0x45bc,{ 0x9d, 0x87, 0x9d, 0x14, 0x80, 0x9c, 0x96, 0x44 } }; ON_DimStyle dimstyle; DimStyleInit(L"Millimeter Large", -6, id, dimstyle); - - //dimstyle.SetDimScale(100.0); - dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Millimeters, 100.0, ON::LengthUnitSystem::Millimeters); - dimstyle.SetUnitSystem(ON::LengthUnitSystem::Millimeters); - - dimstyle.SetExtExtension(1.0); - dimstyle.SetExtOffset(0.5); - dimstyle.SetArrowSize(3.5); - dimstyle.SetCenterMark(3.5); - dimstyle.SetTextGap(1.0); - dimstyle.SetTextHeight(3.5); - dimstyle.SetBaselineSpacing(10.5); - - dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::OpenArrow); - dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::OpenArrow); - - - dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); - dimstyle.SetAngleResolution(0); - - dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::Millmeters); - dimstyle.SetLengthResolution(1); - dimstyle.SetLengthFactor(1.0); - dimstyle.SetAlternate(false); - dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); - dimstyle.SetAlternateLengthResolution(2); - dimstyle.SetAlternateLengthFactor(1.0); - - - dimstyle.SetDimExtension(0.0); - - dimstyle.SetLeaderArrowSize(3.5); - dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::OpenArrow); - dimstyle.SetLeaderLandingLength(3.5); - - //ValidateCpyStuff(dimstyle); - + DimStyleMillimeterLargeInit(dimstyle); Internal_SystemDimStyleFinalize(dimstyle); return dimstyle; } - -static ON_DimStyle DimStyleMillimeterArchitecture() +static ON_DimStyle DimStyleMillimeterArchitectural() { - const ON_UUID id = { 0xe5a4c08f, 0x23b3, 0x4033,{ 0x90, 0xb2, 0xfb, 0x31, 0xec, 0x45, 0x92, 0x9b } }; + const ON_UUID id = + { 0xe5a4c08f, 0x23b3, 0x4033,{ 0x90, 0xb2, 0xfb, 0x31, 0xec, 0x45, 0x92, 0x9b } }; ON_DimStyle dimstyle; DimStyleInit(L"Millimeter Architectural", -7, id, dimstyle); - dimstyle.SetUnitSystem(ON::LengthUnitSystem::Millimeters); - - //dimstyle.SetDimScale(100.0); - dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Millimeters, 100.0, ON::LengthUnitSystem::Millimeters); - - dimstyle.SetExtExtension(1.0); - dimstyle.SetExtOffset(0.5); - dimstyle.SetArrowSize(3.0); - dimstyle.SetCenterMark(3.0); - dimstyle.SetTextGap(1.0); - dimstyle.SetTextHeight(3.0); - dimstyle.SetBaselineSpacing(9.0); - - dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::Tick); - dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::Tick); - - dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); - dimstyle.SetAngleResolution(0); - - dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::Millmeters); - dimstyle.SetLengthResolution(0); - dimstyle.SetLengthFactor(1.0); - dimstyle.SetAlternate(false); - dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); - dimstyle.SetAlternateLengthResolution(2); - dimstyle.SetAlternateLengthFactor(1.0); - - - dimstyle.SetDimExtension(1.5); - - dimstyle.SetLeaderArrowSize(3.0); - dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::OpenArrow); - dimstyle.SetLeaderLandingLength(3.0); - - //ValidateCpyStuff(dimstyle); - + DimStyleMillimeterArchitecturalInit(dimstyle); Internal_SystemDimStyleFinalize(dimstyle); return dimstyle; } +static ON_DimStyle DimStyleFeetDecimal() +{ + // {6F4B1840-8A12-4DE9-BF84-6A98B06C508D} + const ON_UUID id = + { 0x6f4b1840, 0x8a12, 0x4de9, { 0xbf, 0x84, 0x6a, 0x98, 0xb0, 0x6c, 0x50, 0x8d } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Feet Decimal", -8, id, dimstyle); + DimStyleFeetDecimalInit(dimstyle); + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + +static ON_DimStyle DimStyleModelUnitsDecimal() +{ + const ON_UUID id = + { 0x93a38bdf, 0x4c1c, 0x428c, { 0x8b, 0x97, 0x93, 0x59, 0xf1, 0xbd, 0xed, 0x17 } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Model Units Decimal", -9, id, dimstyle); + DimStyleModelUnitsDecimalInit(dimstyle); + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + +static ON_DimStyle DimStyleFeetEngrave() +{ + const ON_UUID id = + { 0xc2d8846b, 0x918d, 0x4779, { 0x96, 0xec, 0x31, 0xb4, 0xe2, 0x75, 0xfb, 0x4e } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Feet Engrave", -10, id, dimstyle); + DimStyleFeetEngraveInit(dimstyle); + const ON_Font* font = ON_Font::InstalledFontFromRichTextProperties(L"SLF-RHN Architect", false, false); + if (nullptr != font) + dimstyle.SetFont(*font); + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + +static ON_DimStyle DimStyleMillimeterEngrave() +{ + const ON_UUID id = + { 0x741980ff, 0xde0f, 0x4ed7, { 0xaa, 0x6f, 0xee, 0x91, 0xb3, 0xbe, 0x96, 0xc6 } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Millimeter Engrave", -11, id, dimstyle); + DimStyleMillimeterEngraveInit(dimstyle); + const ON_Font* font = ON_Font::InstalledFontFromRichTextProperties(L"SLF-RHN Architect", false, false); + if (nullptr != font) + dimstyle.SetFont(*font); + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + +static ON_DimStyle DimStyleModelUnitsEngrave() +{ + const ON_UUID id = + { 0x2cc3a895, 0x5389, 0x467e, { 0x9d, 0xbe, 0x3a, 0xca, 0xb4, 0x38, 0x60, 0xfa } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Model Units Engrave", -12, id, dimstyle); + DimStyleModelUnitsEngraveInit(dimstyle); + const ON_Font* font = ON_Font::InstalledFontFromRichTextProperties(L"SLF-RHN Architect", false, false); + if (nullptr != font) + dimstyle.SetFont(*font); + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} const ON_DimStyle ON_DimStyle::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_DimStyle); + const ON_DimStyle ON_DimStyle::Default(DimStyleDefault()); + const ON_DimStyle ON_DimStyle::DefaultInchDecimal(DimStyleInchDecimal()); const ON_DimStyle ON_DimStyle::DefaultInchFractional(DimStyleInchFractional()); -const ON_DimStyle ON_DimStyle::DefaultFootInchArchitecture(DimStyleFootInchArchitecture()); +const ON_DimStyle ON_DimStyle::DefaultFootInchArchitecture(DimStyleFootInchArchitectural()); + const ON_DimStyle ON_DimStyle::DefaultMillimeterSmall(DimStyleMillimeterSmall()); const ON_DimStyle ON_DimStyle::DefaultMillimeterLarge(DimStyleMillimeterLarge()); -const ON_DimStyle ON_DimStyle::DefaultMillimeterArchitecture(DimStyleMillimeterArchitecture()); +const ON_DimStyle ON_DimStyle::DefaultMillimeterArchitecture(DimStyleMillimeterArchitectural()); + +const ON_DimStyle ON_DimStyle::DefaultFeetDecimal(DimStyleFeetDecimal()); +const ON_DimStyle ON_DimStyle::DefaultModelUnitsDecimal(DimStyleModelUnitsDecimal()); + +const ON_DimStyle ON_DimStyle::DefaultFeetEngrave(DimStyleFeetEngrave()); +const ON_DimStyle ON_DimStyle::DefaultMillimeterEngrave(DimStyleMillimeterEngrave()); +const ON_DimStyle ON_DimStyle::DefaultModelUnitsEngrave(DimStyleModelUnitsEngrave()); const ON_StackedText ON_StackedText::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_StackedText); const ON_TextRun ON_TextRun::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextRun); @@ -2723,7 +2968,12 @@ unsigned int ON_ModelComponent::Internal_SystemComponentHelper() &ON_DimStyle::DefaultMillimeterSmall, &ON_DimStyle::DefaultMillimeterLarge, &ON_DimStyle::DefaultMillimeterArchitecture, - + &ON_DimStyle::DefaultFeetDecimal, + &ON_DimStyle::DefaultModelUnitsDecimal, + &ON_DimStyle::DefaultFeetEngrave, + &ON_DimStyle::DefaultMillimeterEngrave, + &ON_DimStyle::DefaultModelUnitsEngrave, + &ON_HatchPattern::Solid, &ON_HatchPattern::Hatch1, &ON_HatchPattern::Hatch2, diff --git a/opennurbs_string.h b/opennurbs_string.h index ed0ec4c3..bbad8d9d 100644 --- a/opennurbs_string.h +++ b/opennurbs_string.h @@ -4231,11 +4231,10 @@ public: /* Returns - true if m_unit_system is a valid ON::LengthUnitSystem enum value, - which may be ON::LengthUnitSystem::None. If m_unit_system is - ON::LengthUnitSystem::CustomUnits, then IsValid() returns true - if m_custom_unit_scale > 0.0 and false otherwise. - The value of m_custom_unit_name is not tested. + False if UnitSystem() is ON::LengthUnitSystem::Unset. + False if UnitSystem() is ON::LengthUnitSystem::CustomUnits and MetersPerUnits() is not positive. + True if UnitSystem() is ON::LengthUnitSystem::None. + True otherwise. See Also: IsSet() */ @@ -4245,10 +4244,14 @@ public: bool Write( class ON_BinaryArchive& ) const; void Dump( class ON_TextLog& ) const; + const ON_wString ToString() const; + /* Returns - true If the unit system is valid and set to something beside - ON::no_unit_systm; + True if UnitSystem() is neither ON::LengthUnitSystem::Unset nor ON::LengthUnitSystem::None + and IsValid() is true. + See Also: + IsValid() */ bool IsSet() const; @@ -4287,6 +4290,7 @@ public: Avoid using this function. Use SetCustomUnitSystem() or SetUnitSystem() instead. */ + ON_DEPRECATED_MSG("Use SetCustomUnitSystem()") void SetCustomUnitSystemName( const wchar_t* custom_unit_name ); @@ -4294,25 +4298,72 @@ public: /* Description: Changes the unit system to custom units and sets the custom unit scale. + Parameters: + meters_per_custom_unit - [in] + a positive number Remarks: Avoid using this function. Use SetCustomUnitSystem() or SetUnitSystem() instead. */ - void SetCustomUnitSystemScale( + ON_DEPRECATED_MSG("Use SetCustomUnitSystem()") + void SetCustomUnitSystemScale( double meters_per_custom_unit ); + /// NOTE WELL: + /// For standard units, ON_UnitSystem::MetersPerUnit() returns the inverse of the correct value. + /// The reason is the VRay plug-in for Rhino 6 assumes the incorrect value is returned + /// and does not work correctly in Rhino 7 if the correct value is returned. + ON_DEPRECATED_MSG("MetersPerUnit() returns the wrong value. Use this->MetersPerUnit(ON_DBL_QNAN)") double MetersPerUnit() const; + + /* + Parameters: + unset_return_value - [in] + Value to return when this->UnitSystem() is ON::LengthUnitSystem::Unset. + When in doubt, use ON_DBL_QNAN. + Returns: + If this->UnitSystem() is ON::LengthUnitSystem::CustomUnits, then the value set + by SetCustomUnitSystemScale() is returned. + If this->UnitSystem() is ON::LengthUnitSystem::Unset, then unset_return_value is returned. + If this->UnitSystem() is ON::LengthUnitSystem::None, then 1.0 is returned. + Otherwise, ON::UnitScale(this->UnitSystem(), ON::LengthUnitSystem::Meters) is returned. + */ + double MetersPerUnit( + double unset_return_value + ) const; + + /* + Parameters: + unset_return_value - [in] + Value to return when this->UnitSystem() is ON::LengthUnitSystem::Unset. + When in doubt, use ON_DBL_QNAN. + Returns: + If this->UnitSystem() is ON::LengthUnitSystem::CustomUnits, then the 1000 times the + value set by SetCustomUnitSystemScale() is returned. + If this->UnitSystem() is ON::LengthUnitSystem::Unset, then unset_return_value is returned. + If this->UnitSystem() is ON::LengthUnitSystem::None, then 1.0 is returned. + Otherwise, ON::UnitScale(this->UnitSystem(), ON::LengthUnitSystem::Millimeters) is returned. + */ + double MillimetersPerUnit( + double unset_return_value + ) const; + ON::LengthUnitSystem UnitSystem() const; + + /* + Returns: + US English lower case plural unit system name (meters, inches, etc.). + */ const ON_wString& UnitSystemName() const; - - private: ON::LengthUnitSystem m_unit_system = ON::LengthUnitSystem::Meters; unsigned int m_reserved = 0; - // The m_custom_unit_... settings apply when m_unit_system = ON::LengthUnitSystem::CustomUnits - double m_meters_per_unit = 1.0; // 1 meter = m_custom_unit_scale custom units + // The m_meters_per_custom_unit and m_custom_unit_name values apply when + // m_unit_system = ON::LengthUnitSystem::CustomUnits. + // In all other cases they should be ignored. + double m_meters_per_custom_unit = 1.0; // 1 meter = m_meters_per_custom_unit custom units ON_wString m_custom_unit_name; // name of custom units }; #endif diff --git a/opennurbs_string_values.cpp b/opennurbs_string_values.cpp index a247c08c..62b157ce 100644 --- a/opennurbs_string_values.cpp +++ b/opennurbs_string_values.cpp @@ -737,7 +737,7 @@ double ON_LengthValue::Length( if ( ON::LengthUnitSystem::None == context_unit_system.UnitSystem()) return m_length; if ( - m_length_unit_system.MetersPerUnit() == context_unit_system.MetersPerUnit() + m_length_unit_system.MetersPerUnit(ON_DBL_QNAN) == context_unit_system.MetersPerUnit(ON_DBL_QNAN) && ON::LengthUnitSystem::Unset != context_unit_system.UnitSystem() ) return m_length; diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 1ba9d70e..36455454 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -35,11 +35,37 @@ ON_SubDToBrepParameters::VertexProcess ON_SubDToBrepParameters::VertexProcessFro ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::None); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::LocalG1); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::LocalG2); - ////ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::GlobalG1); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::LocalG1x); } return ON_SUBD_RETURN_ERROR(ON_SubDToBrepParameters::VertexProcess::None); } +const ON_wString ON_SubDToBrepParameters::VertexProcessToString( + ON_SubDToBrepParameters::VertexProcess vertex_process +) +{ + const wchar_t* s; + switch (vertex_process) + { + case ON_SubDToBrepParameters::VertexProcess::None: + s = L"None"; + break; + case ON_SubDToBrepParameters::VertexProcess::LocalG1: + s = L"G1"; + break; + case ON_SubDToBrepParameters::VertexProcess::LocalG2: + s = L"G2"; + break; + case ON_SubDToBrepParameters::VertexProcess::LocalG1x: + s = L"G1x"; + break; + default: + s = L"INVALID"; + break; + } + return ON_wString(s); +} + ON_SubDToBrepParameters::VertexProcess ON_SubDToBrepParameters::ExtraordinaryVertexProcess() const { return m_extraordinary_vertex_process; @@ -111,6 +137,74 @@ void ON_SubDToBrepParameters::SetPackFaces( m_bPackFaces = bPackFaces ? true : false; } +const ON_wString ON_SubDToBrepParameters::ToString( bool bVerbose ) const +{ + const ON_wString exvtx(ON_SubDToBrepParameters::VertexProcessToString(ExtraordinaryVertexProcess())); + const ON_wString faces(PackFaces() ? L"Packed" : L"Unpacked"); + + const ON_wString s = ON_wString::FormatToString(L"Faces = %ls ExtraordinaryVertex = %ls", + static_cast(faces), + static_cast(exvtx) + ); + + return bVerbose ? (ON_wString(L"ON_SubDToBrepParameters: ") + s) : s; +} + +bool ON_SubDToBrepParameters::Read(ON_BinaryArchive& archive) +{ + *this = ON_SubDToBrepParameters::Default; + int version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&version)) + return false; + + bool rc = false; + for (;;) + { + if (version < 1) + break; + + bool bPackFaces = this->PackFaces(); + if (false == archive.ReadBool(&bPackFaces)) + break; + this->SetPackFaces(bPackFaces); + + unsigned u = static_cast(this->ExtraordinaryVertexProcess()); + if (false == archive.ReadInt(&u)) + break; + ON_SubDToBrepParameters::VertexProcess exvtx = ON_SubDToBrepParameters::VertexProcessFromUnsigned(u); + this->SetExtraordinaryVertexProcess(exvtx); + + rc = true; + break; + } + + if (false == archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +bool ON_SubDToBrepParameters::Write(ON_BinaryArchive& archive) const +{ + if (false == archive.BeginWrite3dmAnonymousChunk(1)) + return false; + + bool rc = false; + for (;;) + { + if (false == archive.WriteBool(PackFaces())) + break; + const unsigned u = static_cast(this->ExtraordinaryVertexProcess()); + if (false == archive.WriteInt(u)) + break; + rc = true; + break; + } + + if (false == archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned( unsigned int element_pointer_type_as_unsigned ) @@ -4710,6 +4804,65 @@ ON__UINT64 ON_SubD::GeometryContentSerialNumber() const return (nullptr != subdimple) ? subdimple->GeometryContentSerialNumber() : 0; } +const ON_SubDHash ON_SubD::SubDHash( + ON_SubDHashType hash_type, + bool bForceUpdate +) const +{ + ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->SubDHash(hash_type,*this, bForceUpdate) : ON_SubDHash::Create(hash_type , *this); +} + + +const ON_SubDHash ON_SubDimple::SubDHash( + ON_SubDHashType hash_type, + const ON_SubD& parent_subd, + bool bForceUpdate +) const +{ + const unsigned vertex_count = parent_subd.VertexCount(); + if (0 == vertex_count) + return ON_SubDHash::Empty; + + if (ON_SubDHashType::Geometry != hash_type && ON_SubDHashType::Topology != hash_type) + return ON_SubDHash::Empty; + + // m_subd_geometry_hash and m_subd_toplology_hash are mutable and use lazy evaluation to stay updated. + // subd.GeometryContentSerialNumber() is used to detect stale values. + ON_SubDHash& h + = (ON_SubDHashType::Geometry == hash_type) + ? this->m_subd_geometry_hash + : this->m_subd_toplology_hash + ; + + const ON__UINT64 rsn = parent_subd.RuntimeSerialNumber(); + const ON__UINT64 gsn = parent_subd.GeometryContentSerialNumber(); + if ( + false == bForceUpdate + && h.IsNotEmpty() + && hash_type == h.HashType() + && rsn > 0 && rsn == h.SubDRuntimeSerialNumber() + && gsn > 0 && gsn == h.SubDGeometryContentSerialNumber() + && vertex_count == h.VertexCount() + && parent_subd.EdgeCount() == h.EdgeCount() + && parent_subd.FaceCount() == h.FaceCount() + ) + { + // The chached hash values are up to date (or should be). + // If h is out of date, something somewhere modified the SubD components and + // failed to change the GeometryContentSerialNumber(). + // All C++ SDK opennurbs code changes gsn after modifying SubD geometry (or it's a bug that should be fixed). + // The unwashed masses can do just about anything and that's why the bForceUpdate parameter is supplied. + return h; + } + + // update cached value + h = ON_SubDHash::Create(hash_type,parent_subd); + + // return updated value + return h; +} + ON__UINT64 ON_SubD::RenderContentSerialNumber() const { const ON_SubDimple* subdimple = m_subdimple_sp.get(); @@ -5954,6 +6107,10 @@ unsigned int ON_SubD::DumpTopology( const ON_2udex empty_id_range(ON_UNSET_UINT_INDEX, 0); + ON_SubDVertexIdIterator vidit(*this); + ON_SubDEdgeIdIterator eidit(*this); + ON_SubDFaceIdIterator fidit(*this); + unsigned int error_count = 0; for (const ON_SubDLevel* level = lit.First(); nullptr != level; level = lit.Next()) { @@ -5980,64 +6137,224 @@ unsigned int ON_SubD::DumpTopology( level_vertex_id_range, level_edge_id_range, level_face_id_range, + vidit, + eidit, + fidit, text_log); } return error_count; } -static const ON_SHA1_Hash Internal_VertexHash(const ON_SubDVertex* first_vertex) +ON_SubDHashType ON_SubDHashTypeFromUnsigned( + unsigned int subd_hash_type_as_unsigned +) { - ON_SHA1 sha1; - for (const ON_SubDVertex* v = first_vertex; nullptr != v; v = v->m_next_vertex) + switch (subd_hash_type_as_unsigned) { - sha1.AccumulateInteger32(v->m_id); - sha1.AccumulateBytes(&v->m_vertex_tag,sizeof(v->m_vertex_tag)); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDHashType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDHashType::Topology); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDHashType::Geometry); + } + return ON_SUBD_RETURN_ERROR(ON_SubDHashType::Unset); +} + +const ON_wString ON_SubDHashTypeToString( + ON_SubDHashType subd_hash_type, + bool bVerbose +) +{ + const wchar_t* name; + switch (subd_hash_type) + { + case ON_SubDHashType::Unset: + name = L"Unset"; + break; + case ON_SubDHashType::Topology: + name = L"Topology"; + break; + case ON_SubDHashType::Geometry: + name = L"Geometry"; + break; + default: + name = L"invalid"; + break; + } + + return bVerbose ? (ON_wString(L"ON_SubDHashType::") + ON_wString(name)) : ON_wString(name); +} + + + +static void Internal_AccumulateVertexHash( + ON_SHA1& sha1, + ON_SubDHashType hash_type, + const ON_SubDVertex* v +) +{ + sha1.AccumulateInteger32(v->m_id); + if (ON_SubDHashType::Geometry == hash_type) + { + sha1.AccumulateBytes(&v->m_vertex_tag, sizeof(v->m_vertex_tag)); sha1.AccumulateDoubleArray(3, v->m_P); + if (v->SubdivisionDisplacementIsNonzero()) + sha1.Accumulate3dVector(v->SubdivisionDisplacement()); } - return sha1.Hash(); } -static const ON_SHA1_Hash Internal_EdgeHash(const ON_SubDEdge* first_edge) +static const ON_SHA1_Hash Internal_VertexHash(ON_SubDHashType hash_type, const ON_SubDVertex* first_vertex, unsigned int level_index, ON_SubDVertexIdIterator& vidit) { ON_SHA1 sha1; - for (const ON_SubDEdge* e = first_edge; nullptr != e; e = e->m_next_edge) + if (ON_SubDHashType::Unset != hash_type) { - sha1.AccumulateInteger32(e->m_id); - sha1.AccumulateBytes(&e->m_edge_tag, sizeof(e->m_edge_tag)); - sha1.AccumulateInteger32(e->VertexId(0)); - sha1.AccumulateInteger32(e->VertexId(1)); - } - return sha1.Hash(); -} - -static const ON_SHA1_Hash Internal_FaceHash(const ON_SubDFace* first_face) -{ - ON_SHA1 sha1; - for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) - { - sha1.AccumulateInteger32(f->m_id); - sha1.AccumulateInteger16(f->m_edge_count); - const ON_SubDEdgePtr* eptr = f->m_edge4; - for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr) + unsigned prev_id = 0; + for (const ON_SubDVertex* v = first_vertex; nullptr != v; v = v->m_next_vertex) { - if (4 == fei) + if (prev_id > v->m_id) { - eptr = f->m_edgex; - if (nullptr == eptr) - break; + // must use slower vidit to get consistent results when complex editing juggles vertex order + sha1.Reset(); + for (v = vidit.FirstVertex(); nullptr != v; v = vidit.NextVertex()) + { + if ( level_index == v->SubdivisionLevel() && v->IsActive() ) + Internal_AccumulateVertexHash(sha1, hash_type, v); + } + break; } - const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); - if (nullptr == e) - continue; - sha1.AccumulateInteger32(e->m_id); - if (0 != ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)) - sha1.AccumulateBool(true); + Internal_AccumulateVertexHash(sha1, hash_type, v); + prev_id = v->m_id; } } return sha1.Hash(); } +const ON_SHA1_Hash ON_SubD::VertexHash(ON_SubDHashType hash_type) const +{ + ON_SubDVertexIdIterator vidit(*this); + return Internal_VertexHash(hash_type, FirstVertex(), this->ActiveLevelIndex(), vidit); +} + +static void Internal_AccumulateEdgeHash( + ON_SHA1& sha1, + ON_SubDHashType hash_type, + const ON_SubDEdge* e +) +{ + sha1.AccumulateInteger32(e->m_id); + sha1.AccumulateInteger32(e->VertexId(0)); + sha1.AccumulateInteger32(e->VertexId(1)); + sha1.AccumulateBool(e->IsCrease()); + if (ON_SubDHashType::Geometry == hash_type) + { + if (e->SubdivisionDisplacementIsNonzero()) + sha1.Accumulate3dVector(e->SubdivisionDisplacement()); + } +} + + +static const ON_SHA1_Hash Internal_EdgeHash(ON_SubDHashType hash_type, const ON_SubDEdge* first_edge, unsigned int level_index, ON_SubDEdgeIdIterator& eidit) +{ + ON_SHA1 sha1; + if (ON_SubDHashType::Unset != hash_type) + { + unsigned prev_id = 0; + for (const ON_SubDEdge* e = first_edge; nullptr != e; e = e->m_next_edge) + { + if (prev_id > e->m_id) + { + // must use slower eidit to get consistent results when complex editing juggles vertex order + sha1.Reset(); + for (e = eidit.FirstEdge(); nullptr != e; e = eidit.NextEdge()) + { + if (level_index == e->SubdivisionLevel() && e->IsActive()) + Internal_AccumulateEdgeHash(sha1, hash_type, e); + } + break; + } + Internal_AccumulateEdgeHash(sha1, hash_type, e); + prev_id = e->m_id; + } + } + return sha1.Hash(); +} + +const ON_SHA1_Hash ON_SubD::EdgeHash(ON_SubDHashType hash_type) const +{ + ON_SubDEdgeIdIterator eidit(*this); + return Internal_EdgeHash(hash_type,FirstEdge(), this->ActiveLevelIndex(), eidit); +} + +static void Internal_AccumulateFaceHash( + ON_SHA1& sha1, + ON_SubDHashType hash_type, + const ON_SubDFace* f +) +{ + sha1.AccumulateInteger32(f->m_id); + sha1.AccumulateInteger16(f->m_edge_count); + const ON_SubDEdgePtr* eptr = f->m_edge4; + for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr) + { + if (4 == fei) + { + eptr = f->m_edgex; + if (nullptr == eptr) + break; + } + sha1.AccumulateInteger32(eptr->EdgeId()); + if (0 != ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)) + sha1.AccumulateBool(true); + if (ON_SubDHashType::Geometry == hash_type) + { + if (f->SubdivisionDisplacementIsNonzero()) + sha1.Accumulate3dVector(f->SubdivisionDisplacement()); + } + } +} + +static const ON_SHA1_Hash Internal_FaceHash(ON_SubDHashType hash_type, const ON_SubDFace* first_face, unsigned int level_index, ON_SubDFaceIdIterator& fidit) +{ + ON_SHA1 sha1; + if (ON_SubDHashType::Unset != hash_type) + { + unsigned prev_id = 0; + for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) + { + if (prev_id > f->m_id) + { + // must use slower fidit to get consistent results when complex editing juggles vertex order + sha1.Reset(); + for (f = fidit.FirstFace(); nullptr != f; f = fidit.NextFace()) + { + if (level_index == f->SubdivisionLevel() && f->IsActive()) + Internal_AccumulateFaceHash(sha1, hash_type, f); + } + break; + } + Internal_AccumulateFaceHash(sha1, hash_type, f); + prev_id = f->m_id; + } + } + return sha1.Hash(); +} + +const ON_SHA1_Hash ON_SubD::FaceHash(ON_SubDHashType hash_type) const +{ + ON_SubDFaceIdIterator fidit(*this); + return Internal_FaceHash(hash_type,FirstFace(), this->ActiveLevelIndex(), fidit); +} + +//const ON_SHA1_Hash ON_SubD::SubDHash(ON_SubDHashType hash_type) const +//{ +// ON_SHA1 sha1; +// if ( VertexCount() > 0 ) +// sha1.AccumulateSubHash(VertexHash(hash_type)); +// if (EdgeCount() > 0) +// sha1.AccumulateSubHash(EdgeHash(hash_type)); +// if (FaceCount() > 0) +// sha1.AccumulateSubHash(FaceHash(hash_type)); +// return sha1.Hash(); +//} static void Internal_AccumulateFragmentArrayHash(ON_SHA1& sha1, size_t dim, const double* a, unsigned count, size_t stride) { @@ -6053,6 +6370,287 @@ static void Internal_AccumulateFragmentArrayHash(ON_SHA1& sha1, size_t dim, cons } } +const ON_SubDHash ON_SubDHash::Create(ON_SubDHashType hash_type, const class ON_SubD& subd) +{ + ON_SubDHash h; + h.m_hash_type = hash_type; + h.m_vertex_count = subd.VertexCount(); + h.m_edge_count = subd.EdgeCount(); + h.m_face_count = subd.FaceCount(); + h.m_subd_runtime_serial_number = subd.RuntimeSerialNumber(); + if (h.m_vertex_count > 0) + { + h.m_subd_geometry_content_serial_number = subd.GeometryContentSerialNumber(); + if (ON_SubDHashType::Unset != hash_type) + { + h.m_vertex_hash = subd.VertexHash(hash_type); + h.m_edge_hash = subd.EdgeHash(hash_type); + h.m_face_hash = subd.FaceHash(hash_type); + } + } + return h; +} + +int ON_SubDHash::Compare(const ON_SubDHash& lhs, const ON_SubDHash& rhs) +{ + if (lhs.m_vertex_count < rhs.m_vertex_count) + return -1; + if (lhs.m_vertex_count > rhs.m_vertex_count) + return 1; + + if (lhs.m_edge_count < rhs.m_edge_count) + return -1; + if (lhs.m_edge_count > rhs.m_edge_count) + return 1; + + if (lhs.m_face_count < rhs.m_face_count) + return -1; + if (lhs.m_face_count > rhs.m_face_count) + return 1; + + int rc; + + rc = ON_SHA1_Hash::Compare(lhs.m_vertex_hash, rhs.m_vertex_hash); + if (0 != rc) + return rc; + + rc = ON_SHA1_Hash::Compare(lhs.m_edge_hash, rhs.m_edge_hash); + if (0 != rc) + return rc; + + return ON_SHA1_Hash::Compare(lhs.m_face_hash, rhs.m_face_hash); +} + +void ON_SubDHash::Dump(ON_TextLog& text_log) const +{ + if (text_log.IsTextHash()) + return; + bool bIsNotEmpty = IsNotEmpty(); + if (bIsNotEmpty) + { + switch (this->HashType()) + { + case ON_SubDHashType::Topology: + text_log.Print("SubD toplogy hash:\n"); + break; + case ON_SubDHashType::Geometry: + text_log.Print("SubD geometry hash:\n"); + break; + default: + bIsNotEmpty = false; + break; + } + } + if (bIsNotEmpty) + text_log.Print("SubD hash: Empty\n"); + else + { + const ON_TextLogIndent indent(text_log); + const unsigned vcount = this->VertexCount(); + const unsigned ecount = this->EdgeCount(); + const unsigned fcount = this->FaceCount(); + + if (vcount > 0) + { + const ON_wString vsha1 = this->VertexHash().ToStringEx(true); + text_log.Print(L"%u vertices. SHA1 = %ls\n", vcount, static_cast(vsha1)); + } + else + text_log.Print("No vertices.\n"); + + if (ecount > 0) + { + const ON_wString esha1 = this->EdgeHash().ToStringEx(true); + text_log.Print(L"%u edges. SHA1 = %ls\n", ecount, static_cast(esha1)); + } + else + text_log.Print("No edges.\n"); + + if (fcount > 0) + { + const ON_wString fsha1 = this->FaceHash().ToStringEx(true); + text_log.Print(L"%u faces. SHA1 = %ls\n", fcount, static_cast(fsha1)); + } + text_log.Print("No faces.\n"); + } +} + +bool ON_SubDHash::Write(class ON_BinaryArchive& archive) const +{ + if (false == archive.BeginWrite3dmAnonymousChunk(1)) + return false; + bool rc = false; + for (;;) + { + const bool bIsEmpty = IsEmpty(); + if (false == archive.WriteBool(bIsEmpty)) + break; + if (bIsEmpty) + { + rc = true; + break; + } + // The SubD runtime serial number and geometry content serial numbers are runtime values. + // When appropriate, calling contexts need to take appropriate steps when writing and + // set these after reading. + const unsigned char hash_type_as_unsigned = static_cast(m_hash_type); + if (false == archive.WriteChar(hash_type_as_unsigned)) + break; + if (false == archive.WriteInt(m_vertex_count)) + break; + if (false == this->m_vertex_hash.Write(archive)) + break; + if (false == archive.WriteInt(m_edge_count)) + break; + if (false == this->m_edge_hash.Write(archive)) + break; + if (false == archive.WriteInt(m_face_count)) + break; + if (false == this->m_face_hash.Write(archive)) + break; + rc = true; + break; + } + if (false == archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_SubDHash::Read(class ON_BinaryArchive& archive) +{ + *this = ON_SubDHash::Empty; + + int chunk_version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version)) + return false; + + bool rc = false; + for (;;) + { + bool bIsEmpty = true; + if (false == archive.ReadBool(&bIsEmpty)) + break; + if (bIsEmpty) + { + rc = true; + break; + } + + // The SubD runtime serial number and geometry content serial numbers are runtime values. + // When appropriate, calling contexts need to set these after reading. + unsigned char hash_type_as_unsigned = 0; + if (false == archive.ReadChar(&hash_type_as_unsigned)) + break; + m_hash_type = ON_SubDHashTypeFromUnsigned(hash_type_as_unsigned); + + if (false == archive.ReadInt(&m_vertex_count)) + break; + if (false == this->m_vertex_hash.Read(archive)) + break; + if (false == archive.ReadInt(&m_edge_count)) + break; + if (false == this->m_edge_hash.Read(archive)) + break; + if (false == archive.ReadInt(&m_face_count)) + break; + if (false == this->m_face_hash.Read(archive)) + break; + rc = true; + break; + } + + if (false == archive.EndRead3dmChunk()) + rc = false; + return rc; + +} + +ON__UINT64 ON_SubDHash::SubDRuntimeSerialNumber() const +{ + return this->m_subd_runtime_serial_number; +} + +ON__UINT64 ON_SubDHash::SubDGeometryContentSerialNumber() const +{ + return this->m_subd_geometry_content_serial_number; +} + +bool ON_SubDHash::IsEmpty() const +{ + return ON_SubDHashType::Unset == m_hash_type || 0 == m_vertex_count; +} + +bool ON_SubDHash::IsNotEmpty() const +{ + return ON_SubDHashType::Unset != m_hash_type && m_vertex_count > 0; +} + +ON_SubDHashType ON_SubDHash::HashType() const +{ + return m_hash_type; +} + +unsigned int ON_SubDHash::VertexCount() const +{ + return m_vertex_count; +} + +unsigned int ON_SubDHash::EdgeCount() const +{ + return m_edge_count; +} + +unsigned int ON_SubDHash::FaceCount() const +{ + return m_face_count; +} + +const ON_SHA1_Hash ON_SubDHash::VertexHash() const +{ + return m_vertex_hash; +} + +const ON_SHA1_Hash ON_SubDHash::EdgeHash() const +{ + return m_edge_hash; +} + +const ON_SHA1_Hash ON_SubDHash::FaceHash() const +{ + return m_face_hash; +} + +const ON_SHA1_Hash ON_SubDHash::SubDHash() const +{ + ON_SHA1 sha1; + if (m_vertex_count > 0) + sha1.AccumulateInteger32(m_vertex_count); + if (m_edge_count > 0) + sha1.AccumulateInteger32(m_edge_count); + if (m_face_count > 0) + sha1.AccumulateInteger32(m_face_count); + if (ON_SubDHashType::Unset != m_hash_type) + { + if (m_vertex_count > 0) + sha1.AccumulateSubHash(m_vertex_hash); + if (m_edge_count > 0) + sha1.AccumulateSubHash(m_edge_hash); + if (m_face_count > 0) + sha1.AccumulateSubHash(m_face_hash); + } + return sha1.Hash(); +} + +bool operator==(const ON_SubDHash& lhs, const ON_SubDHash& rhs) +{ + return 0 == ON_SubDHash::Compare(lhs, rhs); +} + +bool operator!=(const ON_SubDHash& lhs, const ON_SubDHash& rhs) +{ + return 0 != ON_SubDHash::Compare(lhs, rhs); +} + static const ON_SHA1_Hash Internal_PackRectHash(const ON_SubDFace* first_face) { ON_SHA1 sha1; @@ -6172,6 +6770,9 @@ unsigned int ON_SubDLevel::DumpTopology( ON_2udex vertex_id_range, ON_2udex edge_id_range, ON_2udex face_id_range, + ON_SubDVertexIdIterator& vidit, + ON_SubDEdgeIdIterator& eidit, + ON_SubDFaceIdIterator& fidit, ON_TextLog& text_log ) const { @@ -6323,9 +6924,10 @@ unsigned int ON_SubDLevel::DumpTopology( return 0; // The hash uniquely identifies the subd level topology and geometry. - const ON_SHA1_Hash vhash = Internal_VertexHash(m_vertex[0]); - const ON_SHA1_Hash ehash = Internal_EdgeHash(m_edge[0]); - const ON_SHA1_Hash fhash = Internal_FaceHash(m_face[0]); + const ON_SubDHashType hash_type = ON_SubDHashType::Geometry; + const ON_SHA1_Hash vhash = Internal_VertexHash(hash_type, m_vertex[0], this->m_level_index, vidit); + const ON_SHA1_Hash ehash = Internal_EdgeHash(hash_type, m_edge[0], this->m_level_index, eidit); + const ON_SHA1_Hash fhash = Internal_FaceHash(hash_type, m_face[0], this->m_level_index, fidit); ON_SHA1 level_sha1; level_sha1.AccumulateSubHash(vhash); @@ -12977,6 +13579,9 @@ bool ON_SubDLevel::CopyEvaluationCacheForExperts(const ON_SubDLevel& src, ON_Sub ) return ON_SUBD_RETURN_ERROR(false); + // The built in fragment cache always has adaptive ON_SubDDisplayParameters::DefaultDensity + const unsigned subd_display_density = ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount(ON_SubDDisplayParameters::DefaultDensity,m_face_count); + ON_SubDVertex* this_vertex; const ON_SubDVertex* src_vertex; ON_SubDEdgePtr this_veptr, src_veptr; @@ -13192,7 +13797,7 @@ bool ON_SubDLevel::CopyEvaluationCacheForExperts(const ON_SubDLevel& src, ON_Sub if ( false == this_face->SavedSubdivisionPointIsSet()) this_face->SetSavedSubdivisionPoint(subdivision_point); if (nullptr == this_face->MeshFragments() && nullptr != src_face->MeshFragments()) - this_heap.CopyMeshFragments(src_face, this_face); + this_heap.CopyMeshFragments(src_face, subd_display_density, this_face); } } diff --git a/opennurbs_subd.h b/opennurbs_subd.h index 188b161b..f13b3fdb 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -299,6 +299,203 @@ enum class ON_SubDEdgeTag : unsigned char }; #pragma endregion +#pragma region RH_C_SHARED_ENUM [ON_SubDHashType] [Rhino.Geometry.SubDHashType] [byte] +/// +/// ON_SubDHashType used used to specify what set of information is hashed (topology or geometry). +/// +enum class ON_SubDHashType : unsigned char +{ + /// + /// Unset indicates the hash type still needs to be selected. It is not a valid type + /// for calculating a hash. + /// + Unset = 0, + + /// + /// A topology hash includes component ids, edge tags, and all topological relationships + /// between vertices, edges, and faces. Note that edge tags are not a mathematically topological + /// attribute, however creased edges divide the SubD into strong visual regions and change + /// what many contexts consider to be the connected components. + /// A topology hash ignores vertex control net points. + /// + Topology = 1, + + /// + /// A geometry hash includes all information in a toplogy hash. + /// In addition, a geometry hash includes vertex tags, vertex control net points, + /// and nonzero subdivision displacements on vertices, edges, and faces. + /// If two SubDs have the same geometry hash, then they have identical surface geometry. + /// + Geometry = 2 +}; +#pragma endregion + +ON_DECL +ON_SubDHashType ON_SubDHashTypeFromUnsigned( + unsigned int subd_hash_type_as_unsigned +); + +ON_DECL +const ON_wString ON_SubDHashTypeToString( + ON_SubDHashType subd_hash_type, + bool bVerbose +); + +/// +/// ON_SubDHash provides a simple way to save a SubD's vertex, edge, and face SHA1 hashes. +/// Typically it is used when a calculation needs to know if the current SubD has is geometrically +/// identical to a previous SubD. When speed is not important, comparing the current value of +/// ON_SubD::GeometryHash() to a previously save value of ON_SubD::GeometryHash() is functionally +/// identical but typically much slower when the SubDs are different. +/// +class ON_CLASS ON_SubDHash +{ +public: + ON_SubDHash() = default; + ~ON_SubDHash() = default; + ON_SubDHash(const ON_SubDHash&) = default; + ON_SubDHash& operator=(const ON_SubDHash&) = default; + +public: + /// + /// All counts are zero and all hashes are ON_SHA1::EmptyContentHash. + /// + static const ON_SubDHash Empty; + + // Saves the counts and hashs of the specified type + + + /// + /// Saves the counts and hashs of the specified type. + /// + /// + /// + /// + static const ON_SubDHash Create(ON_SubDHashType hash_type, const class ON_SubD& subd ); + + /// + /// Dictionary compare of VertexCount(), EdgeCount(), FaceCount(), VertexHash(), EdgeHash(), and FaceHash() in that order. + /// + /// NOTE WELL: + /// SubDRuntimeSerialNumber() and SubdGeometryContentSerialNumber() are not compared because the reason this + /// class exists is for it to be used to see if two different SubDs have identical content. + /// + /// + /// + /// + /// -1: lhs < rhs + /// 0: lhs == rsh + /// 1: lhs > rhs + /// + static int Compare(const ON_SubDHash& lhs, const ON_SubDHash& rhs); + + /* + Returns: + True if vertex count is 0 or HashType is unset. + */ + bool IsEmpty() const; + + /* + Returns: + True if vertex count is > 0 and HashType is geometry or toplology. + */ + bool IsNotEmpty() const; + + ON_SubDHashType HashType() const; + + /// + /// Returns: + /// If this hash was created from an ON_SubD, then the value of subd.RuntimeSerialNumber() is returned. + /// Otherwise, 0 is returned. + /// + /// + ON__UINT64 SubDRuntimeSerialNumber() const; + + /// + /// Returns: + /// If this hash was created from an ON_SubD, then the value of subd.GeometryContentSerialNumber() at the + /// at the instant this hash was created is returned. + /// Otherwise, 0 is returned. + /// + /// + ON__UINT64 SubDGeometryContentSerialNumber() const; + + /// + /// Saved value of subd.VertexCount(HashType()). + /// + /// + unsigned int VertexCount() const; + + /// + /// Saved value of subd.EdgeCount(HashType()). + /// + /// + unsigned int EdgeCount() const; + + /// + /// Saved value of subd.FaceCount(HashType()). + /// + /// + unsigned int FaceCount() const; + + /// + /// If two SubDs have identical VertexHash() values, + /// then the SubD vertex information associated with HashType() is identical. + /// + /// + /// A SHA1 hash of the SubD's vertex information associated with HashType(). + /// + const ON_SHA1_Hash VertexHash() const; + + /// + /// If two SubDs have identical EdgeHash() values, + /// then the SubD edge information associated with HashType() is identical. + /// + /// + /// A SHA1 hash of the SubD's edge information associated with HashType(). + /// + const ON_SHA1_Hash EdgeHash() const; + + /// + /// If two SubDs have identical FaceHash() values, + /// then the SubD face information associated with HashType() is identical. + /// + /// + /// A SHA1 hash of the SubD's face information associated with HashType(). + /// + const ON_SHA1_Hash FaceHash() const; + + /// + /// If two SubDs have identical SubDHash() values, + /// then the SubD vertex, edge, and face information associated with HashType() is identical. + /// + /// + /// A SHA1 hash of the SubD's vertex, edge, and face information associated with HashType(). + /// + const ON_SHA1_Hash SubDHash() const; + + void Dump(ON_TextLog&) const; + bool Write(class ON_BinaryArchive&) const; + bool Read(class ON_BinaryArchive&); + +private: + friend class ON_SubDimple; + ON_SubDHashType m_hash_type = ON_SubDHashType::Unset; + unsigned int m_vertex_count = 0; + unsigned int m_edge_count = 0; + unsigned int m_face_count = 0; + ON__UINT64 m_subd_runtime_serial_number = 0; + ON__UINT64 m_subd_geometry_content_serial_number = 0; + ON_SHA1_Hash m_vertex_hash = ON_SHA1_Hash::EmptyContentHash; + ON_SHA1_Hash m_edge_hash = ON_SHA1_Hash::EmptyContentHash; + ON_SHA1_Hash m_face_hash = ON_SHA1_Hash::EmptyContentHash; +}; + +bool operator==(const ON_SubDHash& lhs, const ON_SubDHash& rhs); + +bool operator!=(const ON_SubDHash& lhs, const ON_SubDHash& rhs); + + class ON_CLASS ON_SubDToBrepParameters { public: @@ -336,6 +533,7 @@ public: const ON_SubDToBrepParameters* rhs ); +#pragma region RH_C_SHARED_ENUM [ON_SubDToBrepParameters::VertexProcess] [Rhino.Geometry.SubDToBrepOptions.ExtraordinaryVertexProcessOption] [nested:byte] /// /// ON_SubDToBrepParameters::Vertex identifies the options for post processing extraorindary vertices. /// @@ -349,29 +547,35 @@ public: None = 0, /// - /// The brep vertex is G1. - /// Typically the deviation bewtween the brep and SubD surface is larger than None. + /// At extraordinary vertices, the NURBS patches are modified so they are G1 at the extraordinary vertex. + /// Typically the deviation bewtween the brep and SubD surface is larger than None and smaller than + /// LocalG1x and LocalG2. /// LocalG1 = 1, /// - /// The brep vertex is G2. - /// Typically the deviation bewtween the brep and SubD surface is larger than LocalG1. + /// At extraordinary vertices, the NURBS patches are modified so they are G2 at the extraordinary vertex. + /// Typically the deviation bewtween the brep and SubD surface is larger than LocalG1 and LocalG1x. /// LocalG2 = 2, /// - /// The brep vertex is G1. - /// Typically the deviation bewtween the brep and SubD surface is larger than None. - /// In some cases GlobalG1 produces the most please aesthetic result. + /// At extraordinary vertices, the NURBS patches are modified so they are G1 at the extraordinary vertex + /// and tend to be closer to G1 along edges near the extraordinary vertex. + /// Typically the deviation bewtween the brep and SubD surface is larger than LocalG1 and smaller than LocalG2. /// - ///GlobalG1 = 3, + LocalG1x = 3, }; +#pragma endregion static ON_SubDToBrepParameters::VertexProcess VertexProcessFromUnsigned( unsigned int vertex_process_as_unsigned ); + static const ON_wString VertexProcessToString( + ON_SubDToBrepParameters::VertexProcess vertex_process + ); + /* Returns: Option used for post processing extraorindary vertices. @@ -407,9 +611,17 @@ public: bool bPackFaces ); + const ON_wString ToString( + bool bVerbose + ) const; + + bool Read(ON_BinaryArchive& archive); + + bool Write(ON_BinaryArchive& archive) const; + private: bool m_bPackFaces = false; - ON_SubDToBrepParameters::VertexProcess m_extraordinary_vertex_process = ON_SubDToBrepParameters::VertexProcess::LocalG1; + ON_SubDToBrepParameters::VertexProcess m_extraordinary_vertex_process = ON_SubDToBrepParameters::VertexProcess::LocalG1x; unsigned short m_reserved1 = 0; unsigned int m_reserved2 = 0; double m_reserved3 = 0.0; @@ -1613,16 +1825,22 @@ private: unsigned int m_sector_face_count = 0; }; +ON_DECL bool operator==(ON_SubDSectorId, ON_SubDSectorId); +ON_DECL bool operator!=(ON_SubDSectorId, ON_SubDSectorId); +ON_DECL bool operator>(ON_SubDSectorId, ON_SubDSectorId); +ON_DECL bool operator<(ON_SubDSectorId, ON_SubDSectorId); +ON_DECL bool operator>=(ON_SubDSectorId, ON_SubDSectorId); +ON_DECL bool operator<=(ON_SubDSectorId, ON_SubDSectorId); class ON_CLASS ON_SubDVertexSurfacePointCoefficient @@ -2326,6 +2544,25 @@ public: ON__UINT64 ChangeRenderContentSerialNumber() const; + /* + Description: + Get a hash that uniquely identifies the topology or geometry of this SubD. + Parameters: + hash_type - [in] + To see what is included in the various hashes, read the documentation for the ON_SubDHashType enum. + bForceUpdate - [in] + When in doubt pass false. + The SubD hashes are mutable and cached. When code properly manages GeometryContentSerialNumber(), + then SubDHash(hash_type,false) will alwasys return the correct answer. This is the expected behavior. + If code directly modifies SubD components and fails to call ChangeGeometryContentSerialNumberForExperts(), + then it is possible a stale hash will be returned. Setting bForceUpdate = true forces the SHA1 + values to be recalculated from scratch. For extremely large SubDs, this recalculation can be time consuming. + */ + const ON_SubDHash SubDHash( + ON_SubDHashType hash_type, + bool bForceUpdate + ) const; + /* Description: Get the SubD appearance (surface or control net); @@ -3341,8 +3578,20 @@ public: // Vertex access // + /* + Returns: + Number of vertices in the active level. + */ unsigned int VertexCount() const; + /* + Parameters: + hash_type - [in] + Returns: + A SHA1 hash of the SubD's vertices. + */ + const ON_SHA1_Hash VertexHash( ON_SubDHashType hash_type ) const; + const class ON_SubDVertex* FirstVertex() const; const class ON_SubDVertex* LastVertex() const; @@ -3384,8 +3633,20 @@ public: // Edge access // + /* + Returns: + Number of edges in the active level. + */ unsigned int EdgeCount() const; + /* + Parameters: + hash_type - [in] + Returns: + A SHA1 hash of the SubD's edges. + */ + const ON_SHA1_Hash EdgeHash(ON_SubDHashType hash_type) const; + const class ON_SubDEdge* FirstEdge() const; const class ON_SubDEdge* LastEdge() const; @@ -3427,8 +3688,20 @@ public: // Face access // + /* + Returns: + Number of faces in the active level. + */ unsigned int FaceCount() const; + /* + Parameters: + hash_type - [in] + Returns: + A SHA1 hash of the SubD's faces. + */ + const ON_SHA1_Hash FaceHash(ON_SubDHashType hash_type) const; + const class ON_SubDFace* FirstFace() const; const class ON_SubDFace* LastFace() const; @@ -3466,7 +3739,6 @@ public: ON_COMPONENT_INDEX component_index ) const; - ///////////////////////////////////////////////////////// // // Component (vertex, edge, face) state ( selected, highlighted, ... ) tools @@ -3666,6 +3938,12 @@ public: */ void SetPerFaceColorsFromPackId() const; + /* + Returns: + True if per face colors were set by SetPerFaceColorsFromPackId(). + */ + bool HasPerFaceColorsFromPackId() const; + ///* //Description: @@ -5536,12 +5814,138 @@ private: void CopyHelper(const ON_SubD&); public: + /* Returns: True if every face has a nonzero PackId and a set PackRect. */ bool FacesArePacked() const; + /* + Description: + Validates the face packing. + + If a face pack contains more than one face, then all of the following are required + for that face pack to be valid. + - Every face in the pack is a quad. + - The quads form a rectangular grid. + - All faces in the rectangular grid are quads. + - All interior edges in the rectangular grid are smooth. + - All interior vertices in the rectangular grid are smooth and have 4 edge and 4 faces. + - All edges on the sides of the rectangular grid are either creases or are attached to + exactly one face in the rectangular grid. + + Parameters: + bPermitWrapping - [in] + If true, then the face pack is allowed to wrap. + For example, if bPermitWrapping is true, then a SubD cylinder that is a regular quad grid + can have a single face pack. + bIfValidThenUpdateFacePackingTopologyHash - [in] + When in doubt, pass false to test if all of the current face packing information is + completely valid. + + If you are using an add/remove creases edit approach to modify an initially valid packing, + then pass true. Otherwise pass false. + + If this parameter is true, the packing is valid, and this->FacePackingSubDTopologyHash() + does not match this->SubDTopologyHash(), then this->FacePackingSubDTopologyHash() is updated + to the current value of this->SubDTopologyHash(). + + If this paramter is false and and this->FacePackingSubDTopologyHash() + does not match this->SubDTopologyHash(), then the function returns false. + Returns: + True if FacesArePacked() is true, the quad grids meet all the conditions described above, + this->FacePackingId() is not nil, and either this->FacePackingSubDTopologyHash() is equal to + this->SubDTopologyHash() or bIfValidThenUpdateFacePackingTopologyHash is true. + */ + bool FacePackingIsValid( + bool bIfValidThenUpdateFacePackingTopologyHash + ) const; + +private: + /* + Returns: + True if all of the following are satisfied. + 1. All quads are packed into rectangular grids. + 2. The pack id is used in those grids is not used by any other face. + 3. The rectangular grids do not have interior creases. + 4. The rectangular grids have 4 boundaries (no wrapping). + 5. Non quads have pack ids that are zero or not shared with a quad. + */ + bool QuadPackingIsValid() const; + +public: + + /// + /// The fast and simple face packing uses topology, vertex tags, and edge tags to + /// group quads into rectangular grids. It does not perform geometric feature analysis. + /// {C3D8DD54-F8C8-4455-BB0E-2A2F4988EC81} + /// + static const ON_UUID FastAndSimpleFacePackingId; + + + // ADD NEW PackFaces ids above this comment and below FastAndSimplePackFacesId. + + + /// + /// ON_SubD::DefaultFacePackingId ideitifies the default face packing. + /// Code that wants to use the built-in face packing that is currently + /// the best option for general use, will specify ON_SubD::DefaultFacePackingId. + /// + /// Currently this id is ON_SubD::FastAndSimpleFacePackingId. + /// In the future it may be changed to another id. Code that wants to + /// always apply the same face packing will explicitly specify one of + /// the built-in face pack ids defined above. + /// + static const ON_UUID DefaultFacePackingId; + + static bool IsBuiltInFacePackingId( + ON_UUID candidate_id + ); + + /* + Returns: + An id that identifies the algorithm used to pack the faces in this subd. + */ + const ON_UUID FacePackingId() const; + + /* + Returns: + The value of ON_SubDHash::Create(ON_SubDHashType::Topology, *this) when the faces were packed. + */ + const ON_SubDHash FacePackingTopologyHash() const; + + /* + Description: + Sets the FacePackingTopologyHash() property to Empty. + Experts may need to do this when modifying a face packing. + After calling ClearFacePackingTopologyHashForExperts(), call + FacePackingIsValid(true) to make sure the modified packing was + actually valid and update the FacePackingTopologyHash(). + */ + void ClearFacePackingTopologyHashForExperts() const; + + /* + Description: + When a custom algorithm that is not built into ON_SubD is used to pack the + faces, this function must be called with an id that uniquely identifies the + custom algorithm. The present SubD geometry will be used to set the value + of FacePackingTopologyHash(). + Returns: + True if faces are properly packed and custom_packing_id is not nil and unique. + Otherwise the packing is reset to the default and false is returned. + */ + bool SetFacePackingIdForExperts( + ON_UUID custom_packing_id + ); + + /* + Description: + Clear all face pack ids and related information. + */ + void ClearFacePackIds(); + + private: friend class ON_SubDRef; @@ -6837,21 +7241,21 @@ public: side_segment_count <= ON_SubDMesh::MaximumSideSegmentCount side_segment_count must be a power of 2 - level_of_detail - [in] + mesh_density_reduction - [in] 0: quad count = maximum quad count = (side_count x side_count) 1: quad count = 1/4 maximum quad count 1: quad count = 1/16 maximum quad count ... - If 4^level_of_detail > maximum quad count, then a single quad is returned. + If 4^mesh_density_reduction > maximum quad count, then a single quad is returned. */ static ON_SubDMeshFragmentGrid QuadGridFromSideSegmentCount( unsigned int side_segment_count, - unsigned int level_of_detail + unsigned int mesh_density_reduction ); static ON_SubDMeshFragmentGrid QuadGridFromDisplayDensity( unsigned int display_density, - unsigned int level_of_detail + unsigned int mesh_density_reduction ); private: @@ -6928,11 +7332,19 @@ public: grid_point_index - [in] 0 <= grid_point_index < GridPointCount(). grid_parameters = [out] - normalize parameters for that point. - These could be used in the role of surface evaluation parameters - for texture mapping applications. + g2dex = Grid2dexFromPointIndex(grid_point_index). + grid_parameters[] = {g2dex.i/SideSegmentCount(), g2dex.j/SideSegmentCount()} Returns: True if grid_point_index was valid and grid_parameters was returned. + Remarks: + On a SubD quad face, + face->Vertex(0), face->Vertex(1), face->Vertex(2), face->Vertex(3) + are the SubD quad corners in counter-clockwise order. + When a grid comes from a SubD quad face, the associated grid parameters are: + grid_parameters[] = (0,0) at face->Vertex(0), + grid_parameters[] = (1,0) at face->Vertex(1), + grid_parameters[] = (0,1) at face->Vertex(3), + grid_parameters[] = (1,1) at face->Vertex(2), */ bool GetGridParameters( unsigned int grid_point_index, @@ -6947,6 +7359,23 @@ public: Grid (i,j) for that grid_point_index. 0 <= i < SidePointCount() 0 <= j < SidePointCount() + Remarks: + On a SubD quad face, + face->Vertex(0), face->Vertex(1), face->Vertex(2), face->Vertex(3) + are the SubD quad corners in counter-clockwise order. + + Set + n = grid->GridPointCount()-1 and + k = grid->SideSegmentCount() = (grid->SidePointCount()-1). + Note that (k+1)*(k+1) = (n+1) = GridPointCount(). + + When a grid comes from a SubD quad face, the associated grid point indices are + + vertex, grid point index, grid point 2dex + face->Vertex(0), 0, (0,0) + face->Vertex(1), k, (k,0) + face->Vertex(2), n-k, (k,k) + face->Vertex(3), n, (0,k) */ const ON_2udex Grid2dexFromPointIndex( unsigned int grid_point_index @@ -6960,6 +7389,23 @@ public: 0 <= j < SidePointCount() Returns: 0 <= grid_point_index < GridPointCount(). + Remarks: + On a SubD quad face, + face->Vertex(0), face->Vertex(1), face->Vertex(2), face->Vertex(3) + are the SubD quad corners in counter-clockwise order. + + Set + n = grid->GridPointCount()-1 and + k = grid->SideSegmentCount() = (grid->SidePointCount()-1). + Note that (k+1)*(k+1) = (n+1) = GridPointCount(). + + When a grid comes from a SubD quad face, the associated grid point indices are + + vertex, grid point index, grid point 2dex + face->Vertex(0), 0, (0,0) + face->Vertex(1), k, (k,0) + face->Vertex(2), n-k, (k,k) + face->Vertex(3), n, (0,k) */ unsigned int PointIndexFromGrid2dex( unsigned int i, @@ -6973,21 +7419,57 @@ public: */ unsigned int DisplayDensity() const; + ON_DEPRECATED_MSG("Identical to DisplayDensityReduction(). Use DisplayDensityReduction() instead because its a better name.") unsigned int LevelOfDetail() const; + /* + Description + Each grid set has grids of quads that index the identical set of points (normals, texture coordinates, ...). + The grid in the set with the maximum number of quads has DisplayDensityReduction() = 0. + Additional grids in the set reduce the number of quads by a factor of 4. + The intedger on the left is the DisplayDensityReduction value. The value of the right + is the number of quads where M = maximum quad count for that grid set. + All nonzero values of M are powers of 4 (4^n). The relationship between + P = number of points in the set and M (the maximum number of quads for the point set) + is P = (M/2+1)*(M/2+1). + + DisplayDensityReduction(): number of quads + 0: M + 1: M/4 + 2: M/16 + 3: M/64 + 4: M/256 + ... + + Returns: + The display density reduction from the maximum display density for this grid set. + */ + unsigned int DisplayDensityReduction() const; + private: unsigned char m_reserved; public: unsigned char m_reserved1 = 0; unsigned char m_side_segment_count; // = 2^n for non-empty grids (0 <= n <= 8) - unsigned short m_F_count; // = m_side_segment_count*m_side_segment_count - unsigned short m_F_level_of_detail; // 0 = highest, > 0 = reduced + + // m_F_count = number of quads + // = m_side_segment_count*m_side_segment_count + // (0,1,4,16,256,1024,4096) Afer 0, each permitted value is 4x previous value + unsigned short m_F_count; + + // m_F_level_of_detail is poorly named. It should be named m_F_mesh_density_reduction. + // 0 = no reduction (maximum level of detail) + // 1 = number of quads reduced by 1/4 + // 2 = number of quads reduced by 1/16 + // ... + unsigned short m_F_level_of_detail; + unsigned short m_F_stride; const unsigned int* m_F; const unsigned int* m_S; // [4*m_side_segment_count + 1] indices that form the polyline boundary. - const ON_SubDMeshFragmentGrid* m_prev_level_of_detail; // nullptr or the previous level with more facets. - const ON_SubDMeshFragmentGrid* m_next_level_of_detail; // nullptr or the next level with fewer facets. + const ON_SubDMeshFragmentGrid* m_prev_level_of_detail; // nullptr or the previous level with 4 times the number of quads. + const ON_SubDMeshFragmentGrid* m_next_level_of_detail; // nullptr or the next level with 1/4 times the number of quads. }; class ON_CLASS ON_SubDMeshFragment @@ -8503,10 +8985,10 @@ private: // /// -/// ON_SubDMesh is used to store a traditional quad mesh of either -/// a SubgD surface or SubD control net. It is typically used for -/// rendering and user interface selection cacluations. -/// It is a list of ON_SubDMeshFragment classes. +/// ON_SubDMesh is used to store a high density traditional quad mesh +/// of a SubD surface or a mesh of a SubD control net. +/// In general, is is better to use an ON_SubDMeshFragmentIterator(subd) +/// that iterates the ON_MeshFragments cached on the ON_SubD. /// class ON_CLASS ON_SubDMesh { @@ -8614,8 +9096,17 @@ public: const ON_Xform& xform ); + ON_DEPRECATED_MSG("AbsoluteSubDDisplayDensity") unsigned int DisplayDensity() const; + + /* + Returns: + The absolute subd display density used to create this display mesh. + */ + unsigned int AbsoluteSubDDisplayDensity() const; + ON_SubDDisplayParameters DisplayParameters() const; + unsigned int FragmentCount() const; const ON_SubDMeshFragment* FirstFragment() const; // linked list of mesh fragments @@ -12808,7 +13299,6 @@ public: */ unsigned int MaximumMeshDensity() const; - /* Returns: Minimum mesh density that can be extracted from these fragments. @@ -12818,6 +13308,16 @@ public: */ unsigned int MinimumMeshDensity() const; + enum : unsigned int + { + /// + /// Rhino uses this value in MeshDensityFromMaxMeshQuadCount() to se the default level of detail. + /// The enum will always exist but the value can change without breaking the SDK. + /// You code must assume this value will change with each service release of Rhino. + /// + DefaultMaximumMeshQuadCount = 262144 + }; + /* Parameters: candidate_mesh_density - [in] @@ -12923,6 +13423,11 @@ private: // used to override the appearance of SubD().SubDApperance(). ON_SubDComponentLocation m_subd_appearance_override = ON_SubDComponentLocation::Unset; +private: + // Rendering density = MaximumMeshDensity() - m_density_reduction. + unsigned char m_reserved_density_reduction = 0; // 0 = none + +private: mutable unsigned int m_maximum_mesh_density = 0; // See MaximumMeshDensity() comment // full sized fragment count (for quads) diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp index d0546ed7..1dc7af58 100644 --- a/opennurbs_subd_archive.cpp +++ b/opennurbs_subd_archive.cpp @@ -1614,7 +1614,7 @@ bool ON_SubDimple::Write( { const_cast< ON_SubDHeap* >(&m_heap)->ClearArchiveId(); - const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 3; + const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 4; if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, minor_version) ) return ON_SUBD_RETURN_ERROR(false); bool rc = false; @@ -1672,8 +1672,39 @@ bool ON_SubDimple::Write( if (false == m_symmetry.Write(archive)) break; - // runtime content number used to compare with the on on m_symmetry - if (false == archive.WriteBigInt(GeometryContentSerialNumber())) + // minor version = 3 addtions + // runtime content number used to compare with the one from the saved on m_symmetry + // Dale Lear Sep 2020 - Turns out saving the runtime GeometryContentSerialNumber() + // was a bad idea. Doing it the way I came up with today while adding + // m_face_packing_topology_hash is much better because all decisions get + // made at save time when we have the most reliable infomration. + // I've added bSyncSymmetricContentSerialNumber below, but saving gsn has to stay + // so pre-today Rhino can read files saved from post today Rhino. + const ON__UINT64 gsn = GeometryContentSerialNumber(); + if (false == archive.WriteBigInt(gsn)) + break; + + // minor version = 4 additions + // bSyncSymmetricContentSerialNumber = true when any SubD symmetry constraints are up to date. + const bool bSyncSymmetricContentSerialNumber + = m_symmetry.IsSet() + && gsn > 0 && gsn == m_symmetry.SymmetricObjectContentSerialNumber(); + + if (false == archive.WriteBool(bSyncSymmetricContentSerialNumber)) + break; + + if (false == archive.WriteUuid(m_face_packing_id)) + break; + + const bool bSyncFacePackingHashSerialNumbers + = m_face_packing_topology_hash.IsNotEmpty() + && this->RuntimeSerialNumber == m_face_packing_topology_hash.SubDRuntimeSerialNumber() + && gsn > 0 && gsn == m_face_packing_topology_hash.SubDGeometryContentSerialNumber() + ; + if (false == archive.WriteBool(bSyncFacePackingHashSerialNumbers)) + break; + + if (false == m_face_packing_topology_hash.Write(archive)) break; rc = true; @@ -1703,7 +1734,9 @@ bool ON_SubDimple::Read( unsigned int obsolete_archive_max_vertex_id = 0; unsigned int obsolete_archive_max_edge_id = 0; unsigned int obsolete_archive_max_face_id = 0; - ON__UINT64 saved_content_serial_number = 0; + + bool bSyncSymmetricContentSerialNumber = false; + bool bSyncFacePackingHashSerialNumbers = false; for (;;) { @@ -1760,8 +1793,30 @@ bool ON_SubDimple::Read( if (minor_version >= 3) { - if (false == archive.ReadBigInt(&saved_content_serial_number)) + ON__UINT64 gsn_at_save_time = 0; + if (false == archive.ReadBigInt(&gsn_at_save_time)) break; + if (3 == minor_version) + { + bSyncSymmetricContentSerialNumber + = gsn_at_save_time > 0 + && m_symmetry.IsSet() + && gsn_at_save_time == m_symmetry.SymmetricObjectContentSerialNumber(); + } + + if (minor_version >= 4) + { + // minor version = 4 additions + if (false == archive.ReadBool(&bSyncSymmetricContentSerialNumber)) + break; + + if (false == archive.ReadUuid(m_face_packing_id)) + break; + if (false == archive.ReadBool(&bSyncFacePackingHashSerialNumbers)) + break; + if (false == m_face_packing_topology_hash.Read(archive)) + break; + } } } } @@ -1779,12 +1834,6 @@ bool ON_SubDimple::Read( m_heap.ResetIds(); // breaks component id references, but this is a serious error. } - const bool bSetSymmetricObjectContentSerialNumber - = saved_content_serial_number > 0 - && m_symmetry.IsSet() - && saved_content_serial_number == m_symmetry.SymmetricObjectContentSerialNumber() - ; - if (archive.ArchiveOpenNURBSVersion() < 2382394661) { // try to get texture information set correctly when it comes from old files @@ -1832,11 +1881,24 @@ bool ON_SubDimple::Read( ChangeGeometryContentSerialNumber(false); - if ( bSetSymmetricObjectContentSerialNumber ) + if (bSyncSymmetricContentSerialNumber) m_symmetry.SetSymmetricObjectContentSerialNumber(GeometryContentSerialNumber()); else m_symmetry.ClearSymmetricObjectContentSerialNumber(); + if (bSyncFacePackingHashSerialNumbers) + { + // When the file was saved, the values of subd.GeometryContentSerialNumber() and + // and m_face_packing_topology_hash.SubDGeometryContentSerialNumber() were the same. + m_face_packing_topology_hash.m_subd_runtime_serial_number = this->RuntimeSerialNumber; + m_face_packing_topology_hash.m_subd_geometry_content_serial_number = this->GeometryContentSerialNumber(); + } + else + { + m_face_packing_topology_hash.m_subd_runtime_serial_number = 0; + m_face_packing_topology_hash.m_subd_geometry_content_serial_number = 0; + } + if (rc) return true; return ON_SUBD_RETURN_ERROR(false); diff --git a/opennurbs_subd_copy.cpp b/opennurbs_subd_copy.cpp index 335c3881..a85b0bdf 100644 --- a/opennurbs_subd_copy.cpp +++ b/opennurbs_subd_copy.cpp @@ -767,18 +767,51 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src) m_active_level = level; } + // this ON_SubDimple is an exact copy of src, so they have identical geometry + // and render content. + m_subd_geometry_content_serial_number = src.m_subd_geometry_content_serial_number; + m_subd_render_content_serial_number = src.m_subd_render_content_serial_number; + m_subd_appearance = src.m_subd_appearance; m_texture_coordinate_type = src.m_texture_coordinate_type; m_texture_mapping_tag = src.m_texture_mapping_tag; + m_face_packing_id = src.m_face_packing_id; + m_face_packing_topology_hash = src.m_face_packing_topology_hash; + m_face_packing_topology_hash.m_subd_runtime_serial_number + = (src.RuntimeSerialNumber == src.m_face_packing_topology_hash.SubDRuntimeSerialNumber()) + ? this->RuntimeSerialNumber + : 0; + if (0 == m_face_packing_topology_hash.m_subd_runtime_serial_number) + m_face_packing_topology_hash.m_subd_geometry_content_serial_number = 0; + m_symmetry = src.m_symmetry; - ChangeGeometryContentSerialNumber(false); - - if (src.m_subd_geometry_content_serial_number == src.m_symmetry.SymmetricObjectContentSerialNumber()) - m_symmetry.SetSymmetricObjectContentSerialNumber(GeometryContentSerialNumber()); - else + if (m_subd_geometry_content_serial_number != src.m_symmetry.SymmetricObjectContentSerialNumber()) + { + // symmetric content was out of date on src m_symmetry.ClearSymmetricObjectContentSerialNumber(); + } + + if ( + src.RuntimeSerialNumber == src.m_subd_geometry_hash.SubDRuntimeSerialNumber() + && m_subd_geometry_content_serial_number == src.m_subd_geometry_hash.SubDGeometryContentSerialNumber() + ) + { + // src.m_subd_geometry_hash is valid - copy it to this. + m_subd_geometry_hash = src.m_subd_geometry_hash; + m_subd_geometry_hash.m_subd_runtime_serial_number = this->RuntimeSerialNumber; + } + + if ( + src.RuntimeSerialNumber == src.m_subd_toplology_hash.SubDRuntimeSerialNumber() + && m_subd_geometry_content_serial_number == src.m_subd_toplology_hash.SubDGeometryContentSerialNumber() + ) + { + // src.m_subd_toplology_hash is valid - copy it to this + m_subd_toplology_hash = src.m_subd_toplology_hash; + m_subd_toplology_hash.m_subd_runtime_serial_number = this->RuntimeSerialNumber; + } } bool ON_SubDLevel::IsEmpty() const diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 6dc05434..bd579939 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -606,6 +606,9 @@ public: ON_2udex vertex_id_range, ON_2udex edge_id_range, ON_2udex face_id_range, + ON_SubDVertexIdIterator& vidit, + ON_SubDEdgeIdIterator& eidit, + ON_SubDFaceIdIterator& fidit, ON_TextLog& text_log ) const; @@ -1429,11 +1432,13 @@ public: The fragment grid must be 16x16 or 8x8. */ class ON_SubDMeshFragment* AllocateMeshFragment( + unsigned subd_display_density, const class ON_SubDMeshFragment& src_fragment ); class ON_SubDMeshFragment* CopyMeshFragments( const class ON_SubDFace* source_face, + unsigned destination_subd_display_density, const class ON_SubDFace* destination_face ); @@ -1572,15 +1577,30 @@ private: // // 16x16 ON_SubDMeshFragment // 8x8 ON_SubDMeshFragment + // 4x4 ON_SubDMeshFragment + // 2x2 ON_SubDMeshFragment + // 1x1 ON_SubDMeshFragment // ON_SubDEdgeSurfaceCurve bool Internal_InitializeLimitBlockPool(); ON_FixedSizePool m_limit_block_pool; - // Used to allocate 16x16 fragments for quads - size_t m_sizeof_full_fragment = 0; - class ON_FixedSizePoolElement* m_unused_full_fragments = nullptr; - // Used to allocate 8x8 fragments for N-gons with N != 4 - size_t m_sizeof_half_fragment = 0; - class ON_FixedSizePoolElement* m_unused_half_fragments = nullptr; + + // m_sizeof_fragment[i] = sizeof of a fragment NxN mesh quads where N = 2^i + // m_sizeof_fragment[0] = sizeof of a fragment with 1x1 mesh quads. + // m_sizeof_fragment[1] = sizeof of a fragment with 2x2 mesh quads. + // m_sizeof_fragment[2] = sizeof of a fragment with 4x4 mesh quads. + // m_sizeof_fragment[3] = sizeof of a fragment with 8x8 mesh quads. + // m_sizeof_fragment[4] = sizeof of a fragment with 16x16 mesh quads. + // ... + size_t m_sizeof_fragment[ON_SubDDisplayParameters::MaximumDensity + 1] = {}; + + // m_unused_fragments[0] = unused 1x1 mesh quad fragments. + // m_unused_fragments[1] = unused 2x2 mesh quad fragments. + // m_unused_fragments[2] = unused 4x4 mesh quad fragments. + // m_unused_fragments[3] = unused 8x8 mesh quad fragments. + // m_unused_fragments[4] = unused 16x16 mesh quad fragments. + // ... + class ON_FixedSizePoolElement* m_unused_fragments[ON_SubDDisplayParameters::MaximumDensity + 1] = {}; + // Used to allocate edge curves size_t m_sizeof_limit_curve = 0; class ON_FixedSizePoolElement* m_unused_limit_curves = nullptr; @@ -1668,6 +1688,12 @@ public: */ ON__UINT64 GeometryContentSerialNumber() const; + const ON_SubDHash SubDHash( + ON_SubDHashType hash_type, + const ON_SubD& parent_subd, + bool bForceUpdate + ) const; + /* Returns: A runtime serial number that is incremented every time a component status @@ -1725,6 +1751,8 @@ public: private: mutable ON__UINT64 m_subd_geometry_content_serial_number = 0; mutable ON__UINT64 m_subd_render_content_serial_number = 0; + mutable ON_SubDHash m_subd_geometry_hash = ON_SubDHash::Empty; + mutable ON_SubDHash m_subd_toplology_hash = ON_SubDHash::Empty; public: @@ -2333,6 +2361,29 @@ private: const ON_SubDFace* face ); +public: + const ON_UUID FacePackingId() const; + + const ON_SubDHash FacePackingTopologyHash() const; + + + void SetFacePackingIdAndTopologyHash( + ON_UUID custom_packing_id, + const ON_SubDHash& current_topology_hash + ); + + /* + Description: + Clear all face pack ids and related information. + */ + void ClearFacePackIds(); + + void SetFacePackingTopologyHashForExperts(const ON_SubDHash& current_topology_hash) const; + +private: + ON_UUID m_face_packing_id = ON_nil_uuid; + mutable ON_SubDHash m_face_packing_topology_hash = ON_SubDHash::Empty; + private: ON_Symmetry m_symmetry; @@ -2579,7 +2630,7 @@ public: private: ON__UINT64 m_mesh_content_serial_number = 0; public: - unsigned int m_display_density = 0; + unsigned int m_absolute_subd_display_density = 0; unsigned int m_fragment_count = 0; unsigned int m_fragment_point_count = 0; ON_SubDMeshFragment* m_first_fragment = nullptr; @@ -2587,7 +2638,7 @@ public: bool ReserveCapacity( unsigned int subd_fragment_count, - unsigned int display_density + unsigned int absolute_subd_display_density ); /* diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp index a096d5fc..a0e8ecf9 100644 --- a/opennurbs_subd_fragment.cpp +++ b/opennurbs_subd_fragment.cpp @@ -1824,6 +1824,13 @@ ON_ComponentStatus ON_SubDMeshFragment::Status() const unsigned int ON_SubDMeshFragmentGrid::LevelOfDetail() const +{ + // A better name for this value is Display Density Reduction. + // Identical to ON_SubDMeshFragmentGrid::DisplayDensityReduction(). + return m_F_level_of_detail; +} + +unsigned int ON_SubDMeshFragmentGrid::DisplayDensityReduction() const { return m_F_level_of_detail; } @@ -1898,16 +1905,22 @@ const ON_2udex ON_SubDMeshFragmentGrid::Grid2dexFromPointIndex( { for (;;) { + // On a quad face, first grid points run from face->Vertex(0) to face->Vertex(1). + // The "i" grid index increases from face->Vertex(0) to face->Vertex(1). + // The "j" grid index increases from face->Vertex(0) to face->Vertex(3). const unsigned int side_segment_count = SideSegmentCount(); if (0 == side_segment_count) break; const unsigned int grid_side_point_count = side_segment_count + 1; if (grid_point_index >= grid_side_point_count * grid_side_point_count) break; + + // CORRECT - do not change const ON_2udex griddex( grid_point_index % grid_side_point_count, grid_point_index / grid_side_point_count ); + return griddex; } return ON_SUBD_RETURN_ERROR(ON_2udex::Unset); @@ -1926,7 +1939,9 @@ unsigned int ON_SubDMeshFragmentGrid::PointIndexFromGrid2dex( const unsigned int grid_side_point_count = side_segment_count + 1; if (i >= grid_side_point_count && j >= grid_side_point_count) break; - return grid_side_point_count * i + j; + // BAD BUG Oct 2020 // return grid_side_point_count * i + j; + // Fixed Oct 13, 2020 + return i + grid_side_point_count * j; // CORRECT - do not change } return ON_UNSET_UINT_INDEX; } @@ -1945,15 +1960,15 @@ bool ON_SubDMeshFragmentGrid::GetGridParameters( const unsigned int grid_side_point_count = side_segment_count + 1; if (grid_point_index >= grid_side_point_count*grid_side_point_count) break; - const unsigned int i = grid_point_index % grid_side_point_count; + const ON_2udex g2dex = Grid2dexFromPointIndex(grid_point_index); + grid_parameters[0] - = (i < grid_side_point_count) - ? (((double)i) / ((double)grid_side_point_count)) + = (g2dex.i < side_segment_count) + ? (((double)g2dex.i) / ((double)side_segment_count)) : 1.0; - const unsigned int j = grid_point_index / grid_side_point_count; grid_parameters[1] - = (j < grid_side_point_count) - ? (((double)j) / ((double)grid_side_point_count)) + = (g2dex.j < side_segment_count) + ? (((double)g2dex.j) / ((double)side_segment_count)) : 1.0; return true; } @@ -1965,19 +1980,19 @@ bool ON_SubDMeshFragmentGrid::GetGridParameters( ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::QuadGridFromSideSegmentCount( unsigned int side_segment_count, - unsigned int level_of_detail + unsigned int mesh_density_reduction ) { const unsigned int display_density = ON_SubDMeshFragment::DisplayDensityFromSideSegmentCount(side_segment_count); if ( side_segment_count != ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity(display_density) ) return ON_SUBD_RETURN_ERROR(ON_SubDMeshFragmentGrid::Empty); - return ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity(display_density,level_of_detail); + return ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity(display_density, mesh_density_reduction); } ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity( unsigned int display_density, - unsigned int level_of_detail + unsigned int mesh_density_reduction ) { static const ON_SubDMeshFragmentGrid* grid_cache[9] = { 0 }; // side_segment_count = 1,2,4,8,16,32,64,128,256 @@ -1988,7 +2003,7 @@ ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity( const ON_SubDMeshFragmentGrid* fragment_grid = grid_cache[display_density]; if (nullptr != fragment_grid) { - while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) + while ( fragment_grid->m_F_level_of_detail < mesh_density_reduction && nullptr != fragment_grid->m_next_level_of_detail) fragment_grid = fragment_grid->m_next_level_of_detail; return *fragment_grid; } @@ -2004,7 +2019,7 @@ ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity( if (nullptr != fragment_grid) { lock.ReturnLock(); - while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) + while ( fragment_grid->m_F_level_of_detail < mesh_density_reduction && nullptr != fragment_grid->m_next_level_of_detail) fragment_grid = fragment_grid->m_next_level_of_detail; return *fragment_grid; } @@ -2117,7 +2132,7 @@ ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity( fragment_grid = grid_cache[display_density]; if (nullptr != fragment_grid) { - while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) + while ( fragment_grid->m_F_level_of_detail < mesh_density_reduction && nullptr != fragment_grid->m_next_level_of_detail) fragment_grid = fragment_grid->m_next_level_of_detail; return *fragment_grid; } @@ -2167,20 +2182,20 @@ void ON_SubDMeshImpl::ClearFragmentFacePointers( bool ON_SubDMeshImpl::ReserveCapacity( unsigned int subd_fragment_count, - unsigned int display_density + unsigned int absolute_subd_display_density ) { ClearTree(); - m_display_density = 0; + m_absolute_subd_display_density = 0; m_fragment_count = 0; m_fragment_point_count = 0; m_first_fragment = nullptr; - if ( display_density > ON_SubDDisplayParameters::MaximumDensity) + if (absolute_subd_display_density > ON_SubDDisplayParameters::MaximumDensity) return ON_SUBD_RETURN_ERROR(false); - unsigned int fragment_point_count = ON_SubDMeshFragment::PointCountFromDisplayDensity(display_density); + unsigned int fragment_point_count = ON_SubDMeshFragment::PointCountFromDisplayDensity(absolute_subd_display_density); if ( subd_fragment_count < 1 ) return ON_SUBD_RETURN_ERROR(false); @@ -2197,7 +2212,7 @@ bool ON_SubDMeshImpl::ReserveCapacity( if( false == m_fsp.Create(sizeof_fragment + sizeof_PNTC,subd_fragment_count,0)) return ON_SUBD_RETURN_ERROR(false); - m_display_density = display_density; + m_absolute_subd_display_density = absolute_subd_display_density; m_fragment_point_count = fragment_point_count; return true; @@ -2389,7 +2404,10 @@ bool ON_SubDMeshFragment::CopyFrom( } else { - // srf_fragment is more dense than target. Copy a subset of the points. + // src_fragment is more dense than target. Copy a subset of the points. + // + // This code appears to be rarely used. It is not well tested. + // const unsigned int s0 = grid.SideSegmentCount(); const unsigned int s1 = src_fragment.m_grid.SideSegmentCount(); unsigned int x = 1; @@ -2399,7 +2417,7 @@ bool ON_SubDMeshFragment::CopyFrom( const unsigned int di = src_fragment.m_grid.PointIndexFromGrid2dex(x, 0); const unsigned int dj = src_fragment.m_grid.PointIndexFromGrid2dex(0, x); - // copy m_P[] + // copy from src_fragment.m_P[] to this->m_P[] size_t i_stride = di * src_fragment.m_P_stride; size_t j_stride = m * dj * src_fragment.m_P_stride; for (unsigned int j = 0; j < m; j += dj) @@ -2417,7 +2435,7 @@ bool ON_SubDMeshFragment::CopyFrom( if (V_count <= NormalCapacity()) { - // copy m_N[] + // copy from src_fragment.m_N[] to this->m_N[] if (V_count <= src_fragment.NormalCount()) { p = m_N; @@ -2445,7 +2463,7 @@ bool ON_SubDMeshFragment::CopyFrom( if (V_count <= TextureCoordinateCapacity()) { - // copy m_T[] + // copy from src_fragment.m_T[] to this->m_T[] if (V_count <= src_fragment.TextureCoordinateCount()) { SetTextureCoordinatesExist(true); @@ -2474,7 +2492,7 @@ bool ON_SubDMeshFragment::CopyFrom( if (V_count <= ColorCapacity()) { - // copy m_C[] + // copy from src_fragment.m_C[] to this->m_C[] if (V_count <= src_fragment.ColorCount()) { SetColorsExist(true); @@ -2879,7 +2897,7 @@ ON_SubDMeshImpl::ON_SubDMeshImpl() ON_SubDMeshImpl::ON_SubDMeshImpl( const class ON_SubDMeshImpl& src ) { ChangeContentSerialNumber(); - if ( nullptr != src.m_first_fragment && ReserveCapacity((unsigned int)src.m_fsp.ActiveElementCount(), src.m_display_density ) ) + if ( nullptr != src.m_first_fragment && ReserveCapacity((unsigned int)src.m_fsp.ActiveElementCount(), src.m_absolute_subd_display_density) ) { for (const ON_SubDMeshFragment* src_fragment = src.m_first_fragment; nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment) { @@ -2962,18 +2980,21 @@ ON_SubDDisplayParameters ON_SubDMesh::DisplayParameters() const const ON_SubDMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) { - ON_SubDDisplayParameters dp; - dp.SetDisplayDensity(impl->m_display_density); - return dp; + return ON_SubDDisplayParameters::CreateFromAbsoluteDisplayDensity(impl->m_absolute_subd_display_density); } return ON_SubDDisplayParameters::Empty; } unsigned int ON_SubDMesh::DisplayDensity() const +{ + return AbsoluteSubDDisplayDensity(); +} + +unsigned int ON_SubDMesh::AbsoluteSubDDisplayDensity() const { const ON_SubDMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) - ? impl->m_display_density + ? impl->m_absolute_subd_display_density : 0; } @@ -3249,9 +3270,17 @@ void ON_SubDMesh::Swap( void ON_SubDDisplayParameters::Dump(class ON_TextLog& text_log) const { - text_log.Print(L"DisplayDensity = %u", DisplayDensity()); - switch (DisplayDensity()) + const unsigned display_density = DisplayDensity(ON_SubD::Empty); + if (this->DisplayDensityIsAbsolute()) + text_log.Print(L"Absolute DisplayDensity = %u", display_density); + else + text_log.Print(L"Adaptive DisplayDensity = %u", display_density); + + switch (display_density) { + case ON_SubDDisplayParameters::UnsetDensity: + text_log.Print(L" (UnsetDensity)"); + break; case ON_SubDDisplayParameters::ExtraCoarseDensity: text_log.Print(L" (ExtraCoarse)"); break; @@ -3291,15 +3320,86 @@ void ON_SubDDisplayParameters::Dump(class ON_TextLog& text_log) const } } -const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( - unsigned int display_density - ) +unsigned int ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount( + unsigned adaptive_subd_display_density, + unsigned subd_face_count +) { - if ( display_density > ON_SubDDisplayParameters::MaximumDensity ) - return ON_SUBD_RETURN_ERROR(ON_SubDDisplayParameters::Empty); + // The returned density must always be >= 2 so we can reliably calculate + // NURBS curve forms of SubD edge curves in that begin/end at an + // extraordinary vertex. This is done in + // ON_SubDEdge::GetEdgeSurfaceCurveControlPoints(). + // When there is a better way to get NURBS approsimations of edges that end + // at extraordinary vertices, we can reduce min_display_density to 1. + const unsigned min_display_density = 1; // adaptive cutoff + +#if 1 + if (adaptive_subd_display_density <= min_display_density) + return adaptive_subd_display_density; + if (adaptive_subd_display_density > ON_SubDDisplayParameters::MaximumDensity) + adaptive_subd_display_density = ON_SubDDisplayParameters::DefaultDensity; + + const unsigned max_mesh_quad_count = ON_SubDDisplayParameters::AdaptiveDisplayMeshQuadMaximum; + unsigned absolute_display_density = adaptive_subd_display_density; + unsigned mesh_quad_count = (1 << (2 * absolute_display_density)) * subd_face_count; + while (absolute_display_density > min_display_density && mesh_quad_count > max_mesh_quad_count) + { + --absolute_display_density; + mesh_quad_count /= 4; + } + return absolute_display_density; +#else + +#if !defined(ON_DEBUG) + // This insures an accidental commit will never get merged. +#error NEVER COMMIT THIS CODE! +#endif + + // used to test contanst densities when debugging. + return min_display_density; + //return 3; + //return ON_SubDDisplayParameters::DefaultDensity; + +#endif +} + +unsigned int ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubD( + unsigned adaptive_subd_display_density, + const ON_SubD& subd +) +{ + // If this function changes, then a parallel change must be made in + // ON_SubDLevel::CopyEvaluationCacheForExperts(const ON_SubDLevel& src, ON_SubDHeap& this_heap) + // so it uses the same automatic density value. + return ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount(adaptive_subd_display_density,subd.FaceCount()); +} + +const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( + unsigned int adaptive_subd_display_density +) +{ + if (adaptive_subd_display_density > ON_SubDDisplayParameters::MaximumDensity) + return ON_SUBD_RETURN_ERROR(ON_SubDDisplayParameters::Default); ON_SubDDisplayParameters limit_mesh_parameters; - limit_mesh_parameters.m_display_density = display_density; + limit_mesh_parameters.m_display_density = (unsigned char)adaptive_subd_display_density; + limit_mesh_parameters.m_bDisplayDensityIsAbsolute = false; + return limit_mesh_parameters; +} + +const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromAbsoluteDisplayDensity( + unsigned int absolute_subd_display_density + ) +{ + if (absolute_subd_display_density > ON_SubDDisplayParameters::MaximumDensity) + { + ON_SUBD_ERROR("absolute_subd_display_density parameter is too large."); + absolute_subd_display_density = ON_SubDDisplayParameters::DefaultDensity; + } + + ON_SubDDisplayParameters limit_mesh_parameters; + limit_mesh_parameters.m_display_density = (unsigned char)absolute_subd_display_density; + limit_mesh_parameters.m_bDisplayDensityIsAbsolute = true; return limit_mesh_parameters; } @@ -3311,48 +3411,8 @@ const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromMeshDensity( if (normalized_mesh_density >= 0.0 && normalized_mesh_density <= 1.0) { - if (0.0 == normalized_mesh_density) - return ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::MinimumDensity); - if (1.0 == normalized_mesh_density) - return ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::MaximumDensity); + unsigned int subd_display_density; - - unsigned int subd_display_density = ON_SubDDisplayParameters::Default.DisplayDensity(); - - - // The assignment of slider_value to subd_display_density was arrived at with a long testing period - // including all of Rhino 6 limited subd support and ending with Rhino 7 WIP in June 2020. - // June 2020 mapping of slide_value to subd_display_density - // - // [0.0, ON_ZERO_TOLERANCE] -> 1 = ExtraCoarseDensity - // (ON_ZERO_TOLERANCE, 0.17) -> 2 = CoarseDensity - // [0.17, 0.33) -> 3 = MediumDensity - // [0.33, 0.75] -> 4 = FineDensity - // (0.75, 1 - ON_ZERO_TOLERANCE) -> 5 = ExtraFineDensity - // [1 - ON_ZERO_TOLERANCE, 1.0] -> 6 = MaximumDensity - // Invalid input -> ON_SubDDisplayParameters::DefaultDensity; - // - // Beginning June 20, 2020 the values were slightly adjusted so "simple mesh" UI documentation - // can use more friendly looking values. - // - // [0.0, ON_ZERO_TOLERANCE] -> 1 = ExtraCoarseDensity - // (ON_ZERO_TOLERANCE, 0.20) -> 2 = CoarseDensity - // [0.20, 0.35) -> 3 = MediumDensity - // [0.35, 0.75] -> 4 = FineDensity - // (0.75, 1 - ON_ZERO_TOLERANCE) -> 5 = ExtraFineDensity - // [1 - ON_ZERO_TOLERANCE, 1.0] -> 6 = MaximumDensity - // Invalid input -> ON_SubDDisplayParameters::DefaultDensity; - // - // If you make changes, you must provide name and YouTrack issues link so there is a clear - // history of the reasons the changes were made. You must also update the comments in the - // function declaration. - // Always include the SubD Rhino Logo in your tests. - // - // Be conservative about returning values > 4. - // 5 generates 1024 mesh quads per subd quad and 6 generates 4096 mesh quads per subd quad. - // - // Values of subd_display_density < 4 are visibly chunky on simple subd models (spheres, Rhino subd logo, ...) - // if (normalized_mesh_density <= ON_ZERO_TOLERANCE) subd_display_density = ON_SubDDisplayParameters::ExtraCoarseDensity; else if (normalized_mesh_density < 0.20) @@ -3361,27 +3421,69 @@ const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromMeshDensity( subd_display_density = ON_SubDDisplayParameters::MediumDensity; else if (normalized_mesh_density <= 0.75) subd_display_density = ON_SubDDisplayParameters::FineDensity; - else if (normalized_mesh_density <= 1.0 - ON_ZERO_TOLERANCE) + else if (normalized_mesh_density < 1.0 - ON_ZERO_TOLERANCE) subd_display_density = ON_SubDDisplayParameters::ExtraFineDensity; - else if (normalized_mesh_density >= 1.0 - ON_ZERO_TOLERANCE) - subd_display_density = ON_SubDDisplayParameters::MaximumDensity; + else if (normalized_mesh_density <= 1.0 + ON_ZERO_TOLERANCE) + subd_display_density = ON_SubDDisplayParameters::ExtraFineDensity; + else + { + ON_ERROR("Bug in some if condition in this function."); + subd_display_density = ON_SubDDisplayParameters::DefaultDensity; + } return ON_SubDDisplayParameters::CreateFromDisplayDensity(subd_display_density); } - ON_ERROR("Invalid slider_value parameter."); + ON_ERROR("Invalid normalized_mesh_density parameter."); return ON_SubDDisplayParameters::Default; } +bool ON_SubDDisplayParameters::DisplayDensityIsAdaptive() const +{ + return (false == m_bDisplayDensityIsAbsolute) ? true : false; +} + +bool ON_SubDDisplayParameters::DisplayDensityIsAbsolute() const +{ + return (true == m_bDisplayDensityIsAbsolute) ? true : false; +} + unsigned int ON_SubDDisplayParameters::DisplayDensity() const { - return m_display_density; + // ON_SubDDisplayParameters::DisplayDensity() is deprecated. + // Use DisplayDensity(subd). + return DisplayDensity(ON_SubD::Empty); // Empty dispables reduction } -void ON_SubDDisplayParameters::SetDisplayDensity(unsigned int display_density) +unsigned int ON_SubDDisplayParameters::DisplayDensity(const ON_SubD& subd) const { - if (display_density <= ON_SubDDisplayParameters::MaximumDensity) - m_display_density = display_density; + const unsigned display_density = this->m_display_density; + return + this->DisplayDensityIsAdaptive() ? + ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubD(display_density, subd) + : display_density + ; +} + +void ON_SubDDisplayParameters::SetDisplayDensity(unsigned int adaptive_display_density) +{ + SetAdaptiveDisplayDensity(adaptive_display_density); +} + +void ON_SubDDisplayParameters::SetAdaptiveDisplayDensity(unsigned int adaptive_display_density) +{ + if (adaptive_display_density > ON_SubDDisplayParameters::MaximumDensity) + adaptive_display_density = ON_SubDDisplayParameters::MaximumDensity; + m_display_density = (unsigned char)adaptive_display_density; + m_bDisplayDensityIsAbsolute = false; +} + +void ON_SubDDisplayParameters::SetAbsoluteDisplayDensity(unsigned int absolute_display_density) +{ + if (absolute_display_density > ON_SubDDisplayParameters::MaximumDensity) + absolute_display_density = ON_SubDDisplayParameters::MaximumDensity; + m_display_density = (unsigned char)absolute_display_density; + m_bDisplayDensityIsAbsolute = true; } ON_SubDComponentLocation ON_SubDDisplayParameters::MeshLocation() const @@ -3396,9 +3498,12 @@ void ON_SubDDisplayParameters::SetMeshLocation(ON_SubDComponentLocation mesh_loc unsigned char ON_SubDDisplayParameters::EncodeAsUnsignedChar() const { + const unsigned char max_display_density = ((unsigned char)ON_SubDDisplayParameters::MaximumDensity); + const unsigned char default_display_density = ((unsigned char)ON_SubDDisplayParameters::DefaultDensity); unsigned char encoded_parameters; if ( - ON_SubDDisplayParameters::Default.m_display_density == m_display_density + (default_display_density == m_display_density || m_display_density > max_display_density) + && ON_SubDDisplayParameters::Default.DisplayDensityIsAdaptive() == DisplayDensityIsAdaptive() && ON_SubDDisplayParameters::Default.MeshLocation() == MeshLocation() ) { @@ -3407,25 +3512,30 @@ unsigned char ON_SubDDisplayParameters::EncodeAsUnsignedChar() const } else { - const unsigned int density - = (m_display_density <= ON_SubDDisplayParameters::MaximumDensity) + const unsigned char density + = (m_display_density <= max_display_density) ? m_display_density - : ON_SubDDisplayParameters::DefaultDensity + : default_display_density ; - const unsigned char density_as_char - = ((unsigned char)(density & ((unsigned int)ON_SubDDisplayParameters::subd_mesh_density_mask))); + const unsigned char density_as_char = (density & ON_SubDDisplayParameters::subd_mesh_density_mask); const unsigned char location = (ON_SubDComponentLocation::ControlNet == MeshLocation()) ? ON_SubDDisplayParameters::subd_mesh_location_bit : 0; + const unsigned char absoute_density_as_char + = m_bDisplayDensityIsAbsolute + ? ON_SubDDisplayParameters::subd_mesh_absolute_density_bit + : 0; + const unsigned char nondefault_settings = ON_SubDDisplayParameters::subd_mesh_nondefault_bit; encoded_parameters = nondefault_settings | density_as_char + | absoute_density_as_char | location ; } @@ -3440,11 +3550,13 @@ const ON_SubDDisplayParameters ON_SubDDisplayParameters::DecodeFromUnsignedChar( if (0 != (ON_SubDDisplayParameters::subd_mesh_nondefault_bit & encoded_parameters)) { - const unsigned char density = (ON_SubDDisplayParameters::subd_mesh_density_mask & encoded_parameters); - const unsigned char location = (ON_SubDDisplayParameters::subd_mesh_location_bit & encoded_parameters); - p.m_display_density = (unsigned int)density; - if (0 != location) - p.SetMeshLocation( ON_SubDComponentLocation::ControlNet ); + const unsigned char density_is_absolute = (ON_SubDDisplayParameters::subd_mesh_absolute_density_bit & encoded_parameters); + const unsigned char density_as_char = (ON_SubDDisplayParameters::subd_mesh_density_mask & encoded_parameters); + const unsigned char location_ctrl_net = (ON_SubDDisplayParameters::subd_mesh_location_bit & encoded_parameters); + p.m_bDisplayDensityIsAbsolute = (0 != density_is_absolute) ? true : false; + p.m_display_density = density_as_char; + if (0 != location_ctrl_net) + p.SetMeshLocation(ON_SubDComponentLocation::ControlNet); } return p; diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp index 2b083686..6ff754e6 100644 --- a/opennurbs_subd_heap.cpp +++ b/opennurbs_subd_heap.cpp @@ -1125,8 +1125,9 @@ void ON_SubDHeap::Clear() m_limit_block_pool.ReturnAll(); - m_unused_full_fragments = nullptr; - m_unused_half_fragments = nullptr; + const size_t frag_size_count = sizeof(m_unused_fragments) / sizeof(m_unused_fragments[0]); + for ( size_t i = 0; i < frag_size_count; ++i) + m_unused_fragments[i] = nullptr; m_unused_limit_curves = nullptr; m_unused_vertex = nullptr; @@ -1797,12 +1798,13 @@ bool ON_SubDHeap::Internal_InitializeLimitBlockPool() if (0 == m_limit_block_pool.SizeofElement()) { const bool bCurvatureArray = false; // change to true when principal and sectional curvatures are needed. - m_sizeof_full_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity, bCurvatureArray); - m_sizeof_half_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity-1, bCurvatureArray); + const unsigned frag_size_count = sizeof(m_sizeof_fragment) / sizeof(m_sizeof_fragment[0]); + for ( unsigned i = 0; i < frag_size_count ; ++i) + m_sizeof_fragment[i] = ON_SubDMeshFragment::SizeofFragment((unsigned)i, bCurvatureArray); m_sizeof_limit_curve = sizeof(ON_SubDEdgeSurfaceCurve); - size_t sz = m_sizeof_full_fragment; - if (sz < 4 * m_sizeof_half_fragment) - sz = 4 * m_sizeof_half_fragment; + size_t sz = 2*m_sizeof_fragment[frag_size_count-1]; + if (sz < 4 * m_sizeof_fragment[frag_size_count - 2]) + sz = 4 * m_sizeof_fragment[frag_size_count - 2]; ON_SleepLockGuard guard(m_limit_block_pool); m_limit_block_pool.Create(sz,0,0); @@ -1814,17 +1816,24 @@ bool ON_SubDHeap::Internal_InitializeLimitBlockPool() } ON_SubDMeshFragment* ON_SubDHeap::AllocateMeshFragment( + unsigned subd_display_density, const ON_SubDMeshFragment& src_fragment ) { + if (subd_display_density > ON_SubDDisplayParameters::MaximumDensity) + return ON_SUBD_RETURN_ERROR(nullptr); + // When 4 == ON_SubDDisplayParameters::DefaultDensity (setting used in February 2019) // quads get a single fragment with a 16x16 face grid // N-gons with N != 4 get N 8x8 grids. const unsigned int density = (src_fragment.m_face_fragment_count > 1) - ? (ON_SubDDisplayParameters::DefaultDensity-1) - : ((1==src_fragment.m_face_fragment_count) ? ON_SubDDisplayParameters::DefaultDensity : 0) + ? ((subd_display_density > 0) ? (subd_display_density -1) : ON_UNSET_UINT_INDEX) + : ((1==src_fragment.m_face_fragment_count) ? subd_display_density : ON_UNSET_UINT_INDEX) ; - if (0 == density) + if (ON_UNSET_UINT_INDEX == density) + return ON_SUBD_RETURN_ERROR(nullptr); + const unsigned count = (unsigned)(sizeof(m_unused_fragments) / sizeof(m_unused_fragments[0])); + if (density >= count) return ON_SUBD_RETURN_ERROR(nullptr); const unsigned short side_seg_count = (unsigned short)ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity(density); @@ -1840,58 +1849,32 @@ ON_SubDMeshFragment* ON_SubDHeap::AllocateMeshFragment( char* p = nullptr; char* p1 = nullptr; ON_SleepLockGuard guard(m_limit_block_pool); - if (ON_SubDDisplayParameters::DefaultDensity == density) + if (nullptr == m_unused_fragments[density]) { - if (nullptr == m_unused_full_fragments) + p = (char*)m_limit_block_pool.AllocateDirtyElement(); + if (nullptr == p) + return ON_SUBD_RETURN_ERROR(nullptr); + p1 = p + m_limit_block_pool.SizeofElement(); + m_unused_fragments[density] = (ON_FixedSizePoolElement*)p; + m_unused_fragments[density]->m_next = nullptr; + const size_t sizeof_fragment = m_sizeof_fragment[density]; + for (p += sizeof_fragment; p + sizeof_fragment <= p1; p += sizeof_fragment) { - p = (char*)m_limit_block_pool.AllocateDirtyElement(); - if (nullptr == p) - return ON_SUBD_RETURN_ERROR(nullptr); - p1 = p + m_limit_block_pool.SizeofElement(); - m_unused_full_fragments = (ON_FixedSizePoolElement*)p; - m_unused_full_fragments->m_next = nullptr; - p += m_sizeof_full_fragment; - while (p + m_sizeof_full_fragment < p1) - { - ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; - ele->m_next = m_unused_full_fragments; - m_unused_full_fragments = ele; - p += m_sizeof_full_fragment; - } + ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; + ele->m_next = m_unused_fragments[density]; + m_unused_fragments[density] = ele; } - fragment = (ON_SubDMeshFragment*)m_unused_full_fragments; - m_unused_full_fragments = m_unused_full_fragments->m_next; - } - else - { - if (nullptr == m_unused_half_fragments) - { - p = (char*)m_limit_block_pool.AllocateDirtyElement(); - if (nullptr == p) - return ON_SUBD_RETURN_ERROR(nullptr); - p1 = p + m_limit_block_pool.SizeofElement(); - m_unused_half_fragments = (ON_FixedSizePoolElement*)p; - m_unused_half_fragments->m_next = nullptr; - p += m_sizeof_half_fragment; - while (p + m_sizeof_half_fragment < p1) - { - ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; - ele->m_next = m_unused_half_fragments; - m_unused_half_fragments = ele; - p += m_sizeof_half_fragment; - } - } - fragment = (ON_SubDMeshFragment*)m_unused_half_fragments; - m_unused_half_fragments = m_unused_half_fragments->m_next; } + fragment = (ON_SubDMeshFragment*)m_unused_fragments[density]; + m_unused_fragments[density] = m_unused_fragments[density]->m_next; + if (nullptr != p) { - while (p + m_sizeof_limit_curve < p1) + for (/*empty int*/; p + m_sizeof_limit_curve <= p1; p += m_sizeof_limit_curve) { ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; ele->m_next = m_unused_limit_curves; - m_unused_limit_curves = ele; - p += m_sizeof_limit_curve; + m_unused_limit_curves = ele; } } } @@ -1911,7 +1894,11 @@ ON_SubDMeshFragment* ON_SubDHeap::AllocateMeshFragment( } -ON_SubDMeshFragment* ON_SubDHeap::CopyMeshFragments(const ON_SubDFace* source_face, const ON_SubDFace* destination_face) +ON_SubDMeshFragment* ON_SubDHeap::CopyMeshFragments( + const ON_SubDFace* source_face, + unsigned destination_subd_display_density, + const ON_SubDFace* destination_face +) { if (nullptr == source_face || nullptr == destination_face || nullptr != destination_face->m_mesh_fragments) return ON_SUBD_RETURN_ERROR(nullptr); @@ -1919,7 +1906,7 @@ ON_SubDMeshFragment* ON_SubDHeap::CopyMeshFragments(const ON_SubDFace* source_fa ON_SubDMeshFragment* prev_dst_fragment = nullptr; for (const ON_SubDMeshFragment* src_fragment = source_face->MeshFragments(); nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment) { - ON_SubDMeshFragment* dst_fragment = this->AllocateMeshFragment(*src_fragment); + ON_SubDMeshFragment* dst_fragment = this->AllocateMeshFragment(destination_subd_display_density ,*src_fragment); dst_fragment->m_face = destination_face; if (prev_dst_fragment) prev_dst_fragment->m_next_fragment = dst_fragment; @@ -1939,24 +1926,38 @@ bool ON_SubDHeap::ReturnMeshFragment(ON_SubDMeshFragment * fragment) if (nullptr == fragment) return false; - ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)fragment; - if (17 * 17 == fragment->VertexCapacity()) + const size_t count = sizeof(m_unused_fragments) / sizeof(m_unused_fragments[0]); + size_t i; + switch (fragment->VertexCapacity()) { - ON_SleepLockGuard guard(m_limit_block_pool); - ((unsigned int*)ele)[5] = 0; // zero m_vertex_count_etc and m_vertex_capacity_etc - ele->m_next = m_unused_full_fragments; - m_unused_full_fragments = ele; + case 2 * 2: // 1x1 mesh quad fragment + i = 0; + break; + case 3 * 3: // 2x2 mesh quad fragment + i = 1; + break; + case 5 * 5: // 4x4 mesh quad fragment + i = 2; + break; + case 9 * 9: // 8x8 mesh quad fragment + i = 3; + break; + case 17 * 17: // 16x16 mesh quad fragment + i = 4; + break; + default: + i = count; + break; } - else if (9 * 9 == fragment->VertexCapacity()) - { - ON_SleepLockGuard guard(m_limit_block_pool); - ((unsigned int*)ele)[5] = 0; // zero m_vertex_count_etc and m_vertex_capacity_etc - ele->m_next = m_unused_half_fragments; - m_unused_half_fragments = ele; - } - else + if (i >= count) return ON_SUBD_RETURN_ERROR(false); + ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)fragment; + ON_SleepLockGuard guard(m_limit_block_pool); + ((unsigned int*)ele)[5] = 0; // zero m_vertex_count_etc and m_vertex_capacity_etc + ele->m_next = m_unused_fragments[i]; + m_unused_fragments[i] = ele; + return true; } diff --git a/opennurbs_subd_iter.cpp b/opennurbs_subd_iter.cpp index 888d0da6..29e31c6d 100644 --- a/opennurbs_subd_iter.cpp +++ b/opennurbs_subd_iter.cpp @@ -303,10 +303,10 @@ const ON_SubDVertex* ON_SubDVertexIdIterator::CurrentVertex() const // void ON_SubDHeap::InitializeEdgeIdIterator( - class ON_SubDEdgeIdIterator& vidit + class ON_SubDEdgeIdIterator& eidit ) const { - vidit.ON_FixedSizePoolIterator::Create(&m_fspe); + eidit.ON_FixedSizePoolIterator::Create(&m_fspe); } void ON_SubDimple::InitializeEdgeIdIterator( diff --git a/opennurbs_subd_texture.cpp b/opennurbs_subd_texture.cpp index c3f03633..812b97f0 100644 --- a/opennurbs_subd_texture.cpp +++ b/opennurbs_subd_texture.cpp @@ -1021,6 +1021,31 @@ void ON_SubD::SetPerFaceColorsFromPackId() const ChangeRenderContentSerialNumber(); // face color changes. } +bool ON_SubD::HasPerFaceColorsFromPackId() const +{ + ON_SubDFaceIterator fit(*this); + const ON_SubDFace* f = fit.FirstFace(); + if (nullptr == f) + return false; + bool bHasPerFaceColorsFromPackId = false; + for (/*empty init*/; nullptr != f; f = fit.NextFace()) + { + const ON_Color f_color = f->PerFaceColor(); + if (((unsigned int)f_color) == ((unsigned int)ON_Color::UnsetColor)) + continue; + const unsigned pack_id = f->PackId(); + const ON_Color pack_id_color + = (0 == pack_id) + ? ON_Color::UnsetColor + : ON_Color::RandomColor(pack_id); + if (((unsigned int)pack_id_color) != ((unsigned int)f_color)) + return false; + bHasPerFaceColorsFromPackId = true; + } + return bHasPerFaceColorsFromPackId; +} + + ////////////////////////////////////////////////////////////////////////////// // // ON_SubDMeshFragment - texture coordinates diff --git a/opennurbs_textiterator.cpp b/opennurbs_textiterator.cpp index b58befed..89d466f4 100644 --- a/opennurbs_textiterator.cpp +++ b/opennurbs_textiterator.cpp @@ -3369,10 +3369,10 @@ bool RtfComposer::Compose( } - if(!ComposeFS()) + if (!RtfComposer::ComposeFS()) temp.Format(L"}\\f%d", stylefont_key); else - temp.Format(L"}\\f%d \\fs40", stylefont_key); + temp.Format(L"}\\f%d \\fs%d", stylefont_key, RtfComposer::TextEditorFontSize()); rtf += temp; @@ -3416,6 +3416,20 @@ void RtfComposer::SetComposeFS(bool b) RtfComposer::m_bComposeFS = b; } +int RtfComposer::m_TextEditorSize = 0; +int RtfComposer::TextEditorFontSize() +{ + if (0 < RtfComposer::m_TextEditorSize) + return RtfComposer::m_TextEditorSize; + else + return 18; +} + +void RtfComposer::SetTextEditorFontSize(unsigned int size) +{ + RtfComposer::m_TextEditorSize = size; +} + static const ON_wString Internal_PostScriptNameIfAvailable(const ON_Font& managed_font) { ON_wString style_fontname = managed_font.PostScriptName(); @@ -3618,10 +3632,10 @@ const ON_wString RtfComposer::ComposeAppleRTF( rtf_string += fonttable_string; } - if (!ComposeFS()) + if (!RtfComposer::ComposeFS()) temp.Format(L"}\\f%d", deffont_key); else - temp.Format(L"}\\f%d \\fs40", deffont_key); + temp.Format(L"}\\f%d \\fs%d", deffont_key, RtfComposer::TextEditorFontSize()); rtf_string += temp; diff --git a/opennurbs_textiterator.h b/opennurbs_textiterator.h index b936b26c..faed93a0 100644 --- a/opennurbs_textiterator.h +++ b/opennurbs_textiterator.h @@ -887,7 +887,7 @@ public: ON_wString& rtf, bool bForceRtf); - static const ON_wString ComposeAppleRTF( + static const ON_wString ComposeAppleRTF( const ON_TextContent* text); static bool RecomposeRTF(); @@ -895,10 +895,14 @@ public: static bool ComposeFS(); static void SetComposeFS(bool b); + static int TextEditorFontSize(); + static void SetTextEditorFontSize(unsigned int size); + private: static bool m_bComposeRTF; static bool m_bComposeFS; + static int m_TextEditorSize; RtfComposer(); diff --git a/opennurbs_texture.h b/opennurbs_texture.h index a2542ffc..733853b3 100644 --- a/opennurbs_texture.h +++ b/opennurbs_texture.h @@ -335,9 +335,10 @@ public: pbr_opacity_roughness_texture = 24U, pbr_emission_texture = 25U, pbr_ambient_occlusion_texture = 26U, - //pbr_smudge_texture = 27U, + //pbr_smudge_texture = 27U, pbr_displacement_texture = 28U, - pbr_clearcoat_bump_texture = 29U, + pbr_clearcoat_bump_texture = 29U, + pbr_alpha_texture = 30U, // emap_texture is OBSOLETE - set m_mapping_channel_id = ON_MappingChannel::emap_mapping emap_texture = 86U // spherical environment mapping. diff --git a/opennurbs_unicode.cpp b/opennurbs_unicode.cpp index 1f02b748..3eddc7ef 100644 --- a/opennurbs_unicode.cpp +++ b/opennurbs_unicode.cpp @@ -29,6 +29,28 @@ int ON_IsValidUnicodeCodePoint(ON__UINT32 u) return (u < 0xD800 || (u >= 0xE000 && u <= 0x10FFFF)); } +int ON_IsUnicodeSpaceCodePoint( + ON__UINT32 u +) +{ + // Additional code points may be added in the future. + // The goal is to detect code points that typically separate words + // and which should not be at the beginning or end of a word. + return + ON_UnicodeCodePoint::ON_Space == u + || ON_UnicodeCodePoint::ON_NoBreakSpace == u + || ON_UnicodeCodePoint::ON_NarrowNoBreakSpace == u + || ON_UnicodeCodePoint::ON_ZeroWidthSpace == u + ; +} + +int ON_IsUnicodeC1ControlCodePoint( + ON__UINT32 u +) +{ + return (u >= 0x0080 && u <= 0x009F); +} + int ON_IsValidUTF32Value( ON__UINT32 c ) diff --git a/opennurbs_unicode.h b/opennurbs_unicode.h index 81506b7c..54b67600 100644 --- a/opennurbs_unicode.h +++ b/opennurbs_unicode.h @@ -224,6 +224,31 @@ int ON_IsValidUnicodeCodePoint( ON__UINT32 u ); +/* +Returns: + True if u is one of ON_UnicodeCodePoint::ON_Space, ON_UnicodeCodePoint::ON_NoBreakSpace, + ON_UnicodeCodePoint::ON_NarrowNoBreakSpace, or ON_UnicodeCodePoint::ON_ZeroWidthSpace. +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_IsUnicodeSpaceCodePoint( + ON__UINT32 u +); + +/* +Returns: + True if u >= 0x80 and y <= 0x9F. +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( + ON__UINT32 u +); + /* Description: Test a value to determine if it is a valid unicode code point value. diff --git a/opennurbs_xform.h b/opennurbs_xform.h index 0889788e..5b8e2eb8 100644 --- a/opennurbs_xform.h +++ b/opennurbs_xform.h @@ -232,7 +232,7 @@ public: 0 0 0 0 0 0 0 1 An element of the matrix is "zero" if fabs(x) <= zero_tolerance. - IsZeroTolerance() is the same as IsZeroTransformation( 0.0 ); + IsZeroTransformation() is the same as IsZeroTransformation( 0.0 ); */ bool IsZeroTransformation() const; bool IsZeroTransformation(double zero_tolerance ) const;