diff --git a/opennurbs_3dm_settings.cpp b/opennurbs_3dm_settings.cpp
index 4345c6c7..63413aba 100644
--- a/opennurbs_3dm_settings.cpp
+++ b/opennurbs_3dm_settings.cpp
@@ -1003,6 +1003,8 @@ const ON_3dmRenderSettingsPrivate& ON_3dmRenderSettingsPrivate::operator = (cons
_sun ->OnInternalXmlChanged(p._sun );
_post_effects ->OnInternalXmlChanged(p._post_effects );
+#ifdef _DEBUG // See https://mcneel.myjetbrains.com/youtrack/issue/RH-77284
+
// Check that all the document objects now have matching properties.
ON_ASSERT(*_ground_plane == *p._ground_plane);
ON_ASSERT(*_dithering == *p._dithering);
@@ -1016,6 +1018,7 @@ const ON_3dmRenderSettingsPrivate& ON_3dmRenderSettingsPrivate::operator = (cons
// We can't check post effects because they may be different in terms of the _is_populated flag.
// After a lot of thinking, I simply cannot figure out how to solve this.
//_ASSERT(*_post_effects == *p._post_effects);
+#endif
}
return *this;
diff --git a/opennurbs_brep.h b/opennurbs_brep.h
index 3f180e66..1cc081e1 100644
--- a/opennurbs_brep.h
+++ b/opennurbs_brep.h
@@ -1809,6 +1809,8 @@ public:
mesh_list - [out] meshes are appended to this array.
Returns:
Number of meshes appended to mesh_list[] array.
+ Note:
+ This function is not thread safe.
*/
int CreateMesh(
const ON_MeshParameters& mp,
diff --git a/opennurbs_color.cpp b/opennurbs_color.cpp
index 5de13a69..351fa690 100644
--- a/opennurbs_color.cpp
+++ b/opennurbs_color.cpp
@@ -310,45 +310,60 @@ void ON_Color::SetHSV(
double value // value
)
{
- int i;
- double f, p, q, t, r, g, b;
- if ( saturation <= 1.0/256.0 ) {
- r = value;
- g = value;
- b = value;
- }
- else {
- hue *= 3.0 / ON_PI; // (6.0 / 2.0 * ON_PI);
- i = (int)floor(hue);
- if ( i < 0 || i > 5 ) {
- hue = fmod(hue,6.0);
- if ( hue < 0.0 )
- hue += 6.0;
- i = (int)floor(hue);
- }
- f = hue - i;
- p = value * ( 1.0 - saturation);
- q = value * ( 1.0 - ( saturation * f) );
- t = value * ( 1.0 - ( saturation * ( 1.0 - f) ) );
- switch( i)
+ if ( hue > ON_UNSET_FLOAT && hue < ON_UNSET_POSITIVE_FLOAT
+ && saturation > ON_UNSET_FLOAT && saturation < ON_UNSET_POSITIVE_FLOAT
+ && value > ON_UNSET_FLOAT && value < ON_UNSET_POSITIVE_FLOAT
+ )
+ {
+ double r, g, b;
+ if (value < 0.0)
+ value = 0.0;
+ else if (value > 1.0)
+ value = 1.0;
+ if (saturation <= 1.0 / 256.0)
{
- case 0:
- r = value; g = t; b = p; break;
- case 1:
- r = q; g = value; b = p; break;
- case 2:
- r = p; g = value; b = t; break;
- case 3:
- r = p; g = q; b = value; break;
- case 4:
- r = t; g = p; b = value; break;
- case 5:
- r = value; g = p; b = q; break;
- default:
- r = 0; g = 0; b = 0; break; // to keep lint quiet
+ r = value;
+ g = value;
+ b = value;
}
+ else
+ {
+ if (saturation > 1.0)
+ saturation = 1.0;
+ hue *= 3.0 / ON_PI; // (6.0 / 2.0 * ON_PI);
+ int i = (int)floor(hue);
+ if (i < 0 || i > 5) {
+ hue = fmod(hue, 6.0);
+ if (hue < 0.0)
+ hue += 6.0;
+ i = (int)floor(hue);
+ }
+ const double f = hue - i;
+ const double p = value * (1.0 - saturation);
+ const double q = value * (1.0 - (saturation * f));
+ const double t = value * (1.0 - (saturation * (1.0 - f)));
+ switch (i)
+ {
+ case 0:
+ r = value; g = t; b = p; break;
+ case 1:
+ r = q; g = value; b = p; break;
+ case 2:
+ r = p; g = value; b = t; break;
+ case 3:
+ r = p; g = q; b = value; break;
+ case 4:
+ r = t; g = p; b = value; break;
+ case 5:
+ r = value; g = p; b = q; break;
+ default:
+ r = 0; g = 0; b = 0; break; // to keep lint quiet
+ }
+ }
+ SetFractionalRGB(r, g, b);
}
- SetFractionalRGB(r,g,b);
+ else
+ *this = ON_Color::UnsetColor;
}
diff --git a/opennurbs_color.h b/opennurbs_color.h
index 16689447..f759f1c8 100644
--- a/opennurbs_color.h
+++ b/opennurbs_color.h
@@ -260,6 +260,9 @@ public:
///
/// Specify a color using HSV (hue, saturation, value).
+ /// If h, s or v are not in the open interval
+ /// (ON_UNSET_FLOAT,ON_UNSET_POSITIVE_FLOAT),
+ /// then this color is set to ON_Color::UnsetColor.
///
///
/// hue in radians
diff --git a/opennurbs_embedded_file.cpp b/opennurbs_embedded_file.cpp
index 34321343..62b8d06d 100644
--- a/opennurbs_embedded_file.cpp
+++ b/opennurbs_embedded_file.cpp
@@ -38,6 +38,7 @@ public:
std::unique_ptr m_buffer;
size_t m_length = 0;
size_t m_compressed_length = 0;
+ bool m_error = false;
};
bool LoadFile(const wchar_t* filename);
@@ -66,6 +67,8 @@ const ON_EmbeddedFile::CImpl::Data& ON_EmbeddedFile::CImpl::Data::operator = (co
m_length = m_compressed_length = 0;
}
+ m_error = d.m_error;
+
return *this;
}
@@ -103,6 +106,8 @@ bool ON_EmbeddedFile::CImpl::LoadFile(const wchar_t* filename)
// Read the file data into the buffer.
const bool bOK = (ON_FileStream::Read(pFile, d.m_length, d.m_buffer.get()) == d.m_length);
+ d.m_error = !bOK;
+
// Close the file.
ON_FileStream::Close(pFile);
@@ -111,6 +116,9 @@ bool ON_EmbeddedFile::CImpl::LoadFile(const wchar_t* filename)
bool ON_EmbeddedFile::CImpl::SaveFile(const wchar_t* filename) const
{
+ if (m_data.m_error)
+ return false; // Can't save when in error state.
+
if (0 == m_data.m_length)
return false; // Not loaded.
@@ -209,14 +217,19 @@ bool ON_EmbeddedFile::LoadFromBuffer(ON_Buffer& buf)
d.SetLength(size_t(buf.Size()));
// Load the buffer from 'buf'.
- if (buf.Read(d.m_length, d.m_buffer.get()) != d.m_length)
- return false;
+ if (buf.Read(d.m_length, d.m_buffer.get()) == d.m_length)
+ return true;
- return true;
+ m_impl->m_data.m_error = true;
+
+ return false;
}
bool ON_EmbeddedFile::SaveToBuffer(ON_Buffer& buf) const
{
+ if (m_impl->m_data.m_error)
+ return false; // Can't save when in error state.
+
// Write the data to 'buf'.
buf.Write(m_impl->m_data.m_length, m_impl->m_data.m_buffer.get());
@@ -230,14 +243,20 @@ bool ON_EmbeddedFile::Read(ON_BinaryArchive& archive)
// Read the full file path of the original file.
ON_wString filename;
if (!archive.ReadString(filename))
+ {
+ m_impl->m_data.m_error = true;
return false;
+ }
m_impl->m_orig_file = ON_FileSystemPath::CleanPath(filename);
// Read the original (uncompressed) size of the compressed buffer.
size_t uncompressed_size = 0;
if (!archive.ReadCompressedBufferSize(&uncompressed_size))
+ {
+ m_impl->m_data.m_error = true;
return false;
+ }
// Allocate a buffer for the uncompressed data.
auto& d = m_impl->m_data;
@@ -249,7 +268,10 @@ bool ON_EmbeddedFile::Read(ON_BinaryArchive& archive)
const ON__UINT64 pos_before = archive.CurrentPosition();
if (!archive.ReadCompressedBuffer(uncompressed_size, d.m_buffer.get(), &bFailedCRC) && !bFailedCRC)
- return false;
+ {
+ m_impl->m_data.m_error = true;
+ return false;
+ }
d.m_compressed_length = size_t(archive.CurrentPosition() - pos_before);
@@ -259,6 +281,8 @@ bool ON_EmbeddedFile::Read(ON_BinaryArchive& archive)
bool ON_EmbeddedFile::Write(ON_BinaryArchive& archive) const
{
auto& d = m_impl->m_data;
+ if (d.m_error)
+ return false; // Can't write when in error state.
// Write the original filename to the archive.
if (!archive.WriteString(m_impl->m_orig_file))
@@ -281,6 +305,11 @@ size_t ON_EmbeddedFile::CompressedLength(void) const
return m_impl->m_data.m_compressed_length;
}
+bool ON_EmbeddedFile::Error(void) const
+{
+ return m_impl->m_data.m_error;
+}
+
bool ON_EmbeddedFile::Clear(void)
{
m_impl->m_orig_file.Empty();
@@ -288,6 +317,8 @@ bool ON_EmbeddedFile::Clear(void)
m_impl->m_data.SetLength(0);
m_impl->m_data.m_compressed_length = 0;
+ m_impl->m_data.m_error = false;
+
return true;
}
diff --git a/opennurbs_embedded_file.h b/opennurbs_embedded_file.h
index ea979f7d..c8056b82 100644
--- a/opennurbs_embedded_file.h
+++ b/opennurbs_embedded_file.h
@@ -72,6 +72,10 @@ public:
// loaded by LoadFromFile() or LoadFromBuffer(), this method returns zero.
virtual size_t CompressedLength(void) const;
+ // Returns true if the embedded file was loaded, but the load failed. This should only happen
+ // if a buffer or archive being loaded is corrupted.
+ bool Error(void) const;
+
// Clears the embedded file data. Returns true if successful, else false.
virtual bool Clear(void);
diff --git a/opennurbs_extensions.cpp b/opennurbs_extensions.cpp
index 4bb9b215..9ee8a295 100644
--- a/opennurbs_extensions.cpp
+++ b/opennurbs_extensions.cpp
@@ -5429,11 +5429,10 @@ bool ONX_ModelPrivate::GetEntireRDKDocument(const ONX_Model_UserData& docud, ON_
// Create an ON_EmbeddedFile object for each embedded file.
for (int i = 0; i < num_embedded_files; i++)
{
+ // We keep the embedded file object even if it fails to load; then it will have an error flag set.
+ // See ON_EmbeddedFile::Error().
ON_EmbeddedFile ef;
-
- if (!ef.Read(archive))
- return false;
-
+ ef.Read(archive);
model->AddModelComponent(ef);
}
}
diff --git a/opennurbs_planesurface.cpp b/opennurbs_planesurface.cpp
index eb8b18cd..12820578 100644
--- a/opennurbs_planesurface.cpp
+++ b/opennurbs_planesurface.cpp
@@ -991,9 +991,74 @@ bool ON_ClippingPlaneData::HasDefaultContent() const
return true;
}
-static ON_ClassArray g_data_list;
+class ON_ClippingPlaneDataList
+{
+public:
+ ON_ClippingPlaneDataList() = default;
+ ~ON_ClippingPlaneDataList();
+
+ ON_ClippingPlaneData* AppendNew();
+ void DeleteEntry(unsigned int sn);
+ ON_ClippingPlaneData* FromSerialNumber(unsigned int sn);
+private:
+ ON_SimpleArray m_list;
+};
+
+static ON_ClippingPlaneDataList g_data_list;
static ON_SleepLock g_data_list_lock;
+ON_ClippingPlaneDataList::~ON_ClippingPlaneDataList()
+{
+ for(int i=0; im_sn == sn)
+ {
+ delete data;
+ m_list.Remove(i);
+ return;
+ }
+ }
+}
+
+ON_ClippingPlaneData* ON_ClippingPlaneDataList::AppendNew()
+{
+ static unsigned int serial_number = 1;
+ ON_ClippingPlaneData* data = new ON_ClippingPlaneData();
+ m_list.Append(data);
+ data->m_sn = serial_number++;
+ return data;
+}
+
+ON_ClippingPlaneData* ON_ClippingPlaneDataList::FromSerialNumber(unsigned int sn)
+{
+ if (0==sn)
+ return nullptr;
+
+ // TODO: use binary search
+ int count = m_list.Count();
+ for (int i=0; im_sn == sn)
+ return data;
+ }
+ return nullptr;
+}
+
+/*
static int CompareClippingPlaneData(const ON_ClippingPlaneData* a, const ON_ClippingPlaneData* b)
{
if (a && b)
@@ -1010,25 +1075,16 @@ static int CompareClippingPlaneData(const ON_ClippingPlaneData* a, const ON_Clip
return 1;
return 0;
}
-
-static int ClippingPlaneDataIndex(unsigned int serialNumber)
-{
- if (0==serialNumber)
- return -1;
- ON_ClippingPlaneData data;
- data.m_sn = serialNumber;
- int index = g_data_list.BinarySearch(&data, CompareClippingPlaneData);
- return index;
-}
+ */
static void DeleteClippingPlaneData(ON_ClippingPlaneDataStore& dataStore)
{
- if (dataStore.m_sn>0)
+ const unsigned int serial_number = dataStore.m_sn;
+ if (serial_number>0)
{
bool bReturnLock = g_data_list_lock.GetLock();
- int index = ClippingPlaneDataIndex(dataStore.m_sn);
dataStore.m_sn = 0;
- g_data_list.Remove(index);
+ g_data_list.DeleteEntry(serial_number);
if(bReturnLock)
g_data_list_lock.ReturnLock();
}
@@ -1040,8 +1096,7 @@ static ON_ClippingPlaneData* GetClippingPlaneData(unsigned int sn)
return nullptr;
bool bReturnLock = g_data_list_lock.GetLock();
- int index = ClippingPlaneDataIndex(sn);
- ON_ClippingPlaneData* rc = g_data_list.At(index);
+ ON_ClippingPlaneData* rc = g_data_list.FromSerialNumber(sn);
if(bReturnLock)
g_data_list_lock.ReturnLock();
return rc;
@@ -1050,19 +1105,13 @@ static ON_ClippingPlaneData* GetClippingPlaneData(unsigned int sn)
static ON_ClippingPlaneData* GetClippingPlaneData(ON_ClippingPlaneDataStore& dataStore, bool createIfMissing)
{
bool bReturnLock = g_data_list_lock.GetLock();
- int index = ClippingPlaneDataIndex(dataStore.m_sn);
- ON_ClippingPlaneData* rc = g_data_list.At(index);
+
+ ON_ClippingPlaneData* rc = g_data_list.FromSerialNumber(dataStore.m_sn);
if (nullptr==rc && createIfMissing)
{
- unsigned int serial_number = 1;
- const ON_ClippingPlaneData* last = g_data_list.Last();
- if (last)
- serial_number = last->m_sn + 1;
-
- ON_ClippingPlaneData& data = g_data_list.AppendNew();
- data.m_sn = serial_number;
- dataStore.m_sn = data.m_sn;
- rc = g_data_list.Last();
+ rc = g_data_list.AppendNew();
+ if (rc)
+ dataStore.m_sn = rc->m_sn;
}
if(bReturnLock)
g_data_list_lock.ReturnLock();
diff --git a/opennurbs_planesurface.h b/opennurbs_planesurface.h
index 77de5bef..aa0f0076 100644
--- a/opennurbs_planesurface.h
+++ b/opennurbs_planesurface.h
@@ -438,7 +438,7 @@ public:
domain.
Parameters:
dir - [in] 0 sets plane's x coordinate extents
- 0 sets plane's y coordinate extents
+ 1 sets plane's y coordinate extents
extents - [in] increasing interval
bSynchDomain - [in] if true, the corresponding evaluation interval
domain is set so that it matches the extents interval
@@ -458,7 +458,7 @@ public:
Gets the extents of the rectangle.
Parameters:
dir - [in] 0 gets plane's x coordinate extents
- 0 gets plane's y coordinate extents
+ 1 gets plane's y coordinate extents
Returns:
Increasing interval
See Also:
diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h
index 6f80424b..62ed2a66 100644
--- a/opennurbs_public_version.h
+++ b/opennurbs_public_version.h
@@ -14,10 +14,10 @@
// first step in each build.
//
#define RMA_VERSION_YEAR 2023
-#define RMA_VERSION_MONTH 8
-#define RMA_VERSION_DATE 22
-#define RMA_VERSION_HOUR 12
-#define RMA_VERSION_MINUTE 13
+#define RMA_VERSION_MONTH 10
+#define RMA_VERSION_DATE 31
+#define RMA_VERSION_HOUR 13
+#define RMA_VERSION_MINUTE 30
////////////////////////////////////////////////////////////////
//
@@ -35,8 +35,8 @@
// 3 = build system release build
#define RMA_VERSION_BRANCH 0
-#define VERSION_WITH_COMMAS 8,0,23234,12130
-#define VERSION_WITH_PERIODS 8.0.23234.12130
+#define VERSION_WITH_COMMAS 8,0,23304,13300
+#define VERSION_WITH_PERIODS 8.0.23304.13300
#define COPYRIGHT "Copyright (C) 1993-2023, Robert McNeel & Associates. All Rights Reserved."
#define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library."
@@ -47,8 +47,8 @@
#define RMA_VERSION_NUMBER_SR_STRING "SR0"
#define RMA_VERSION_NUMBER_SR_WSTRING L"SR0"
-#define RMA_VERSION_WITH_PERIODS_STRING "8.0.23234.12130"
-#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.23234.12130"
+#define RMA_VERSION_WITH_PERIODS_STRING "8.0.23304.13300"
+#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.23304.13300"
diff --git a/opennurbs_sha1.cpp b/opennurbs_sha1.cpp
index afe72cd6..3b9991e3 100644
--- a/opennurbs_sha1.cpp
+++ b/opennurbs_sha1.cpp
@@ -23,26 +23,17 @@
ON_SHA1_Hash::ON_SHA1_Hash()
{
- ON__UINT32* p = (ON__UINT32*)m_digest;
- p[0] = 0U;
- p[1] = 0U;
- p[2] = 0U;
- p[3] = 0U;
- p[4] = 0U;
+ memset(m_digest, 0, sizeof(m_digest));
}
bool operator==(const ON_SHA1_Hash& a, const ON_SHA1_Hash& b)
{
- const ON__UINT32* ai = (const ON__UINT32*)&a;
- const ON__UINT32* bi = (const ON__UINT32*)&b;
- return (ai[0] == bi[0] && ai[1] == bi[1] && ai[2] == bi[2] && ai[3] == bi[3] && ai[4] == bi[4]);
+ return memcmp(a.m_digest, b.m_digest, sizeof(a.m_digest)) == 0;
}
bool operator!=(const ON_SHA1_Hash& a, const ON_SHA1_Hash& b)
{
- const ON__UINT32* ai = (const ON__UINT32*)&a;
- const ON__UINT32* bi = (const ON__UINT32*)&b;
- return (ai[0] != bi[0] || ai[1] != bi[1] || ai[2] != bi[2] || ai[3] != bi[3] || ai[4] != bi[4]);
+ return memcmp(a.m_digest, b.m_digest, sizeof(a.m_digest)) != 0;
}
const ON_String ON_SHA1_Hash::ToUTF8String(
diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp
index ab3a3eb0..b4532a65 100644
--- a/opennurbs_subd.cpp
+++ b/opennurbs_subd.cpp
@@ -8747,11 +8747,11 @@ unsigned int ON_SubD::DumpTopology(
if (false == text_log.IsTextHash())
{
- text_log.Print(L"Fragment per vertex color settings:\n");
+ text_log.Print(L"Per vertex color settings:\n");
{
ON_TextLogIndent indent1(text_log);
- text_log.Print(L"FragmentColorsMappingTag() = ");
- const ON_MappingTag colors_tag = this->FragmentColorsMappingTag();
+ text_log.Print(L"ColorsMappingTag() = ");
+ const ON_MappingTag colors_tag = this->ColorsMappingTag();
const ON_TextLog::LevelOfDetail lod = text_log.DecreaseLevelOfDetail();
colors_tag.Dump(text_log);
text_log.SetLevelOfDetail(lod);
@@ -10366,11 +10366,10 @@ unsigned int ON_SubDLevel::DumpTopology(
if (bNeedComma)
text_log.Print(", ");
else
- text_log.Print("Per face");
+ text_log.Print("Per face ");
bNeedComma = true;
- text_log.Print("face color=(");
- text_log.PrintColor(per_face_color);
- text_log.Print(")");
+ text_log.Print("color=");
+ per_face_color.ToText(ON_Color::TextFormat::HashRGBa, 0, true, text_log);
}
const int per_face_material_channel_index = f->MaterialChannelIndex();
@@ -10379,9 +10378,9 @@ unsigned int ON_SubDLevel::DumpTopology(
if (bNeedComma)
text_log.Print(", ");
else
- text_log.Print("Per face");
+ text_log.Print("Per face ");
bNeedComma = true;
- text_log.Print(" material channel index=%d", per_face_material_channel_index);
+ text_log.Print("material channel index=%d", per_face_material_channel_index);
}
if (bNeedComma)
text_log.PrintNewLine();
@@ -17429,27 +17428,39 @@ bool ON_SubD::CopyEvaluationCacheForExperts(const ON_SubD& src)
{
const ON_SubDimple* src_subdimple = src.m_subdimple_sp.get();
ON_SubDimple* this_subdimple = m_subdimple_sp.get();
- return (nullptr != src_subdimple && nullptr != this_subdimple) ? this_subdimple->CopyEvaluationCacheForExperts(*src_subdimple) : false;
+ const bool bCopied
+ = (nullptr != src_subdimple && nullptr != this_subdimple)
+ ? this_subdimple->CopyEvaluationCacheForExperts(*src_subdimple)
+ : false;
+ if (bCopied)
+ {
+ if (this->HasFragmentTextureCoordinates())
+ this_subdimple->Internal_SetFragmentTextureCoordinatesTextureSettingsHash(src_subdimple->FragmentColorsSettingsHash());
+ if (this->HasFragmentColors())
+ this_subdimple->Internal_SetFragmentColorsSettingsHash(src_subdimple->FragmentColorsSettingsHash());
+ }
+ return bCopied;
}
bool ON_SubDimple::CopyEvaluationCacheForExperts(const ON_SubDimple& src)
{
const ON_SubDLevel* src_level = src.ActiveLevelConstPointer();
ON_SubDLevel* this_level = this->ActiveLevelPointer();
- bool bFragmentsWereCopied = false;
- const bool bCopied = (nullptr != src_level && nullptr != this_level) ? this_level->CopyEvaluationCacheForExperts(this->m_heap , *src_level, src.m_heap, bFragmentsWereCopied) : false;
- if (bFragmentsWereCopied)
+ unsigned fragment_status = 0u;
+ const bool bCopied = (nullptr != src_level && nullptr != this_level) ? this_level->CopyEvaluationCacheForExperts(this->m_heap , *src_level, src.m_heap, fragment_status) : false;
+ if (0 != (1u& fragment_status))
{
- this->m_fragment_colors_mapping_tag = src.m_fragment_colors_mapping_tag;
- this->m_fragment_texture_settings_hash = src.m_fragment_texture_settings_hash;
- this->m_fragment_colors_settings_hash = src.m_fragment_colors_settings_hash;
+ if (2 != (1u & fragment_status))
+ this->m_fragment_texture_settings_hash = src.m_fragment_texture_settings_hash;
+ if (8 != (1u & fragment_status))
+ this->m_fragment_colors_settings_hash = src.m_fragment_colors_settings_hash;
}
return bCopied;
}
-bool ON_SubDLevel::CopyEvaluationCacheForExperts( ON_SubDHeap& this_heap, const ON_SubDLevel& src, const ON_SubDHeap& src_heap, bool& bFragmentsWereCopied)
+bool ON_SubDLevel::CopyEvaluationCacheForExperts( ON_SubDHeap& this_heap, const ON_SubDLevel& src, const ON_SubDHeap& src_heap, unsigned& copy_status)
{
- bFragmentsWereCopied = false;
+ copy_status = 0u;
// Validate conditions for coping the cached evaluation information
if (
this == &src
@@ -17713,8 +17724,17 @@ bool ON_SubDLevel::CopyEvaluationCacheForExperts( ON_SubDHeap& this_heap, const
this_face->SetSavedSubdivisionPoint(subdivision_point);
if (nullptr == this_face->MeshFragments() && nullptr != src_face->MeshFragments())
{
- if (nullptr != this_heap.CopyMeshFragments(src_face, subd_display_density, this_face))
- bFragmentsWereCopied = true;
+ const ON_SubDMeshFragment* this_frag = this_heap.CopyMeshFragments(src_face, subd_display_density, this_face);
+ if (nullptr != this_frag)
+ {
+ copy_status |= 1u;
+ if (this_frag->TextureCoordinateCount() > 0)
+ copy_status |= 2u;
+ if (this_frag->CurvatureCount() > 0)
+ copy_status |= 4u;
+ if (this_frag->ColorCount() > 0)
+ copy_status |= 8u;
+ }
}
}
}
diff --git a/opennurbs_subd.h b/opennurbs_subd.h
index 880a07a2..cb8f5cb7 100644
--- a/opennurbs_subd.h
+++ b/opennurbs_subd.h
@@ -8171,6 +8171,69 @@ public:
*/
void ClearFragmentTextureCoordinatesTextureSettingsHash() const;
+ ///
+ /// Determing if this SubD's mesh fragments have per vertex texture coordinates.
+ ///
+ ///
+ /// If this SubD has mesh fragments with per vertex texture coordinates, then true is returned.
+ /// Otherwise false is returned.
+ ///
+ bool HasFragmentTextureCoordinates() const;
+
+ ///
+ /// This tag identifies the method and computation used to set the
+ /// per vertex texture coordinates on the fragments. The tag is persistent so
+ /// that the texture coordinates can be recomputed from the id in situations where
+ /// fragments need to be recalculated.
+ ///
+ ///
+ /// If this SubD has mesh fragments with per vertex texture coordinates and
+ /// texture_mapping_tag = TextureMappingTag(), then true is returned.
+ /// Otherwise false is returned.
+ ///
+ bool HasFragmentTextureCoordinates(
+ ON_MappingTag texture_mapping_tag
+ ) const;
+
+ ///
+ /// This hash uniquely identifies the method and computation used to
+ /// set the per vertex texture coordinates on the fragments. The hash is a runtime
+ /// value that has meaning only when fragments with per vertex texture coordinates
+ /// exist.
+ ///
+ ///
+ /// If this SubD has mesh fragments with per vertex texture coordinates and
+ /// texture_settings_hash = TextureSettingsHash(), then true is returned.
+ /// Otherwise false is returned.
+ ///
+ bool HasFragmentTextureCoordinates(
+ ON_SHA1_Hash texture_settings_hash
+ ) const;
+
+ ///
+ /// This hash uniquely identifies the method and computation used to
+ /// set the per vertex texture coordinates on the fragments. The hash is a runtime
+ /// value that has meaning only when fragments with per vertex texture coordinates
+ /// exist.
+ ///
+ ///
+ /// This tag identifies the method and computation used to set the
+ /// per vertex texture coordinates on the fragments. The tag is persistent so
+ /// that the texture coordinates can be recomputed from the id in situations where
+ /// fragments need to be recalculated.
+ ///
+ ///
+ /// If this SubD has mesh fragments with per vertex texture coordinates and
+ /// texture_settings_hash = TextureSettingsHash() and
+ /// texture_mapping_tag = TextureMappingTag(), then true is returned.
+ /// Otherwise false is returned.
+ ///
+ bool HasFragmentTextureCoordinates(
+ ON_SHA1_Hash texture_settings_hash,
+ ON_MappingTag texture_mapping_tag
+ ) const;
+
+
private:
/*
Description:
@@ -8247,17 +8310,27 @@ public:
///
bool HasFragmentColors() const;
- ///
+ ///
+ /// This tag identifies the method and computation used to set the
+ /// per vertex colors on the fragments. The tag is persistent so
+ /// that the colors can be recomputed from the id in situations where
+ /// fragments need to be recalculated.
+ ///
///
/// If this SubD has mesh fragments with per vertex colors and
/// color_tag = FragmentColorsMappingTag(), then true is returned.
/// Otherwise false is returned.
///
bool HasFragmentColors(
- ON_MappingTag color_tag
+ ON_MappingTag color_mapping_tag
) const;
- ///
+ ///
+ /// This hash uniquely identifies the method and computation used to
+ /// set the per vertex colors on the fragments. The has is a runtime
+ /// value that has meaning only when fragments with per vertex colors
+ /// exist.
+ ///
///
/// If this SubD has mesh fragments with per vertex colors and
/// color_settings_hash = FragmentColorsSettingsHash(), then true is returned.
@@ -8267,17 +8340,27 @@ public:
ON_SHA1_Hash color_settings_hash
) const;
- ///
- ///
+ ///
+ /// This hash uniquely identifies the method and computation used to
+ /// set the per vertex colors on the fragments. The has is a runtime
+ /// value that has meaning only when fragments with per vertex colors
+ /// exist.
+ ///
+ ///
+ /// This tag identifies the method and computation used to set the
+ /// per vertex colors on the fragments. The tag is persistent so
+ /// that the colors can be recomputed from the id in situations where
+ /// fragments need to be recalculated.
+ ///
///
/// If this SubD has mesh fragments with per vertex colors and
/// color_settings_hash = FragmentColorsSettingsHash() and
- /// color_tag = FragmentColorsMappingTag(), then true is returned.
+ /// color_mapping_tag = ColorsMappingTag(), then true is returned.
/// Otherwise false is returned.
///
bool HasFragmentColors(
ON_SHA1_Hash color_settings_hash,
- ON_MappingTag color_tag
+ ON_MappingTag color_mapping_tag
) const;
@@ -8293,33 +8376,39 @@ public:
bool bClearFragmentColorsMappingTag
);
+
+ /*
+ Returns:
+ This mapping tag ideitifies the color mapping used to set fragment per vertex colors.
+ */
+ const ON_MappingTag ColorsMappingTag() const;
+
+ /*
+ Description:
+ Set the colors mapping tag.
+ Remarks:
+ Calling this->SetColorsMappingTag() does not change existing cached
+ fragment vertex colors. At an appropriate time, call this->SetFragmentColorsFromCallback()
+ to update fragment vertex colors on any cached fragments.
+
+ The color mapping tag and per vertex colors are mutable properties.
+ They can be changed by rendering applications as needed.
+ */
+ void SetColorsMappingTag(const class ON_MappingTag&) const;
+
+
/*
Returns:
hash identifying the way the fragment vertex colors were set.
*/
const ON_SHA1_Hash FragmentColorsSettingsHash() const;
- /*
- Returns:
- The current fragment vertex colors mapping tag.
- */
+ ON_DEPRECATED_MSG("Use ON_SubD::ColorsMappingTag()")
const ON_MappingTag FragmentColorsMappingTag() const;
- /*
- Description:
- Set the fragment colors mapping tag.
- Remarks:
- Calling this->SetFragmentColorsMappingTag() does not change existing cached
- fragment vertex colors. At an appropriate time, call this->SetFragmentColorsFromCallback()
- to update fragment vertex colors on any cached fragments.
-
- SubD fragment vertex tag and colors are a mutable property.
- They can be changed by rendering applications as needed.
- */
+ ON_DEPRECATED_MSG("Use ON_SubD::SetColorsMappingTag()")
void SetFragmentColorsMappingTag(const class ON_MappingTag&) const;
-
-
public:
/*
Description:
@@ -8594,7 +8683,7 @@ public:
subd must point to an ON_SubD that was constructed on the heap using
an operator new call with a public ON_SubD constructor.
Returns:
- a pointer to the managed subd or nullptr subd in not valid.
+ a pointer to the managed subd or nullptr subd if not valid.
Example:
ON_SubD* subd = new ON_SubD(...);
ON_SubDRef subr;
@@ -10766,9 +10855,46 @@ public:
*/
ON_ComponentStatus Status() const;
+ ///
+ /// This simple version
+ /// transforms the points and normals and unconditionally
+ /// makes no changes to the curvatures, texture coordinates and colors.
+ ///
+ /// Typically lots of fragments are being transformed and
+ /// the type and context of the transformation determines
+ /// if texture coordinate, curvature and color inforation should be
+ /// preserved or destroyed. It is better to determine the answers to these
+ /// questions and call the version of Transform with
+ /// the bKeepTextures, bKeepCurvatures and bKeepColors parameters.
+ /// For example if the transformation is an isometry and the colors
+ /// are set from the curvatures, then curvatures and colors should be
+ /// kept. If the transformation is not an isometry, the curvatures should
+ /// be destroyed.
+ /// If the texture coordinates are set from grid location
+ /// (fake surface paramaters), the the texture coordinates should be kept.
+ /// If transform is not an identity and the texture coordinates come from a
+ /// world object mapping, the should generally be destroyed.
+ ///
+ ///
+ ///
bool Transform(
const ON_Xform& xform
- );
+ );
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ bool Transform(
+ bool bKeepTextures,
+ bool bKeepCurvatures,
+ bool bKeepColors,
+ const ON_Xform& xform
+ );
ON_SubDMeshFragment* m_next_fragment;
ON_SubDMeshFragment* m_prev_fragment;
@@ -12511,7 +12637,7 @@ protected:
mutable double m_saved_subd_point1[3]; // saved subdivision point
private:
- // Reserved for future use for attributes that apply to allSubD components (ON_SubDVertex, ON_SubDEdge, and ON_SubDFace).
+ // Reserved for future use for attributes that apply to all SubD components (ON_SubDVertex, ON_SubDEdge, and ON_SubDFace).
ON__UINT64 m_reserved8bytes1;
ON__UINT64 m_reserved8bytes2;
ON__UINT64 m_reserved8bytes3;
@@ -12770,9 +12896,8 @@ public:
bTransformationSavedSubdivisionPoint - [in]
If the transformation is being applied to every vertex, edge and
face in every level of a subdivision object, and the transformation
- is an isometry (rotation, translation, ...), a uniform scale, or a
- composition of these types, then set
- bTransformationSavedSubdivisionPoint = true to apply the
+ is an orientation preserving isometry (rotation, translation, ...),
+ then set bTransformationSavedSubdivisionPoint = true to apply the
transformation to saved subdivision and saved limit point information.
In all other cases, set bTransformationSavedSubdivisionPoint = false
and any saved subdivision points or saved limit points will be
@@ -13308,7 +13433,7 @@ public:
two attached edges are attached to one face,
the remaining edges are attached to two faces.
Returns:
- True if the vertex has interior vertex toplology.
+ True if the vertex has boundary vertex toplology.
Remarks:
Tags are ignored. This property is often used during construction
and modification when tags are not set.
@@ -13716,11 +13841,10 @@ public:
Parameters:
bTransformationSavedSubdivisionPoint - [in]
- If the transformation is being applied to every vertex, edge and
+ If the transformation is being applied to every vertex, edge and
face in every level of a subdivision object, and the transformation
- is an isometry (rotation, translation, ...), a uniform scale, or a
- composition of these types, then set
- bTransformationSavedSubdivisionPoint = true to apply the
+ is an orientation preserving isometry (rotation, translation, ...),
+ then set bTransformationSavedSubdivisionPoint = true to apply the
transformation to saved subdivision and saved limit point information.
In all other cases, set bTransformationSavedSubdivisionPoint = false
and any saved subdivision points or saved limit points will be
@@ -14610,9 +14734,8 @@ public:
bTransformationSavedSubdivisionPoint - [in]
If the transformation is being applied to every vertex, edge and
face in every level of a subdivision object, and the transformation
- is an isometry (rotation, translation, ...), a uniform scale, or a
- composition of these types, then set
- bTransformationSavedSubdivisionPoint = true to apply the
+ is an orientation preserving isometry (rotation, translation, ...),
+ then set bTransformationSavedSubdivisionPoint = true to apply the
transformation to saved subdivision and saved limit point information.
In all other cases, set bTransformationSavedSubdivisionPoint = false
and any saved subdivision points or saved limit points will be
@@ -15108,18 +15231,71 @@ public:
const class ON_SubDMeshFragment* MeshFragments() const;
+ ///
+ /// The face's control net center point is the average of the face's
+ /// vertex control net points. This is the same point as the face's
+ /// subdivision point.
+ ///
+ ///
+ /// The average of the face's vertex control net points
+ ///
const ON_3dPoint ControlNetCenterPoint() const;
+ ///
+ /// When the face's control net polygon is planar, the face's
+ /// control net normal is a unit vector perpindicular to the plane
+ /// that points outwards. If the control net polygon is not
+ /// planar, the control net normal is control net normal is a unit
+ /// vector that is the average of the control polygon's corner normals.
+ ///
+ ///
+ /// A unit vector that is normal to planar control net polygons and a good
+ /// compromise for nonplanar control net polygons.
+ ///
const ON_3dVector ControlNetCenterNormal() const;
+ ///
+ /// The face's control net center frame is a plane
+ /// with normal equal to this->ControlNetCenterNormal()
+ /// and origin equal to this->ControlNetCenterPoint().
+ /// The x and y axes of the frame have no predictable relationship
+ /// to the face or SubD control net topology.
+ ///
+ ///
+ /// A plane with unit normal equal to this->ControlNetCenterNormal()
+ /// and origin equal to this->ControlNetCenterPoint().
+ /// If the face is not valid, ON_Plane::NanPlane is returned.
+ ///
const ON_Plane ControlNetCenterFrame() const;
+ ///
+ /// True if the control net polygon is convex with respect to the
+ /// plane this->ControlNetCenterFrame().
+ ///
bool IsConvex() const;
+ ///
+ /// True if the control net polygon is not convex with respect to the
+ /// plane this->ControlNetCenterFrame().
+ ///
bool IsNotConvex() const;
+ ///
+ /// Determine if the face's control net polygon is planar.
+ ///
+ ///
+ ///
+ /// True if the face's control net polygon is planar.
+ ///
bool IsPlanar(double planar_tolerance = ON_ZERO_TOLERANCE) const;
+ ///
+ /// Determine if the face's control net polygon is not planar.
+ ///
+ ///
+ ///
+ /// True if the face's control net polygon is not planar.
+ ///
bool IsNotPlanar(double planar_tolerance = ON_ZERO_TOLERANCE) const;
public:
diff --git a/opennurbs_subd_copy.cpp b/opennurbs_subd_copy.cpp
index b64f2943..fc0b723e 100644
--- a/opennurbs_subd_copy.cpp
+++ b/opennurbs_subd_copy.cpp
@@ -866,13 +866,13 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src)
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_colors_mapping_tag = src.m_colors_mapping_tag;
// NOTE WELL: (Dale Lear Aug 2023)
- // Fragment settings like the three m_fragment_... values
+ // Fragment settings like the two m_fragment_... values
// should be copied only if the mesh fragments are copied
// and that happens conditionally and happens later
// if ON_SubDimple::CopyEvaluationCacheForExperts() is called.
- // NO // m_fragment_colors_mapping_tag = src.m_fragment_colors_mapping_tag;
// NO // m_fragment_texture_settings_hash = src.m_fragment_texture_settings_hash;
// NO // m_fragment_colors_settings_hash = src.m_fragment_colors_settings_hash;
diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp
index 784a0715..ba7eef62 100644
--- a/opennurbs_subd_data.cpp
+++ b/opennurbs_subd_data.cpp
@@ -50,9 +50,11 @@ void ON_SubDimple::Clear()
m_subd_appearance = ON_SubD::DefaultSubDAppearance;
m_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset;
m_texture_mapping_tag = ON_MappingTag::Unset;
- m_fragment_colors_mapping_tag = ON_MappingTag::Unset;
+ m_colors_mapping_tag = ON_MappingTag::Unset;
+
m_fragment_texture_settings_hash = ON_SHA1_Hash::EmptyContentHash;
m_fragment_colors_settings_hash = ON_SHA1_Hash::EmptyContentHash;
+
for (unsigned i = 0; i < m_levels.UnsignedCount(); ++i)
{
ON_SubDLevel* level = m_levels[i];
@@ -638,27 +640,33 @@ bool ON_SubDVertex::Transform(
{
TransformPoint(&xform.m_xform[0][0],m_P);
- Internal_TransformComponentBase(bTransformationSavedSubdivisionPoint, xform);
-
- // TODO:
- // If the vertex
- // is tagged as ON_SubDVertexTag::Corner
- // and bTransformationSavedSubdivisionPoint is true,
- // and the corner sector(s) contains interior smooth edges,
- // and the transformation changes the angle between a corner sector's crease boundary,
- // then the sector's interior smooth edge's m_sector_coefficient[] could change
- // and invalidate the subdivison points and limit points.
- // This is only possible for uncommon (in practice) transformations
- // and corner sectors and will require a fair bit of testing for
- // now it's easier to simply set bTransformationSavedSubdivisionPoint to false
- // at a higher level when these types of transformations are encountered.
- if ( bTransformationSavedSubdivisionPoint && Internal_SurfacePointFlag() )
+ if (bTransformationSavedSubdivisionPoint)
{
- for (const ON_SubDSectorSurfacePoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point)
- const_cast(lp)->Transform(xform);
+ // Transform saved subdivision point
+ Internal_TransformComponentBase(bTransformationSavedSubdivisionPoint, xform);
+
+ // NOTE WELL:
+ // If the vertex
+ // is tagged as ON_SubDVertexTag::Corner
+ // and bTransformationSavedSubdivisionPoint is true,
+ // and the corner sector(s) contains interior smooth edges,
+ // and the transformation changes the angle between a corner sector's crease boundary,
+ // then the sector's interior smooth edge's m_sector_coefficient[] could change
+ // and invalidate the subdivison points and limit points.
+ // This is only possible for uncommon (in practice) transformations
+ // and corner sectors and will require a fair bit of testing for
+ // now it's easier to simply set bTransformationSavedSubdivisionPoint to false
+ // at a higher level when these types of transformations are encountered.
+ if (bTransformationSavedSubdivisionPoint && Internal_SurfacePointFlag())
+ {
+ for (const ON_SubDSectorSurfacePoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point)
+ const_cast(lp)->Transform(xform);
+ }
+ else
+ Internal_ClearSurfacePointFlag();
}
else
- Internal_ClearSurfacePointFlag();
+ this->ClearSavedSubdivisionPoints();
return true;
}
@@ -669,6 +677,10 @@ void ON_SubDVertex::UnsetControlNetPoint()
m_P[1] = ON_DBL_QNAN;
m_P[2] = ON_DBL_QNAN;
ClearSavedSubdivisionPoints();
+ // With a nan control net point, there is no need for an expensive unset
+ // of the neighborhod because the caller will either later pass
+ // bClearNeighborhoodCache=true to ON_SubDVertex::SetControlNetPoint(...,bClearNeighborhoodCache)
+ // or deal with cleaning up the cached evaluations in some other way.
}
bool ON_SubDVertex::SetControlNetPoint(
@@ -679,54 +691,115 @@ bool ON_SubDVertex::SetControlNetPoint(
if (false == control_net_point.IsValid())
return false;
- if (!(m_P[0] == control_net_point.x && m_P[1] == control_net_point.y && m_P[2] == control_net_point.z))
+ if (false == (m_P[0] == control_net_point.x && m_P[1] == control_net_point.y && m_P[2] == control_net_point.z))
{
m_P[0] = control_net_point.x;
m_P[1] = control_net_point.y;
m_P[2] = control_net_point.z;
ClearSavedSubdivisionPoints();
- if (bClearNeighborhoodCache)
+ for(;;)
{
- for (unsigned short vei = 0; vei < m_edge_count; vei++)
+ if (false == bClearNeighborhoodCache)
+ break;
+
+ if (this->m_edge_count <= 0 || nullptr == this->m_edges)
+ break;
+
+ // need to clear 2 rings of faces around "this" vertex.
+
+ const bool bThisVertexIsACorner = ON_SubDVertexTag::Corner == this->m_vertex_tag;
+ for (unsigned short vei = 0; vei < this->m_edge_count; vei++)
{
- ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
+ const ON__UINT_PTR edgeptr = this->m_edges[vei].m_ptr;
+ const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edgeptr);
if (nullptr == edge)
continue;
- edge->ClearSavedSubdivisionPoints();
- ON_SubDFacePtr* fptr = edge->m_face2;
- for (unsigned short efi = 0; efi < edge->m_face_count; efi++, fptr++)
- {
- if (2 == efi)
- {
- fptr = edge->m_facex;
- if (nullptr == fptr)
- break;
- }
- ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr);
- if (nullptr == face)
- continue;
- face->ClearSavedSubdivisionPoints();
+ edge->ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags();
+ // v1 = vertex opposite this on edge
+ const ON_SubDVertex* v1 = edge->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(edgeptr)];
+ if (nullptr == v1)
+ continue;
- ON_SubDEdgePtr* eptr = face->m_edge4;
- for (unsigned short fei = 0; fei < face->m_edge_count; fei++, eptr++)
+ v1->ClearSavedSubdivisionPoints();
+
+ if (ON_SubDVertexTag::Smooth != v1->m_vertex_tag)
+ continue;
+ if (false == bThisVertexIsACorner)
+ continue;
+ if (false == edge->IsSmooth())
+ continue;
+ // When a corner vertex is moved, the sector coefficients
+ // for smooth edges can change because the corner sector
+ // coefficient depends on the angle between the creases that
+ // bound the sector. For any other tag, the sector
+ // coefficients depend only on the topology and tags and
+ // moving a control net point does not change those.
+ edge->UnsetSectorCoefficientsForExperts();
+ }
+
+ if (this->m_face_count <= 0 || nullptr == this->m_faces)
+ break;
+
+ //const ON_SubDFace* face = this->m_faces[m_face_count - 1];
+ for(unsigned short vfi = 0; vfi < m_face_count; vfi++)
+ {
+ //const ON_SubDFace* prevface = face;
+ const ON_SubDFace* face = this->m_faces[vfi];
+ if (nullptr == face)
+ continue;
+
+ // face->ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags() is fast
+ face->ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags();
+
+ const ON_SubDEdgePtr* face_eptr = face->m_edge4;
+ for (unsigned short fei = 0; fei < face->m_edge_count; fei++, face_eptr++)
+ {
+ if (4 == fei)
{
- if (4 == fei)
- {
- eptr = face->m_edgex;
- if (nullptr == eptr)
- break;
- }
- ON_SubDEdge* fedge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
- if (nullptr == fedge)
- continue;
- ON_SubDVertex* fvertex = const_cast(fedge->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)]);
- if (nullptr == fvertex)
- continue;
- fvertex->ClearSavedSubdivisionPoints();
+ face_eptr = face->m_edgex;
+ if (nullptr == face_eptr)
+ break;
+ }
+ const ON__UINT_PTR e1ptr = face_eptr->m_ptr;
+ ON_SubDEdge* e1 = ON_SUBD_EDGE_POINTER(e1ptr);
+ if (nullptr == e1)
+ continue;
+
+ // e1->ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags() is fast.
+ // There is no need to unset e1 sector coefficients.
+ e1->ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags();
+ const ON_SubDVertex* v1 = e1->m_vertex[ON_SUBD_EDGE_DIRECTION(e1ptr)];
+ if (this == v1 || nullptr == v1)
+ continue;
+ v1->ClearSavedSubdivisionPoints();
+
+ if (v1->m_edge_count <= 0 || nullptr == v1->m_edges)
+ continue;
+ for (unsigned short v1ei = 0; v1ei < v1->m_edge_count; v1ei++)
+ {
+ // e2 is sometime in ring 1, sometimes between ring 1 and 2,
+ // and sometimes in ring 2, but this is enough to clear the
+ // ring 2 edges that can be modified by moving "this" vertex.
+ const ON_SubDEdge* e2 = ON_SUBD_EDGE_POINTER(v1->m_edges[v1ei].m_ptr);
+ if (nullptr != e2)
+ e2->ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags();
+ }
+
+ if (v1->m_face_count <= 0 || nullptr == v1->m_faces)
+ continue;
+ for (unsigned short v1fi = 0; v1fi < v1->m_face_count; v1fi++)
+ {
+ // f2 is sometimes in ring 1 and sometimes in ring 2, but this
+ // is enough to clear the ring 2 faces that can be modified by
+ // moving "this" vertex.
+ const ON_SubDFace* f2 = v1->m_faces[v1fi];
+ if (nullptr != f2)
+ f2->ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags();
}
}
}
+ break;
}
}
@@ -764,15 +837,27 @@ bool ON_SubDFace::Transform(
const class ON_Xform& xform
)
{
- Internal_TransformComponentBase(bTransformationSavedSubdivisionPoint, xform);
-
- if (bTransformationSavedSubdivisionPoint && Internal_SurfacePointFlag() )
+ if (bTransformationSavedSubdivisionPoint)
{
- for (ON_SubDMeshFragment* f = m_mesh_fragments; nullptr != f; f = f->m_next_fragment)
- f->Transform(xform);
+ Internal_TransformComponentBase(true, xform);
+
+ if (Internal_SurfacePointFlag())
+ {
+ // bTransformationSavedSubdivisionPoint = true means xform is an isometry.
+ // If its more complicated than this, the calling code should
+ // reset or addjut colors as needed based on information in the
+ // SubD's texture coordinate mapping tag and color mapping tag.
+ // Note that both of those tags have their transformation updated
+ // so intelligent decisions can be made at a higher level where
+ // there is enough context to make the correct decision.
+ for (ON_SubDMeshFragment* f = m_mesh_fragments; nullptr != f; f = f->m_next_fragment)
+ f->Transform(true, true, true, xform);
+ }
+ else
+ Internal_ClearSurfacePointFlag();
}
else
- Internal_ClearSurfacePointFlag();
+ this->ClearSavedSubdivisionPoints();
return true;
}
@@ -871,7 +956,10 @@ bool ON_SubDimple::Transform(
// In all other cases, set bTransformationSavedSubdivisionPoint = false
// and any saved subdivision points or saved limit points will be
// deleted.
- const bool bTransformationSavedSubdivisionPoint = false; // todo - set this correctly
+ const bool bTransformationSavedSubdivisionPoint = (1 == xform.IsRigid());
+
+ bool bHasTextures = false;
+ bool bHasColors = false;
for (unsigned int level_index = 0; level_index < level_count; level_index++)
{
@@ -887,8 +975,25 @@ bool ON_SubDimple::Transform(
rc = false;
break;
}
+
+ if (level->m_face_count > 0 && level->m_face)
+ {
+ const ON_SubDMeshFragment* frag = level->m_face[0]->MeshFragments();
+ if (nullptr != frag)
+ {
+ if (frag->TextureCoordinateCount() > 0)
+ bHasTextures = true;
+ if (frag->ColorCount() > 0)
+ bHasColors = true;
+ }
+ }
}
+ if (bHasTextures)
+ this->m_texture_mapping_tag.Transform(xform);
+ if (bHasColors)
+ this->m_colors_mapping_tag.Transform(xform);
+
// SubD has been moved - geometry changed and we need to bump the geometry content serial number.
this->ChangeGeometryContentSerialNumber(false);
@@ -951,7 +1056,17 @@ bool ON_SubDimple::Transform(
bool ON_SubDMeshFragment::Transform(
const ON_Xform& xform
- )
+)
+{
+ return this->Transform(true, true, true, xform);
+}
+
+bool ON_SubDMeshFragment::Transform(
+ bool bKeepCurvatures,
+ bool bKeepTextureCoordinates,
+ bool bKeepColors,
+ const ON_Xform& xform
+)
{
const unsigned count = PointCount();
if (0 == count)
@@ -996,6 +1111,31 @@ bool ON_SubDMeshFragment::Transform(
}
}
ON_GetPointListBoundingBox(3,0,count,(int)m_P_stride,m_P,&m_surface_bbox.m_min.x,&m_surface_bbox.m_max.x,false);
+
+ if (false == bKeepTextureCoordinates)
+ {
+ this->SetTextureCoordinatesExistForExperts(false);
+ double* p = &this->m_ctrlnetT[0][0];
+ double* p1 = p + sizeof(this->m_ctrlnetT) / sizeof(this->m_ctrlnetT[0][0]);
+ while (p < p1)
+ *p++ = ON_DBL_QNAN;
+ }
+ if (false == bKeepCurvatures)
+ {
+ this->SetCurvaturesExistForExperts(false);
+ this->m_ctrlnetK[0] = ON_SurfaceCurvature::Nan;
+ this->m_ctrlnetK[1] = ON_SurfaceCurvature::Nan;
+ this->m_ctrlnetK[2] = ON_SurfaceCurvature::Nan;
+ this->m_ctrlnetK[3] = ON_SurfaceCurvature::Nan;
+ }
+ if (false == bKeepColors)
+ {
+ this->SetColorsExistForExperts(false);
+ this->m_ctrlnetC[0] = ON_Color::UnsetColor;
+ this->m_ctrlnetC[1] = ON_Color::UnsetColor;
+ this->m_ctrlnetC[2] = ON_Color::UnsetColor;
+ this->m_ctrlnetC[3] = ON_Color::UnsetColor;
+ }
return true;
}
@@ -1003,11 +1143,12 @@ bool ON_SubDMeshImpl::Transform(
const ON_Xform& xform
)
{
+ const bool bIsometry = (1 == xform.IsRigid());
m_bbox = ON_BoundingBox::EmptyBoundingBox;
ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox;
for ( const ON_SubDMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment)
{
- if ( false == const_cast(fragment)->Transform(xform) )
+ if ( false == const_cast(fragment)->Transform(bIsometry, bIsometry, bIsometry, xform) )
return ON_SUBD_RETURN_ERROR(false);
if ( fragment == m_first_fragment )
bbox = fragment->m_surface_bbox;
diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h
index daf85149..a289d39d 100644
--- a/opennurbs_subd_data.h
+++ b/opennurbs_subd_data.h
@@ -1160,11 +1160,24 @@ public:
void ClearEvaluationCache() const;
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// If (0 != 1(&)copy_status, then fragments were copied.
+ /// If (0 != 2(&)copy_status, then fragment per vertex texture coordinates were copied.
+ /// If (0 != 4(&)copy_status, then fragment per vertex principal curvatures were copied.
+ /// If (0 != 8(&)copy_status, then fragment per vertex colors were copied.
+ ///
+ ///
bool CopyEvaluationCacheForExperts(
class ON_SubDHeap& this_heap,
const ON_SubDLevel& src,
const class ON_SubDHeap& src_heap,
- bool& bFragmentsWereCopied
+ unsigned& copy_status
);
void ClearTopologicalAttributes() const
@@ -2491,21 +2504,33 @@ public:
m_fragment_colors_settings_hash = hash;
}
- const ON_MappingTag FragmentColorsMappingTag() const;
- void SetFragmentColorsMappingTag(const ON_MappingTag& mapping_tag) const;
+ const ON_MappingTag ColorsMappingTag() const;
+ void SetColorsMappingTag(const ON_MappingTag& mapping_tag) const;
private:
mutable ON_SubDComponentLocation m_subd_appearance = ON_SubD::DefaultSubDAppearance;
mutable ON_SubDTextureCoordinateType m_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset;
unsigned short m_reserved = 0;
- mutable ON_MappingTag m_texture_mapping_tag;
- mutable ON_MappingTag m_fragment_colors_mapping_tag;
- // hash of the settings used to create the current fragment texture coordinates
+ // m_texture_mapping_tag identifies the mapping used to set the fragment per vertex texture coordinates.
+ // If m_texture_mapping_tag is set and fragment per vertex texture coordinates are missing,
+ // m_texture_mapping_tag.m_mapping_id specifies the method used to update the texture coordinates.
+ mutable ON_MappingTag m_texture_mapping_tag;
+
+ // m_colors_mapping_tag identifies the mapping used to set the fragment per vertex colors.
+ // If m_colors_mapping_tag is set and fragment per vertex colors are missing,
+ // m_colors_mapping_tag.m_mapping_id specifies the method used to update the colors.
+ // Per vertex colors are used for false color analysis modes (curvature, draft angle) and
+ // other specific uses.
+ mutable ON_MappingTag m_colors_mapping_tag;
+
+ // hash of the settings used to create the current fragment texture coordinates.
+ // This hash has meaning only when fragments with per vertex texture coordinates exist.
mutable ON_SHA1_Hash m_fragment_texture_settings_hash = ON_SHA1_Hash::EmptyContentHash;
// hash of the settings used to create the current fragment vertex colors
+ // This hash has meaning only when fragments with per vertex colors exist.
mutable ON_SHA1_Hash m_fragment_colors_settings_hash = ON_SHA1_Hash::EmptyContentHash;
ON_SimpleArray< ON_SubDLevel* > m_levels;
diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp
index 3d43aa93..04de3cf7 100644
--- a/opennurbs_subd_eval.cpp
+++ b/opennurbs_subd_eval.cpp
@@ -1089,7 +1089,6 @@ bool ON_SubDVertex::SurfacePointIsSet() const
void ON_SubDEdge::ClearSavedSubdivisionPoints() const
{
- // considering using a global pool for the limit curve cache - not yet.
ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags();
}
diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp
index 6eb9f2fd..ba788e39 100644
--- a/opennurbs_subd_fragment.cpp
+++ b/opennurbs_subd_fragment.cpp
@@ -635,11 +635,19 @@ bool ON_SubD::SetFragmentColorsFromCallback(
const ON_SurfaceCurvature& K)
) const
{
- if (bLazySet
+ if (bLazySet
&& fragment_colors_settings_hash == FragmentColorsSettingsHash()
- && fragment_colors_mapping_tag == FragmentColorsMappingTag()
+ && fragment_colors_mapping_tag == ColorsMappingTag()
+ && this->HasFragmentColors()
)
+ {
+ // This subd has fragments with per vertex colors.
+ // The settings used to create those colors exactly
+ // match the settings that color_callback() will use
+ // to assign colors. The caller said to be lazy, so
+ // assume the existing colors are correct and return.
return true;
+ }
bool bFragmentVetexColorsSet = false;
const ON_SubDimple* subdimple = this->SubDimple();
@@ -665,13 +673,13 @@ bool ON_SubD::SetFragmentColorsFromCallback(
if (bFragmentVetexColorsSet)
{
subdimple->Internal_SetFragmentColorsSettingsHash(fragment_colors_settings_hash);
- SetFragmentColorsMappingTag(fragment_colors_mapping_tag);
+ SetColorsMappingTag(fragment_colors_mapping_tag);
ChangeRenderContentSerialNumber();
}
else
{
subdimple->Internal_SetFragmentColorsSettingsHash(ON_SHA1_Hash::EmptyContentHash);
- this->SetFragmentColorsMappingTag(ON_MappingTag::Unset);
+ this->SetColorsMappingTag(ON_MappingTag::Unset);
}
}
@@ -680,25 +688,31 @@ bool ON_SubD::SetFragmentColorsFromCallback(
bool ON_SubD::HasFragmentColors() const
{
+ bool bHasColors = false;
const ON_SubDimple* subdimple = this->SubDimple();
if (nullptr != subdimple)
{
ON_SubDMeshFragmentIterator fragit(*this);
for (const ON_SubDMeshFragment* frag = fragit.FirstFragment(); nullptr != frag; frag = fragit.NextFragment())
{
- if (frag->ColorCount() > 0)
- return true;
+ if (0 == frag->ColorCount())
+ return false;
+ // NOTE WELL:
+ // All fragments need to be tested as lasy updates of the
+ // surface mesh cache result in the update fragments having
+ // no colors while the preexisting fragments have colors.
+ bHasColors = true; // fragments with colors exist
}
}
- return false;
+ return bHasColors;
}
bool ON_SubD::HasFragmentColors(
- ON_MappingTag color_tag
+ ON_MappingTag color_mapping_tag
) const
{
return
- this->FragmentColorsMappingTag() == color_tag
+ this->ColorsMappingTag() == color_mapping_tag
&& this->HasFragmentColors();
}
@@ -713,16 +727,63 @@ bool ON_SubD::HasFragmentColors(
bool ON_SubD::HasFragmentColors(
ON_SHA1_Hash color_settings_hash,
- ON_MappingTag color_tag
+ ON_MappingTag color_mapping_tag
) const
{
return
this->FragmentColorsSettingsHash() == color_settings_hash
- && this->FragmentColorsMappingTag() == color_tag
+ && this->ColorsMappingTag() == color_mapping_tag
&& this->HasFragmentColors();
}
+
+bool ON_SubD::HasFragmentTextureCoordinates() const
+{
+ const ON_SubDimple* subdimple = this->SubDimple();
+ if (nullptr != subdimple)
+ {
+ ON_SubDMeshFragmentIterator fragit(*this);
+ for (const ON_SubDMeshFragment* frag = fragit.FirstFragment(); nullptr != frag; frag = fragit.NextFragment())
+ {
+ if (frag->ColorCount() > 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ON_SubD::HasFragmentTextureCoordinates(
+ ON_MappingTag texture_mapping_tag
+) const
+{
+ return
+ this->TextureMappingTag(true) == texture_mapping_tag
+ && this->HasFragmentTextureCoordinates();
+}
+
+bool ON_SubD::HasFragmentTextureCoordinates(
+ ON_SHA1_Hash texture_settings_hash
+) const
+{
+ return
+ this->TextureSettingsHash() == texture_settings_hash
+ && this->HasFragmentTextureCoordinates();
+}
+
+bool ON_SubD::HasFragmentTextureCoordinates(
+ ON_SHA1_Hash texture_settings_hash,
+ ON_MappingTag texture_mapping_tag
+) const
+{
+ return
+ this->TextureSettingsHash() == texture_settings_hash
+ && this->TextureMappingTag(true) == texture_mapping_tag
+ && this->HasFragmentTextureCoordinates();
+}
+
+
+
void ON_SubD::ClearFragmentColors(
bool bClearFragmentColorsMappingTag
)
@@ -741,7 +802,7 @@ void ON_SubD::ClearFragmentColors(
if (bClearFragmentColorsMappingTag)
{
subdimple->Internal_SetFragmentColorsSettingsHash(ON_SHA1_Hash::EmptyContentHash);
- this->SetFragmentColorsMappingTag(ON_MappingTag::Unset);
+ this->SetColorsMappingTag(ON_MappingTag::Unset);
}
if (bFragmentsChanged)
this->ChangeRenderContentSerialNumber();
@@ -755,16 +816,26 @@ const ON_SHA1_Hash ON_SubD::FragmentColorsSettingsHash() const
}
void ON_SubD::SetFragmentColorsMappingTag(const ON_MappingTag& colors_mapping_tag) const
+{
+ return SetColorsMappingTag(colors_mapping_tag);
+}
+
+void ON_SubD::SetColorsMappingTag(const ON_MappingTag& colors_mapping_tag) const
{
const ON_SubDimple* dimple = SubDimple();
if (nullptr != dimple)
- dimple->SetFragmentColorsMappingTag(colors_mapping_tag);
+ dimple->SetColorsMappingTag(colors_mapping_tag);
}
const ON_MappingTag ON_SubD::FragmentColorsMappingTag() const
+{
+ return ColorsMappingTag();
+}
+
+const ON_MappingTag ON_SubD::ColorsMappingTag() const
{
const ON_SubDimple* dimple = SubDimple();
- return (nullptr != dimple) ? dimple->FragmentColorsMappingTag() : ON_MappingTag::Unset;
+ return (nullptr != dimple) ? dimple->ColorsMappingTag() : ON_MappingTag::Unset;
}
const ON_3dPoint ON_SubDMeshFragment::ControlNetQuadPoint(
diff --git a/opennurbs_subd_texture.cpp b/opennurbs_subd_texture.cpp
index 40100857..46ec7b8c 100644
--- a/opennurbs_subd_texture.cpp
+++ b/opennurbs_subd_texture.cpp
@@ -256,16 +256,16 @@ const ON_MappingTag ON_SubDimple::TextureMappingTag(bool bIgnoreTextureCoordinat
#pragma endregion
#endif
-const ON_MappingTag ON_SubDimple::FragmentColorsMappingTag() const
+const ON_MappingTag ON_SubDimple::ColorsMappingTag() const
{
- return m_fragment_colors_mapping_tag;
+ return m_colors_mapping_tag;
}
-void ON_SubDimple::SetFragmentColorsMappingTag(const ON_MappingTag& mapping_tag) const
+void ON_SubDimple::SetColorsMappingTag(const ON_MappingTag& mapping_tag) const
{
- if (0 != ON_MappingTag::CompareAll(this->m_fragment_colors_mapping_tag, mapping_tag))
+ if (0 != ON_MappingTag::CompareAll(this->m_colors_mapping_tag, mapping_tag))
{
- this->m_fragment_colors_mapping_tag = mapping_tag;
+ this->m_colors_mapping_tag = mapping_tag;
ChangeRenderContentSerialNumber();
}
}
diff --git a/opennurbs_text.cpp b/opennurbs_text.cpp
index 697ec81b..dd5d6291 100644
--- a/opennurbs_text.cpp
+++ b/opennurbs_text.cpp
@@ -1133,19 +1133,15 @@ int ON_TextContent::Dimension() const
const ON_BoundingBox ON_TextContent::TextContentBoundingBox() const
{
ON_TextRunArray* runs = const_cast(this)->TextRuns(false);
- const int run_count
- = (nullptr != runs)
- ? runs->Count()
- : 0;
+ const int run_count = (nullptr != runs) ? runs->Count() : 0;
- if (run_count <= 0)
- return ON_BoundingBox::EmptyBoundingBox;
-
- const ON_SHA1_Hash text_content_hash = TextContentHash();
- if (
- m_text_content_bbox.IsValid()
- && m_text_content_bbox_hash == text_content_hash
- )
+ // 28 Aug 2023 S. Baer
+ // Computing a content hash takes longer than just computing the bounding box
+ // for simple single run situations. Only use the cached bbox when there are
+ // more than one run.
+ if ( run_count > 1 &&
+ m_text_content_bbox.IsValid() &&
+ m_text_content_bbox_hash == TextContentHash())
{
return m_text_content_bbox;
}
@@ -1162,8 +1158,8 @@ const ON_BoundingBox ON_TextContent::TextContentBoundingBox() const
if (nullptr == run)
continue;
- if (ON_TextRun::RunType::kText == (*runs)[i]->Type() ||
- ON_TextRun::RunType::kField == (*runs)[i]->Type())
+ if (ON_TextRun::RunType::kText == run->Type() ||
+ ON_TextRun::RunType::kField == run->Type())
{
ON_BoundingBox runbox = run->BoundingBox();
if (false == runbox.IsValid())
@@ -1204,10 +1200,10 @@ const ON_BoundingBox ON_TextContent::TextContentBoundingBox() const
bbox.m_min.z = 0.0;
bbox.m_max.z = 0.0;
- if (bbox.IsValid())
+ if (run_count>1 && bbox.IsValid())
{
m_text_content_bbox = bbox;
- m_text_content_bbox_hash = text_content_hash;
+ m_text_content_bbox_hash = TextContentHash();
}
return bbox;
diff --git a/opennurbs_viewport.cpp b/opennurbs_viewport.cpp
index 3a7ad8be..9c140f5c 100644
--- a/opennurbs_viewport.cpp
+++ b/opennurbs_viewport.cpp
@@ -4964,8 +4964,8 @@ void ON_Viewport::GetPerspectiveClippingPlaneConstraints(
if ( depth_buffer_bit_depth >= 32 )
{
- nof = 0.0001;
- n = 0.001;
+ nof = 0.0005; // Changed to match 24 bit defaults: https://mcneel.myjetbrains.com/youtrack/issue/RH-77623
+ n = 0.005; // Do not change back unless you've fixed the problems shown in the YT somewhere else.
}
else if ( depth_buffer_bit_depth >= 24 )
{
diff --git a/opennurbs_xform.h b/opennurbs_xform.h
index d6fcd341..ef9cf9d3 100644
--- a/opennurbs_xform.h
+++ b/opennurbs_xform.h
@@ -277,23 +277,41 @@ public:
/*
Description:
- A rigid transformation can be broken into a proper rotation and a translation.
- while an isometry transformation could also include a reflection.
- Parameters:
- *this - must be IsAffine().
- Translation - [out] Translation vector
- Rotation - [out] Proper Rotation transformation, ie. R*Transpose(R)=I and det(R)=1
- Details:
- If X.DecomposeRigid(T, R) is 1 then X ~ TranslationTransformation(T)*R
- -1 X ~ ON_Xform(-1) *TranslationTransformation(T)*R
- where ~ means approximates to within tolerance.
- DecomposeRigid will find the closest rotation to the linear part of this transformation.
+ NOTE: A better name for this fuction would be IsIsometry().
+
+ An "isometry" transformation is an affine transformation that preserves
+ distances. Isometries include transformation like reflections
+ that reverse orientation.
+
+ A "rigid" transformation is an isometry that preserves orientation
+ and can be broken into a proper rotation and a translation.
Returns:
- +1: This transformation is an rigid transformation.
- -1: This transformation is an orientation reversing isometry.
+ +1: This transformation is an orientation preserving isometry transformation ("rigid and determinant = 1).
+ -1: This transformation is an orientation reversing isometry (determinant = -1).
0 : This transformation is not an orthogonal transformation.
*/
int IsRigid(double tolerance = ON_ZERO_TOLERANCE) const;
+
+
+ /*
+ Description:
+ A rigid transformation preserves distances and orientation
+ and can be broken into a proper rotation and a translation.
+ An isometry transformation preserves distance and may include a reflection.
+ Parameters:
+ *this - must be IsAffine().
+ Translation - [out] Translation vector
+ Rotation - [out] Proper Rotation transformation, ie. R*Transpose(R)=I and det(R)=1
+ Details:
+ If X.DecomposeRigid(T, R) is 1 then X ~ TranslationTransformation(T)*R
+ -1 X ~ ON_Xform(-1) *TranslationTransformation(T)*R
+ where ~ means approximates to within tolerance.
+ DecomposeRigid will find the closest rotation to the linear part of this transformation.
+ Returns:
+ +1: This transformation is an rigid transformation.
+ -1: This transformation is an orientation reversing isometry.
+ 0 : This transformation is not an orthogonal transformation.
+ */
int DecomposeRigid(ON_3dVector& Translation, ON_Xform& Rotation, double tolerance = ON_ZERO_TOLERANCE) const;
/*
diff --git a/opennurbs_xml.cpp b/opennurbs_xml.cpp
index 0860c678..b4f8bc0f 100644
--- a/opennurbs_xml.cpp
+++ b/opennurbs_xml.cpp
@@ -294,8 +294,8 @@ public:
const ON_wString& ConvertDoubleArrayToString(int count) const
{
- constexpr int maxCount = 16;
- if ((count == 0) || (count > maxCount))
+ constexpr int maxCount = array_val_max;
+ if ((count < 1) || (count > maxCount))
return _string_val;
constexpr int maxLen = 30;
@@ -329,6 +329,8 @@ public:
mutable ON_Buffer* _buffer = nullptr;
mutable ON_wString _string_val;
+
+ static constexpr int array_val_max = 16;
union
{
bool _bool_val;
@@ -338,7 +340,7 @@ public:
ON_Xform m_xform;
time_t _time_val;
ON_UUID _uuid_val;
- double _array_val[16] = { 0 };
+ double _array_val[array_val_max] = { 0 };
};
ON::LengthUnitSystem _units = ON::LengthUnitSystem::None;
@@ -559,7 +561,7 @@ bool ON_XMLVariant::operator == (const ON_XMLVariant& v) const
return _private->_time_val == v._private->_time_val;
case Types::Matrix:
- for (int i = 0; i < 16; i++)
+ for (int i = 0; i < _private->array_val_max; i++)
{
if (_private->_array_val[i] != v._private->_array_val[i])
return false;
@@ -975,18 +977,18 @@ ON_Xform ON_XMLVariant::AsXform(void) const
switch (_private->_type)
{
case Types::Matrix:
- break;
+ return _private->m_xform;
case Types::String:
if (_private->_string_val.IsValidMatrix())
- StringToPoint(16); //////////////////////////////////// Risky
- break;
+ {
+ StringToPoint(16);
+ return _private->m_xform;
+ }
default:
return ON_Xform::Zero4x4;
}
-
- return _private->m_xform;
}
ON_4fColor ON_XMLVariant::AsColor(void) const
@@ -1208,27 +1210,66 @@ ON_XMLVariant::operator ON_Buffer() const
void ON_XMLVariant::StringToPoint(int numValues) const
{
- if ((numValues < 0) || (numValues > 16))
- return;
+ // 22nd September 2022 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-77182
+ // This function crashed on the Mac inside wcstod_l() which must be getting called from the call to
+ // ON_wtof(). The only way that function can fail is if either the input pointers are invalid or the
+ // input string or locale is corrupted. I don't have any control over the locale (I don't even know
+ // how it's being accessed on the Mac), so all I can do is check the input string. It's unlikely that
+ // an incorrectly formatted string (i.e., not a float) would cause a crash, but I can do some simple
+ // validation to try and avoid trouble.
- ON_wString s = _private->_string_val + L",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,";
- auto* p = s.Array();
+ bool good = true;
- for (int i = 0; i < numValues; i++)
+ if ((numValues < 0) || (numValues > _private->array_val_max))
{
- while (iswspace(*p))
+ good = false;
+ }
+ else
+ if (_private->_string_val.IsEmpty())
+ {
+ good = false;
+ }
+
+ if (good)
+ {
+ ON_wString s = _private->_string_val + L",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,";
+
+ auto* p = static_cast(s);
+ for (int i = 0; i < numValues; i++)
{
+ // Skip white space. iswspace() returns 0 for the terminator so we can't go off the end of the buffer.
+ while (0 != iswspace(*p))
+ {
+ p++;
+ }
+
+ // Quick and simple sanity check at the start of a float value.
+ if (isdigit(*p) || (*p == '.') || (*p == '+') || (*p == '-'))
+ {
+ _private->_array_val[i] = ON_wtof(p);
+ }
+
+ // Because we've appended a fixed string containing commas, we know the pointer can't go off
+ // the end of the buffer while checking for a comma.
+ while (*p != L',')
+ {
+ p++;
+ }
+
+ // 'p' is now pointing to a comma.
+ ON_ASSERT(*p == L',');
+
+ // After incrementing it, the worst case scenario is that it's pointing to the terminator.
p++;
}
-
- _private->_array_val[i] = ON_wtof(p);
-
- while (*p != L',')
+ }
+ else
+ {
+ // Bad input; clear the result to all zeroes.
+ for (int i = 0; i < _private->array_val_max; i++)
{
- p++;
+ _private->_array_val[i] = 0.0;
}
-
- p++;
}
}
diff --git a/opennurbs_xml.h b/opennurbs_xml.h
index 053b93fc..9eb4c6b4 100644
--- a/opennurbs_xml.h
+++ b/opennurbs_xml.h
@@ -236,6 +236,7 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*);
#define ON_PBR_MATERIAL_CLEARCOAT_BUMP L"pbr-clearcoat-bump"
#define ON_PBR_MATERIAL_CLEARCOAT_ROUGHNESS L"pbr-clearcoat-roughness"
#define ON_PBR_MATERIAL_EMISSION_COLOR L"pbr-emission"
+#define ON_PBR_MATERIAL_EMISSION_MULTIPLIER L"emission-multiplier"
#define ON_PBR_MATERIAL_METALLIC L"pbr-metallic"
#define ON_PBR_MATERIAL_OPACITY L"pbr-opacity"
#define ON_PBR_MATERIAL_OPACITY_IOR L"pbr-opacity-ior"