diff --git a/opennurbs_3dm_attributes.cpp b/opennurbs_3dm_attributes.cpp index 71cdd60b..667d0cc4 100644 --- a/opennurbs_3dm_attributes.cpp +++ b/opennurbs_3dm_attributes.cpp @@ -2525,15 +2525,36 @@ ON_MeshModifiers& ON_3dmObjectAttributes::MeshModifiers(void) const return m_private->m_mesh_modifiers; } -const ON_SimpleArray& ON_3dmObjectAttributes::GetDecalArray(void) const +const ON_SimpleArray& ON_3dmObjectAttributes::GetDecalArray(void) const // Deprecated. +{ + std::vector> decals; + GetDecalArray(decals); + + static ON_SimpleArray dummy; + dummy.Destroy(); + + for (const auto& decal_sp : decals) + { + dummy.Append(decal_sp.get()); + } + + return dummy; +} + +void ON_3dmObjectAttributes::GetDecalArray(std::vector>& array_out) const { if (nullptr == m_private) m_private = new ON_3dmObjectAttributesPrivate(this); - return m_private->m_decals.GetDecalArray(); + array_out = m_private->m_decals.GetDecalArray(); } -ON_Decal* ON_3dmObjectAttributes::AddDecal(void) +ON_Decal* ON_3dmObjectAttributes::AddDecal(void) // Deprecated. +{ + return AddDecalEx().get(); +} + +const std::shared_ptr ON_3dmObjectAttributes::AddDecalEx(void) { if (nullptr == m_private) m_private = new ON_3dmObjectAttributesPrivate(this); diff --git a/opennurbs_3dm_attributes.h b/opennurbs_3dm_attributes.h index 833437bd..31838224 100644 --- a/opennurbs_3dm_attributes.h +++ b/opennurbs_3dm_attributes.h @@ -518,19 +518,26 @@ public: // Decals. + // This method is deprecated in favor of the one below. + ON_DEPRECATED const ON_SimpleArray& GetDecalArray(void) const; + /* Description: Get an array of decals that are stored on this attributes object. - Do not store or delete pointers from the array. + Param array_out is first cleared and then filled with shared pointers to decals (if any). + Do not store or delete raw pointers from the array. */ - const ON_SimpleArray& GetDecalArray(void) const; + void GetDecalArray(std::vector>& array_out) const; + + // This method is deprecated in favor of AddDecalEx(). + ON_DEPRECATED ON_Decal* AddDecal(void); /* Description: Add a new decal to this attributes object. The returned pointer points to an object that is owned by the attributes. Do not store or delete it. */ - ON_Decal* AddDecal(void); + const std::shared_ptr AddDecalEx(void); /* Description: diff --git a/opennurbs_archive.cpp b/opennurbs_archive.cpp index c338b1c4..5e299915 100644 --- a/opennurbs_archive.cpp +++ b/opennurbs_archive.cpp @@ -4620,6 +4620,24 @@ ON_BinaryArchive::WriteObject( const ON_Object& model_object ) return Internal_WriteObject(V2_text_dot); } break; + + case ON::object_type::subd_object: + { + if (m_3dm_version >= 60) + break; + const ON_SubD* subd = ON_SubD::Cast(&model_object); + if (nullptr == subd) + break; + + // Use a SubD mesh proxy for V5 and earlier file formats. + std::unique_ptr mesh(ON_SubDMeshProxyUserData::MeshProxyFromSubD(subd)); + if (nullptr == mesh) + return false; + + return Internal_WriteObject(*mesh.get()); + } + break; + default: break; } @@ -6578,65 +6596,95 @@ bool ON_BinaryArchive::EndRead3dmChunk(bool bSupressPartiallyReadChunkWarning) { // partially read chunk - happens when chunks are skipped or old code // reads a new minor version of a chunk whnich has added information. - if ( file_offset != c->m_start_offset ) + if ( file_offset != c->m_start_offset) { - if ( m_3dm_version != 1 || (m_error_message_mask&0x02) == 0 ) + for (;;) { - // when reading v1 files, there are some situations where - // it is reasonable to attempt to read 4 bytes at the end - // of a file. The above test prevents making a call - // to ON_WARNING() in these situations. - - unsigned int file_year = 0; - unsigned int file_month = 0; - unsigned int file_date = 0; - unsigned int file_major_version = 0; - const bool bHaveFileDate = ON_VersionNumberParse( - m_3dm_opennurbs_version, - &file_major_version, - 0, - &file_year, - &file_month, - &file_date, - 0 - ); - - const unsigned int file_ymd - = bHaveFileDate - ? ((file_year * 100 + file_month) * 100 + file_date) - : 0; - - unsigned int app_year = 0; - unsigned int app_month = 0; - unsigned int app_date = 0; - unsigned int app_major_version = 0; - const bool bHaveAppDate = ON_VersionNumberParse( - ON::Version(), - &app_major_version, - 0, - &app_year, - &app_month, - &app_date, - 0 - ); - - const unsigned int app_ymd - = bHaveAppDate - ? ((app_year * 100 + app_month) * 100 + app_date) - : 0; - - if (file_major_version <= app_major_version - && file_ymd <= app_ymd - ) + if (bSupressPartiallyReadChunkWarning) { - // We are reading a file written by this version or an - // earlier version of opennurbs. - // There should not be any partially read chunks. - if (!bSupressPartiallyReadChunkWarning) - { - ON_WARNING("ON_BinaryArchive::EndRead3dmChunk: partially read chunk - skipping bytes at end of current chunk."); - } + // The calling code expects there to be a partially read chunk. + break; } + + // The calling code had no reason to supress warnings about this chunk + // being partially read. + const bool bIsV1EndOfFile = this->Archive3dmVersion() == 1 && 0 != (m_error_message_mask & 0x02); + if (bIsV1EndOfFile) + { + // when reading v1 files, there are some situations where + // it is reasonable to attempt to read 4 bytes at the end + // of a file. The above test prevents making a call + // to ON_WARNING() in these situations. + break; + } + + // m_3dm_opennurbs_version = version of opennurbs that wrote this 3dm file + // "onver" is short for opennurbs version + unsigned int file_writer_onver_year = 0; + unsigned int file_writer_onver_month = 0; + unsigned int file_writer_onver_date = 0; + unsigned int file_writer_onver_major_version = 0; + const bool bHaveFileONVer = ON_VersionNumberParse( + m_3dm_opennurbs_version, + &file_writer_onver_major_version, + nullptr, + &file_writer_onver_year, + &file_writer_onver_month, + &file_writer_onver_date, + nullptr + ); + + if (false == bHaveFileONVer) + break; // weird for sure, but we can't compare nothing to something + + const unsigned int file_writer_version_ymd + = ((file_writer_onver_year * 100 + file_writer_onver_month) * 100 + file_writer_onver_date); + + // ON::Version = version of this instance of opennurbs + unsigned int this_onver_year = 0; + unsigned int this_onver_month = 0; + unsigned int this_onver_date = 0; + unsigned int this_onver_major_version = 0; + const bool bHaveThisONVer = ON_VersionNumberParse( + ON::Version(), + &this_onver_major_version, + nullptr, + &this_onver_year, + &this_onver_month, + &this_onver_date, + nullptr + ); + + if (false == bHaveThisONVer) + break; // weird for sure, but we can't compare nothing to something + + const unsigned int this_onver_ymd + = ((this_onver_year * 100 + this_onver_month) * 100 + this_onver_date); + + const bool bOldCodeReadingNewFile + = this_onver_major_version < file_writer_onver_major_version + || (this_onver_major_version == file_writer_onver_major_version && this_onver_ymd < file_writer_version_ymd) + ; + if (bOldCodeReadingNewFile) + { + // The code that wrote this file is newer than the code that is reading this file. + // For example, SaveAs V7 in Rhino 8 will use v8 code to write a v7 file and Rhino 7 might be reading the file. + // + // Or a newer version of Rhino N wrote the file and a user with an older version of Rhino N + // is reading the file. + // + // In these two cases, the new code may have added new saved new information at the end of + // this chunk that was not saved by the older version. + break; + } + + // We are reading a file written by this version or an earlier version of opennurbs. + // This chunk should have been completely read. Typically, this is a bug that can + // be fixed after carefully studying why it occured. + // Issue a warning and continue reading. + ON_WARNING("ON_BinaryArchive::EndRead3dmChunk: partially read chunk - skipping bytes at end of current chunk."); + + break; } } @@ -18424,12 +18472,12 @@ const void* ON_Read3dmBufferArchive::Buffer() const return (const void*)m_buffer; } -ON_Write3dmBufferArchive::ON_Write3dmBufferArchive( - size_t initial_sizeof_buffer, - size_t max_sizeof_buffer, - int archive_3dm_version, - unsigned int archive_opennurbs_version - ) +ON_Write3dmBufferArchive::ON_Write3dmBufferArchive( + size_t initial_sizeof_buffer, + size_t max_sizeof_buffer, + int archive_3dm_version, + unsigned int archive_opennurbs_version + ) : ON_BinaryArchive(ON::archive_mode::write3dm) , m_p(0) , m_buffer(0) diff --git a/opennurbs_array.cpp b/opennurbs_array.cpp index 8c870636..7264e9aa 100644 --- a/opennurbs_array.cpp +++ b/opennurbs_array.cpp @@ -1189,10 +1189,6 @@ ON_UuidPtr* ON_UuidPtrList::SearchHelper(const ON_UUID* uuid) const return p; } -// static_assert(sizeof(ON_UuidPairList) == sizeof(ON_UuidPairList2), "ON_UuidPairList and ON_UuidPairList2 are not the same size"); -// static_assert(sizeof(ON_UuidPtrList) == sizeof(ON_UuidPtrList2), "ON_UuidPtrList and ON_UuidPtrList2 are not the same size"); -// static_assert(sizeof(ON_UuidIndexList) == sizeof(ON_UuidIndexList2), "ON_UuidIndexList and ON_UuidIndexList2 are not the same size"); - template struct ON_UuidList2_Private { diff --git a/opennurbs_array.h b/opennurbs_array.h index 46053ece..7e51a018 100644 --- a/opennurbs_array.h +++ b/opennurbs_array.h @@ -1282,104 +1282,7 @@ private: }; -class ON_CLASS ON_UuidPtrList2 -{ -public: - ON_UuidPtrList2(); - ~ON_UuidPtrList2(); - ON_UuidPtrList2(const ON_UuidPtrList2& src); - ON_UuidPtrList2& operator=(const ON_UuidPtrList2& src); - unsigned int Count() const; - void RemoveAll(); - void Reserve(size_t capacity); - bool AddUuidPtr(const ON_UUID& uuid, ON__UINT_PTR ptr); - bool RemoveUuid(const ON_UUID& uuid); - bool FindUuid(const ON_UUID& uuid) const; - bool FindUuid(const ON_UUID& uuid, ON__UINT_PTR* ptr) const; - bool FindUuidPtr(const ON_UUID& uuid, ON__UINT_PTR index) const; - unsigned int GetUuids(ON_SimpleArray& uuid_list) const; - void ImproveSearchSpeed(); - -private: -#pragma warning (push) -#pragma warning (disable : 4251) - std::unique_ptr m_private; - unsigned int padding[2]; -#if defined (ON_RUNTIME_ANDROID) //Android has 32-bit pointers? - unsigned char padding_array[12]; -#else - unsigned char padding_array[16]; -#endif -#pragma warning (pop) -}; - -class ON_CLASS ON_UuidPairList2 -{ -public: - ON_UuidPairList2(); - ~ON_UuidPairList2(); - ON_UuidPairList2(const ON_UuidPairList2& src); - ON_UuidPairList2& operator=(const ON_UuidPairList2& src); - - static const ON_UuidPairList2 EmptyList; - - unsigned int Count() const; - void Empty(); - void Reserve(size_t capacity); - bool AddPair(const ON_UUID& id1, const ON_UUID& id2); - bool RemovePair(const ON_UUID& id1); - bool RemovePair(const ON_UUID& id1, const ON_UUID& id2); - bool FindId1(const ON_UUID& id1, ON_UUID* id2 = 0) const; - bool FindPair(const ON_UUID& id1, const ON_UUID& id2) const; - int GetId1s(ON_SimpleArray& uuid_list) const; - - void ImproveSearchSpeed(); - -private: -#pragma warning (push) -#pragma warning (disable : 4251) - std::unique_ptr m_private; - unsigned int padding[2]; -#if defined (ON_RUNTIME_ANDROID) //Android has 32-bit pointers? - unsigned char padding_array[12]; -#else - unsigned char padding_array[16]; -#endif -#pragma warning (pop) -}; - -class ON_CLASS ON_UuidIndexList2 -{ -public: - ON_UuidIndexList2(); - ~ON_UuidIndexList2(); - ON_UuidIndexList2(const ON_UuidIndexList2& src); - ON_UuidIndexList2& operator=(const ON_UuidIndexList2& src); - - unsigned int Count() const; - void RemoveAll(); - void Reserve(size_t capacity); - bool AddUuidIndex(const ON_UUID& uuid, int index); - bool RemoveUuid(const ON_UUID& uuid); - bool FindUuid(const ON_UUID& uuid) const; - bool FindUuid(const ON_UUID& uuid, int* index) const; - bool FindUuidIndex(const ON_UUID& uuid, int index) const; - unsigned int GetUuids(ON_SimpleArray& uuid_list) const; - void ImproveSearchSpeed(); - -private: -#pragma warning (push) -#pragma warning (disable : 4251) - std::unique_ptr m_private; - unsigned int padding[2]; -#if defined (ON_RUNTIME_ANDROID) //Android has 32-bit pointers? - unsigned char padding_array[12]; -#else - unsigned char padding_array[16]; -#endif -#pragma warning (pop) -}; @@ -1536,6 +1439,92 @@ private: +class ON_CLASS ON_UuidPtrList2 +{ +public: + ON_UuidPtrList2(); + ~ON_UuidPtrList2(); + ON_UuidPtrList2(const ON_UuidPtrList2& src); + ON_UuidPtrList2& operator=(const ON_UuidPtrList2& src); + + unsigned int Count() const; + void RemoveAll(); + void Reserve(size_t capacity); + bool AddUuidPtr(const ON_UUID& uuid, ON__UINT_PTR ptr); + bool RemoveUuid(const ON_UUID& uuid); + bool FindUuid(const ON_UUID& uuid) const; + bool FindUuid(const ON_UUID& uuid, ON__UINT_PTR* ptr) const; + bool FindUuidPtr(const ON_UUID& uuid, ON__UINT_PTR index) const; + unsigned int GetUuids(ON_SimpleArray& uuid_list) const; + void ImproveSearchSpeed(); + +private: +#pragma warning (push) +#pragma warning (disable : 4251) + std::unique_ptr m_private; + unsigned char padding[sizeof(ON_UuidPtrList) - sizeof(m_private)]; +#pragma warning (pop) +}; + +class ON_CLASS ON_UuidPairList2 +{ +public: + ON_UuidPairList2(); + ~ON_UuidPairList2(); + ON_UuidPairList2(const ON_UuidPairList2& src); + ON_UuidPairList2& operator=(const ON_UuidPairList2& src); + + static const ON_UuidPairList2 EmptyList; + + unsigned int Count() const; + void Empty(); + void Reserve(size_t capacity); + bool AddPair(const ON_UUID& id1, const ON_UUID& id2); + bool RemovePair(const ON_UUID& id1); + bool RemovePair(const ON_UUID& id1, const ON_UUID& id2); + bool FindId1(const ON_UUID& id1, ON_UUID* id2 = 0) const; + bool FindPair(const ON_UUID& id1, const ON_UUID& id2) const; + int GetId1s(ON_SimpleArray& uuid_list) const; + + void ImproveSearchSpeed(); + +private: +#pragma warning (push) +#pragma warning (disable : 4251) + std::unique_ptr m_private; + unsigned char padding[sizeof(ON_UuidPairList) - sizeof(m_private)]; +#pragma warning (pop) +}; + +class ON_CLASS ON_UuidIndexList2 +{ +public: + ON_UuidIndexList2(); + ~ON_UuidIndexList2(); + ON_UuidIndexList2(const ON_UuidIndexList2& src); + ON_UuidIndexList2& operator=(const ON_UuidIndexList2& src); + + unsigned int Count() const; + void RemoveAll(); + void Reserve(size_t capacity); + bool AddUuidIndex(const ON_UUID& uuid, int index); + bool RemoveUuid(const ON_UUID& uuid); + bool FindUuid(const ON_UUID& uuid) const; + bool FindUuid(const ON_UUID& uuid, int* index) const; + bool FindUuidIndex(const ON_UUID& uuid, int index) const; + unsigned int GetUuids(ON_SimpleArray& uuid_list) const; + void ImproveSearchSpeed(); + +private: +#pragma warning (push) +#pragma warning (disable : 4251) + std::unique_ptr m_private; + unsigned char padding[sizeof(ON_UuidIndexList) - sizeof(m_private)]; +#pragma warning (pop) +}; + + + class ON_CLASS ON_2dexMap : private ON_SimpleArray { diff --git a/opennurbs_decals.cpp b/opennurbs_decals.cpp index 4ad1dbd2..a9662eec 100644 --- a/opennurbs_decals.cpp +++ b/opennurbs_decals.cpp @@ -26,7 +26,7 @@ static ON_4dPoint UNSET_4D_POINT = ON_4dPoint(ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE); -ON__UINT32 ON_DecalCRCFromNode(const ON_XMLNode& node) +ON_DECAL_CRC ON_DecalCRCFromNode(const ON_XMLNode& node) { return ON_Decal::ComputeDecalCRC(0, node); } @@ -34,8 +34,9 @@ ON__UINT32 ON_DecalCRCFromNode(const ON_XMLNode& node) class ON_Decal::CImpl : public ON_InternalXMLImpl { public: - CImpl() { ON_CreateUuid(_decal_id); } - CImpl(ON_DecalCollection& dc, ON_XMLNode& node) : _collection(&dc), ON_InternalXMLImpl(&node) { ON_CreateUuid(_decal_id); } + CImpl(); + CImpl(ON_DecalCollection* dc, ON_XMLNode& node); + CImpl(ON_DecalCollection* dc, const ON_XMLNode& node); ON_UUID TextureInstanceId(void) const; void SetTextureInstanceId(const ON_UUID& id); @@ -110,6 +111,27 @@ static ON_Decal::Mappings MappingFromString(const ON_wString& s) return ON_Decal::Mappings::None; } +ON_Decal::CImpl::CImpl() +{ + ON_CreateUuid(_decal_id); +} + +ON_Decal::CImpl::CImpl(ON_DecalCollection* dc, ON_XMLNode& node) + : + _collection(dc), + ON_InternalXMLImpl(&node) +{ + ON_CreateUuid(_decal_id); +} + +ON_Decal::CImpl::CImpl(ON_DecalCollection* dc, const ON_XMLNode& node) + : + _collection(dc), + ON_InternalXMLImpl(&const_cast(node)) +{ + ON_CreateUuid(_decal_id); +} + ON_XMLVariant ON_Decal::CImpl::GetParameter(const wchar_t* param_name, const ON_XMLVariant& def) const { return ON_InternalXMLImpl::GetParameter(L"", param_name, def); @@ -462,11 +484,25 @@ ON_Decal::ON_Decal() _impl = new CImpl; } +ON_Decal::ON_Decal(ON_XMLNode& node) +{ + ON_ASSERT(node.TagName() == ON_RDK_DECAL); + + _impl = new CImpl(nullptr, node); +} + +ON_Decal::ON_Decal(const ON_XMLNode& node) +{ + ON_ASSERT(node.TagName() == ON_RDK_DECAL); + + _impl = new CImpl(nullptr, node); +} + ON_Decal::ON_Decal(ON_DecalCollection& dc, ON_XMLNode& node) { ON_ASSERT(node.TagName() == ON_RDK_DECAL); - _impl = new CImpl(dc, node); + _impl = new CImpl(&dc, node); } ON_Decal::ON_Decal(const ON_Decal& d) @@ -679,9 +715,9 @@ ON_UUID ON_Decal::Id(void) const return _impl->Id(); } -ON__UINT32 ON_Decal::DecalCRC(void) const +ON_DECAL_CRC ON_Decal::DecalCRC(void) const { - return DataCRC(0); + return ComputeDecalCRC(0, _impl->Node()); } ON__UINT32 ON_Decal::DataCRC(ON__UINT32 current_remainder) const @@ -734,6 +770,21 @@ bool ON_Decal::SetCustomXML(const ON_UUID& renderEngineId, const ON_XMLNode& cus return true; } +void ON_Decal::AppendCustomXML(const ON_XMLNode& custom_node) +{ + // This function only exists to support the RDK. + + ON_ASSERT(custom_node.TagName() == L"entire-custom-xml"); + + ON_XMLNode* child = custom_node.FirstChild(); + while (nullptr != child) + { + _impl->Node().AttachChildNode(new ON_XMLNode(*child)); + + child = custom_node.NextSibling(); + } +} + // Copied from IRhRdkDecal::GetTextureMapping -- TODO: Refactor. [JOHN-DECAL-FIX] bool ON_Decal::GetTextureMapping(ON_TextureMapping& mappingOut) const { @@ -810,24 +861,24 @@ bool ON_Decal::GetTextureMapping(ON_TextureMapping& mappingOut) const This object encapsulates the reading of all decal properties from XML nodes. It is used by the decal CRC calculation in ComputeDecalCRC(). - TODO: It could also be used by the ON_Decal XML node access. + TODO: It could also be used by the ON_Decal XML node access (for Rhino 9). */ class ON_DecalNodeReader { public: - ON_DecalNodeReader(const ON_XMLNode* p) : m_pNode(p) { } + ON_DecalNodeReader(const ON_XMLNode* decal_node); ON_XMLVariant Mapping(void) const { return Value(ON_RDK_DECAL_MAPPING, ON_RDK_DECAL_MAPPING_NONE); } ON_XMLVariant Projection(void) const { return Value(ON_RDK_DECAL_PROJECTION, ON_RDK_DECAL_PROJECTION_NONE); } - ON_XMLVariant MapToInside(void) const { return Value(ON_RDK_DECAL_MAP_TO_INSIDE_ON, m_def.MapToInside()); } - ON_XMLVariant Transparency(void) const { return Value(ON_RDK_DECAL_TRANSPARENCY , m_def.Transparency()); } - ON_XMLVariant TextureInstanceId(void) const { return Value(ON_RDK_DECAL_TEXTURE_INSTANCE, m_def.TextureInstanceId()); } - ON_XMLVariant Height(void) const { return Value(ON_RDK_DECAL_HEIGHT , m_def.Height()); } - ON_XMLVariant Radius(void) const { return Value(ON_RDK_DECAL_RADIUS , m_def.Radius()); } - ON_XMLVariant Origin(void) const { return Value(ON_RDK_DECAL_ORIGIN , m_def.Origin()); } - ON_XMLVariant VectorUp(void) const { return Value(ON_RDK_DECAL_VECTOR_UP , ON_3dPoint(m_def.VectorUp())); } - ON_XMLVariant VectorAcross(void) const { return Value(ON_RDK_DECAL_VECTOR_ACROSS , ON_3dPoint(m_def.VectorAcross())); } + ON_XMLVariant MapToInside(void) const { return Value(ON_RDK_DECAL_MAP_TO_INSIDE_ON, _def.MapToInside()); } + ON_XMLVariant Transparency(void) const { return Value(ON_RDK_DECAL_TRANSPARENCY , _def.Transparency()); } + ON_XMLVariant TextureInstanceId(void) const { return Value(ON_RDK_DECAL_TEXTURE_INSTANCE, _def.TextureInstanceId()); } + ON_XMLVariant Height(void) const { return Value(ON_RDK_DECAL_HEIGHT , _def.Height()); } + ON_XMLVariant Radius(void) const { return Value(ON_RDK_DECAL_RADIUS , _def.Radius()); } + ON_XMLVariant Origin(void) const { return Value(ON_RDK_DECAL_ORIGIN , _def.Origin()); } + ON_XMLVariant VectorUp(void) const { return Value(ON_RDK_DECAL_VECTOR_UP , ON_3dPoint(_def.VectorUp())); } + ON_XMLVariant VectorAcross(void) const { return Value(ON_RDK_DECAL_VECTOR_ACROSS , ON_3dPoint(_def.VectorAcross())); } ON_XMLVariant HorzSweepSta(void) const { return Value(ON_RDK_DECAL_HORZ_SWEEP_STA , DefaultHorzSweepSta()); } ON_XMLVariant HorzSweepEnd(void) const { return Value(ON_RDK_DECAL_HORZ_SWEEP_END , DefaultHorzSweepEnd()); } ON_XMLVariant VertSweepSta(void) const { return Value(ON_RDK_DECAL_VERT_SWEEP_STA , DefaultVertSweepSta()); } @@ -837,109 +888,79 @@ public: ON_XMLVariant MaxU(void) const { return Value(ON_RDK_DECAL_MAX_U , DefaultMaxU()); } ON_XMLVariant MaxV(void) const { return Value(ON_RDK_DECAL_MAX_V , DefaultMaxV()); } ON_XMLVariant IsTemporary(void) const { return Value(ON_RDK_DECAL_IS_TEMPORARY , false); } - ON_XMLVariant IsVisible(void) const { return Value(ON_RDK_DECAL_IS_VISIBLE , m_def.IsVisible()); } - ON_XMLVariant InstanceId(void) const { return Value(ON_RDK_DECAL_INSTANCE_ID , m_def.Id()); } + ON_XMLVariant IsVisible(void) const { return Value(ON_RDK_DECAL_IS_VISIBLE , _def.IsVisible()); } + ON_XMLVariant InstanceId(void) const { return Value(ON_RDK_DECAL_INSTANCE_ID , _def.Id()); } private: ON_XMLVariant Value(const wchar_t* wszName, const ON_XMLVariant& vDefault) const; - double DefaultHorzSweepSta(void) const { double a, b; m_def.GetHorzSweep(a, b); return a; } - double DefaultHorzSweepEnd(void) const { double a, b; m_def.GetHorzSweep(a, b); return b; } - double DefaultVertSweepSta(void) const { double a, b; m_def.GetVertSweep(a, b); return a; } - double DefaultVertSweepEnd(void) const { double a, b; m_def.GetVertSweep(a, b); return b; } + double DefaultHorzSweepSta(void) const { double a, b; _def.GetHorzSweep(a, b); return a; } + double DefaultHorzSweepEnd(void) const { double a, b; _def.GetHorzSweep(a, b); return b; } + double DefaultVertSweepSta(void) const { double a, b; _def.GetVertSweep(a, b); return a; } + double DefaultVertSweepEnd(void) const { double a, b; _def.GetVertSweep(a, b); return b; } - double DefaultMinU(void) const { double a, b, c, d; m_def.GetUVBounds(a, b, c, d); return a; } - double DefaultMinV(void) const { double a, b, c, d; m_def.GetUVBounds(a, b, c, d); return b; } - double DefaultMaxU(void) const { double a, b, c, d; m_def.GetUVBounds(a, b, c, d); return c; } - double DefaultMaxV(void) const { double a, b, c, d; m_def.GetUVBounds(a, b, c, d); return d; } + double DefaultMinU(void) const { double a, b, c, d; _def.GetUVBounds(a, b, c, d); return a; } + double DefaultMinV(void) const { double a, b, c, d; _def.GetUVBounds(a, b, c, d); return b; } + double DefaultMaxU(void) const { double a, b, c, d; _def.GetUVBounds(a, b, c, d); return c; } + double DefaultMaxV(void) const { double a, b, c, d; _def.GetUVBounds(a, b, c, d); return d; } private: - const ON_XMLNode* m_pNode; - const ON_Decal m_def; + const ON_XMLNode* _decal_node; + const ON_Decal _def; }; +ON_DecalNodeReader::ON_DecalNodeReader(const ON_XMLNode* decal_node) + : + _decal_node(decal_node) +{ + ON_ASSERT(_decal_node && (_decal_node->TagName() == ON_RDK_DECAL)); +} + ON_XMLVariant ON_DecalNodeReader::Value(const wchar_t* wszName, const ON_XMLVariant& vDefault) const { ON_XMLVariant vValue = vDefault; - if (nullptr != m_pNode) + if (nullptr != _decal_node) { - const ON_XMLParameters p(*m_pNode); + const ON_XMLParameters p(*_decal_node); p.GetParam(wszName, vValue); } return vValue; } -static void DecalUpdateCRC(ON__UINT32& crc, const ON_XMLVariant value) +#if (defined _DEBUG) && (defined HUMAN_READABLE_DECAL_CRC) +#define ON_DECAL_PROP_NAME(s) , s +static void DecalUpdateCRC(ON_DECAL_CRC& crc, const ON_XMLVariant value, const wchar_t* name) +{ + crc = value.DataCRC(crc); + crc._info1 += ON_wString(name) + L"=" + value.AsString() + ON_wString(L" "); + crc._info2 += ON_wString(name) + L"=" + value.AsString() + ON_wString(L"\n"); +} +#else +#define ON_DECAL_PROP_NAME(s) +static void DecalUpdateCRC(ON_DECAL_CRC& crc, const ON_XMLVariant& value) { crc = value.DataCRC(crc); } +#endif -ON__UINT32 ON_Decal::ComputeDecalCRC(ON__UINT32 crc, const ON_XMLNode& node) // Static. +static void DecalUpdateCRC_Custom(const ON_XMLNode& decal_node, ON_DECAL_CRC& crc) { - const ON_DecalNodeReader d(&node); - - const ON_wString s = d.Mapping().AsString(); - const auto mapping = MappingFromString(s); - - DecalUpdateCRC(crc, d.Mapping()); - DecalUpdateCRC(crc, d.IsVisible()); - DecalUpdateCRC(crc, d.IsTemporary()); - DecalUpdateCRC(crc, d.Transparency()); - DecalUpdateCRC(crc, d.TextureInstanceId()); - - if (Mappings::Planar == mapping) - { - DecalUpdateCRC(crc, d.MinU()); - DecalUpdateCRC(crc, d.MinV()); - DecalUpdateCRC(crc, d.MaxU()); - DecalUpdateCRC(crc, d.MaxV()); - } - else - { - DecalUpdateCRC(crc, d.Origin()); - DecalUpdateCRC(crc, d.VectorUp()); - DecalUpdateCRC(crc, d.VectorAcross()); - DecalUpdateCRC(crc, d.Projection()); - - if ((Mappings::Cylindrical == mapping) || (Mappings::Spherical == mapping)) - { - DecalUpdateCRC(crc, d.Radius()); - DecalUpdateCRC(crc, d.MapToInside()); - DecalUpdateCRC(crc, d.HorzSweepSta()); - DecalUpdateCRC(crc, d.HorzSweepEnd()); - - if (Mappings::Cylindrical == mapping) - { - DecalUpdateCRC(crc, d.Height()); - } - else - if (Mappings::Spherical == mapping) - { - DecalUpdateCRC(crc, d.VertSweepSta()); - DecalUpdateCRC(crc, d.VertSweepEnd()); - } - } - } - // Look for custom data nodes and for each one, find the parameter node and then iterate over its // children and CRC the properties. For now, we will have to rely on the raw XML. A better solution // would be to have the plug-in that created this XML calculate the CRC itself. - auto it = node.GetChildIterator(); + + const ON_wString custom = L"[CUSTOM] "; + + auto it = decal_node.GetChildIterator(); ON_XMLNode* pChildNode = nullptr; while (nullptr != (pChildNode = it.GetNextChild())) { if (pChildNode->TagName() != ON_RDK_DECAL_CUSTOM) continue; // Not a custom data node. - ON_XMLProperty* prop = pChildNode->GetNamedProperty(ON_RDK_DECAL_CUSTOM_RENDERER); - if (nullptr != prop) - { - // Include the render engine id. - const ON_UUID uuid = prop->GetValue().AsUuid(); - crc = ON_CRC32(crc, sizeof(uuid), &uuid); - } + const ON__UINT32 crc_before_custom_params = crc; // Find the custom parameter node. const ON_XMLNode* pParamNode = pChildNode->GetNamedChild(ON_RDK_DECAL_CUSTOM_PARAMS); @@ -947,22 +968,103 @@ ON__UINT32 ON_Decal::ComputeDecalCRC(ON__UINT32 crc, const ON_XMLNode& node) // { // Iterate over the nodes inside the custom parameter node. const ON_XMLParameters p(*pParamNode); - auto* pIterator = p.NewIterator(); - - ON_wString sParamName; - ON_XMLVariant vParamValue; - while (pIterator->Next(sParamName, vParamValue)) + auto* iterator = p.NewIterator(); + if (nullptr != iterator) { - DecalUpdateCRC(crc, vParamValue); + ON_wString sParamName; + ON_XMLVariant vParamValue; + while (iterator->Next(sParamName, vParamValue)) + { + DecalUpdateCRC(crc, vParamValue ON_DECAL_PROP_NAME(custom + sParamName)); + } + + delete iterator; } - delete pIterator; + if (crc != crc_before_custom_params) + { + // 20th January 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-71351 + // Since the crc has changed, there must be some custom params, so only now do we include the render + // engine id. Prior to this, the render engine id was getting included even when there were no custom + // params. This caused the UI to overlook decals which were programatically created by clients. + const ON_XMLProperty* prop = pChildNode->GetNamedProperty(ON_RDK_DECAL_CUSTOM_RENDERER); + if (nullptr != prop) + { + // Include the render engine id. + const ON_UUID uuid = prop->GetValue().AsUuid(); + DecalUpdateCRC(crc, uuid ON_DECAL_PROP_NAME(custom + L"render_engine_id")); + } + } } } +} - // Make sure it's not zero which would mean 'nil'. - if (0 == crc) - crc--; +ON_DECAL_CRC ON_Decal::ComputeDecalCRC(ON__UINT32 current_remainder, const ON_XMLNode& decal_node) // Static. +{ + // The CRC of a decal is a unique value based on its state. It's created by CRC-ing all the decal properties + // that affect the decal's appearance. We do not include the 'IsTemporary' property in the CRC because whether + // or not a decal is temporary has nothing to do with what it looks like. We do however, include the 'IsVisible' + // property because a decal being visible or invisible actually affects its appearance. Also, the RDK change + // queue relies on the CRC to update the viewport so including the visibility is critical. Furthermore, the + // CRC only includes the properties that are relevant for the decal's mapping type. + + ON_DECAL_CRC crc = current_remainder; + + if (decal_node.TagName() == ON_RDK_DECAL) + { + const ON_DecalNodeReader d(&decal_node); + + DecalUpdateCRC(crc, d.Mapping() ON_DECAL_PROP_NAME(L"mapping")); + DecalUpdateCRC(crc, d.IsVisible() ON_DECAL_PROP_NAME(L"visible")); + DecalUpdateCRC(crc, d.Transparency() ON_DECAL_PROP_NAME(L"transparency")); + DecalUpdateCRC(crc, d.TextureInstanceId() ON_DECAL_PROP_NAME(L"texture_id")); + + const ON_Decal::Mappings mapping = MappingFromString(d.Mapping().AsString()); + + if (Mappings::UV == mapping) + { + DecalUpdateCRC(crc, d.MinU() ON_DECAL_PROP_NAME(L"min_u")); + DecalUpdateCRC(crc, d.MinV() ON_DECAL_PROP_NAME(L"min_v")); + DecalUpdateCRC(crc, d.MaxU() ON_DECAL_PROP_NAME(L"max_u")); + DecalUpdateCRC(crc, d.MaxV() ON_DECAL_PROP_NAME(L"max_v")); + } + else + { + DecalUpdateCRC(crc, d.Origin() ON_DECAL_PROP_NAME(L"origin")); + DecalUpdateCRC(crc, d.VectorUp() ON_DECAL_PROP_NAME(L"up")); + DecalUpdateCRC(crc, d.VectorAcross() ON_DECAL_PROP_NAME(L"across")); + + if ((Mappings::Cylindrical == mapping) || (Mappings::Spherical == mapping)) + { + DecalUpdateCRC(crc, d.MapToInside() ON_DECAL_PROP_NAME(L"map_to_inside")); + DecalUpdateCRC(crc, d.Radius() ON_DECAL_PROP_NAME(L"radius")); + DecalUpdateCRC(crc, d.HorzSweepSta() ON_DECAL_PROP_NAME(L"horz_sweep_sta")); + DecalUpdateCRC(crc, d.HorzSweepEnd() ON_DECAL_PROP_NAME(L"horz_sweep_end")); + + if (Mappings::Cylindrical == mapping) + { + DecalUpdateCRC(crc, d.Height() ON_DECAL_PROP_NAME(L"height")); + } + else + if (Mappings::Spherical == mapping) + { + DecalUpdateCRC(crc, d.VertSweepSta() ON_DECAL_PROP_NAME(L"vert_sweep_sta")); + DecalUpdateCRC(crc, d.VertSweepEnd() ON_DECAL_PROP_NAME(L"vert_sweep_end")); + } + } + else + if (Mappings::Planar == mapping) + { + DecalUpdateCRC(crc, d.Projection() ON_DECAL_PROP_NAME(L"projection")); + } + } + + DecalUpdateCRC_Custom(decal_node, crc); + + // Make sure it's not nil. + if (crc == ON_NIL_DECAL_CRC) + crc = 0xFFFFFFFF; + } return crc; } @@ -976,7 +1078,7 @@ ON_DecalCollection::~ON_DecalCollection() int ON_DecalCollection::FindDecalIndex(const ON_UUID& id) const { - for (int i = 0; i < m_decals.Count(); i++) + for (int i = 0; i < m_decals.size(); i++) { if (m_decals[i]->Id() == id) return i; @@ -985,12 +1087,12 @@ int ON_DecalCollection::FindDecalIndex(const ON_UUID& id) const return -1; } -ON_Decal* ON_DecalCollection::AddDecal(void) +std::shared_ptr ON_DecalCollection::AddDecal(void) { // Ensure the array is populated before adding a new decal. GetDecalArray(); - ON_Decal* decal = nullptr; + std::shared_ptr decal; ON_XMLNode* decals_node = m_root_node.CreateNodeAtPath(ON_RDK_UD_ROOT ON_XML_SLASH ON_RDK_DECALS); if (nullptr != decals_node) @@ -1001,8 +1103,8 @@ ON_Decal* ON_DecalCollection::AddDecal(void) // Add the new decal. It stores a pointer to the new XML node. This is safe because // the decals have the same lifetime as the root node that owns the XML nodes. - decal = new ON_Decal(*this, *decal_node); - m_decals.Append(decal); + decal = std::make_shared(*this, *decal_node); + m_decals.push_back(decal); SetChanged(); } @@ -1041,8 +1143,7 @@ bool ON_DecalCollection::RemoveDecal(const ON_Decal& decal) return false; // Delete it. - delete m_decals[index]; - m_decals.Remove(index); + m_decals.erase(m_decals.begin() + index); return true; } @@ -1059,16 +1160,10 @@ void ON_DecalCollection::ClearDecalArray(void) { // 12th July 2023 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-75697 // Only call SetChanged() if a decal is actually deleted. - const int count = m_decals.Count(); - if (count > 0) + + if (!m_decals.empty()) { - for (int i = 0; i < count; i++) - { - delete m_decals[i]; - } - - m_decals.Destroy(); - + m_decals.clear(); SetChanged(); } @@ -1079,12 +1174,11 @@ const ON_DecalCollection& ON_DecalCollection::operator = (const ON_DecalCollecti { ClearDecalArray(); - for (int i = 0; i < dc.m_decals.Count(); i++) + for (const auto& decal : dc.m_decals) { - ON_Decal* decal = dc.m_decals[i]; - if (nullptr != decal) + if (decal) { - m_decals.Append(new ON_Decal(*decal)); + m_decals.push_back(std::make_shared(*decal)); } } @@ -1093,10 +1187,19 @@ const ON_DecalCollection& ON_DecalCollection::operator = (const ON_DecalCollecti return *this; } -const ON_SimpleArray& ON_DecalCollection::GetDecalArray(void) const +const std::vector>& ON_DecalCollection::GetDecalArray(void) const { - if (!m_populated) + // 19th February 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-86089 + // The m_populated flag is an optimization designed to make sure we only populate the array when + // something changes. Unfortunately, in Rhino 8, I forgot to add code to detect when a decal changes + // externally to this class, and the array (which is essentially a cache) becomes invalid. This is + // fixed in Rhino 9 but the fix is too complicated to backport to Rhino 8, so to get around this I'm + // just going to remove the optimization. + +//if (!m_populated) // Always repopulate the array. { + m_decals.clear(); + Populate(); m_populated = true; @@ -1117,13 +1220,13 @@ void ON_DecalCollection::Populate(void) const if (nullptr != decals_node) { // Iterate over the decals under the decals node adding a new decal for each one. - ON_ASSERT(m_decals.Count() == 0); + ON_ASSERT(m_decals.size() == 0); auto it = decals_node->GetChildIterator(); ON_XMLNode* child_node = nullptr; while (nullptr != (child_node = it.GetNextChild())) { - auto* decal = new ON_Decal(*const_cast(this), *child_node); - m_decals.Append(decal); + auto decal = std::make_shared(*const_cast(this), *child_node); + m_decals.push_back(decal); } } } diff --git a/opennurbs_decals.h b/opennurbs_decals.h index f148cd3e..997af74a 100644 --- a/opennurbs_decals.h +++ b/opennurbs_decals.h @@ -14,12 +14,97 @@ #if !defined(ON_DECALS_INC_) #define ON_DECALS_INC_ +// Comment in this line to enable human-readable decal CRCs. One of the most difficult things about +// diagnosing decal bugs is not being able to get useful information from their CRCs. By enabling +// this feature, the decal CRC will change from being an integer to being a class with debug information +// in it. THIS BREAKS THE SDK SO ONLY USE IT FOR LOCAL DEBUGGING. NEVER CHECK THIS LINE IN WITH IT ENABLED. +//#define HUMAN_READABLE_DECAL_CRC + +#ifndef _DEBUG +#ifdef HUMAN_READABLE_DECAL_CRC +#error "HUMAN_READABLE_DECAL_CRC is defined in release build" +#endif +#endif + +#if (defined _DEBUG) && (defined HUMAN_READABLE_DECAL_CRC) +class ON_HumanReadableDecalCRC +{ +public: + ON_HumanReadableDecalCRC(ON__UINT32 v=0) : _value(v) { } + + void operator = (ON__UINT32 v) { _value = v; } + bool operator == (ON__UINT32 v) const { return _value == v; } + bool operator != (ON__UINT32 v) const { return _value != v; } + + operator ON__UINT32(void) const { return _value; } + + operator const wchar_t*(void) const { return _info1; } + +public: + ON__UINT32 _value = 0; + ON_wString _info1; + ON_wString _info2; +}; +#define ON_DECAL_CRC ON_HumanReadableDecalCRC +#else +#define ON_DECAL_CRC ON__UINT32 +#endif + +#define ON_NIL_DECAL_CRC ON_DECAL_CRC(0) // If a decal CRC is nil, it means 'no decal'. + +// ON_Decal encapsulates a rendering decal which is an image that sticks to the surface of an object +// like a real-life decal does. Decals are identified by their CRC which is a value generated from the +// decal's current state. When a property changes, the CRC will also change. +// +// Decals have a choice of mappings: +// +// - UV: Maps a texture to (u,v) coordinates on an object. +// - Planar: Maps a texture to a rectangle like an ordinary sticker. +// - Spherical: Maps a texture to a spherical object like a label on a ball. +// - Cylindrical: Maps a texture to a cylinder like a label on a can. +// +// They also have a choice of projections: +// +// - Forward: Projects the decal forward onto the object. +// - Backward: Projects the decal backward onto the object. +// - Both: Projects the decal forward and backward onto the object. +// +// Some of the properties are only used by certain mappings: +// +// - UV bounds: UV mapping only. +// - Transparency: All mappings. +// - Origin: All mappings except UV. +// - Up vector: All mappings except UV. +// - Across vector: All mappings except UV. +// - Height: Cylindrical mapping only. +// - Map to inside: Cylindrical and spherical mappings only. +// - Radius: Cylindrical and spherical mappings only. +// - Horizontal sweep: Cylindrical and spherical mappings only. +// - Vertical sweep: Spherical mapping only. +// +// The class stores its data as XML but it is optimized with a built-in cache. As each property is +// requested, it is read from the XML and cached. Thereafter, the cached value is returned and the XML +// for that property is never read again. On writing to a property, the cache is updated and by default +// the XML is also updated. However, it is possible to inhibit the XML update for increased performance. +// See SetCacheOnly() below. + class ON_CLASS ON_Decal { public: + // Construct a decal with all properties set to defaults. ON_Decal(); - ON_Decal(class ON_DecalCollection& coll, ON_XMLNode& node); - ON_Decal(const ON_Decal& d); + + // Construct a decal passing the XML node to be used for its storage. + ON_Decal(ON_XMLNode& node); + + // Construct a decal passing the const XML node to be used for its storage. This decal is read only. + ON_Decal(const ON_XMLNode& node); + + ON_Decal(class ON_DecalCollection& coll, ON_XMLNode& node); // For internal use only. + + // Construct this decal as a copy of another decal. + ON_Decal(const ON_Decal& other); + virtual ~ON_Decal(); virtual const ON_Decal& operator = (const ON_Decal& d); @@ -136,7 +221,7 @@ public: bool GetTextureMapping(ON_TextureMapping& tm) const; // Returns the Decal CRC of the decal. - ON__UINT32 DecalCRC(void) const; + ON_DECAL_CRC DecalCRC(void) const; // Returns the Data CRC of the decal. This is not necessarily the same as the decal CRC // because it allows a starting current remainder. @@ -167,7 +252,9 @@ public: bool SetCustomXML(const ON_UUID& renderEngineId, const ON_XMLNode& custom_param_node); public: // For internal use only. - static ON__UINT32 ComputeDecalCRC(ON__UINT32, const ON_XMLNode&); + static ON_DECAL_CRC ComputeDecalCRC(ON__UINT32, const ON_XMLNode&); + void GetEntireCustomXML(ON_XMLNode&) const; + void AppendCustomXML(const ON_XMLNode&); private: class CImpl; @@ -175,6 +262,6 @@ private: }; // For internal use only. -ON__UINT32 ON_DECL ON_DecalCRCFromNode(const ON_XMLNode& node); +ON_DECAL_CRC ON_DECL ON_DecalCRCFromNode(const ON_XMLNode& node); #endif diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp index f1e25d35..a0d90998 100644 --- a/opennurbs_dimensionstyle.cpp +++ b/opennurbs_dimensionstyle.cpp @@ -3130,17 +3130,29 @@ bool ON_DimStyle::Read( rc = true; break; } - // Dale Lear April 8, 2016 - // working on http://mcneel.myjetbrains.com/youtrack/issue/RH-31796 - // bSupressPartiallyReadChunkWarning suppresses a partially read chunk warning - // of skipping 16 bytes. - const bool bSupressPartiallyReadChunkWarning - = 60 == file.Archive3dmVersion() - && file.ArchiveOpenNURBSVersion() <= 2348833437 - && 1 == major_version - && 0 == minor_version + // Fix below for RH-86880 covers this case + ////// Dale Lear April 8, 2016 + ////// working on http://mcneel.myjetbrains.com/youtrack/issue/RH-31796 + ////// bSupressPartiallyReadChunkWarning suppresses a partially read chunk warning + ////// of skipping 16 bytes. + ////const bool bSupressPartiallyReadChunkWarning + //// = 60 == file.Archive3dmVersion() + //// && file.ArchiveOpenNURBSVersion() <= 2348833437 + //// && 1 == major_version + //// && 0 == minor_version + //// ; + + // Dale Lear April 3, 2025 - fix for reading the v8 file in RH-86880 + // Somebody added UseKerning() (minor_version = 10) and LineSpaceScale() (minor_version = 11) + // to the v9 dimstyle (see 9.x repo) while v9 was writing v8 files. + // When v8 opennurbs reads this files the chunk is partially read + // This fix also fixes the bug I fixed on April 8, 2016 RH-31796 + const bool bSupressPartiallyReadChunkWarning + = (file.Archive3dmVersion() < 80) + || (80 == file.Archive3dmVersion() && 1 == major_version && minor_version > 9) ; + if (!file.EndRead3dmChunk(bSupressPartiallyReadChunkWarning)) rc = false; diff --git a/opennurbs_input_libsdir.h b/opennurbs_input_libsdir.h index 3936cc77..b3ce1579 100644 --- a/opennurbs_input_libsdir.h +++ b/opennurbs_input_libsdir.h @@ -16,6 +16,12 @@ #if !defined(OPENNURBS_INPUT_LIBSDIR_INC_) #define OPENNURBS_INPUT_LIBSDIR_INC_ + + +// RH-86025, RH3DM-179, 2025-02-14, Pierre: +// ON_CMAKE_BUILD should be renamed "ON_NOT_USING_MSVC_LINK_LIB_PRAGMA" +// This whole thing should be reworked so MSVC builds also use build properties, +// instead of the non-portable #pragma comment(lib, "path"). #if defined(ON_COMPILER_MSC) && !defined(OPENNURBS_INPUT_LIBS_DIR) && !defined(ON_CMAKE_BUILD) // This header file insures OPENNURBS_INPUT_LIBS_DIR is defined to be diff --git a/opennurbs_internal_V5_annotation.cpp b/opennurbs_internal_V5_annotation.cpp index 1ea8bfdb..77b558a6 100644 --- a/opennurbs_internal_V5_annotation.cpp +++ b/opennurbs_internal_V5_annotation.cpp @@ -749,33 +749,6 @@ ON_3dPoint ON_OBSOLETE_V2_AnnotationArrow::Tail() const } -static bool ON_Internal_UseSubDMeshProxy( - const ON_BinaryArchive& archive -) -{ - if (archive.Archive3dmVersion() != 60) - { - return false; // subd mesh proxy only applies when reading or writing a V6 files. - } - - // In a WIP, used subd objects. - const unsigned int min_subd_version = -#if defined(RHINO_COMMERCIAL_BUILD) - // Sep 5 2017 Dale Lear RH-41113 - // Rhino 6 commercial will have SubD objects. - // Nope. -> V6 commercial builds do not have subd objects - use proxy mesh when version < 7. - // 7 - 6 -#else - // V6 WIP builds and all V7 and later builds have subd objects. - // Use proxy mesh when version < 6. - 6 -#endif - ; - return (ON::VersionMajor() < min_subd_version); -} - - /* Description: In rare cases one object must be converted into another. @@ -888,38 +861,19 @@ ON_Object* ON_BinaryArchive::Internal_ConvertObject( if (ON::object_type::mesh_object == archive_object->ObjectType()) { - const ON_Mesh* mesh = ON_Mesh::Cast(archive_object); - if (nullptr != mesh ) + if (m_3dm_version <= 60) { - if ( false == ON_Internal_UseSubDMeshProxy(*this) ) + const ON_Mesh* mesh = ON_Mesh::Cast(archive_object); + if (nullptr != mesh) { // If mesh is a subd mesh proxy, return the original subd. ON_SubD* subd = ON_SubDMeshProxyUserData::SubDFromMeshProxy(mesh); - if ( nullptr != subd ) + if (nullptr != subd) return subd; } } } - else if (ON::object_type::subd_object == archive_object->ObjectType()) - { - const ON_SubD* subd = ON_SubD::Cast(archive_object); - if (nullptr != subd) - { - ON_Mesh* mesh = nullptr; - if ( Archive3dmVersion() < 60 ) - { - mesh = subd->GetControlNetMesh(nullptr, ON_SubDGetControlNetMeshPriority::Geometry); - } - else if ( ON_Internal_UseSubDMeshProxy(*this) ) - { - // Use a subd mesh proxy for V6 commercial builds. - mesh = ON_SubDMeshProxyUserData::MeshProxyFromSubD(subd); - } - if (nullptr != mesh) - return mesh; - } - } - + // no conversion required. return nullptr; } diff --git a/opennurbs_internal_defines.h b/opennurbs_internal_defines.h index 60130a4d..205b5f6f 100644 --- a/opennurbs_internal_defines.h +++ b/opennurbs_internal_defines.h @@ -113,11 +113,11 @@ public: const ON_DecalCollection& operator = (const ON_DecalCollection& dc); - ON_Decal* AddDecal(void); + std::shared_ptr AddDecal(void); bool RemoveDecal(const ON_Decal&); void RemoveAllDecals(void); void ClearDecalArray(void); - const ON_SimpleArray& GetDecalArray(void) const; + const std::vector>& GetDecalArray(void) const; void SetChanged(void); @@ -130,7 +130,7 @@ private: private: ON_3dmObjectAttributes* m_attr; mutable ON_XMLRootNode m_root_node; - mutable ON_SimpleArray m_decals; + mutable std::vector> m_decals; mutable bool m_populated = false; mutable bool m_changed = false; }; diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp index d4f5e941..16f3147c 100644 --- a/opennurbs_mesh.cpp +++ b/opennurbs_mesh.cpp @@ -15901,21 +15901,21 @@ ON_Mesh* ON_Mesh::CopyComponents( return CopyComponents(ci_list.Array(), ci_list.UnsignedCount(), destination_mesh); } -bool ON_Mesh::SetSurfaceParamtersFromTextureCoodinates() +bool ON_Mesh::SetSurfaceParametersFromTextureCoodinates(const ON_SimpleArray& TCs) { unsigned int i; const unsigned int vcount = m_V.UnsignedCount(); bool rc; ON_Interval dom; - if (vcount == m_T.UnsignedCount()) + if (vcount == TCs.UnsignedCount()) { dom.Set(0.0, 1.0); m_S.SetCount(0); m_S.Reserve(vcount); for (i = 0; i < vcount; i++) { - ON_2dPoint S = m_T[i]; + ON_2dPoint S = TCs[i]; m_S.Append(S); } rc = true; @@ -16041,6 +16041,12 @@ bool ON_Mesh::SetSurfaceParamtersFromTextureCoodinates() //return true; } +bool ON_Mesh::SetSurfaceParamtersFromTextureCoodinates() +{ + return SetSurfaceParametersFromTextureCoodinates(m_T); +} + + ////////////////////////////////////////////////////////////////////////// // // ON_MeshCache diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h index 0ba4b75a..a749b36f 100644 --- a/opennurbs_mesh.h +++ b/opennurbs_mesh.h @@ -6135,6 +6135,8 @@ The map is an array of length m_F.Count(), ngon_map[] */ bool SetSurfaceParamtersFromTextureCoodinates(); + bool SetSurfaceParametersFromTextureCoodinates(const ON_SimpleArray& TCs); + ///////////////////////////////////////////////////////////////// // Implementation - curvature diff --git a/opennurbs_model_component.cpp b/opennurbs_model_component.cpp index a7379179..0505d0b6 100644 --- a/opennurbs_model_component.cpp +++ b/opennurbs_model_component.cpp @@ -3888,6 +3888,10 @@ ON_ModelComponentReference ON_ModelComponentReference::CreateConstantSystemCompo if ( system_model_component.IsSystemComponent()) return CreateForExperts(const_cast(&system_model_component),false); + + // If you get this error, first double check and make sure you + // are calling ON::Begin() before you make any other calls + // into opennurbs. ON_ERROR("Invalid system_model_component parameter."); return ON_ModelComponentReference::Empty; } diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp index a7aadbb5..306a4c7c 100644 --- a/opennurbs_nurbssurface.cpp +++ b/opennurbs_nurbssurface.cpp @@ -778,60 +778,127 @@ bool ON_NurbsSurface::Read( // NOTE - check legacy I/O code if changed int major_version = 0; int minor_version = 0; - bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); - if (rc && major_version==1) { + while (file.Read3dmChunkVersion(&major_version, &minor_version)) + { + if (1 != major_version) + break; + // common to all 1.x versions - int dim = 0, is_rat = 0, order0 = 0, order1 = 0, cv_count0 = 0, cv_count1 = 0; - int reserved1 = 0, reserved2 = 0; - if (rc) rc = file.ReadInt( &dim ); - if (rc) rc = file.ReadInt( &is_rat ); - if (rc) rc = file.ReadInt( &order0 ); - if (rc) rc = file.ReadInt( &order1 ); - if (rc) rc = file.ReadInt( &cv_count0 ); - if (rc) rc = file.ReadInt( &cv_count1 ); + int dim = 0; + if (false == file.ReadInt(&dim)) + break; + if (dim < 1) + break; + int is_rat = 0; + if (false == file.ReadInt(&is_rat)) + break; - if (rc) rc = file.ReadInt(&reserved1); - if (rc) rc = file.ReadInt(&reserved2); + int order0 = 0; + if (false == file.ReadInt( &order0 )) + break; + if (order0 < 2) + break; + int order1 = 0; + if (false == file.ReadInt( &order1 )) + break; + if (order1 < 2) + break; - if (rc) { - ON_BoundingBox bbox; // read bounding box - may be used in future - rc = file.ReadBoundingBox(bbox); - } + int cv_count0 = 0; + if (false == file.ReadInt( &cv_count0 )) + break; + if (cv_count0 < order0) + break; + int cv_count1 = 0; + if (false == file.ReadInt( &cv_count1 )) + break; + if (cv_count1 < order1) + break; + + // These are sanity checks for overflow. + // Safe fix for RH-83540 + // They prevent attempt to allocate large numbers of knots when + // one of the orders or cv_counts red above is from a corrupt file. + const int expected_knot_count0 = ON_KnotCount(order0, cv_count0); + if (expected_knot_count0 < 2) + break; + const int expected_knot_count1 = ON_KnotCount(order1, cv_count1); + if (expected_knot_count1 < 2) + break; + const int expected_cv_count = cv_count0 * cv_count1; + if (expected_cv_count < 4) + break; + const int expected_cv_capacity = (is_rat != 0 ? (dim + 1) : dim) * expected_cv_count; + if (expected_cv_capacity < 4) + break; + + + int reserved1 = 0; + if (false == file.ReadInt(&reserved1)) + break; + int reserved2 = 0; + if (false == file.ReadInt(&reserved2)) + break; + + ON_BoundingBox ignored_bbox; // read bounding box place holder - may be used in future + if (false == file.ReadBoundingBox(ignored_bbox)) + break; - Create( dim, is_rat, order0, order1, cv_count0, cv_count1 ); - - int count = 0; - if (rc) rc = file.ReadInt(&count); - if (rc && count < 0) - rc = false; - if (rc ) rc = ReserveKnotCapacity(0,count); - if (rc) rc = file.ReadDouble( count, m_knot[0] ); - - count = 0; - if (rc) rc = file.ReadInt(&count); - if (rc && count < 0) - rc = false; - if (rc ) rc = ReserveKnotCapacity(1,count); - if (rc) rc = file.ReadDouble( count, m_knot[1] ); - - count = 0; - if (rc) rc = file.ReadInt(&count); - if (rc && count < 0) - rc = false; + if (false == Create(dim, is_rat, order0, order1, cv_count0, cv_count1)) + break; const int cv_size = CVSize(); - if (rc) rc = ReserveCVCapacity( count*cv_size ); - if (count > 0 && cv_size > 0 && rc ) { - int i, j; - for ( i = 0; i < m_cv_count[0] && rc; i++ ) { - for ( j = 0; j < m_cv_count[1] && rc; j++ ) { - rc = file.ReadDouble( cv_size, CV(i,j) ); - } + if (cv_size < dim) + break; + + int knot_count0 = 0; + if (false == file.ReadInt(&knot_count0)) + break; + if (knot_count0 != expected_knot_count0) + break; + if (false == ReserveKnotCapacity(0, knot_count0)) + break; + if (false == file.ReadDouble(knot_count0, m_knot[0])) + break; + + int knot_count1 = 0; + if (false == file.ReadInt(&knot_count1)) + break; + if (knot_count1 != expected_knot_count1) + break; + if (false == ReserveKnotCapacity(1, knot_count1)) + break; + if (false == file.ReadDouble(knot_count1, m_knot[1])) + break; + + int cv_count = 0; + if (false == file.ReadInt(&cv_count)) + break; + if (cv_count != expected_cv_count) + break; + if (false == ReserveCVCapacity(cv_count * cv_size)) + break; + int i = 0; + for ( i = 0; i < m_cv_count[0]; i++ ) + { + int j = 0; + for ( j = 0; j < m_cv_count[1]; j++ ) + { + if (false == file.ReadDouble(cv_size, CV(i, j))) + break; } + if (j != m_cv_count[1]) + break; } + if (i != m_cv_count[0]) + break; + + // successful read + return true; } - if ( !rc ) - Destroy(); - return rc; + + // Corrupt file + Destroy(); + return false; } ON_Interval ON_NurbsSurface::Domain( int dir ) const diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 71a657bf..6213b3c5 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 8 -#define RMA_VERSION_MINOR 17 +#define RMA_VERSION_MINOR 19 //////////////////////////////////////////////////////////////// // @@ -14,9 +14,9 @@ // first step in each build. // #define RMA_VERSION_YEAR 2025 -#define RMA_VERSION_MONTH 3 +#define RMA_VERSION_MONTH 4 #define RMA_VERSION_DATE 7 -#define RMA_VERSION_HOUR 7 +#define RMA_VERSION_HOUR 2 #define RMA_VERSION_MINUTE 0 //////////////////////////////////////////////////////////////// @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,17,25066,7000 -#define VERSION_WITH_PERIODS 8.17.25066.07000 +#define VERSION_WITH_COMMAS 8,19,25097,2000 +#define VERSION_WITH_PERIODS 8.19.25097.02000 #define COPYRIGHT "Copyright (C) 1993-2025, 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"8" #define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"7" -#define RMA_VERSION_NUMBER_SR_STRING "SR17" -#define RMA_VERSION_NUMBER_SR_WSTRING L"SR17" +#define RMA_VERSION_NUMBER_SR_STRING "SR19" +#define RMA_VERSION_NUMBER_SR_WSTRING L"SR19" -#define RMA_VERSION_WITH_PERIODS_STRING "8.17.25066.07000" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.17.25066.07000" +#define RMA_VERSION_WITH_PERIODS_STRING "8.19.25097.02000" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.19.25097.02000" diff --git a/opennurbs_subd.h b/opennurbs_subd.h index b8cda2d2..f329e63d 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -3131,7 +3131,7 @@ public: /// Postfix operator ++ sets this to ON_SubDComponentPtr::NextComponent() /// and returns the previous value of this. /// - /// ON_SubDComponentPtr::NextComponent() + /// *this, before increment const ON_SubDComponentPtr operator++(int); @@ -17694,13 +17694,34 @@ public: /* Description: - Increment the iterator. + Prefix increment the iterator. + Returns: + Next vertex. + Remarks: + operator++(void) and NextVertex() behave the same. + In OpenNURBS 8.17 and earlier, this function was incorrectly implemented as + a postfix increment (operator++(int)). This has been corrected in OpenNURBS 8.18. + If you have a plugin compiled without inlining optimizations (e.g. in Debug mode), + operator++(void) will be calling the version in opennurbs.dll distributed + with Rhino that is used to run the plugin. + If you have a plugin compiled with inlining optimizations (e.g. in Release mode), + operator++(void) will behave like the version in opennurbs_subd.h distributed + with the Rhino SDK that was used to compiled the plugin. + */ + const class ON_SubDVertex* operator++() + { + return NextVertex(); + } + + /* + Description: + Postfix increment the iterator. Returns: Current vertex. Remarks: - operator++ and NextVertex() behave differently. + operator++(int) and NextVertex() behave differently. */ - const class ON_SubDVertex* operator++() + const class ON_SubDVertex* operator++(int) { const class ON_SubDVertex* v = m_v_current; NextVertex(); @@ -17739,11 +17760,12 @@ public: /* Description: - Increment the iterator. + Pre-increment the iterator and return the new current vertex. Returns: Next vertex. Remarks: - operator++ and NextVertex() behave differently. + operator++(void) and NextVertex() behave the same. + operator++(int) and NextVertex() behave differently. */ const class ON_SubDVertex* NextVertex() { @@ -17801,6 +17823,41 @@ public: return (m_v_current = m_v_last); } + /* + Description: + Get the iterator's base component in which we are iterating, if it exists. + Returns: + m_component_ptr if it exists, or ON_SubDComponentPtr::Null. + */ + ON_SubDComponentPtr BaseComponentPtr() const + { + return m_component_ptr.m_ptr == 0 ? ON_SubDComponentPtr::Null : m_component_ptr; + } + + /* + Description: + Get the iterator's base edge in which we are iterating, if it exists. + Returns: + m_component_ptr.Edge() if it exists, or nullptr. + */ + ON_SubDEdge* BaseEdge() const + { + if (m_component_ptr.m_ptr == 0) return nullptr; + return m_component_ptr.IsEdge() ? m_component_ptr.Edge() : nullptr; + } + + /* + Description: + Get the iterator's base edge in which we are iterating, if it exists. + Returns: + m_component_ptr.Face() if it exists, or nullptr. + */ + ON_SubDFace* BaseFace() const + { + if (m_component_ptr.m_ptr == 0) return nullptr; + return m_component_ptr.IsFace() ? m_component_ptr.Face() : nullptr; + } + private: void Internal_Init( const ON_SubDRef& subd_ref, @@ -17967,13 +18024,34 @@ public: /* Description: - Increment the iterator. + Prefix increment the iterator. + Returns: + Next edge. + Remarks: + operator++(void) and NextEdge() behave the same. + In OpenNURBS 8.17 and earlier, this function was incorrectly implemented as + a postfix increment (operator++(int)). This has been corrected in OpenNURBS 8.18. + If you have a plugin compiled without inlining optimizations (e.g. in Debug mode), + operator++(void) will be calling the version in opennurbs.dll distributed + with Rhino that is used to run the plugin. + If you have a plugin compiled with inlining optimizations (e.g. in Release mode), + operator++(void) will behave like the version in opennurbs_subd.h distributed + with the Rhino SDK that was used to compiled the plugin. + */ + const class ON_SubDEdge* operator++() + { + return NextEdge(); + } + + /* + Description: + Postfix increment the iterator. Returns: Current edge. Remarks: - operator++ and NextEdge() behave differently. + operator++(int) and NextEdge() behave differently. */ - const class ON_SubDEdge* operator++() + const class ON_SubDEdge* operator++(int) { const class ON_SubDEdge* e = m_e_current; NextEdge(); @@ -18012,11 +18090,12 @@ public: /* Description: - Increment the iterator. + Pre-increment the iterator and return the new current edge. Returns: Next edge. Remarks: - operator++ and NextEdge() behave differently. + operator++(void) and NextEdge() behave the same. + operator++(int) and NextEdge() behave differently. */ const class ON_SubDEdge* NextEdge() { @@ -18074,6 +18153,41 @@ public: return m_e_current = m_e_last; } + /* + Description: + Get the iterator's base component in which we are iterating, if it exists. + Returns: + m_component_ptr if it exists, or ON_SubDComponentPtr::Null. + */ + ON_SubDComponentPtr BaseComponentPtr() const + { + return m_component_ptr.m_ptr == 0 ? ON_SubDComponentPtr::Null : m_component_ptr; + } + + /* + Description: + Get the iterator's base vertex in which we are iterating, if it exists. + Returns: + m_component_ptr.Vertex() if it exists, or nullptr. + */ + ON_SubDVertex* BaseVertex() const + { + if (m_component_ptr.m_ptr == 0) return nullptr; + return m_component_ptr.IsVertex() ? m_component_ptr.Vertex() : nullptr; + } + + /* + Description: + Get the iterator's base edge in which we are iterating, if it exists. + Returns: + m_component_ptr.Face() if it exists, or nullptr. + */ + ON_SubDFace* BaseFace() const + { + if (m_component_ptr.m_ptr == 0) return nullptr; + return m_component_ptr.IsFace() ? m_component_ptr.Face() : nullptr; + } + private: void Internal_Init( const ON_SubDRef& subd_ref, @@ -18240,13 +18354,34 @@ public: /* Description: - Returns the current face and increment the iterator. + Prefix increment the iterator. + Returns: + Next face. + Remarks: + operator++(void) and NextFace() behave the same. + In OpenNURBS 8.17 and earlier, this function was incorrectly implemented as + a postfix increment (operator++(int)). This has been corrected in OpenNURBS 8.18. + If you have a plugin compiled without inlining optimizations (e.g. in Debug mode), + operator++(void) will be calling the version in opennurbs.dll distributed + with Rhino that is used to run the plugin. + If you have a plugin compiled with inlining optimizations (e.g. in Release mode), + operator++(void) will behave like the version in opennurbs_subd.h distributed + with the Rhino SDK that was used to compiled the plugin. + */ + const class ON_SubDFace* operator++() + { + return NextFace(); + } + + /* + Description: + Postfix increment the iterator. Returns: Current face. Remarks: - operator++ and NextFace() behave differently. + operator++(int) and NextFace() behave differently. */ - const class ON_SubDFace* operator++() + const class ON_SubDFace* operator++(int) { const class ON_SubDFace* f = m_face_current; NextFace(); @@ -18286,11 +18421,12 @@ public: /* Description: - Returns the next face and increments the iterator. + Pre-increment the iterator and return the new current face. Returns: Next face. Remarks: - operator++ and NextFace() behave differently. + operator++(void) and NextFace() behave the same. + operator++(int) and NextFace() behave differently. */ const class ON_SubDFace* NextFace() { @@ -18348,6 +18484,41 @@ public: return (m_face_current = m_face_last); } + /* + Description: + Get the iterator's base component in which we are iterating, if it exists. + Returns: + m_component_ptr if it exists, or ON_SubDComponentPtr::Null. + */ + ON_SubDComponentPtr BaseComponentPtr() const + { + return m_component_ptr.m_ptr == 0 ? ON_SubDComponentPtr::Null : m_component_ptr; + } + + /* + Description: + Get the iterator's base vertex in which we are iterating, if it exists. + Returns: + m_component_ptr.Vertex() if it exists, or nullptr. + */ + ON_SubDVertex* BaseVertex() const + { + if (m_component_ptr.m_ptr == 0) return nullptr; + return m_component_ptr.IsVertex() ? m_component_ptr.Vertex() : nullptr; + } + + /* + Description: + Get the iterator's base edge in which we are iterating, if it exists. + Returns: + m_component_ptr.Edge() if it exists, or nullptr. + */ + ON_SubDEdge* BaseEdge() const + { + if (m_component_ptr.m_ptr == 0) return nullptr; + return m_component_ptr.IsEdge() ? m_component_ptr.Edge() : nullptr; + } + private: void Internal_Init( @@ -22064,7 +22235,7 @@ public: #if defined(ON_COMPILING_OPENNURBS) /* The ON_SubDAsUserData class is used to attach a subd to it proxy mesh -when writing V6 files in commercial rhino. +when writing prior than V6 files in commercial rhino. */ class ON_SubDMeshProxyUserData : public ON_UserData { diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp index 5595509a..4388947e 100644 --- a/opennurbs_subd_archive.cpp +++ b/opennurbs_subd_archive.cpp @@ -2408,7 +2408,7 @@ bool ON_SubDMeshProxyUserData::WriteToArchive( { for (;;) { - if (archive.Archive3dmVersion() < 60) + if (archive.Archive3dmVersion() >= 60) break; if (false == IsValid()) return false; diff --git a/opennurbs_sun.cpp b/opennurbs_sun.cpp index 84fb8c19..e95164e4 100644 --- a/opennurbs_sun.cpp +++ b/opennurbs_sun.cpp @@ -892,6 +892,11 @@ double ON_Sun::CImpl::North(void) const void ON_Sun::CImpl::SetNorth(double north) { + // 28th February 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-81036 + // Only set the north if it actually changes. + if (north == North()) + return; + if (nullptr != _earth_anchor_point) { // Store the north in the earth anchor point. This is more complicated than just setting one value. @@ -938,6 +943,11 @@ double ON_Sun::CImpl::Latitude(void) const void ON_Sun::CImpl::SetLatitude(double lat) { + // 28th February 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-81036 + // Only set the latitude if it actually changes. + if (lat == Latitude()) + return; + if (nullptr != _earth_anchor_point) { // Store the latitude in the earth anchor point. @@ -971,6 +981,11 @@ double ON_Sun::CImpl::Longitude(void) const void ON_Sun::CImpl::SetLongitude(double lon) { + // 28th February 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-81036 + // Only set the longitude if it actually changes. + if (lon == Longitude()) + return; + if (nullptr != _earth_anchor_point) { // Store the longitude in the earth anchor point. @@ -1538,6 +1553,12 @@ void ON_Sun::OnInternalXmlChanged(const ON_Sun* sun) SetLatitude(sun->Latitude()); SetLongitude(sun->Longitude()); } + + // 18th March 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-86536 + // Since the XML has been bulk-overwritten, we need to make sure the sun calculation is done next time + // Azimuth() or Altitude() is called. This bug was introduced by the fix for RH-81036 because that fix + // prevented code which had the side-effect of setting _calc_dirty from being executed. + _impl->_calc_dirty = true; } static const int SunVersion = 1; diff --git a/opennurbs_symmetry.cpp b/opennurbs_symmetry.cpp index c7acfe93..55dffae8 100644 --- a/opennurbs_symmetry.cpp +++ b/opennurbs_symmetry.cpp @@ -1360,6 +1360,7 @@ int ON_Symmetry::CompareSymmetryTransformation(const ON_Symmetry* lhs, const ON_ default: break; } + break; } return ON_Symmetry::Compare(lhs, rhs); diff --git a/opennurbs_xml.cpp b/opennurbs_xml.cpp index 20934e66..da9c343b 100644 --- a/opennurbs_xml.cpp +++ b/opennurbs_xml.cpp @@ -2174,6 +2174,13 @@ void ON_XMLNodePrivate::MoveBefore(ON_XMLNode& other) { pBeforeOther->_private->m_next_sibling = &m_node; } + + // 13th February 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-86050 + if (m_parent->_private->m_last_child == &m_node) + { + // 'this' was the tail; redirect the parent's last child. + m_parent->_private->m_last_child = pPrev; + } } void ON_XMLNodePrivate::MoveAfter(ON_XMLNode& other) @@ -2197,6 +2204,13 @@ void ON_XMLNodePrivate::MoveAfter(ON_XMLNode& other) m_parent->_private->m_first_child = m_next_sibling; } + // 13th February 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-86050 + if (m_parent->_private->m_last_child == &m_node) + { + // 'this' was the tail; redirect the parent's last child. + m_parent->_private->m_last_child = pPrev; + } + m_next_sibling = other._private->m_next_sibling; other._private->m_next_sibling = &m_node; @@ -3950,8 +3964,20 @@ const ON_XMLRootNode& ON_XMLUserData::XMLRootForRead(void) const return _private->m_XMLRoot.NodeForRead(); } -ON_XMLRootNode& ON_XMLUserData::XMLRootForWrite(void) const +ON_XMLRootNode& ON_XMLUserData::XMLRootForWrite(void) const // const is a mistake. [SDK_UNFREEZE] { + // 22nd January 2025 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-67878 + // Per conversation with Dale Lear, this is bad because we are actually going to change the user data while + // it's already attached to the attributes. We're actually expected to delete the old user data and create + // new user data with the changes in it, because otherwise Rhino can't know that it was changed. However, + // Dale suggested the easiest way to fix this is to just bump the copy count, because the optimization in + // CRhinoObject::ModifyAttributes() involving the copy count is really a hack anyway. + + if (m_userdata_copycount > 0) // Zero means we are not even copying user data. + { + const_cast(this)->m_userdata_copycount++; + } + return _private->m_XMLRoot.NodeForWrite(); } diff --git a/opennurbs_xml.h b/opennurbs_xml.h index 58a668b7..f5bd4d76 100644 --- a/opennurbs_xml.h +++ b/opennurbs_xml.h @@ -139,6 +139,8 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*); // Decals (stored in object attributes user data). +#define ON_RDK_USER_DATA_ROOT L"render-content-manager-data" + #define ON_RDK_DECALS L"decals" #define ON_RDK_DECAL L"decal" diff --git a/opennurbs_zlib.cpp b/opennurbs_zlib.cpp index 2ca0d40a..cbe93598 100644 --- a/opennurbs_zlib.cpp +++ b/opennurbs_zlib.cpp @@ -23,6 +23,13 @@ #include "opennurbs_zlib.h" +// RH-86025, RH3DM-179, 2025-02-14, Pierre: +// We only really need the #pragma comment(lib, "path") to work when we are using +// MSVC and the Microsoft linker, but not defining include dirs in build properties. +// Other compilers do not recognize this pragma and instead use build properties to link to libs. +// ON_CMAKE_BUILD should be renamed "ON_NOT_USING_MSVC_LINK_LIB_PRAGMA" +// This whole thing should be reworked so MSVC builds also use build properties, +// instead of this non-portable pragma. #if defined(ON_COMPILER_MSC) && !defined(ON_CMAKE_BUILD) #if !defined(OPENNURBS_ZLIB_LIB_DIR) @@ -35,9 +42,8 @@ #else // Define OPENNURBS_ZLIB_LIB_DIR to be the directory containing zlib.lib #error You must define OPENNURBS_ZLIB_LIB_DIR -#endif +#endif // defined(OPENNURBS_INPUT_LIBS_DIR) -#endif #if defined(_LIB) && defined(_MT) && !defined(_DLL) @@ -48,9 +54,11 @@ // using Microsoft DLL C-runtime #pragma message ( "Linking with zlib.lib in " OPENNURBS_PP2STR(OPENNURBS_ZLIB_LIB_DIR) ) #pragma comment(lib, "\"" OPENNURBS_ZLIB_LIB_DIR "/" "zlib.lib" "\"") -#endif +#endif // defined(_LIB) && defined(_MT) && !defined(_DLL) -#endif +#endif // !defined(OPENNURBS_ZLIB_LIB_DIR) + +#endif // defined(ON_COMPILER_MSC) && !defined(ON_CMAKE_BUILD) // compressed buffer I/O uses zlib 1.1.3 inflate()/deflate() class ON_CompressorImplementation