diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h
index 191ebba5..ac362bd7 100644
--- a/opennurbs_annotationbase.h
+++ b/opennurbs_annotationbase.h
@@ -204,7 +204,7 @@ public:
/*
Returns:
- Text information with rich text formatting insturctions removed.
+ Text information with rich text formatting instructions removed.
The result string from evaluating fields is included
Field results may be cached from previous evaluation
*/
diff --git a/opennurbs_defines.h b/opennurbs_defines.h
index 8b5db81c..847c28c3 100644
--- a/opennurbs_defines.h
+++ b/opennurbs_defines.h
@@ -132,6 +132,7 @@
#endif
+
// ON_DEPRECATED is used to mark deprecated functions.
#if defined(ON_COMPILER_MSC)
#define ON_DEPRECATED __declspec(deprecated)
@@ -156,8 +157,40 @@
#else
#define ON_DEPRECATED
#define ON_DEPRECATED_MSG(s)
+
+// Dale Lear 2024-March-14
+// Why are ON_WIP_SDK and ON_INTERNAL_SDK defined here?
+// This seems exactly the opposite of what should happen.
+// I think these should be wrapped in a
+// #if defined(OPENNURBS_IN_RHINO) ... #endif block
+// and that change needs extensive testing.
+//
+// #if defined(OPENNURBS_IN_RHINO)
#define ON_WIP_SDK
#define ON_INTERNAL_SDK
+// #endif
+
+#endif
+
+#if defined(ON_WIP_SDK)
+// Functions with ON_WIP_DECL are works in progress.
+// Classes with ON_WIP_CLASS are works in progress.
+// Externals with ON_WIP_EXTERN_DECL are works in progress.
+// These items can be seen and used in Rhino core code.
+// These items are not part of the public SDK.
+// These items can and will change or be removed at any time without notice.
+// Any C++ code using ON_WIP_* features is likely to fail catastrophically
+// at the most inconvenient time imaginable.
+#define ON_WIP_CLASS ON_CLASS
+#define ON_WIP_DECL ON_DECL
+#define ON_WIP_EXTERN_DECL ON_EXTERN_DECL
+#else
+// This header is not being parsed while building core Rhino modules and plug-ins.
+// Any 3rd party code linking with the public C++ Rhino SDK will be unable to link
+// with the work-in-progess items. Code could
+#define ON_WIP_CLASS
+#define ON_WIP_DECL
+#define ON_WIP_EXTERN_DECL
#endif
#if defined(PI)
@@ -314,7 +347,6 @@ extern ON_EXTERN_DECL const float ON_FLT_QNAN;
extern ON_EXTERN_DECL const float ON_FLT_PINF;
extern ON_EXTERN_DECL const float ON_FLT_NINF;
-
/*
The ON_PTR_SEMAPHORE* values are used in rare cases
when a special signal must be passed as a pointer argument.
@@ -387,6 +419,126 @@ ON__UINT64 ON_NextContentSerialNumber();
ON_END_EXTERNC
#if defined(ON_CPLUSPLUS)
+
+class ON_WIP_CLASS ON_DBL
+{
+public:
+
+ ///
+ /// ON_DBL::Nan is an IEEE quiet nan (not a number).
+ ///
+ static const double Nan;
+
+ ///
+ /// ON_DBL::PositiveInfinity is IEEE +infinity.
+ ///
+ static const double PositiveInfinity;
+
+ ///
+ /// ON_DBL::NegativeInfinity is IEEE -infinity.
+ ///
+ static const double NegativeInfinity;
+
+ ///
+ /// ON_DBL::PositiveMax = +1.7976931348623158e+308
+ ///
+ static const double PositiveMax;
+
+ ///
+ /// ON_DBL::NegativeMax = -1.7976931348623158e+308
+ ///
+ static const double NegativeMax;
+
+ ///
+ /// ON_DBL::PositiveMax = +2.22507385850720200e-308
+ ///
+ static const double PositiveMin;
+
+ ///
+ /// ON_DBL::NegativeMax = -2.22507385850720200e-308
+ ///
+ static const double NegativeMin;
+
+ ///
+ /// ON_DBL::Unset = -1.23432101234321e+308
+ ///
+ static const double Unset;
+
+ ///
+ /// ON_DBL::PositiveUnset = +1.23432101234321e+308
+ ///
+ static const double PositiveUnset;
+
+ ///
+ /// True if x is any type of nan (signaling or quiet).
+ static bool IsNan(double x);
+
+ ///
+ /// True if x is any type of infinity (positive, negative, projective).
+ static bool IsInfinity(double x);
+
+ static bool IsPositiveInfinity(double x);
+ static bool IsNegativeInfinity(double x);
+
+ ///
+ ///
+ /// If x > 0, then +1 is returned.
+ /// If x < 0, then -1 is returned.
+ /// Otherwise 0 is returned.
+ ///
+ static int Sign(double x);
+
+ ///
+ /// All nans, +infinity, -infinity,
+ /// x <= than ON_DBL::Unset and
+ /// x >= ON_DBL::PositiveUnset
+ /// are considered invalid because using them in typical calculations
+ /// almost always returns useless results.
+ ///
+ ///
+ /// value to test.
+ ///
+ ///
+ /// (ON_DBL::Unset < x && x < ON_DBL::PositiveUnset)
+ ///
+ static bool IsValid(double x);
+
+ ///
+ /// True if x is not a nan..
+ static bool IsNotNan(double x);
+
+ static bool IsUnset(double x);
+
+
+ ///
+ /// Well ordered compare that handles nans and sorts them to the end.
+ /// ON_DBL::CompareValue(nan, nan) = 0;
+ /// ON_DBL::CompareValue(non_nan, nan) = -1;
+ /// ON_DBL::CompareValue(nan, non_nan) = +1;
+ ///
+ ///
+ ///
+ ///
+ /// -1 if lhs < rhs or IsNotNan(lhs) and IsNan(rhs).
+ /// 0 if lhs == rhs or IsNan(lhs) && IsNan(rhs).
+ /// +1 if lhs > rhs or IsNotNan(lhs) && IsNan(rhs).
+ ///
+ static int CompareValue(double lhs, double rhs);
+
+ ///
+ /// Well ordered compare that handles nullpt and nans and sorts them
+ /// to the end.
+ /// ON_DBL::Compare(nullptr, nullptr) = 0;
+ /// ON_DBL::Compare(not nullptr, nullptr) = -1;
+ /// ON_DBL::Compare(nullptr, not nullptr) = +1;
+ /// ON_DBL::Compare(not nullptr, not nullptr) = ON_DBL::CompareValue(*lhs,*rhs);
+ ///
+ ///
+ ///
+ ///
+ static int Compare(const double* lhs, const double* rhs);
+};
+
ON_DECL
bool ON_IsNullPtr(const void* ptr);
diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp
index b2208cb7..474d540c 100644
--- a/opennurbs_line.cpp
+++ b/opennurbs_line.cpp
@@ -82,7 +82,9 @@ bool ON_Line::Create( const ON_2dPoint from_pt, const ON_2dPoint to_pt )
bool ON_Line::IsValid() const
{
- return (from != to && from.IsValid() && to.IsValid());
+ bool from_rc = ON_CV_COORDINATE_IS_VALID(from.x) && ON_CV_COORDINATE_IS_VALID(from.y) && ON_CV_COORDINATE_IS_VALID(from.z);
+ bool to_rc = ON_CV_COORDINATE_IS_VALID(to.x) && ON_CV_COORDINATE_IS_VALID(to.y) && ON_CV_COORDINATE_IS_VALID(to.z);
+ return (from != to && true == from_rc && true == to_rc);
}
double ON_Line::Length() const
diff --git a/opennurbs_linecurve.cpp b/opennurbs_linecurve.cpp
index 4819065a..5969ead7 100644
--- a/opennurbs_linecurve.cpp
+++ b/opennurbs_linecurve.cpp
@@ -186,7 +186,7 @@ ON_LineCurve::SwapCoordinates( int i, int j )
bool ON_LineCurve::IsValid( ON_TextLog* text_log ) const
{
- return ( m_t[0] < m_t[1] && !m_line.from.IsCoincident(m_line.to) ) ? true : false;
+ return (m_line.IsValid() && m_t[0] < m_t[1] && !m_line.from.IsCoincident(m_line.to) ) ? true : false;
}
void ON_LineCurve::Dump( ON_TextLog& dump ) const
diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp
index fa9ee3d6..893420f8 100644
--- a/opennurbs_material.cpp
+++ b/opennurbs_material.cpp
@@ -2314,6 +2314,87 @@ bool ON_Texture::IsWcsBoxProjected() const
return (m_mapping_channel_id == (unsigned int)MAPPING_CHANNEL::wcs_box_channel);
}
+ON_3dPoint ON_Texture::WcsBoxMapping(const ON_3dPoint& pt, const ON_3dVector& n)
+{
+ // This code is moved here from CRhRdkTexture::WcsBoxMapping
+
+ int side0 = 0;
+
+ const ON_3dPoint& rst(pt);
+
+ // set side0 = side closest to the point
+ int side1 = (std::abs(rst.x) >= std::abs(rst.y)) ? 0 : 1;
+ if (std::abs(rst.z) > std::abs(((double*)&rst.x)[side1]))
+ side1 = 2;
+
+ double t1 = (&rst.x)[side1];
+ if (t1 < 0.0)
+ side0 = 2 * side1 + 1;
+ else
+ side0 = 2 * side1 + 2;
+
+ side1 = (std::abs(n.x) >= std::abs(n.y)) ? 0 : 1;
+ if (std::abs(n.z) > std::abs((&n.x)[side1]))
+ {
+ side1 = 2;
+ }
+
+ t1 = n[side1];
+ if (0.0 != t1)
+ {
+ if (t1 < 0.0)
+ side0 = 2 * side1 + 1;
+ else
+ if (t1 > 0.0)
+ side0 = 2 * side1 + 2;
+ }
+
+ // side flag
+ // 1 = left side (x=-1)
+ // 2 = right side (x=+1)
+ // 3 = back side (y=-1)
+ // 4 = front side (y=+1)
+ // 5 = bottom side (z=-1)
+ // 6 = top side (z=+1)
+ ON_3dPoint v;
+ switch (side0)
+ {
+ case 1:
+ v.x = -pt.y;
+ v.y = pt.z;
+ v.z = pt.x;
+ break;
+ case 2:
+ v.x = pt.y;
+ v.y = pt.z;
+ v.z = pt.x;
+ break;
+ case 3:
+ v.x = pt.x;
+ v.y = pt.z;
+ v.z = pt.y;
+ break;
+ case 4:
+ v.x = -pt.x;
+ v.y = pt.z;
+ v.z = pt.y;
+ break;
+ case 5:
+ v.x = -pt.x;
+ v.y = pt.y;
+ v.z = pt.z;
+ break;
+ case 6:
+ default:
+ v.x = pt.x;
+ v.y = pt.y;
+ v.z = pt.z;
+ break;
+ }
+
+ return v;
+}
+
ON_Texture::TYPE ON_Texture::TypeFromUnsigned(unsigned int type_as_unsigned)
{
switch (type_as_unsigned)
@@ -2622,6 +2703,8 @@ const ON_wString ON_TextureMapping::TypeToString(ON_TextureMapping::TYPE texture
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::brep_mapping_primitive);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::ocs_mapping);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::false_colors);
+ ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::wcs_projection);
+ ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::wcsbox_projection);
}
ON_ERROR("Invalid texture_mapping_type value.");
@@ -3687,6 +3770,16 @@ int ON_TextureMapping::Evaluate(
rc = 0;
break;
+ case ON_TextureMapping::TYPE::wcs_projection:
+ *T = m_Pxyz * P;
+ rc = 1;
+ break;
+
+ case ON_TextureMapping::TYPE::wcsbox_projection:
+ *T = ON_Texture::WcsBoxMapping(m_Pxyz * P, m_Nxyz * N);
+ rc = 1;
+ break;
+
default:
rc = EvaluatePlaneMapping(P,N,T);
break;
@@ -3927,6 +4020,9 @@ bool ON_TextureMapping::RequiresVertexNormals() const
if(m_type == ON_TextureMapping::TYPE::box_mapping)
return true;
if(m_type == ON_TextureMapping::TYPE::cylinder_mapping && m_bCapped)
+ return true;
+
+ if (m_type == ON_TextureMapping::TYPE::wcsbox_projection)
return true;
return false;
@@ -4410,6 +4506,7 @@ bool ON_TextureMapping::GetTextureCoordinates(
|| ON_TextureMapping::TYPE::box_mapping == m_type
|| ON_TextureMapping::TYPE::cylinder_mapping == m_type
|| ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type
+ || ON_TextureMapping::TYPE::wcsbox_projection == m_type
)
)
{
diff --git a/opennurbs_math.h b/opennurbs_math.h
index 863f7c52..b89e12f9 100644
--- a/opennurbs_math.h
+++ b/opennurbs_math.h
@@ -469,6 +469,10 @@ private:
#define ON_IS_INFINITE_FLOAT(x) ((x) > 3.402823466e+38F || (x) < -3.402823466e+38F)
#define ON_IS_VALID(x) ((x) > ON_UNSET_VALUE && (x) < ON_UNSET_POSITIVE_VALUE)
+// March 27, 2024 - Tim
+// Fix for https://mcneel.myjetbrains.com/youtrack/issue/RH-81184
+// The vertexes were crap just not quite ON_UNSET_VALUE crap
+#define ON_CV_COORDINATE_IS_VALID(x) (-1.0e307 < x && x < 1.0e307)
#define ON_IS_VALID_FLOAT(x) ((x) > ON_UNSET_FLOAT && (x) < ON_UNSET_POSITIVE_FLOAT)
#define ON_IS_UNSET_DOUBLE(x) (ON_UNSET_VALUE == (x) || ON_UNSET_POSITIVE_VALUE == (x))
#define ON_IS_UNSET_FLOAT(x) (ON_UNSET_FLOAT == (x) || ON_UNSET_POSITIVE_FLOAT == (x))
diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp
index 42107ee1..a02c0c77 100644
--- a/opennurbs_mesh.cpp
+++ b/opennurbs_mesh.cpp
@@ -12,6 +12,8 @@
////////////////////////////////////////////////////////////////
#include "opennurbs.h"
+#include
+#include
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
@@ -4309,6 +4311,402 @@ bool ON_Mesh::HasCachedTextureCoordinates() const
return false;
}
+// Note: We are exporting this function for use in CRhinoObject::SetCachedTextureCoordinatesFromPlugIn
+ON_DECL bool ON_Mesh_Private_CacheMappingChannel(std::function get_mapping_func, const ON_MappingChannel* pMC, const ON_SimpleArray& mappings_to_cache, const int mapping_channel_id, std::unordered_map& mapInOut)
+{
+ const bool cache_all = (mappings_to_cache.Count() == 0);
+
+ if (nullptr == pMC)
+ {
+ const ON_TextureMapping& srfp_mapping = ON_TextureMapping::SurfaceParameterTextureMapping;
+ if(cache_all || mappings_to_cache.Search(srfp_mapping.m_type) >= 0)
+ {
+ // If the mapping channel is null, then we can assume that the textures implicitly
+ // reference the surface parameter mapping.
+ // This is done according to the documentation for ON_Texture::m_mapping_channel_id.
+ mapInOut[mapping_channel_id] = srfp_mapping;
+ }
+ }
+ else
+ {
+ ON_TextureMapping mapping = get_mapping_func(pMC);
+
+ if (mapping.m_type != ON_TextureMapping::TYPE::no_mapping)
+ {
+ // How to handle ON_TextureMapping::TYPE::ocs_mapping
+ // and ON_TextureMapping::TYPE::false_colors ?
+ if(cache_all || mappings_to_cache.Search(mapping.m_type) >= 0)
+ {
+ mapInOut[mapping_channel_id] = mapping;
+ }
+ }
+ }
+
+ return true;
+}
+
+// Note: We are exporting this function for use in CRhinoObject::SetCachedTextureCoordinatesFromPlugIn
+ON_DECL bool ON_Mesh_Private_AddPerVertexMappingChannels(std::function get_texture_mapping_func, const ON_MappingRef* mapping_ref, const ON_SimpleArray& mappings_to_cache, const ON_Material& material, std::unordered_map& mappings)
+{
+ for (int i = 0; i < material.m_textures.Count(); i++)
+ {
+ if (material.m_textures[i].m_bOn)
+ {
+ // Get mapping channel id and check if mapping already is in the map
+ const unsigned int mapping_channel_id = material.m_textures[i].m_mapping_channel_id;
+ if (mappings.find(mapping_channel_id) == mappings.end())
+ {
+ const ON_MappingChannel* mapping_channel = nullptr;
+ if (mapping_ref)
+ {
+ // Get texture mapping and add it to the map
+ mapping_channel = mapping_ref->MappingChannel(mapping_channel_id);
+ if (mapping_channel == nullptr)
+ {
+ // If a mapping channel is not found using 'mapping_channel_id', then try with a mapping channel id of 1.
+ // This is done according to the documentation for ON_Texture::m_mapping_channel_id.
+ mapping_channel = mapping_ref->MappingChannel(1);
+ }
+ }
+
+ if (!ON_Mesh_Private_CacheMappingChannel(get_texture_mapping_func, mapping_channel, mappings_to_cache, mapping_channel_id, mappings))
+ return false;
+ }
+ }
+ }
+
+ // If we didn't cache any texture coordinates above, but we have a material with anisotropy,
+ // which requires tangents, which in turn requires texture coordinates, then we cache
+ // the texture coordinates from mapping channel 1.
+ if (mappings.size() == 0)
+ {
+ if (material.IsPhysicallyBased() && material.PhysicallyBased()->Anisotropic() > 0.0)
+ {
+ const ON_MappingChannel* mapping_channel = mapping_ref ? mapping_ref->MappingChannel(1) : nullptr;
+
+ if (!ON_Mesh_Private_CacheMappingChannel(get_texture_mapping_func, mapping_channel, mappings_to_cache, 1, mappings))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Note: We are exporting this function for use in CRhinoObject::SetCachedTextureCoordinatesFromPlugIn
+ON_DECL bool ON_Mesh_Private_SetCachedTextureCoordinatesFromMaterial(ON_SimpleArray& meshes, std::unordered_map& per_vertex_channels, const ON_MappingRef* mapping_ref, bool perform_cleanup, std::shared_ptr* vbo_data)
+{
+ ON_ClassArray> indices_to_preserve;
+ indices_to_preserve.SetCapacity(meshes.Count());
+ indices_to_preserve.SetCount(meshes.Count());
+
+ // Set cached texture coordinates for each mapping on the map for every render mesh
+ for (auto pvcit = per_vertex_channels.begin(); pvcit != per_vertex_channels.end(); pvcit++)
+ {
+ const unsigned int mapping_channel_id = pvcit->first;
+ const ON_TextureMapping& mapping = pvcit->second;
+
+ // Get object mapping transformation
+ const ON_MappingChannel* pMC = mapping_ref ? mapping_ref->MappingChannel(mapping_channel_id) : nullptr;
+
+ const ON_Xform* pObjectXform = (pMC == nullptr || pMC->m_object_xform.IsIdentity() || pMC->m_object_xform.IsZero()) ? nullptr : &pMC->m_object_xform;
+
+ // Set cached texture coordinates for each of the render meshes
+ for (int mi = 0; mi < meshes.Count(); mi++)
+ {
+ // Const cast render mesh and set texture coordinates
+ ON_Mesh* pMesh = const_cast(meshes[mi]);
+ if (nullptr != pMesh)
+ {
+ bool has_matching_cached_tcs = false;
+ for (int i = 0; i < pMesh->m_TC.Count(); i++)
+ {
+ if (pMesh->VertexCount() == pMesh->m_TC[i].m_T.Count() && mapping.HasMatchingTextureCoordinates(pMesh->m_TC[i].m_tag, pObjectXform))
+ {
+ indices_to_preserve[mi].Append(i);
+ has_matching_cached_tcs = true;
+ break;
+ }
+ }
+
+ if (!has_matching_cached_tcs)
+ {
+ indices_to_preserve[mi].Append(pMesh->m_TC.Count());
+
+ pMesh->SetCachedTextureCoordinatesEx(mapping, pObjectXform, true, false);
+
+ if(vbo_data)
+ vbo_data->reset();
+ }
+ }
+ }
+ }
+
+ if(perform_cleanup)
+ {
+ for (int mi = 0; mi < meshes.Count(); mi++)
+ {
+ ON_Mesh* pMesh = const_cast(meshes[mi]);
+ if (nullptr != pMesh)
+ {
+ const int tc_count = pMesh->m_TC.Count();
+ if (tc_count > per_vertex_channels.size())
+ {
+ int wcs_mapping_count = 0;
+ int wcs_box_mapping_count = 0;
+ ON_SimpleArray indices_to_remove;
+
+ for (int tci = tc_count - 1; tci >= 0; tci--)
+ {
+ if (indices_to_preserve[mi].Search(tci) == -1)
+ {
+ ON_TextureMapping::TYPE mapping_type = pMesh->m_TC[tci].m_tag.m_mapping_type;
+ bool remove_index = true;
+
+ // Only clean up WCS and WCS Box mappings if there exist more recent versions
+ // in the array. They are considered more recent if they appear later in the array.
+ if(mapping_type == ON_TextureMapping::TYPE::wcs_projection && wcs_mapping_count++ == 0)
+ {
+ remove_index = false; // Don't remove first one we find
+ }
+ else if(mapping_type == ON_TextureMapping::TYPE::wcsbox_projection && wcs_box_mapping_count++ == 0)
+ {
+ remove_index = false; // Don't remove first one we find
+ }
+
+ if(remove_index)
+ indices_to_remove.Append(tci);
+ }
+ }
+
+ for (int tci = indices_to_remove.Count() - 1; tci >= 0; tci--)
+ pMesh->m_TC.Remove(indices_to_remove[tci]);
+
+ if(vbo_data && indices_to_remove.Count() > 0)
+ vbo_data->reset();
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static ON_UUID WcsMappingId(const ON_Xform& ocsTransform, bool bBox)
+{
+ // {47D62129-341E-4E3D-BF11-9C051AF4ED98}
+ static const ON_UUID uuidBaseWcs =
+ { 0x47d62129, 0x341e, 0x4e3d, { 0xbf, 0x11, 0x9c, 0x5, 0x1a, 0xf4, 0xed, 0x98 } };
+
+ // {4DC9AA78-9617-46FB-923F-3E65E14182FC}
+ static const ON_UUID uuidBaseWcsBox =
+ { 0x4dc9aa78, 0x9617, 0x46fb, { 0x92, 0x3f, 0x3e, 0x65, 0xe1, 0x41, 0x82, 0xfc } };
+
+ ON_UUID uuid = bBox ? uuidBaseWcsBox : uuidBaseWcs;
+ uuid.Data1 = ocsTransform.CRC32(uuid.Data1);
+ return uuid;
+}
+
+static ON_Xform GetOcsTransform(std::function get_mapping_func, const ON_MappingRef* mapping_ref)
+{
+ ON_Xform ocsTransform = ON_Xform::IdentityTransformation;
+ if (nullptr != mapping_ref)
+ {
+ const ON_MappingChannel* pMC = mapping_ref->MappingChannel(ON_ObjectRenderingAttributes::OCSMappingChannelId());
+ if (nullptr != pMC)
+ {
+ const ON_TextureMapping tm = get_mapping_func(pMC);
+ ON_Xform inverseObjectTransform = pMC->m_object_xform;
+ if (inverseObjectTransform.Invert())
+ {
+ ocsTransform = tm.m_Pxyz * inverseObjectTransform;
+ }
+ else
+ {
+ ocsTransform = tm.m_Pxyz;
+ }
+ }
+ }
+ return ocsTransform;
+}
+
+static void CacheWcsProjections(const ON_Mesh& mesh, std::function get_mapping_func, const ON_Material& material, const ON_MappingRef* mapping_ref)
+{
+ bool bWcsProjectionNeeded = false;
+ bool bWcsBoxProjectionNeeded = false;
+ for (int ti = 0; ti < material.m_textures.Count(); ti++)
+ {
+ if (material.m_textures[ti].IsWcsProjected())
+ bWcsProjectionNeeded = true;
+ if (material.m_textures[ti].IsWcsBoxProjected())
+ bWcsBoxProjectionNeeded = true;
+ }
+
+ if (bWcsProjectionNeeded)
+ {
+ const ON_Xform ocsTransform = GetOcsTransform(get_mapping_func, mapping_ref);
+ const ON_UUID uuidWcsMapping = WcsMappingId(ocsTransform, false);
+ ON_TextureMapping wcsMapping;
+ wcsMapping.m_type = ON_TextureMapping::TYPE::wcs_projection;
+ wcsMapping.SetId(uuidWcsMapping);
+ wcsMapping.m_Pxyz = ocsTransform;
+ if (!wcsMapping.HasMatchingCachedTextureCoordinates(mesh))
+ {
+ ON_TextureCoordinates& tcs = const_cast(&mesh)->m_TC.AppendNew();
+ wcsMapping.GetTextureCoordinates(mesh, tcs.m_T);
+ tcs.m_tag.Set(wcsMapping);
+ tcs.m_tag.m_mesh_xform = ON_Xform::ZeroTransformation;
+ tcs.m_dim = 2;
+ return;
+ }
+ }
+ if (bWcsBoxProjectionNeeded)
+ {
+ const ON_Xform ocsTransform = GetOcsTransform(get_mapping_func, mapping_ref);
+ const ON_UUID uuidWcsBoxMapping = WcsMappingId(ocsTransform, true);
+ ON_TextureMapping wcsBoxMapping;
+ wcsBoxMapping.m_type = ON_TextureMapping::TYPE::wcsbox_projection;
+ wcsBoxMapping.SetId(uuidWcsBoxMapping);
+ wcsBoxMapping.m_Pxyz = ocsTransform;
+ wcsBoxMapping.m_Pxyz.GetSurfaceNormalXform(wcsBoxMapping.m_Nxyz);
+ if (!wcsBoxMapping.HasMatchingCachedTextureCoordinates(mesh))
+ {
+ ON_TextureCoordinates& tcs = const_cast(&mesh)->m_TC.AppendNew();
+ wcsBoxMapping.GetTextureCoordinates(mesh, tcs.m_T);
+ tcs.m_tag.Set(wcsBoxMapping);
+ tcs.m_tag.m_mesh_xform = ON_Xform::ZeroTransformation;
+ tcs.m_dim = 2;
+ return;
+ }
+ }
+}
+
+bool SetCachedTextureCoordinatesFromMaterial(const ON_Mesh& mesh, std::function get_mapping_func, const ON_Material& material, const ON_MappingRef* mapping_ref, const ON_SimpleArray& mappings_to_cache, bool perform_cleanup)
+{
+ // Create a map of all texture mappings that require per-vertex texture coordinates
+ std::unordered_map per_vertex_channels;
+ if (!ON_Mesh_Private_AddPerVertexMappingChannels(get_mapping_func, mapping_ref, mappings_to_cache, material, per_vertex_channels))
+ return false;
+
+ ON_SimpleArray meshes;
+ meshes.Append(&mesh);
+
+ CacheWcsProjections(mesh, get_mapping_func, material, mapping_ref);
+
+ return ON_Mesh_Private_SetCachedTextureCoordinatesFromMaterial(meshes, per_vertex_channels, mapping_ref, perform_cleanup, nullptr);
+}
+
+bool ON_Mesh::SetCachedTextureCoordinatesFromMaterial(const ONX_Model& onx_model, const ON_Material& material, const ON_MappingRef* mapping_ref) const
+{
+ auto get_mapping_func = [&onx_model](const ON_MappingChannel* pMC)
+ {
+ ON_TextureMapping mapping = ON_TextureMapping::SurfaceParameterTextureMapping;
+
+ ONX_ModelComponentIterator it(onx_model, ON_ModelComponent::Type::TextureMapping);
+
+ for ( ON_ModelComponentReference cr = it.FirstComponentReference(); false == cr.IsEmpty(); cr = it.NextComponentReference())
+ {
+ const ON_TextureMapping* texture_mapping = ON_TextureMapping::Cast(cr.ModelComponent());
+ if (nullptr == texture_mapping)
+ continue;
+
+ if(ON_UuidCompare(&texture_mapping->Id(), &pMC->m_mapping_id) == 0)
+ {
+ mapping = *texture_mapping;
+ break;
+ }
+ }
+
+ return mapping;
+ };
+
+ ON_SimpleArray mappings_to_cache;
+ return ::SetCachedTextureCoordinatesFromMaterial(*this, get_mapping_func, material, mapping_ref, mappings_to_cache, false);
+}
+
+static const ON_TextureCoordinates* Internal_GetCachedTextureCoordinates(const ON_Mesh& mesh, std::function get_mapping_func, const ON_Texture& texture, const ON_MappingRef* mapping_ref)
+{
+ if (texture.IsWcsProjected() || texture.IsWcsBoxProjected())
+ {
+ const ON_Xform ocsTransform = GetOcsTransform(get_mapping_func, mapping_ref);
+ const ON_UUID mappingId = WcsMappingId(ocsTransform, texture.IsWcsBoxProjected());
+ const ON_TextureCoordinates* pTCs = mesh.CachedTextureCoordinates(mappingId);
+ return pTCs;
+ }
+ else if (texture.m_mapping_channel_id >= 0)
+ {
+ if (nullptr != mapping_ref)
+ {
+ const ON_MappingChannel* pMC = mapping_ref->MappingChannel(texture.m_mapping_channel_id);
+ if (nullptr != pMC)
+ {
+ const ON_TextureCoordinates* pTCs = mesh.CachedTextureCoordinates(pMC->m_mapping_id);
+ // Probably best to not const cast and start setting the texture coordinates.
+ // All previously returned pointers to the m_TC should be kept valid.
+ //if (nullptr == pTCs)
+ //{
+ // ON_TextureMapping mapping = get_mapping_func(pMC);
+ // pTCs = const_cast(this)->SetCachedTextureCoordinatesEx(mapping, &pMC->m_object_xform, true, false);
+ //}
+ return pTCs;
+ }
+ }
+ else
+ {
+ // Return surface parameter mapping if there is nothing else
+ const ON_TextureCoordinates* pTCs = mesh.CachedTextureCoordinates(ON_TextureMapping::SurfaceParameterTextureMappingId);
+ return pTCs;
+ }
+ }
+ return nullptr;
+}
+
+const ON_TextureCoordinates* ON_Mesh::GetCachedTextureCoordinates(const ONX_Model& onx_model, const ON_Texture& texture, const ON_MappingRef* mapping_ref) const
+{
+ auto get_mapping_func = [&onx_model](const ON_MappingChannel* pMC)
+ {
+ ON_TextureMapping mapping = ON_TextureMapping::SurfaceParameterTextureMapping;
+
+ ONX_ModelComponentIterator it(onx_model, ON_ModelComponent::Type::TextureMapping);
+
+ for (ON_ModelComponentReference cr = it.FirstComponentReference(); false == cr.IsEmpty(); cr = it.NextComponentReference())
+ {
+ const ON_TextureMapping* texture_mapping = ON_TextureMapping::Cast(cr.ModelComponent());
+ if (nullptr == texture_mapping)
+ continue;
+
+ if (ON_UuidCompare(&texture_mapping->Id(), &pMC->m_mapping_id) == 0)
+ {
+ mapping = *texture_mapping;
+ break;
+ }
+ }
+
+ return mapping;
+ };
+
+ return ::Internal_GetCachedTextureCoordinates(*this, get_mapping_func, texture, mapping_ref);
+}
+
+static GET_TEXMAP_FROM_DOCUMENT pFnGetTexMapFromDocument = nullptr;
+
+// Note: We are exporting this function for use in CRhinoObject::SetCachedTextureCoordinatesFromPlugIn
+ON_DECL std::function ON_Mesh_Private_GetTextureMappingFromDocumentLambda(const CRhinoDoc& rhino_doc)
+{
+ return [&rhino_doc](const ON_MappingChannel* pMC)
+ {
+ if ( pFnGetTexMapFromDocument )
+ {
+ // Call into Rhino
+ return pFnGetTexMapFromDocument(rhino_doc, pMC);
+ }
+ else
+ {
+ return ON_TextureMapping::SurfaceParameterTextureMapping;
+ }
+ };
+}
+
+
const ON_TextureCoordinates*
ON_Mesh::CachedTextureCoordinates( const ON_UUID& mapping_id ) const
{
@@ -4393,6 +4791,18 @@ bool ON_Mesh::SetCurvatureColorAnalysisColors(
ON_SurfaceCurvatureColorMapping kappa_colors
)
{
+ if (false == this->HasPrincipalCurvatures())
+ {
+#if 0
+ // will be added April/May of 2024
+ if (kappa_colors.IsSet())
+ {
+ this->ComputeVertexPrincipalCurvatures(ON_Mesh::CurvatureSettingMethod::DiscreteDefault, false);
+ bLazy = false; // m_C[] is probably out of date even if the mapping tag is set.
+ }
+#endif
+ return this->HasPrincipalCurvatures();
+ }
const bool bSetColors = kappa_colors.IsSet() && this->HasPrincipalCurvatures();
const ON_MappingTag Ctag = kappa_colors.ColorMappingTag();
if (bSetColors && bLazy && HasVertexColors() && this->m_Ctag == Ctag)
@@ -6274,6 +6684,40 @@ void ON_Mesh::Append(int mesh_count, const ON_Mesh* const* meshes)
}
}
+ bool bAllowNewCachedTCs = (vcount0 == 0);
+ for (mi = 0; mi < mesh_count; mi++)
+ {
+ m = meshes[mi];
+ if (0 == m)
+ continue;
+
+ if (bAllowNewCachedTCs)
+ {
+ m_TC = m->m_TC;
+ bAllowNewCachedTCs = false;
+ }
+ else
+ {
+ for (int tci = 0; tci < m_TC.Count(); tci++)
+ {
+ bool bKeep = false;
+ for (int tcimi = 0; tcimi < m->m_TC.Count(); tcimi++)
+ {
+ if (m_TC[tci].m_tag == m->m_TC[tcimi].m_tag)
+ {
+ m_TC[tci].m_T.Append(m->m_TC[tcimi].m_T.Count(), m->m_TC[tcimi].m_T.Array());
+ bKeep = true;
+ }
+ }
+ if (!bKeep)
+ {
+ m_TC.Remove(tci);
+ tci--;
+ }
+ }
+ }
+ }
+
SetClosed(-99);
SetSolidOrientation(-99);
InvalidateBoundingBoxes();
@@ -8163,6 +8607,10 @@ bool ON_Mesh::ConvertQuadsToTriangles()
double ON_TriangleArea3d(ON_3dPoint A, ON_3dPoint B, ON_3dPoint C)
{
+ // MF, March 18 24:
+ // a faster and numerically more robust version would be William Kahan's version of Heron's formula,
+ // see "Accuracy and Stability of Numerical Algorithms", N. Higham.
+
// speed this up if needed
return 0.5*ON_CrossProduct(B-A,C-A).Length();
}
@@ -8713,6 +9161,19 @@ bool ON_Mesh::ComputeVertexNormals()
return rc;
}
+
+#if 0
+// Will be added April/may of 2024
+bool ON_Mesh::ComputeVertexPrincipalCurvatures(
+ ON_Mesh::CurvatureSettingMethod method,
+ bool bLazy
+)
+{
+ // NOT INCLUDED IN PUBLIC OPENNURBS
+ return false;
+}
+#endif
+
bool ON_Mesh::NormalizeTextureCoordinates()
{
ON_2fPoint t0;//, t1;
@@ -9352,6 +9813,123 @@ const ON_SurfaceCurvature ON_SurfaceCurvature::CreateFromPrincipalCurvatures(
return k;
}
+const ON_SurfaceCurvature ON_SurfaceCurvature::CreateFromGaussianAndMeanCurvatures(
+ double gaussian_curvature,
+ double mean_curvature
+)
+{
+ if (ON_IS_VALID(gaussian_curvature) && ON_IS_VALID(mean_curvature))
+ {
+ const double r = mean_curvature * mean_curvature - gaussian_curvature;
+ if (r >= 0.0)
+ {
+ const double k1 = mean_curvature + sqrt(r);
+ const double k2 = mean_curvature - sqrt(r);
+ return ON_SurfaceCurvature::CreateFromPrincipalCurvatures(k1, k2);
+ }
+ else if (r < 0.0)
+ {
+ // Dale Lear - March 2024 - RH-81078
+ // We end up here when r < 0 and gaussian_curvature > 0.
+ // We are in one of 3 cases:
+ // 1) We are dealing with a very small and noisy negative r.
+ // 2) We are dealing with esimates of mean and gaussian.
+ // 3) Something else that is uncommon.
+
+ // In case 1, mean*mean and gaussian are both larger relative to fabs(r).
+ // We don't know which input value is more precise, but people typically
+ // care more about gaussian so we use k1=k2=sign(mean)*sqrt(gaussian) instead of
+ // k1 = k2 = mean.
+ //
+ // In case 2, the value of gaussian is typically more important than the value
+ // of mean, because people are often testing to see how a mesh can be flattened.
+ //
+ // So, we assume we are at or near an umbilic (k1=k2) and set them so that
+ // k1*k2 = gaussian_curvature and sign(k1+k2) = sign(mean).
+ // These choices preserve gaussian minimize fabs( 0.5*(k1+k2) - mean_curvature).
+ const double k1 = ((mean_curvature < 0.0) ? -1.0 : 1.0) * sqrt(gaussian_curvature);
+ const double k2 = k1;
+ return ON_SurfaceCurvature::CreateFromPrincipalCurvatures(k1, k2);
+ // put a breakpoint on the line above to dectect when we are in
+ // this unusual situation.
+ }
+ }
+ if (ON_IS_VALID(gaussian_curvature))
+ {
+ // It can happen that only Gaussian was estimated, whether on purpose or not.
+ const double k1 = sqrt(fabs(gaussian_curvature));
+ const double k2 = ((gaussian_curvature < 0.0) ? -1.0 : 1.0) * k1;
+ return ON_SurfaceCurvature::CreateFromPrincipalCurvatures(k1, k2);
+ }
+ if (ON_IS_VALID(mean_curvature))
+ {
+ // It can also happen that only mean was estimated, whether on purpose or not.
+ return ON_SurfaceCurvature::CreateFromPrincipalCurvatures(mean_curvature, mean_curvature);
+ }
+ return ON_SurfaceCurvature::Nan;
+}
+
+const ON_SurfaceCurvature ON_SurfaceCurvature::FlipSurfaceOrientation() const
+{
+ // negate and reverse the order of (this->k1, this->k2)
+ return ON_SurfaceCurvature::CreateFromPrincipalCurvatures(-k2, -k1);
+}
+
+int ON_SurfaceCurvature::Compare(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ if (lhs.IsNan())
+ return rhs.IsNan() ? 0 : 1; // nans sort last
+ if (rhs.IsNan())
+ return -1; // nans sort last
+ if (lhs.k1 < rhs.k1)
+ return -1;
+ if (lhs.k1 > rhs.k1)
+ return 1;
+ if (lhs.k2 < rhs.k2)
+ return -1;
+ if (lhs.k2 > rhs.k2)
+ return 1;
+ return 0;
+}
+
+int ON_SurfaceCurvature::CompareMaximumAndMinimumPrincipalCurvatures(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ const int rc = ON_SurfaceCurvature::CompareMaximumPrincipalCurvature(lhs, rhs);
+ return 0 != rc ? rc : ON_SurfaceCurvature::CompareMinimumPrincipalCurvature(lhs, rhs);
+}
+
+int ON_SurfaceCurvature::CompareMaximumPrincipalCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ return ON_DBL::CompareValue(lhs.MaximumPrincipalCurvature(), rhs.MaximumPrincipalCurvature());
+}
+
+int ON_SurfaceCurvature::CompareMinimumPrincipalCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ return ON_DBL::CompareValue(lhs.MinimumPrincipalCurvature(), rhs.MinimumPrincipalCurvature());
+}
+
+int ON_SurfaceCurvature::CompareGaussianCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ return ON_DBL::CompareValue(lhs.GaussianCurvature(), rhs.GaussianCurvature());
+}
+
+int ON_SurfaceCurvature::CompareMeanCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ return ON_DBL::CompareValue(lhs.MeanCurvature(), rhs.MeanCurvature());
+}
+
+int ON_SurfaceCurvature::CompareMaximumRadius(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ return ON_DBL::CompareValue(lhs.MaximumRadius(), rhs.MaximumRadius());
+}
+
+int ON_SurfaceCurvature::CompareMinimumRadius(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs)
+{
+ return ON_DBL::CompareValue(lhs.MinimumRadius(), rhs.MinimumRadius());
+}
+
+
+
double ON_SurfaceCurvature::KappaValue(ON::curvature_style kappa_style) const
{
double k;
@@ -9497,10 +10075,28 @@ bool operator!=(const ON_SurfaceCurvatureColorMapping& lhs, const ON_SurfaceCurv
return false;
}
+bool operator==(
+ const ON_SurfaceCurvature& lhs,
+ const ON_SurfaceCurvature& rhs
+ )
+{
+ return (lhs.k1 == rhs.k1 && lhs.k2 == rhs.k2);
+}
+
+bool operator!=(
+ const ON_SurfaceCurvature& lhs,
+ const ON_SurfaceCurvature& rhs
+ )
+{
+ // Note well:
+ // ON_SurfaceCurvature::IsNan() is true if either of k1 or k2 is a nan.
+ // and the code below is correct.
+ return (lhs.k1 != rhs.k1 || lhs.k2 != rhs.k2) || (lhs.IsNan() && rhs.IsNan());
+}
bool ON_SurfaceCurvature::IsSet() const
{
- return (ON_UNSET_VALUE < k1&& k1 < ON_UNSET_POSITIVE_VALUE&& ON_UNSET_VALUE < k2&& k2 < ON_UNSET_POSITIVE_VALUE);
+ return (ON_UNSET_VALUE < k1 && k1 < ON_UNSET_POSITIVE_VALUE && ON_UNSET_VALUE < k2 && k2 < ON_UNSET_POSITIVE_VALUE);
}
bool ON_SurfaceCurvature::IsZero() const
@@ -9513,25 +10109,30 @@ bool ON_SurfaceCurvature::IsUnset() const
return IsSet() ? false : true;
}
+bool ON_SurfaceCurvature::IsNan() const
+{
+ return (ON_IS_NAN(k1) || ON_IS_NAN(k2));
+}
+
+
+double ON_SurfaceCurvature::MaximumPrincipalCurvature() const
+{
+ return IsSet() ? ((k1 >= k2) ? k1 : k2) : ON_DBL_QNAN;
+}
+
+double ON_SurfaceCurvature::MinimumPrincipalCurvature() const
+{
+ return IsSet() ? ((k1 <= k2) ? k1 : k2) : ON_DBL_QNAN;
+}
double ON_SurfaceCurvature::GaussianCurvature() const
{
- if (ON_IS_VALID(k1) && ON_IS_VALID(k2))
- {
- return k1 * k2;
- }
-
- return ON_DBL_QNAN;
+ return (ON_IS_VALID(k1) && ON_IS_VALID(k2)) ? (k1 * k2) : ON_DBL_QNAN;
}
double ON_SurfaceCurvature::MeanCurvature() const
{
- if (ON_IS_VALID(k1) && ON_IS_VALID(k2))
- {
- return 0.5 * (k1 + k2);
- }
-
- return ON_DBL_QNAN;
+ return (ON_IS_VALID(k1) && ON_IS_VALID(k2)) ? ((k1 == k2) ? k1 : 0.5 * (k1 + k2)) : ON_DBL_QNAN;
}
double ON_SurfaceCurvature::MinimumRadius() const
@@ -9557,7 +10158,7 @@ double ON_SurfaceCurvature::MaximumRadius() const
{
// k = minimum directional curvature
double k;
- if (k1 * k2 <= 0.0 || fabs(k1) <= 1e-300 || fabs(k2) <= 1e-300)
+ if (ON_DBL::Sign(k1)*ON_DBL::Sign(k2) <= 0 || fabs(k1) <= 1e-300 || fabs(k2) <= 1e-300)
{
// If principal curvatures have opposite signs,
// there is a direction with zero directional curvature.
@@ -12731,6 +13332,12 @@ void ON_MappingTag::Dump( ON_TextLog& text_log ) const
case ON_TextureMapping::TYPE::false_colors:
text_log.Print("false colors");
break;
+ case ON_TextureMapping::TYPE::wcs_projection:
+ text_log.Print("wcs projection");
+ break;
+ case ON_TextureMapping::TYPE::wcsbox_projection:
+ text_log.Print("wcs box projection");
+ break;
}
text_log.Print("\n");
diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h
index 1bc82310..3e463856 100644
--- a/opennurbs_mesh.h
+++ b/opennurbs_mesh.h
@@ -4253,7 +4253,78 @@ Returns:
);
bool ComputeVertexNormals(); // uses face normals to cook up a vertex normal
-
+
+#if 0
+ // Will be added April/may of 2024
+
+#pragma region NOT_YET_RH_C_SHARED_ENUM [ON_Mesh::CurvatureSettingMethod] [Rhino.Geometry.MeshCurvatureSettingMethod] [byte]
+ ///
+ /// Enumberates the methods available to set the vertex principal curvature
+ /// values saved in the ON_Mesh m_K[] array.
+ ///
+ enum class CurvatureSettingMethod : unsigned char
+ {
+ ///
+ /// The most appropriate method for current context will be used.
+ ///
+ Unset = 0,
+
+ ///
+ /// The mesh is being created from a surface and the mesh's principal curvatures
+ /// will be set from the surface's first and second partial deriatives.
+ ///
+ SurfaceEvaluation = 1,
+
+ ///
+ /// The mesh's principal curvatures will be set using
+ /// ON_SurfaceCurvature::CreateFromGaussianAndMeanCurvatures()
+ /// with input values from
+ /// ON_Mesh::ComputeDiscreteGaussianMeshCurvature() and
+ /// ON_Mesh::ComputeDiscreteLaplacianMeanMeshCurvatures().
+ ///
+ DiscreteDefault = 2,
+
+ ///
+ /// The mesh's principal curvatures will be set using
+ /// ON_SurfaceCurvature::CreateFromGaussianAndMeanCurvatures()
+ /// with input values from
+ /// ON_Mesh::ComputeDiscreteGaussianMeshCurvature() and
+ /// ON_Mesh::ComputeDiscreteLaplacianMeanMeshCurvatures().
+ ///
+ DiscreteLaplacianMean = 3,
+
+ ///
+ /// The mesh's principal curvatures will be set using
+ /// ON_SurfaceCurvature::CreateFromGaussianAndMeanCurvatures()
+ /// with input values from
+ /// ON_Mesh::ComputeDiscreteGaussianMeshCurvature() and
+ /// ON_Mesh::ComputeDiscreteDihedralMeanMeshCurvature().
+ ///
+ DiscreteDihedralMean = 4
+ };
+#pragma endregion
+
+ ///
+ /// Use discrete curvature estimates to set the principal curvature values
+ /// in the mesh's m_K[] array.
+ ///
+ ///
+ /// Specifies the method to use.
+ /// One of the discrete methods must be specified.
+ /// When in doubt, pass ON_Mesh::CurvatureSettingMethod::DiscreteDefault.
+ ///
+ ///
+ /// If bLazy=true, only unset m_K[] values will be set.
+ /// If bLazy=false, only unset m_K[] values will be set.
+ ///
+ ///
+ bool ComputeVertexPrincipalCurvatures(
+ ON_Mesh::CurvatureSettingMethod method,
+ bool bLazy
+ );
+
+#endif
+
//////////
// Scales textures so the texture domains are [0,1] and
// eliminates any texture rotations.
@@ -4332,18 +4403,150 @@ Returns:
bool bSeamCheck = true
);
+ /*
+ Description:
+ Returns true if the mesh has at least one set of valid
+ cached texture coordinates in the m_TC array.
+ Returns:
+ True if the mesh contains cached texture coordinates.
+ See Also:
+ ON_Mesh::SetCachedTextureCoordinatesFromMaterial
+ ON_Mesh::CachedTextureCoordinates
+ ON_Mesh::SetCachedTextureCoordinatesEx
+ */
bool HasCachedTextureCoordinates() const;
+
+ /*
+ Description:
+ Prepares the cached texture coordinates by filling the
+ m_TC array with the relevant texture coordinates needed
+ to render the provided material using the provided
+ mapping ref and ONX model.
+ Call this function first if you are planning on storing
+ the results from repeated calls to any of the following
+ methods:
+ * CachedTextureCoordinates
+ * SetCachedTextureCoordinates
+ * SetCachedTextureCoordinatesEx
+ * GetCachedTextureCoordinates
+ Parameters:
+ onx_model - [in]
+ The ONX model that contains the texture mappings.
+ material - [in]
+ The material which contains the textures we want to
+ calculate the texture coordinates for.
+ mapping_ref - [in]
+ The texture mapping ref to use to get at the texture mapping.
+ Returns:
+ True if successful.
+ See Also:
+ ON_Mesh::CachedTextureCoordinates
+ ON_Mesh::SetCachedTextureCoordinatesEx
+ */
+ bool SetCachedTextureCoordinatesFromMaterial(const class ONX_Model& onx_model, const ON_Material& material, const ON_MappingRef* mapping_ref) const;
+
+ /*
+ Description:
+ Returns the cached texture coordinates corresponding to
+ the provided texture if they exist. If they don't
+ exist, they can be created using the following methods:
+ * SetCachedTextureCoordinatesFromMaterial
+ * SetCachedTextureCoordinates
+ * SetCachedTextureCoordinatesEx
+ NOTE: If you store the pointers of the results from this
+ function, you need to first call
+ SetCachedTextureCoordinatesFromMaterial to make sure the
+ pointers don't get invalidated by subsequent calls to
+ functions like SetCachedTextureCoordinates and
+ SetCachedTextureCoordinatesEx.
+ Parameters:
+ onx_model - [in]
+ The ONX model that contains the texture mappings.
+ texture - [in]
+ The texture to calculate the texture coordinates for.
+ mapping_ref - [in]
+ The texture mapping ref to use to get at the texture mapping.
+ Returns:
+ A pointer to the matching cached texture coordinates,
+ nullptr if none exist.
+ See Also:
+ ON_Mesh::SetCachedTextureCoordinatesFromMaterial
+ ON_Mesh::SetCachedTextureCoordinatesEx
+ */
+ const ON_TextureCoordinates* GetCachedTextureCoordinates(const class ONX_Model& onx_model, const ON_Texture& texture, const ON_MappingRef* mapping_ref) const;
+
+
+ /*
+ Description:
+ Returns the cached texture coordinates corresponding to
+ the provided mapping id if they exist. If they don't
+ exist, they can be created using the following methods:
+ * SetCachedTextureCoordinatesFromMaterial
+ * SetCachedTextureCoordinates
+ * SetCachedTextureCoordinatesEx
+
+ NOTE: If you store the pointers of the results from this
+ function, you need to first call
+ SetCachedTextureCoordinatesFromMaterial to make sure the
+ pointers don't get invalidated by subsequent calls to
+ functions like SetCachedTextureCoordinates and
+ SetCachedTextureCoordinatesEx.
+ Parameters:
+ mapping_id - [in]
+ The texture mapping to use for the calculation.
+ Returns:
+ A pointer to the matching cached texture coordinates,
+ nullptr if none exist.
+ See Also:
+ ON_Mesh::SetCachedTextureCoordinatesFromMaterial
+ ON_Mesh::SetCachedTextureCoordinatesEx
+ */
const ON_TextureCoordinates* CachedTextureCoordinates(
const ON_UUID& mapping_id
) const;
+ // Use SetCachedTextureCoordinatesEx instead
const ON_TextureCoordinates* SetCachedTextureCoordinates(
const class ON_TextureMapping& mapping,
const class ON_Xform* mesh_xform = 0,
bool bLazy = true
);
+ /*
+ Description:
+ Returns the cached texture coordinates corresponding to
+ the provided mapping and other parameters. If they don't
+ exist, this function will attempt to create them first.
+ If they do exist and bLazy is true, then no calculation
+ is performed.
+
+ NOTE: Subsequent calls to this function with different
+ parameters can invalidate previously returned
+ ON_TextureCoordinates pointers. If you want store the
+ pointers of previous results, you need to first call
+ SetCachedTextureCoordinatesFromMaterial.
+ Parameters:
+ mapping - [in]
+ The texture mapping to use for the calculation.
+ mesh_xform - [in]
+ If not nullptr, the mapping calculation is performed as
+ if the mesh were transformed by mesh_xform
+ bLazy - [in]
+ If true and the m_TC[] values were set using the same
+ mapping parameters, then no calculation is performed.
+ bSeamCheck - [in]
+ If true then some mesh edges might be unwelded to better
+ represent UV discontinuities in the texture mapping.
+ This only happens for the following mappings:
+ Box, Sphere, Cylinder.
+ Returns:
+ A pointer to the cached texture coordinates, nullptr if
+ the function failed to calculate the texture coordinates.
+ See Also:
+ ON_Mesh::SetCachedTextureCoordinatesFromMaterial
+ ON_Mesh::CachedTextureCoordinates
+ */
const ON_TextureCoordinates* SetCachedTextureCoordinatesEx(
const class ON_TextureMapping& mapping,
const class ON_Xform* mesh_xform = 0,
diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp
index 2d22b4c4..c9cf67ed 100644
--- a/opennurbs_nurbssurface.cpp
+++ b/opennurbs_nurbssurface.cpp
@@ -580,8 +580,7 @@ bool ON_NurbsSurface::IsValid( ON_TextLog* text_log ) const
else
{
rc = true;
- int i;
- for ( i = 0; i < 2 && rc; i++ )
+ for ( int i = 0; i < 2 && rc; i++ )
{
rc = false;
if (m_order[i] < 2 )
@@ -640,6 +639,23 @@ bool ON_NurbsSurface::IsValid( ON_TextLog* text_log ) const
}
}
}
+
+ if (rc)
+ {
+ const int cvdim = CVSize();
+ for (int i = 0; m_cv_count[0] > i; ++i)
+ {
+ for (int j = 0; m_cv_count[1] > j; ++j)
+ {
+ const double* cv = CV(i, j);
+ for (int k = 0; cvdim > k; ++k)
+ {
+ if (false == ON_CV_COORDINATE_IS_VALID(cv[k]))
+ return false;
+ }
+ }
+ }
+ }
}
return rc;
diff --git a/opennurbs_point.h b/opennurbs_point.h
index b253022b..c16996ec 100644
--- a/opennurbs_point.h
+++ b/opennurbs_point.h
@@ -2253,12 +2253,136 @@ double ON_MaximumCoordinate(const double* data, int dim, bool is_rat, int count,
class ON_CLASS ON_SurfaceCurvature
{
public:
+
+ ///
+ /// Create an ON_SurfaceCurvature from the principal curvature values.
+ /// The principal curvature values are the most fundamental curvature properties
+ /// of a surface. Other curvatures are calculated from them. For example
+ /// gaussian curvature = k1 * k2 and mean curvature = (k1 + k2)/2.
+ ///
+ ///
+ ///
+ ///
+ /// An ON_SurfaceCurvature with the specified principal curvatures.
+ ///
static const ON_SurfaceCurvature CreateFromPrincipalCurvatures(
double k1,
double k2
);
+
+ ///
+ /// Create an ON_SurfaceCurvature from a gaussian and mean curvature values using the relationsip
+ /// between the principal curvatures, gaussian and mean.
+ /// k1 = mean + sqrt(mean*mean - gaussian)
+ /// k2 = mean - sqrt(mean*mean - gaussian)
+ /// If the radicand is negative, we assume we're dealing with a bit of numerical noise or
+ /// estimates and set the principal curvatures
+ /// k1 = k2 = sign(mean)*sqrt(gaussian)
+ ///
+ ///
+ /// Gaussian curvature = k1*k2 (product of principal curvatures).
+ ///
+ /// Mean curvature = (k1+k2)/2 (average of principal curvatures).
+ ///
+ ///
+ /// A surface curvature with GaussianCurvature() = gaussian_curvature
+ /// and MeanCurvature() = gaussian_curvature.
+ ///
+ static const ON_SurfaceCurvature CreateFromGaussianAndMeanCurvatures(
+ double gaussian_curvature,
+ double mean_curvature
+ );
+
+ ///
+ /// The sign of the principal curvatures are with respect to a choice
+ /// of surface orientation. This function returns the principal curvatures
+ /// of the surface with opposite choice of orientation.
+ ///
+ ///
+ /// ON_SurfaceCurvature::CreateFromPrincipalCurvatures(-this->k2, -this->k1);
+ ///
+ const ON_SurfaceCurvature FlipSurfaceOrientation() const;
+
+ ///
+ /// Dictionary compare k1 and k2. Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int Compare(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// Dictionary compare lhs.MaximumPrincipalCurvature() and rhs.MaximumPrincipalCurvature().
+ /// Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int CompareMaximumAndMinimumPrincipalCurvatures(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// Compare lhs.MaximumPrincipalCurvature() and rhs.MaximumPrincipalCurvature().
+ /// Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int CompareMaximumPrincipalCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// Compare lhs.MinimumPrincipalCurvature() and rhs.MinimumPrincipalCurvature().
+ /// Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int CompareMinimumPrincipalCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// Compare lhs.MaximumRadius() and rhs.MaximumRadius().
+ /// Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int CompareMaximumRadius(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// Compare lhs.MinimumRadius() and rhs.MinimumRadius().
+ /// Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int CompareMinimumRadius(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// Compare lhs.GaussianCurvature() and rhs.GaussianCurvature().
+ /// Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int CompareGaussianCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// Compare lhs.MeanCurvature() and rhs.MeanCurvature().
+ /// Values with nans sort last.
+ ///
+ ///
+ ///
+ /// -1, 0 or +1.
+ static int CompareMeanCurvature(const ON_SurfaceCurvature& lhs, const ON_SurfaceCurvature& rhs);
+
+ ///
+ /// k1 = k2 = ON_DBL_QNAN
+ ///
static const ON_SurfaceCurvature Nan;
+
+ ///
+ /// k1 = k2 = 0
+ ///
static const ON_SurfaceCurvature Zero;
///
@@ -2270,21 +2394,36 @@ public:
double k1, k2; // principal curvatures
public:
+
+ /// True if k1 and k2 are both valid finite values.
bool IsSet() const;
+
+ /// True if k1 and k2 are both zero.
bool IsZero() const;
+
+ /// True if either of k1 or k2 is not a valid finite value.
bool IsUnset() const;
+ /// True if either of k1 or k2 is a nan.
+ bool IsNan() const;
+
public:
///
/// The Gaussian curvature is k1*k2.
///
- /// The Gausian curvature.
+ ///
+ /// If this is set, the Gausian curvature is returned.
+ /// Otherwise ON_DBL_QNAN is returned.
+ ///
double GaussianCurvature() const;
///
/// The mean curvature is (k1+k2)/2.
///
- /// The signed mean curvature.
+ ///
+ /// If this is set, the signed mean curvature is returned.
+ /// Otherwise ON_DBL_QNAN is returned.
+ ///
double MeanCurvature() const;
///
@@ -2328,9 +2467,42 @@ public:
///
double KappaValue(ON::curvature_style kappa_style) const;
+ ///
+ /// If this is set, the maximum of k1 and k2 is returned.
+ /// Otherwise ON_DBL_QNAN is returned.
+ ///
+ double MaximumPrincipalCurvature() const;
+ ///
+ /// If this is set, the minimum of k1 and k2.
+ /// Otherwise ON_DBL_QNAN is returned.
+ ///
+ double MinimumPrincipalCurvature() const;
};
+ON_DECL
+///
+///
+///
+/// Returns (lhs.k1==rhs.k1 && lhs.k2==rhs.k2)
+/// Note that if any princial curvature is a nan, then false is returned.
+///
+bool operator==(
+ const ON_SurfaceCurvature& lhs,
+ const ON_SurfaceCurvature& rhs
+ );
+
+ON_DECL
+///
+///
+///
+/// Returns (lhs.k1 != rhs.k1 || lhs.k2!=rhs.k2) || (lhs.IsNan() && rhs.IsNan()).
+///
+bool operator!=(
+ const ON_SurfaceCurvature& lhs,
+ const ON_SurfaceCurvature& rhs
+ );
+
#if defined(ON_DLL_TEMPLATE)
diff --git a/opennurbs_polyline.cpp b/opennurbs_polyline.cpp
index 65f6c698..6f9c3f2e 100644
--- a/opennurbs_polyline.cpp
+++ b/opennurbs_polyline.cpp
@@ -38,16 +38,21 @@ bool ON_Polyline::IsValid( double tolerance ) const
{
for ( i = 1; rc && i < m_count; i++ )
{
- if ( m_a[i].DistanceTo(m_a[i-1]) <= tolerance )
+ if (false == m_a[i].IsValid() || false == m_a[i-1].IsValid())
+ rc = false;
+ else if ( m_a[i].DistanceTo(m_a[i-1]) <= tolerance )
rc = false;
}
if ( rc && m_count < 4 && m_a[0].DistanceTo(m_a[m_count-1]) <= tolerance )
rc = false;
}
- else {
+ else
+ {
for ( i = 1; rc && i < m_count && rc; i++ )
{
- if ( m_a[i] == m_a[i-1] )
+ if (false == m_a[i].IsValid() || false == m_a[i - 1].IsValid())
+ rc = false;
+ else if ( m_a[i] == m_a[i-1] )
rc = false;
}
if ( rc && m_count < 4 && m_a[0] == m_a[m_count-1] )
diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h
index c9e95bd4..5822a3f4 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 6
+#define RMA_VERSION_MINOR 7
////////////////////////////////////////////////////////////////
//
@@ -15,9 +15,9 @@
//
#define RMA_VERSION_YEAR 2024
#define RMA_VERSION_MONTH 4
-#define RMA_VERSION_DATE 2
-#define RMA_VERSION_HOUR 11
-#define RMA_VERSION_MINUTE 0
+#define RMA_VERSION_DATE 3
+#define RMA_VERSION_HOUR 5
+#define RMA_VERSION_MINUTE 55
////////////////////////////////////////////////////////////////
//
@@ -35,8 +35,8 @@
// 3 = build system release build
#define RMA_VERSION_BRANCH 0
-#define VERSION_WITH_COMMAS 8,6,24093,11000
-#define VERSION_WITH_PERIODS 8.6.24093.11000
+#define VERSION_WITH_COMMAS 8,7,24094,5550
+#define VERSION_WITH_PERIODS 8.7.24094.05550
#define COPYRIGHT "Copyright (C) 1993-2024, 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 "SR6"
-#define RMA_VERSION_NUMBER_SR_WSTRING L"SR6"
+#define RMA_VERSION_NUMBER_SR_STRING "SR7"
+#define RMA_VERSION_NUMBER_SR_WSTRING L"SR7"
-#define RMA_VERSION_WITH_PERIODS_STRING "8.6.24093.11000"
-#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.6.24093.11000"
+#define RMA_VERSION_WITH_PERIODS_STRING "8.7.24094.05550"
+#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.7.24094.05550"
diff --git a/opennurbs_rand.h b/opennurbs_rand.h
index 90a02089..f76ad8ab 100644
--- a/opennurbs_rand.h
+++ b/opennurbs_rand.h
@@ -186,9 +186,8 @@ public:
/*
Description:
- Places two Gaussians with mean zero and standard devation one,
- generated by a Box-Muller transform, into the doubles pointed to by its arguments.
- /*
+ Places two Gaussians with mean zero and standard devation one,
+ generated by a Box-Muller transform, into the doubles pointed to by its arguments.
Parameters:
u - [out]
v - [out]
diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp
index 2d6ab95c..ba2a81a2 100644
--- a/opennurbs_statics.cpp
+++ b/opennurbs_statics.cpp
@@ -287,6 +287,85 @@ const double ON_DBL_QNAN = Internal_ON__dblinithelper(1);
const double ON_DBL_PINF = Internal_ON__dblinithelper(2);
const double ON_DBL_NINF = -Internal_ON__dblinithelper(2);
+const double ON_DBL::Nan = ON_DBL_QNAN;
+const double ON_DBL::PositiveInfinity = ON_DBL_PINF;
+const double ON_DBL::NegativeInfinity = ON_DBL_NINF;
+
+const double ON_DBL::PositiveMax = ON_DBL_MAX;
+const double ON_DBL::NegativeMax = -ON_DBL_MAX;
+
+const double ON_DBL::PositiveMin = ON_DBL_MIN;
+const double ON_DBL::NegativeMin = -ON_DBL_MIN;
+
+const double ON_DBL::Unset = ON_UNSET_VALUE;
+const double ON_DBL::PositiveUnset = ON_UNSET_POSITIVE_VALUE;
+
+bool ON_DBL::IsValid(double x)
+{
+ return x > ON_UNSET_VALUE && x < ON_UNSET_POSITIVE_VALUE;
+}
+
+bool ON_DBL::IsNan(double x)
+{
+ return (x == x) ? false : true;
+}
+
+int ON_DBL::Sign(double x)
+{
+ return (x > 0.0 ? 1 : ((x < 0.0) ? -1 : 0));
+}
+
+bool ON_DBL::IsInfinity(double x)
+{
+ return (x >= ON_DBL_PINF || x <= ON_DBL_NINF);
+}
+
+bool ON_DBL::IsPositiveInfinity(double x)
+{
+ return (x >= ON_DBL_PINF);
+}
+
+bool ON_DBL::IsNegativeInfinity(double x)
+{
+ return (x <= ON_DBL_NINF);
+}
+
+bool ON_DBL::IsNotNan(double x)
+{
+ return (x == x);
+}
+
+bool ON_DBL::IsUnset(double x)
+{
+ return (ON_UNSET_VALUE == fabs(x));
+}
+
+int ON_DBL::CompareValue(double lhs, double rhs)
+{
+ if (lhs < rhs)
+ return -1;
+ if (lhs > rhs)
+ return 1;
+ if (lhs == rhs)
+ return 0;
+
+ // At least one of lhs or rhs is a nan. Sort nans to end
+ return (ON_IS_NAN(lhs) ? 1 : 0) - (ON_IS_NAN(rhs) ? 1 : 0);
+}
+
+
+int ON_DBL::Compare(const double* lhs, const double* rhs)
+{
+ // sort nullpt to the end
+ if (lhs == rhs)
+ return 0;
+ if (nullptr == lhs)
+ return 1;
+ if (nullptr == rhs)
+ return -1;
+ return ON_DBL::CompareValue(*lhs, *rhs);
+}
+
const float ON_FLT_QNAN = Internal_ON__fltinithelper(1);
const float ON_FLT_PINF = Internal_ON__fltinithelper(2);
const float ON_FLT_NINF = -Internal_ON__fltinithelper(2);
@@ -334,6 +413,10 @@ const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Nan = ON_SubDEdgeSharpness::Fro
const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Crease = ON_SubDEdgeSharpness::FromConstant(ON_SubDEdgeSharpness::CreaseValue);
+const ON_SubDFaceCornerDex ON_SubDFaceCornerDex::Unset(0, 0);
+
+const ON_SubDFaceParameter ON_SubDFaceParameter::Nan = { ON_SubDFaceCornerDex::Unset, ON_DBL_QNAN, ON_DBL_QNAN };
+
const double ON_SubDSectorType::MinimumCornerAngleRadians = (2.0*ON_PI)/((double)(ON_SubDSectorType::MaximumCornerAngleIndex));
const double ON_SubDSectorType::MaximumCornerAngleRadians = 2.0*ON_PI - ON_SubDSectorType::MinimumCornerAngleRadians;
@@ -2757,6 +2840,10 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::NullVertex = { 2 };
const ON_SubDComponentPtr ON_SubDComponentPtr::NullEdge = { 4 };
const ON_SubDComponentPtr ON_SubDComponentPtr::NullFace = { 6 };
+const ON_SubDComponentId ON_SubDComponentId::Unset(ON_SubDComponentPtr::Null);
+
+const ON_SubDComponentParameter ON_SubDComponentParameter::Unset = {};
+
const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::Null = ON_SubDComponentPtrPair::Create(ON_SubDComponentPtr::Null,ON_SubDComponentPtr::Null);
const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::NullAndNan = ON_SubDComponentAndNumber::Create(ON_SubDComponentPtr::Null, ON_DBL_QNAN);
diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp
index 750ec677..fd8c1c88 100644
--- a/opennurbs_subd.cpp
+++ b/opennurbs_subd.cpp
@@ -1543,7 +1543,12 @@ bool ON_SubDComponentTest::Passes(const ON_SubDFace* f) const
ON_SubDComponentId::ON_SubDComponentId(ON_SubDComponentPtr::Type component_type, unsigned int component_id)
: m_id(component_id)
- , m_type(component_type)
+ , m_type_and_dir((unsigned char)component_type)
+{}
+
+ON_SubDComponentId::ON_SubDComponentId(ON_SubDComponentPtr::Type component_type, unsigned int component_id, ON__UINT_PTR dir)
+ : m_id(component_id)
+ , m_type_and_dir(((unsigned char)component_type) | ((unsigned char)(dir%2)))
{}
ON_SubDComponentId::ON_SubDComponentId(ON_SubDComponentPtr cptr)
@@ -1552,7 +1557,8 @@ ON_SubDComponentId::ON_SubDComponentId(ON_SubDComponentPtr cptr)
if (nullptr != b)
{
m_id = b->m_id;
- m_type = cptr.ComponentType();
+ Internal_SetType(cptr.ComponentType());
+ Internal_SetDir(0 == cptr.ComponentDirection() ? 0u : 1u);
}
}
@@ -1561,7 +1567,18 @@ ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDVertex* v)
if (nullptr != v)
{
m_id = v->m_id;
- m_type = ON_SubDComponentPtr::Type::Vertex;
+ Internal_SetType(ON_SubDComponentPtr::Type::Vertex);
+ }
+}
+
+ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDVertexPtr vptr)
+{
+ const ON_SubDVertex* v = vptr.Vertex();
+ if (nullptr != v)
+ {
+ m_id = v->m_id;
+ Internal_SetType(ON_SubDComponentPtr::Type::Vertex);
+ Internal_SetDir(0 == vptr.VertexDirection() ? 0u : 1u);
}
}
@@ -1570,7 +1587,18 @@ ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDEdge* e)
if (nullptr != e)
{
m_id = e->m_id;
- m_type = ON_SubDComponentPtr::Type::Edge;
+ Internal_SetType(ON_SubDComponentPtr::Type::Edge);
+ }
+}
+
+ON_SubDComponentId::ON_SubDComponentId(ON_SubDEdgePtr eptr)
+{
+ const ON_SubDEdge* e = eptr.Edge();
+ if (nullptr != e)
+ {
+ m_id = e->m_id;
+ Internal_SetType(ON_SubDComponentPtr::Type::Edge);
+ Internal_SetDir(0 == eptr.EdgeDirection() ? 0u : 1u);
}
}
@@ -1579,16 +1607,60 @@ ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDFace* f)
if (nullptr != f)
{
m_id = f->m_id;
- m_type = ON_SubDComponentPtr::Type::Face;
+ Internal_SetType(ON_SubDComponentPtr::Type::Face);
+ }
+}
+
+ON_SubDComponentId::ON_SubDComponentId(ON_SubDFacePtr fptr)
+{
+ const ON_SubDFace* f = fptr.Face();
+ if (nullptr != f)
+ {
+ m_id = f->m_id;
+ Internal_SetType(ON_SubDComponentPtr::Type::Face);
+ Internal_SetDir(0 == fptr.FaceDirection() ? 0u : 1u);
+ }
+}
+
+ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDFace* f, unsigned face_corner_index)
+{
+ if (nullptr != f)
+ {
+ m_id = f->m_id;
+ Internal_SetType(ON_SubDComponentPtr::Type::Face);
+ if (face_corner_index < (unsigned)f->m_edge_count && (unsigned)f->m_edge_count < 4096u)
+ {
+ Internal_SetValueA(face_corner_index);
+ Internal_SetValueB(f->m_edge_count);
+ }
+ }
+}
+
+ON_SubDComponentId::ON_SubDComponentId(ON_SubDFacePtr fptr, unsigned face_corner_index)
+{
+ const ON_SubDFace* f = fptr.Face();
+ if (nullptr != f)
+ {
+ m_id = f->m_id;
+ Internal_SetType(ON_SubDComponentPtr::Type::Face);
+ Internal_SetDir(0 == fptr.FaceDirection() ? 0u : 1u);
+ if (face_corner_index < (unsigned)f->m_edge_count && (unsigned)f->m_edge_count < 4096u)
+ {
+ Internal_SetValueA(face_corner_index);
+ Internal_SetValueB(f->m_edge_count);
+ }
}
}
+
int ON_SubDComponentId::CompareTypeAndId(const ON_SubDComponentId& lhs, const ON_SubDComponentId& rhs)
{
- if (static_cast(lhs.m_type) < static_cast(rhs.m_type))
+ const unsigned char lhs_type = (lhs.m_type_and_dir & ON_SubDComponentId::bits_type_mask);
+ const unsigned char rhs_type = (rhs.m_type_and_dir & ON_SubDComponentId::bits_type_mask);
+ if (lhs_type < rhs_type)
return -1;
- if (static_cast(lhs.m_type) > static_cast(rhs.m_type))
+ if (lhs_type > rhs_type)
return 1;
if (lhs.m_id < rhs.m_id)
return -1;
@@ -1597,6 +1669,22 @@ int ON_SubDComponentId::CompareTypeAndId(const ON_SubDComponentId& lhs, const ON
return 0;
}
+int ON_SubDComponentId::CompareTypeAndIdAndDirection(const ON_SubDComponentId& lhs, const ON_SubDComponentId& rhs)
+{
+ const int rc = CompareTypeAndId(lhs, rhs);
+ if (0 != rc)
+ return rc;
+
+ // compare dir
+ const unsigned char lhs_dir = (lhs.m_type_and_dir & ON_SubDComponentId::bits_dir_mask);
+ const unsigned char rhs_dir = (rhs.m_type_and_dir & ON_SubDComponentId::bits_dir_mask);
+ if (lhs_dir < rhs_dir)
+ return -1;
+ if (lhs_dir > rhs_dir)
+ return 1;
+ return 0;
+}
+
int ON_SubDComponentId::CompareTypeAndIdFromPointer(const ON_SubDComponentId* lhs, const ON_SubDComponentId* rhs)
{
if (lhs == rhs)
@@ -1608,14 +1696,20 @@ int ON_SubDComponentId::CompareTypeAndIdFromPointer(const ON_SubDComponentId* lh
if (nullptr == rhs)
return -1;
- if (static_cast(lhs->m_type) < static_cast(rhs->m_type))
+ // compare type
+ const unsigned char lhs_type = (lhs->m_type_and_dir & ON_SubDComponentId::bits_type_mask);
+ const unsigned char rhs_type = (rhs->m_type_and_dir & ON_SubDComponentId::bits_type_mask);
+ if (lhs_type < rhs_type)
return -1;
- if (static_cast(lhs->m_type) > static_cast(rhs->m_type))
+ if (lhs_type > rhs_type)
return 1;
+
+ // compare id
if (lhs->m_id < rhs->m_id)
return -1;
if (lhs->m_id > rhs->m_id)
return 1;
+
return 0;
}
@@ -1624,14 +1718,198 @@ unsigned int ON_SubDComponentId::ComponentId() const
return m_id;
}
+void ON_SubDComponentId::Internal_SetType(ON_SubDComponentPtr::Type type)
+{
+ m_type_and_dir &= ~((unsigned char)ON_SubDComponentId::bits_type_mask);
+ m_type_and_dir |= (((unsigned char)type) & ON_SubDComponentId::bits_type_mask);
+}
+
+void ON_SubDComponentId::Internal_SetDir(unsigned dir)
+{
+ // invalid dir is treaded as 0
+ if (1 == dir)
+ m_type_and_dir |= ((unsigned char)ON_SubDComponentId::bits_dir_mask);
+ else
+ m_type_and_dir &= ~((unsigned char)ON_SubDComponentId::bits_dir_mask);
+}
+
+void ON_SubDComponentId::Internal_SetValueA(unsigned a)
+{
+ if (a < 0x0FFFu)
+ {
+ m_valueAB[0] = (unsigned char)(a & 0x00FFu);
+ const unsigned char ahigh = (unsigned char)((a & 0x0F00u) >> 8);
+ m_valueAB[2] = (m_valueAB[2] & 0xF0u) | ahigh;
+ }
+}
+
+void ON_SubDComponentId::Internal_SetValueB(unsigned b)
+{
+ if (b < 0x0FFFu)
+ {
+ m_valueAB[1] = (unsigned char)(b & 0x00FFu);
+ const unsigned char bhigh = (unsigned char)((b & 0x0F00u) >> 4);
+ m_valueAB[2] = (m_valueAB[2] & 0x0Fu) | bhigh;
+ }
+}
+
+unsigned ON_SubDComponentId::Internal_ValueA() const
+{
+ const unsigned alow = m_valueAB[0];
+ const unsigned ahigh = ((unsigned)(m_valueAB[2] & 0x0Fu)) << 8;
+ return alow | ahigh;
+}
+
+unsigned ON_SubDComponentId::Internal_ValueB() const
+{
+ const unsigned blow = m_valueAB[0];
+ const unsigned bhigh = ((unsigned)(m_valueAB[2] & 0xF0u)) << 4;
+ return blow | bhigh;
+}
+
+unsigned ON_SubDComponentId::ComponentDirection() const
+{
+ return (m_type_and_dir & ON_SubDComponentId::bits_dir_mask);
+}
+
ON_SubDComponentPtr::Type ON_SubDComponentId::ComponentType() const
{
- return m_type;
+ return ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned(m_type_and_dir & ON_SubDComponentId::bits_type_mask);
}
bool ON_SubDComponentId::IsSet() const
{
- return 0 != m_id && ON_SubDComponentPtr::Type::Unset != m_type;
+ return 0 != m_id && ON_SubDComponentPtr::Type::Unset != ComponentType();
+}
+
+bool ON_SubDComponentId::IsNotSet() const
+{
+ return 0 == m_id || ON_SubDComponentPtr::Type::Unset == ComponentType();
+}
+
+bool ON_SubDComponentId::IsVertexId() const
+{
+ return m_id > 0 && ON_SubDComponentPtr::Type::Vertex == this->ComponentType();
+}
+
+bool ON_SubDComponentId::IsEdgeId() const
+{
+ return m_id > 0 && ON_SubDComponentPtr::Type::Edge == this->ComponentType();
+}
+
+bool ON_SubDComponentId::IsFaceId() const
+{
+ return m_id > 0 && ON_SubDComponentPtr::Type::Face == this->ComponentType();
+}
+
+const ON_SubDComponentId ON_SubDComponentId::Reversed() const
+{
+ ON_SubDComponentId c = *this;
+ c.Internal_SetDir(1u - this->ComponentDirection());
+ return c;
+}
+
+const ON_SubDComponentPtr ON_SubDComponentId::ComponentPtr(const class ON_SubD* subd) const
+{
+ return nullptr != subd ? this->ComponentPtr(*subd) : ON_SubDComponentPtr::Null;
+}
+
+const ON_SubDComponentPtr ON_SubDComponentId::ComponentPtr(const class ON_SubD& subd) const
+{
+ if (0 != m_id)
+ {
+ ON_SubDComponentPtr cptr;
+ switch (this->ComponentType())
+ {
+ case ON_SubDComponentPtr::Type::Vertex:
+ cptr = ON_SubDComponentPtr::Create(subd.VertexFromId(m_id));
+ break;
+ case ON_SubDComponentPtr::Type::Edge:
+ cptr = ON_SubDComponentPtr::Create(subd.EdgeFromId(m_id));
+ break;
+ case ON_SubDComponentPtr::Type::Face:
+ cptr = ON_SubDComponentPtr::Create(subd.FaceFromId(m_id));
+ break;
+ default:
+ cptr = ON_SubDComponentPtr::Null;
+ break;
+ }
+ if (cptr.IsNotNull() && 0 != (m_type_and_dir & ON_SubDComponentId::bits_dir_mask))
+ cptr.SetComponentDirection(1);
+ return cptr;
+ }
+ return ON_SubDComponentPtr::Null;
+}
+
+const ON_SubDVertex* ON_SubDComponentId::Vertex(const class ON_SubD& subd) const
+{
+ return this->ComponentPtr(subd).Vertex();
+}
+
+const ON_SubDVertexPtr ON_SubDComponentId::VertexPtr(const class ON_SubD& subd) const
+{
+ return this->ComponentPtr(subd).VertexPtr();
+}
+
+const ON_SubDVertex* ON_SubDComponentId::Vertex(const class ON_SubD* subd) const
+{
+ return (nullptr != subd) ? this->ComponentPtr(*subd).Vertex() : nullptr;
+}
+
+const ON_SubDVertexPtr ON_SubDComponentId::VertexPtr(const class ON_SubD* subd) const
+{
+ return (nullptr != subd) ? this->ComponentPtr(*subd).VertexPtr() : ON_SubDVertexPtr::Null;
+}
+
+const ON_SubDEdge* ON_SubDComponentId::Edge(const class ON_SubD& subd) const
+{
+ return this->ComponentPtr(subd).Edge();
+}
+
+const ON_SubDEdgePtr ON_SubDComponentId::EdgePtr(const class ON_SubD& subd) const
+{
+ return this->ComponentPtr(subd).EdgePtr();
+}
+
+const ON_SubDEdge* ON_SubDComponentId::Edge(const class ON_SubD* subd) const
+{
+ return (nullptr != subd) ? this->ComponentPtr(*subd).Edge() : nullptr;
+}
+
+const ON_SubDEdgePtr ON_SubDComponentId::EdgePtr(const class ON_SubD* subd) const
+{
+ return (nullptr != subd) ? this->ComponentPtr(*subd).EdgePtr() : ON_SubDEdgePtr::Null;
+}
+
+const ON_SubDFace* ON_SubDComponentId::Face(const class ON_SubD& subd) const
+{
+ return this->ComponentPtr(subd).Face();
+}
+
+const ON_SubDFacePtr ON_SubDComponentId::FacePtr(const class ON_SubD& subd) const
+{
+ return this->ComponentPtr(subd).FacePtr();
+}
+
+const ON_SubDFace* ON_SubDComponentId::Face(const class ON_SubD* subd) const
+{
+ return (nullptr != subd) ? this->ComponentPtr(*subd).Face() : nullptr;
+}
+
+const ON_SubDFacePtr ON_SubDComponentId::FacePtr(const class ON_SubD* subd) const
+{
+ return (nullptr != subd) ? this->ComponentPtr(*subd).FacePtr() : ON_SubDFacePtr::Null;
+}
+
+const ON_SubDFaceCornerDex ON_SubDComponentId::FaceCornerDex() const
+{
+ if (IsFaceId())
+ {
+ const ON_SubDFaceCornerDex cid(this->Internal_ValueA(), this->Internal_ValueB());
+ if (cid.IsSet())
+ return cid;
+ }
+ return ON_SubDFaceCornerDex::Unset;
}
bool ON_SubDComponentIdList::Passes(const ON_SubDComponentPtr cptr) const
@@ -9116,6 +9394,12 @@ unsigned int ON_SubD::DumpTopology(
case ON_TextureMapping::TYPE::false_colors:
text_log.Print("false colors");
break;
+ case ON_TextureMapping::TYPE::wcs_projection :
+ text_log.Print("wcs projection");
+ break;
+ case ON_TextureMapping::TYPE::wcsbox_projection :
+ text_log.Print("wcs box projection");
+ break;
}
text_log.PrintNewLine();
text_log.Print("m_mapping_id = ");
@@ -28053,4 +28337,3 @@ double ON_SubDExpandEdgesParameters::CleanupOffset(double x)
}
-
diff --git a/opennurbs_subd.h b/opennurbs_subd.h
index f4fbe919..d3a6d09f 100644
--- a/opennurbs_subd.h
+++ b/opennurbs_subd.h
@@ -895,6 +895,344 @@ bool operator==(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs
ON_DECL
bool operator!=(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs);
+///
+/// A ON_SubDFaceCornerDex is a value that identifies a subd face corner.
+///
+class ON_WIP_CLASS ON_SubDFaceCornerDex
+{
+private:
+ unsigned short m_corner_index = 0;
+ unsigned short m_edge_count = 0;
+
+public:
+ ON_SubDFaceCornerDex() = default;
+ ~ON_SubDFaceCornerDex() = default;
+ ON_SubDFaceCornerDex(const ON_SubDFaceCornerDex&) = default;
+ ON_SubDFaceCornerDex& operator=(const ON_SubDFaceCornerDex&) = default;
+
+ ON_SubDFaceCornerDex(unsigned face_corner_index, unsigned face_edge_count);
+
+ ///
+ /// ON_SubDFaceCorner::Unset has CornerIndex()=0 and EdgeCount()=0.
+ ///
+ static const ON_SubDFaceCornerDex Unset;
+
+ ///
+ /// Dictionary compares EdgeCount() then CornerIndex().
+ /// Any set value < any unset value.
+ ///
+ ///
+ ///
+ ///
+ /// -1 if lsh < rhs.
+ /// 0 if lhs = rhs.
+ /// +1 if lhs > rhs.
+ ///
+ static int CompareAll(const ON_SubDFaceCornerDex& lhs, const ON_SubDFaceCornerDex& rhs);
+
+ ///
+ /// Uses ON_SubDFaceCornerDex::CompareAll to dictionary sort nonnull inputs and safely sorts
+ /// nullptr to the end.
+ ///
+ ///
+ ///
+ ///
+ static int Compare(const ON_SubDFaceCornerDex* lhs, const ON_SubDFaceCornerDex* rhs);
+
+ ///
+ /// True if EdgeCount() >= 3 and 0< =CornerIndex() < EdgeCount().
+ /// False otherwise.
+ ///
+ bool IsSet() const;
+
+ ///
+ /// True if EdgeCount() < 3 or CornerIndex() >= EdgeCount().
+ /// False otherwise.
+ ///
+ bool IsNotSet() const;
+
+ ///
+ ///
+ /// True if IsSet() is true, nullptr != face, and EdgeCount() = face->EdgeCount().
+ ///
+ bool IsValid(const class ON_SubDFace* face) const;
+
+ ///
+ /// Zero based index identifying the corner of a subd face.
+ ///
+ unsigned CornerIndex() const;
+
+ ///
+ /// The number of edges in the subd face.
+ ///
+ unsigned EdgeCount() const;
+
+ /// True if this is a corner of a quad face (IsSet() && 4 == EdgeCount()).
+ bool IsQuadFace() const;
+
+ ///
+ /// NextCornerDex() = (this->CornerDex() + 1) % this->EdgeCount().
+ /// If this is not set, then ON_SubDFaceCornerDex::Unset is returned.
+ ///
+ ///
+ /// The next face coner in the counter-clocwise sense.
+ ///
+ const ON_SubDFaceCornerDex NextCornerDex() const;
+
+ ///
+ /// NextCornerDex() = (this->CornerDex() + this->EdgeCount() - 1) % this->EdgeCount().
+ /// If this is not set, then ON_SubDFaceCornerDex::Unset is returned.
+ ///
+ ///
+ /// The previous face coner in the counter-clocwise sense.
+ ///
+ const ON_SubDFaceCornerDex PreviousCornerDex() const;
+
+ ///
+ /// Get the vertex at the corner of the face identified by this.
+ /// If this is not set, face is nullptr, or face->EdgeCount() != this->EdgeCount(),
+ /// then nullptr is returned.
+ ///
+ ///
+ /// The vertex at the face corner
+ const ON_SubDVertex* CornerVertex(const class ON_SubDFace* face) const;
+
+ ///
+ /// Get the vertex at the corner of the face identified by this->PreviousCornerDex().
+ /// If this is not set, face is nullptr, or face->EdgeCount() != this->EdgeCount(),
+ /// then nullptr is returned.
+ ///
+ ///
+ /// The vertex at the previous corner of the face
+ const ON_SubDVertex* PreviousCornerVertex(const class ON_SubDFace* face) const;
+
+ ///
+ /// Get the vertex at the corner of the face identified by this->NextCornerDex().
+ /// If this is not set, face is nullptr, or face->EdgeCount() != this->EdgeCount(),
+ /// then nullptr is returned.
+ ///
+ ///
+ /// The vertex at the next corner of the face
+ const ON_SubDVertex* NextCornerVertex(const class ON_SubDFace* face) const;
+
+ ///
+ /// Get the edge face->EdgePtr(this->CornerIndex()).
+ /// If this is not set, face is nullptr, or face->EdgeCount() != this->EdgeCount(),
+ /// then ON_SubDEdgePtr::Null is returned.
+ ///
+ ///
+ ///
+ /// 0=edge entering the corner = this->LeftEdgePtr(face);
+ /// 1=edge leaving the corner
+ ///
+ ///
+ const ON_SubDEdgePtr EdgePtr(const class ON_SubDFace* face, unsigned corner_edge_dex) const;
+
+ ///
+ /// Get the edge of face that goes from the the previous
+ /// face corner to this face corner. The edge pointer
+ /// is oriented from PreviousCornerVertex() to CornerVertex().
+ /// If this is not set, face is nullptr, or face->EdgeCount() != this->EdgeCount(),
+ /// then ON_SubDEdgePtr::Null is returned.
+ ///
+ ///
+ /// face->EdgePtr(this->PreviousCornerDex().CornerIndex())
+ const ON_SubDEdgePtr LeftEdgePtr(const class ON_SubDFace* face) const;
+
+ ///
+ /// Get the edge of face that goes from the this face corner
+ /// to the next face corner. The edge pointer
+ /// is oriented from CornerVertex() to NextCornerVertex().
+ /// If this is not set, face is nullptr, or face->EdgeCount() != this->EdgeCount(),
+ /// then ON_SubDEdgePtr::Null is returned.
+ ///
+ ///
+ /// face->EdgePtr(this->CornerIndex())
+ const ON_SubDEdgePtr RightEdgePtr(const class ON_SubDFace* face) const;
+};
+
+class ON_WIP_CLASS ON_SubDFaceParameter
+{
+public:
+ ON_SubDFaceParameter() = default;
+ ~ON_SubDFaceParameter() = default;
+ ON_SubDFaceParameter(const ON_SubDFaceParameter&) = default;
+ ON_SubDFaceParameter& operator=(const ON_SubDFaceParameter&) = default;
+
+
+ ///
+ /// Create a SubD face parameter that identifies a point on the face.
+ /// The parameters (0,0) correspond the the corner vertex cdex.Vertex(face).
+ /// The corner_s parameter runs from the corner vertex to the midpoint of cdex.RightEdge(face).
+ /// The corner_t parameter runs from the corner vertex to the midpoint of cdex.LeftEdge(face).
+ /// The parameters (1/2, 1/2) correspond the the center of the face.
+ ///
+ ///
+ /// Identifies the face's corner subdivison quad.
+ ///
+ ///
+ /// 0 <= corner_s <= 1/2
+ ///
+ ///
+ /// 0 <= corner_t <= 1/2
+ ///
+ ON_SubDFaceParameter(
+ ON_SubDFaceCornerDex cdex,
+ double corner_s,
+ double corner_t
+ );
+
+ ///
+ /// Create at ON_SubDFaceParameter the corresponds to the the specified quad face parameters.
+ /// The quad face parameters for face.Vertex(0) are (0,0).
+ /// The quad face parameters for face.Vertex(1) are (1,0).
+ /// The quad face parameters for face.Vertex(2) are (1,1).
+ /// The quad face parameters for face.Vertex(3) are (0,1).
+ ///
+ ///
+ /// 0 <= quad_face_s <= 1.
+ ///
+ ///
+ /// 0 <= quad_face_t <= 1.
+ ///
+ ///
+ /// The ON_SubDFaceParameter the references the quad face point.
+ ///
+ static const ON_SubDFaceParameter CreateFromQuadFaceParameteters(
+ double quad_face_s,
+ double quad_face_t
+ );
+
+
+ ///
+ /// ON_SubDFaceParameter::Nan has face_edge_count=0, corner_index=0, s=ON_DBL_QNAN, t=ON_DBL_QNAN.
+ ///
+ static const ON_SubDFaceParameter Nan;
+
+ /// True if all values are valid.
+ bool IsSet();
+
+ /// True if all values are not valid.
+ bool IsNotSet();
+
+ ///
+ /// Well ordered dictionary compare of m_cdex, m_s, and m_t using
+ /// ON_SubDFaceCornerDex::CompareAll() and ON_DBL::CompareValue().
+ /// Any set value < any unset value.
+ ///
+ ///
+ ///
+ ///
+ /// -1: lhs < rhs
+ /// 0: lhs = rhs
+ /// +1: lhs > rhs
+ ///
+ static int CompareAll(const ON_SubDFaceParameter& lhs, const ON_SubDFaceParameter& rhs);
+
+ ///
+ /// Well ordered dictionary compare that uses ON_SubDFaceParameter::CompareAll() to
+ /// compare nonnull values and safely sorts nullptr to the end.
+ ///
+ ///
+ ///
+ ///
+ static int Compare(const ON_SubDFaceParameter* lhs, const ON_SubDFaceParameter* rhs);
+
+ bool IsSet() const;
+
+ bool IsNotSet() const;
+
+ ///
+ /// m_cdex identifies face's subdivision quad the s and t parameters apply to.
+ /// The corner vertex is V = m_cdex.Vertex(face).
+ /// The edge entering the corner is LE = m_cdex.LeftEdge(face).
+ /// The edge leaving the corner is RE = m_cdex.RightEdge(face).
+ /// V = LE.RelativeVertex(1) = RE.RelativeVertex(0).
+ ///
+ const ON_SubDFaceCornerDex FaceCornerDex() const;
+
+ ///
+ /// cdex = FaceCornerDex() identifies face's subdivision quad the s and t parameters apply to.
+ /// The corner vertex is V = cdex.Vertex(face).
+ /// The edge entering the corner is LE = cdex.LeftEdge(face).
+ /// The edge leaving the corner is RE = cdex.RightEdge(face).
+ /// V = LE.RelativeVertex(1) = RE.RelativeVertex(0).
+ ///
+ /// p = FaceCornerParameters();
+ /// The p.x parameter runs along half of RE from V to the midpoint of RE.
+ /// 0 <= p.x < 1/2.
+ /// The p.y parameter runs along half of LE from V to the midpoint of LE.
+ /// 0 <= p.y < 1/2.
+ /// At V, p=(0,0).
+ /// At the midpoint of RE, p=(1/2,0)
+ /// At the midpoint of LE, p=(0,1/2)
+ /// At the center of the face, p=(1/2,1/2).
+ ///
+ const ON_2dPoint FaceCornerParameters() const;
+
+ ///
+ /// For quad faces, QuadFaceParameters() returns 2 normalized parameters that span the entire quad face.
+ /// The 2d points (0,0), (1,0), (1,1), (0,1) corresponed to the quad face's vertices
+ /// face.Vertex(0), face.Vertex(1), face.Vertex(2), face.Vertex(3).
+ ///
+ ///
+ /// If m_cdex.IsQUadFace() is true, then the 2 normalized quad face parameters
+ /// corresponding to this ON_SubDFaceParameter are returned.
+ /// Otherwise ON_2dPoint::NanPoint is returned.
+ ///
+ const ON_2dPoint QuadFaceParameters() const;
+
+
+private:
+ ///
+ /// m_cdex identifies face's subdivision quad the s and t parameters apply to.
+ /// The corner vertex is V = m_cdex.Vertex(face).
+ /// The edge entering the corner is LE = m_cdex.LeftEdge(face).
+ /// The edge leaving the corner is RE = m_cdex.RightEdge(face).
+ /// V = LE.RelativeVertex(1) = RE.RelativeVertex(0).
+ ///
+ ON_SubDFaceCornerDex m_cdex;
+
+ ///
+ /// m_cdex identifies face's subdivision quad the s and t parameters apply to.
+ /// The corner vertex is V = m_cdex.Vertex(face).
+ /// The edge entering the corner is LE = m_cdex.LeftEdge(face).
+ /// The edge leaving the corner is RE = m_cdex.RightEdge(face).
+ /// V = LE.RelativeVertex(1) = RE.RelativeVertex(0).
+ ///
+ /// The m_s parameter runs along half of RE from V to the midpoint of RE.
+ /// 0 <= m_s < 1/2.
+ /// m_s = 0 at V = RE.RelativeVertex(0) = LE.RelativeVertex(1).
+ /// m_s = 1/2 at the midpoint of RE.
+ /// At V, (m_s,m_t)=(0,0).
+ /// At the center of the face, (m_s,m_t)=(1/2,1/2).
+ ///
+ double m_s = ON_DBL_QNAN;
+
+ ///
+ /// m_cdex identifies face's subdivision quad the s and t parameters apply to.
+ /// The corner vertex is V = m_cdex.Vertex(face).
+ /// The edge entering the corner is LE = m_cdex.LeftEdge(face).
+ /// The edge leaving the corner is RE = m_cdex.RightEdge(face).
+ /// V = LE.RelativeVertex(1) = RE.RelativeVertex(0).
+ ///
+ /// The m_t parameter runs along half of LE from V to the midpoint of LE.
+ /// 0 <= m_t < 1/2.
+ /// m_t = 0 at V = RE.RelativeVertex(0) = LE.RelativeVertex(1).
+ /// m_t = 1/2 at the midpoint of LE.
+ /// At V, (m_s,m_t)=(0,0).
+ /// At the center of the face, (m_s,m_t)=(1/2,1/2).
+ ///
+ double m_t = ON_DBL_QNAN;
+};
+
+ON_WIP_DECL
+bool operator==(const ON_SubDFaceParameter& lhs, const ON_SubDFaceParameter& rhs);
+
+ON_WIP_DECL
+bool operator!=(const ON_SubDFaceParameter& lhs, const ON_SubDFaceParameter& rhs);
+
+
///
/// ON_SubDHash provides a simple way to save a SubD's vertex, edge, and face SHA1 hashes.
/// Typically it is used when a calculation needs to know if the current SubD has is geometrically
@@ -1064,7 +1402,6 @@ bool operator==(const ON_SubDHash& lhs, const ON_SubDHash& rhs);
bool operator!=(const ON_SubDHash& lhs, const ON_SubDHash& rhs);
-
class ON_CLASS ON_SubDToBrepParameters
{
public:
@@ -2809,28 +3146,133 @@ public:
ON_SubDComponentId& operator=(const ON_SubDComponentId&) = default;
ON_SubDComponentId(ON_SubDComponentPtr::Type component_type, unsigned int component_id);
+ ON_SubDComponentId(ON_SubDComponentPtr::Type component_type, unsigned int component_id, ON__UINT_PTR dir);
ON_SubDComponentId(ON_SubDComponentPtr cptr);
ON_SubDComponentId(const class ON_SubDVertex* v);
+ ON_SubDComponentId(ON_SubDVertexPtr vptr);
ON_SubDComponentId(const class ON_SubDEdge* e);
+ ON_SubDComponentId(ON_SubDEdgePtr eptr);
ON_SubDComponentId(const class ON_SubDFace* f);
+ ON_SubDComponentId(ON_SubDFacePtr fptr);
+ ON_SubDComponentId(const class ON_SubDFace* f, unsigned face_corner_index);
+ ON_SubDComponentId(ON_SubDFacePtr fptr, unsigned face_corner_index);
static int CompareTypeAndId(const ON_SubDComponentId& lhs, const ON_SubDComponentId& rhs);
+ static int CompareTypeAndIdAndDirection(const ON_SubDComponentId& lhs, const ON_SubDComponentId& rhs);
static int CompareTypeAndIdFromPointer(const ON_SubDComponentId* lhs, const ON_SubDComponentId* rhs);
- unsigned int ComponentId() const;
+
+ ///
+ /// The type of the referenced component.
+ ///
ON_SubDComponentPtr::Type ComponentType() const;
+ ///
+ /// The id of the reference component. 0 means the id is not set.
+ ///
+ unsigned int ComponentId() const;
+
+ ///
+ /// 0 or 1.
+ /// 1 indicates the orientation or direction of the referenced components
+ /// is opposite its intrinsic orientation or direction.
+ ///
+ unsigned int ComponentDirection() const;
+
/*
Returns:
- true if type is not unset and id > 0
+ true if id > 0 and type is set to vertex, edge or face.
*/
bool IsSet() const;
+ /*
+ Returns:
+ true if type is unset or id is 0.
+ */
+ bool IsNotSet() const;
+
+ ///
+ /// True if id > 0 and type = vertex.
+ ///
+ bool IsVertexId() const;
+
+ ///
+ /// True if id > 0 and type = edge.
+ ///
+ bool IsEdgeId() const;
+
+ ///
+ /// True if id > 0 and type = face.
+ ///
+ bool IsFaceId() const;
+
+ ///
+ /// Create an ON_SubDComponentId that references the same SubD component
+ /// but with the opposite value of this->ComponentDirection().
+ ///
+ /// A ON_SubDComponentId with the same type, same id, and opposite direction.
+ const ON_SubDComponentId Reversed() const;
+
+ ///
+ /// Get the referenced SubD component from a component id.
+ ///
+ ///
+ /// The subd containing the referenced component.
+ ///
+ ///
+ /// The referenced SubD component from subd.
+ ///
+ const ON_SubDComponentPtr ComponentPtr(const class ON_SubD& subd) const;
+
+ ///
+ /// Get the referenced SubD component from a component id.
+ ///
+ ///
+ /// The subd containing the referenced component.
+ ///
+ ///
+ /// The referenced SubD component from subd.
+ ///
+ const ON_SubDComponentPtr ComponentPtr(const class ON_SubD* subd) const;
+
+ const ON_SubDVertex* Vertex(const class ON_SubD& subd) const;
+ const ON_SubDVertexPtr VertexPtr(const class ON_SubD& subd) const;
+ const ON_SubDVertex* Vertex(const class ON_SubD* subd) const;
+ const ON_SubDVertexPtr VertexPtr(const class ON_SubD* subd) const;
+
+ const ON_SubDEdge* Edge(const class ON_SubD& subd) const;
+ const ON_SubDEdgePtr EdgePtr(const class ON_SubD& subd) const;
+ const ON_SubDEdge* Edge(const class ON_SubD* subd) const;
+ const ON_SubDEdgePtr EdgePtr(const class ON_SubD* subd) const;
+
+ const ON_SubDFace* Face(const class ON_SubD& subd) const;
+ const ON_SubDFacePtr FacePtr(const class ON_SubD& subd) const;
+ const ON_SubDFace* Face(const class ON_SubD* subd) const;
+ const ON_SubDFacePtr FacePtr(const class ON_SubD* subd) const;
+ const ON_SubDFaceCornerDex FaceCornerDex() const;
+
private:
unsigned int m_id = 0;
- ON_SubDComponentPtr::Type m_type = ON_SubDComponentPtr::Type::Unset;
- unsigned char m_reserved1 = 0;
- unsigned short m_reserved2 = 0;
+
+ unsigned char m_type_and_dir = 0;
+ enum : unsigned char
+ {
+ bits_dir_mask = 0x01,
+ bits_type_mask = 0x06,
+ // the remaining bits may be used in the future
+ };
+ void Internal_SetType(ON_SubDComponentPtr::Type type);
+ void Internal_SetDir(unsigned dir);
+
+ // The "A" and "B" values are two 12 bit unsigned integer values
+ // (0 to 4095 decimal) that are encoded in the 3 bytes m_valueAB[].
+ // When the referenced component is a SubD face, A = number of face edges
+ // and B = face corner index.
+ void Internal_SetValueA(unsigned a);
+ void Internal_SetValueB(unsigned b);
+ unsigned Internal_ValueA() const;
+ unsigned Internal_ValueB() const;
+ unsigned char m_valueAB[3] = {};
};
#if defined(ON_DLL_TEMPLATE)
@@ -4386,6 +4828,292 @@ private:
bool operator==(const ON_SubDExpandEdgesParameters& lhs, const ON_SubDExpandEdgesParameters& rhs);
bool operator!=(const ON_SubDExpandEdgesParameters& lhs, const ON_SubDExpandEdgesParameters& rhs);
+class ON_WIP_CLASS ON_SubDComponentParameter
+{
+public:
+ ON_SubDComponentParameter() = default;
+ ~ON_SubDComponentParameter() = default;
+ ON_SubDComponentParameter(const ON_SubDComponentParameter&) = default;
+ ON_SubDComponentParameter& operator=(const ON_SubDComponentParameter&) = default;
+
+
+ ON_SubDComponentParameter(ON_SubDComponentId cid);
+ ON_SubDComponentParameter(ON_SubDComponentPtr cptr);
+
+ ON_SubDComponentParameter(
+ const class ON_SubDVertex* v,
+ const class ON_SubDEdge* active_edge,
+ const class ON_SubDFace* active_face
+ );
+
+ ON_SubDComponentParameter(
+ const ON_SubDVertexPtr vptr,
+ const class ON_SubDEdge* active_edge,
+ const class ON_SubDFace* active_face
+ );
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 0 <= p <= 1.
+ ///
+ ///
+ /// nullptr or a face attached to the edge.
+ ///
+ ON_SubDComponentParameter(
+ const class ON_SubDEdge* e,
+ double p,
+ const class ON_SubDFace* active_face
+ );
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 0 <= p <= 1.
+ /// NOTE WELL: p is relative to the direction of eptr.
+ ///
+ ///
+ /// nullptr or a face attached to the edge.
+ ///
+ ON_SubDComponentParameter(
+ const ON_SubDEdgePtr eptr,
+ double p,
+ const class ON_SubDFace* active_face
+ );
+
+ ON_SubDComponentParameter(
+ const ON_SubDFace* face,
+ ON_SubDFaceParameter fp
+ );
+
+ ON_SubDComponentParameter(
+ const ON_SubDFacePtr fptr,
+ ON_SubDFaceParameter fp
+ );
+
+ ON_SubDComponentParameter(
+ const class ON_SubDFace* quad_face,
+ double quad_s,
+ double quad_t
+ );
+ ON_SubDComponentParameter(
+ const class ON_SubDFacePtr quad_fptr,
+ double quad_s,
+ double quad_t
+ );
+
+
+ static const ON_SubDComponentParameter Unset;
+
+ ///
+ /// Dictionary compares component type and component id.
+ ///
+ ///
+ ///
+ ///
+ static int CompareComponentTypeAndId(const ON_SubDComponentParameter& lhs, const ON_SubDComponentParameter& rhs);
+
+ ///
+ /// Dictionary compares component type, component id, and component dir.
+ ///
+ ///
+ ///
+ ///
+ static int CompareComponentTypeAndIdAndDirection(const ON_SubDComponentParameter& lhs, const ON_SubDComponentParameter& rhs);
+
+
+ ///
+ /// Dictionary compares component type, component id, component direction, first parameter, second parameter.
+ ///
+ ///
+ ///
+ ///
+ /// -1: lhs < rhs
+ /// 0: lhs = rhs
+ /// +1: lhs > rhs
+ ///
+ static int CompareAll(const ON_SubDComponentParameter& lhs, const ON_SubDComponentParameter& rhs);
+
+ ///
+ /// Dictionary compares component type, component id, component direction, first parameter, second parameter
+ /// and safely sorts nullptr to end.
+ ///
+ ///
+ ///
+ ///
+ static int Compare(const ON_SubDComponentParameter* lhs, const ON_SubDComponentParameter* rhs);
+
+ const ON_SubDComponentId ComponentIdAndType() const;
+ unsigned ComponentId() const;
+ ON_SubDComponentPtr::Type ComponentType() const;
+ unsigned ComponentDirection() const;
+
+ ///
+ /// True if the referenced component is a vertex.
+ ///
+ bool IsVertexParameter() const;
+
+ ///
+ /// If this parameter references a vertex and the subd has a vertex
+ /// with this->ComponentId(), then that vertex is returned.
+ ///
+ ///
+ ///
+ /// The referenced vertex in subd.
+ ///
+ const ON_SubDVertex* Vertex(const ON_SubD* subd) const;
+
+ ///
+ /// If this parameter references a vertex and the subd has a vertex
+ /// with this->ComponentId(), then that vertex is returned.
+ ///
+ ///
+ ///
+ /// The referenced vertex in subd along with the direction value.
+ ///
+ const ON_SubDVertexPtr VertexPtr(const ON_SubD* subd) const;
+
+ ///
+ /// In some cases, an edge attached to this parameter's vertex is required.
+ /// In that case, this edge is used.
+ ///
+ ///
+ /// The prefered edge attached to this vertex.
+ ///
+ const ON_SubDComponentId VertexEdge() const;
+
+ ///
+ /// In some cases, a face attached to this parameter's vertex is required.
+ /// In these cases this face is used.
+ ///
+ ///
+ /// The prefered face attached to this vertex.
+ ///
+ const ON_SubDComponentId VertexFace() const;
+
+ ///
+ /// True if the referenced component is an edge.
+ ///
+ bool IsEdgeParameter() const;
+
+ ///
+ /// If this parameter references an edge and the subd has an edge
+ /// with this->ComponentId(), then that edge is returned.
+ ///
+ ///
+ ///
+ /// The referenced edge in subd.
+ ///
+ const ON_SubDEdge* Edge(const ON_SubD* subd) const;
+
+ ///
+ /// If this parameter references an edge and the subd has an edge
+ /// with this->ComponentId(), then that edge is returned.
+ ///
+ ///
+ ///
+ /// The referenced edge in subd along with the direction value.
+ ///
+ const ON_SubDEdgePtr EdgePtr(const ON_SubD* subd) const;
+
+ ///
+ /// Returns a parameter between 0 and 1 that identifies a point on the edge.
+ /// This is always an intrisic parameter;
+ /// ComponentDirection() is not taken into account.
+ /// If the reference component is not an edge, then ON_DBL_QNAN is returned.
+ ///
+ ///
+ /// Returns a parameter between 0 and 1 identifying the point
+ /// on the edge. Note that ComponentDirection() is not taken
+ /// into account. If this does not reference an edge or the
+ /// parameter is not set, then ON_DBL_QNAN is returned.
+ ///
+ double EdgeParameter() const;
+
+ ///
+ /// In some cases, a face attached to this parameter's edge is required.
+ /// In that case, this face is used.
+ ///
+ ///
+ /// The prefered edge attached to this vertex.
+ ///
+ const ON_SubDComponentId EdgeFace() const;
+
+ ///
+ /// True if the referenced component is a face.
+ ///
+ bool IsFaceParameter() const;
+
+ ///
+ /// If this parameter references a face and the subd has a face
+ /// with this->ComponentId(), then that face is returned.
+ ///
+ ///
+ ///
+ /// The referenced face in subd.
+ ///
+ const ON_SubDFace* Face(const ON_SubD* subd) const;
+
+ ///
+ /// If this parameter references a face and the subd has a face
+ /// with this->ComponentId(), then that face is returned.
+ ///
+ ///
+ ///
+ /// The referenced face in subd along with the direction value.
+ ///
+ const ON_SubDFacePtr FacePtr(const ON_SubD* subd) const;
+
+ ///
+ /// If a valid face and face parameter were passed to the constructor,
+ /// then that face parameter is returned.
+ /// Otherwise ON_SubDFaceParameter::Nan is returned.
+ ///
+ const ON_SubDFaceParameter FaceParameter() const;
+
+ ///
+ /// If the subd has a component with the same type and id,
+ /// then that component is returned.
+ ///
+ ///
+ ///
+ /// The referenced component in subd along with the direction value.
+ ///
+ const ON_SubDComponentPtr ComponentPtr(const class ON_SubD* subd) const;
+
+ ///
+ /// If the subd has a component with the same type and id,
+ /// then that component is returned.
+ ///
+ ///
+ ///
+ /// The referenced component in subd along with the direction value.
+ ///
+ const ON_SubDComponentPtr ComponentPtr(const class ON_SubD& subd) const;
+
+private:
+ ON_SubDComponentId m_cid = ON_SubDComponentId::Unset;
+
+ union
+ {
+ ON_SubDComponentId v_active_e; // vertex's active edge
+ double eptr_s; // Relative with respect to m_cid.Direction().
+ double f_corner_s; // 0 <= f_corner_s <= 1/2
+ } m_p0 = {};
+
+ union
+ {
+ ON_SubDComponentId v_active_f; // vertex's active face
+ ON_SubDComponentId e_active_f; // edge's active face
+ double f_corner_t; // 0 <= f_corner_t <= 1/2
+ } m_p1 = {};
+
+ bool Internal_Init(ON_SubDComponentId cid);
+};
//////////////////////////////////////////////////////////////////////////
//
@@ -5302,6 +6030,7 @@ public:
ON_SubD* subd
);
+
public:
#pragma region RH_C_SHARED_ENUM [ON_SubD::AutomaticMeshToSubDContext] [Rhino.Geometry.SubDAutomaticMeshToSubDContext] [byte]
///
diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp
index 90a64716..022b4809 100644
--- a/opennurbs_subd_eval.cpp
+++ b/opennurbs_subd_eval.cpp
@@ -1414,3 +1414,614 @@ const ON_Plane ON_SubDFace::ControlNetCenterFrame() const
}
return ON_Plane::NanPlane;
}
+
+
+int ON_SubDFaceCornerDex::CompareAll(const ON_SubDFaceCornerDex& lhs, const ON_SubDFaceCornerDex& rhs)
+{
+ // invalids go to the end
+ int i = lhs.IsSet() ? 0 : 1;
+ int j = lhs.IsSet() ? 0 : 1;
+ if (i < j)
+ return -1;
+ if (i > j)
+ return -1;
+
+ if (lhs.m_edge_count < rhs.m_edge_count)
+ return -1;
+ if (lhs.m_edge_count > rhs.m_edge_count)
+ return 1;
+
+ if (lhs.m_corner_index < rhs.m_corner_index)
+ return -1;
+ if (lhs.m_corner_index > rhs.m_corner_index)
+ return 1;
+
+ return 0;
+}
+
+int ON_SubDFaceCornerDex::Compare(const ON_SubDFaceCornerDex* lhs, const ON_SubDFaceCornerDex* rhs)
+{
+ // sort nullptr to end
+ if (lhs == rhs)
+ return 0;
+ if (nullptr == lhs)
+ return 1;
+ if (nullptr == rhs)
+ return -1;
+
+ return ON_SubDFaceCornerDex::CompareAll(*lhs, *rhs);
+}
+
+bool ON_SubDFaceCornerDex::IsSet() const
+{
+ return m_corner_index < m_edge_count && m_edge_count >= 3;
+}
+
+bool ON_SubDFaceCornerDex::IsNotSet() const
+{
+ return m_corner_index >= m_edge_count || m_edge_count < 3;
+}
+
+bool ON_SubDFaceCornerDex::IsValid(const ON_SubDFace* face) const
+{
+ return nullptr != face && face->m_edge_count == m_edge_count && IsSet();
+}
+
+unsigned ON_SubDFaceCornerDex::CornerIndex() const
+{
+ return m_corner_index;
+}
+
+unsigned ON_SubDFaceCornerDex::EdgeCount() const
+{
+ return m_edge_count;
+}
+
+bool ON_SubDFaceCornerDex::IsQuadFace() const
+{
+ return 4 == m_edge_count && IsSet();
+}
+
+const ON_SubDFaceCornerDex ON_SubDFaceCornerDex::NextCornerDex() const
+{
+ if (IsSet())
+ {
+ ON_SubDFaceCornerDex c;
+ c.m_corner_index = (m_corner_index + 1u) % m_edge_count;
+ c.m_edge_count = m_edge_count;
+ return c;
+ }
+ return ON_SubDFaceCornerDex::Unset;
+}
+
+const ON_SubDFaceCornerDex ON_SubDFaceCornerDex::PreviousCornerDex() const
+{
+ if (IsSet())
+ {
+ ON_SubDFaceCornerDex c;
+ c.m_corner_index = (m_corner_index + m_edge_count - 1u) % m_edge_count;
+ c.m_edge_count = m_edge_count;
+ return c;
+ }
+ return ON_SubDFaceCornerDex::Unset;
+}
+
+const ON_SubDVertex* ON_SubDFaceCornerDex::CornerVertex(const ON_SubDFace* face) const
+{
+ return IsValid(face) ? face->Vertex(m_corner_index) : nullptr;
+}
+
+const ON_SubDVertex* ON_SubDFaceCornerDex::PreviousCornerVertex(const ON_SubDFace* face) const
+{
+ return this->PreviousCornerDex().CornerVertex(face);
+}
+
+const ON_SubDVertex* ON_SubDFaceCornerDex::NextCornerVertex(const ON_SubDFace* face) const
+{
+ return this->NextCornerDex().CornerVertex(face);
+}
+
+const ON_SubDEdgePtr ON_SubDFaceCornerDex::EdgePtr(const ON_SubDFace* face, unsigned corner_edge_dex) const
+{
+ if (0 == corner_edge_dex)
+ return this->LeftEdgePtr(face);
+
+ if (0 == corner_edge_dex)
+ return this->RightEdgePtr(face);
+
+ return ON_SubDEdgePtr::Null;
+}
+
+const ON_SubDEdgePtr ON_SubDFaceCornerDex::LeftEdgePtr(const ON_SubDFace* face) const
+{
+ return this->PreviousCornerDex().RightEdgePtr(face);
+}
+
+const ON_SubDEdgePtr ON_SubDFaceCornerDex::RightEdgePtr(const ON_SubDFace* face) const
+{
+ return IsValid(face) ? face->EdgePtr(m_corner_index) : ON_SubDEdgePtr::Null;
+}
+
+ON_SubDFaceCornerDex::ON_SubDFaceCornerDex(unsigned corner_index, unsigned edge_count)
+{
+ if (corner_index < edge_count && edge_count >= 3 && edge_count <= ON_SubDFace::MaximumEdgeCount)
+ {
+ m_corner_index = (unsigned short)corner_index;
+ m_edge_count = (unsigned short)edge_count;
+
+ }
+ else
+ {
+ m_corner_index = 0;
+ m_edge_count = 0;
+ }
+}
+
+bool ON_SubDComponentParameter::Internal_Init(ON_SubDComponentId cid)
+{
+ if (cid.IsSet())
+ {
+ switch (cid.ComponentType())
+ {
+ case ON_SubDComponentPtr::Type::Vertex:
+ m_cid = cid;
+ m_p0.v_active_e = ON_SubDEdgePtr::Null;
+ m_p1.v_active_f = ON_SubDFacePtr::Null;
+ break;
+ case ON_SubDComponentPtr::Type::Edge:
+ m_cid = cid;
+ m_p0.eptr_s = ON_DBL_QNAN;
+ m_p1.e_active_f = ON_SubDFacePtr::Null;
+ break;
+ case ON_SubDComponentPtr::Type::Face:
+ m_cid = cid;
+ m_p0.f_corner_s = ON_DBL_QNAN;
+ m_p1.f_corner_t = ON_DBL_QNAN;
+ break;
+ default:
+ m_cid = ON_SubDComponentParameter::Unset.m_cid;
+ m_p0.f_corner_s = 0.0;
+ m_p1.f_corner_t = 0.0;
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+ON_SubDFaceParameter::ON_SubDFaceParameter(ON_SubDFaceCornerDex cdex, double s, double t)
+ : m_cdex(cdex)
+ , m_s((0.0 <= s && s <= 1.0) ? s : ON_DBL_QNAN)
+ , m_t((0.0 <= t && t <= 1.0) ? t : ON_DBL_QNAN)
+{}
+
+bool ON_SubDFaceParameter::IsSet()
+{
+ return m_cdex.IsSet() && 0.0 <= m_s && m_s <= 0.5 && 0.0 <= m_t && m_t <= 0.5;
+}
+
+/// True if all values are not valid.
+bool ON_SubDFaceParameter::IsNotSet()
+{
+ return ON_SubDFaceParameter::IsSet() ? false : true;
+}
+
+
+const ON_SubDFaceParameter ON_SubDFaceParameter::CreateFromQuadFaceParameteters(double quad_face_s, double quad_face_t)
+{
+ if (0.0 <= quad_face_s && 0.0 <= quad_face_t)
+ {
+ if (quad_face_t <= 0.5)
+ {
+ if (quad_face_s <= 0.5)
+ return ON_SubDFaceParameter(ON_SubDFaceCornerDex(0, 4), quad_face_s, quad_face_t);
+ if (quad_face_s <= 1.0)
+ return ON_SubDFaceParameter(ON_SubDFaceCornerDex(1, 4), 1.0 - quad_face_t, quad_face_s);
+ }
+ else if (quad_face_s <= 1.0 && quad_face_t <= 1.0)
+ {
+ if (quad_face_s >= 0.5)
+ return ON_SubDFaceParameter(ON_SubDFaceCornerDex(2, 4), 1.0 - quad_face_s, 1.0 - quad_face_t);
+ return ON_SubDFaceParameter(ON_SubDFaceCornerDex(3, 4), 1.0 - quad_face_t, quad_face_s);
+ }
+ }
+ return ON_SubDFaceParameter::Nan;
+}
+
+int ON_SubDFaceParameter::CompareAll(const ON_SubDFaceParameter& lhs, const ON_SubDFaceParameter& rhs)
+{
+ // invalids go to the end
+ int i = lhs.IsSet() ? 0 : 1;
+ int j = rhs.IsSet() ? 0 : 1;
+ if (i < j)
+ return -1;
+ if (i > j)
+ return 1;
+
+ int rc = ON_SubDFaceCornerDex::CompareAll(lhs.m_cdex, rhs.m_cdex);
+ if (0 == rc)
+ {
+ rc = ON_DBL::CompareValue(lhs.m_s, rhs.m_s);
+ if (0 == rc)
+ rc = ON_DBL::CompareValue(lhs.m_t, rhs.m_t);
+ }
+ return rc;
+}
+
+int ON_SubDFaceParameter::Compare(const ON_SubDFaceParameter* lhs, const ON_SubDFaceParameter* rhs)
+{
+ if (lhs == rhs)
+ return 0;
+ if (nullptr == lhs)
+ return 1;
+ if (nullptr == rhs)
+ return -1;
+ return ON_SubDFaceParameter::CompareAll(*lhs, *rhs);
+}
+
+bool ON_SubDFaceParameter::IsSet() const
+{
+ return m_cdex.IsSet() && 0.0 <= m_s && m_s <= 1.0 && 0.0 <= m_t && m_t <= 1.0;
+}
+
+bool ON_SubDFaceParameter::IsNotSet() const
+{
+ return IsSet() ? false : true;
+}
+
+const ON_SubDFaceCornerDex ON_SubDFaceParameter::FaceCornerDex() const
+{
+ return m_cdex;
+}
+
+const ON_2dPoint ON_SubDFaceParameter::FaceCornerParameters() const
+{
+ return IsSet() ? ON_2dPoint(m_s, m_t) : ON_2dPoint::NanPoint;
+}
+
+const ON_2dPoint ON_SubDFaceParameter::QuadFaceParameters() const
+{
+ if (IsSet() && m_cdex.IsQuadFace())
+ {
+ switch (m_cdex.CornerIndex())
+ {
+ case 0:
+ return ON_2dPoint(m_s, m_t);
+ break;
+ case 1:
+ return ON_2dPoint(1.0 - m_t, m_s);
+ break;
+ case 2:
+ return ON_2dPoint(1.0 - m_s, 1.0 - m_t);
+ break;
+ case 3:
+ return ON_2dPoint(m_t, 1.0 - m_s);
+ break;
+ }
+ }
+ return ON_2dPoint::NanPoint;
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(ON_SubDComponentId cid)
+{
+ Internal_Init(cid);
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(ON_SubDComponentPtr cptr)
+{
+ Internal_Init(ON_SubDComponentId(cptr));
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const ON_SubDVertex* v,
+ const class ON_SubDEdge* active_edge,
+ const class ON_SubDFace* active_face
+)
+{
+ if (Internal_Init(ON_SubDComponentId(v)))
+ {
+ if (nullptr != active_edge)
+ {
+ const int vei = v->EdgeArrayIndex(active_edge);
+ m_p0.v_active_e = ON_SubDComponentId(v->EdgePtr(vei));
+ }
+ if (nullptr != active_face)
+ {
+ const int vfi = v->FaceArrayIndex(active_face);
+ m_p1.v_active_f = ON_SubDComponentId(v->Face(vfi));
+ }
+ }
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const ON_SubDVertexPtr vptr,
+ const class ON_SubDEdge* active_edge,
+ const class ON_SubDFace* active_face
+)
+{
+ if (Internal_Init(ON_SubDComponentId(vptr)))
+ {
+ const ON_SubDVertex* v = vptr.Vertex();
+ if (nullptr != v)
+ {
+ if (nullptr != active_edge)
+ {
+ const int vei = v->EdgeArrayIndex(active_edge);
+ m_p0.v_active_e = ON_SubDComponentId(v->EdgePtr(vei));
+ }
+ if (nullptr != active_face)
+ {
+ const int vfi = v->FaceArrayIndex(active_face);
+ m_p1.v_active_f = ON_SubDComponentId(v->Face(vfi));
+ }
+ }
+ }
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const ON_SubDEdge* e,
+ double edge_s,
+ const class ON_SubDFace* active_face
+)
+{
+ if (Internal_Init(ON_SubDComponentId(e)))
+ {
+ m_p0.eptr_s = (edge_s >= 0.0 && edge_s <= 1.0) ? edge_s : ON_DBL_QNAN;
+ if (nullptr != active_face)
+ {
+ const unsigned efi = e->FaceArrayIndex(active_face);
+ m_p1.e_active_f = e->FacePtr(efi);
+ }
+ }
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const ON_SubDEdgePtr eptr,
+ double edge_s,
+ const class ON_SubDFace* active_face
+)
+{
+ if (Internal_Init(ON_SubDComponentId(eptr)))
+ {
+ const ON_SubDEdge* e = eptr.Edge();
+ if (nullptr != e)
+ {
+ m_p0.eptr_s = (edge_s >= 0.0 && edge_s <= 1.0) ? edge_s : ON_DBL_QNAN;
+ if (nullptr != active_face)
+ {
+ const unsigned efi = e->FaceArrayIndex(active_face);
+ m_p1.e_active_f = e->FacePtr(efi);
+ }
+ }
+ }
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const ON_SubDFace* f,
+ ON_SubDFaceParameter fp
+)
+{
+ if (Internal_Init(ON_SubDComponentId(f)) && fp.IsSet())
+ {
+ const ON_SubDFaceCornerDex cdex = fp.FaceCornerDex();
+ if (cdex.IsSet() && cdex.EdgeCount() == f->EdgeCount())
+ {
+ const ON_2dPoint p = fp.FaceCornerParameters();
+ if (p.IsValid())
+ {
+ this->m_cid = ON_SubDComponentId(f, cdex.CornerIndex());
+ this->m_p0.f_corner_s = p.x;
+ this->m_p1.f_corner_t = p.y;
+ }
+ }
+ }
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const ON_SubDFacePtr fptr,
+ ON_SubDFaceParameter fp
+)
+{
+ if (Internal_Init(ON_SubDComponentId(fptr)) && fp.IsSet())
+ {
+ const ON_SubDFaceCornerDex cdex = fp.FaceCornerDex();
+ if (cdex.IsSet() && cdex.EdgeCount() == fptr.FaceEdgeCount())
+ {
+ const ON_2dPoint p = fp.FaceCornerParameters();
+ if (p.IsValid())
+ {
+ this->m_cid = ON_SubDComponentId(fptr, cdex.CornerIndex());
+ this->m_p0.f_corner_s = p.x;
+ this->m_p1.f_corner_t = p.y;
+ }
+ }
+ }
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const class ON_SubDFace* quad_face,
+ double quad_s,
+ double quad_t
+)
+{
+ if (
+ nullptr != quad_face
+ && 4 == quad_face->m_edge_count
+ && Internal_Init(quad_face)
+ )
+ {
+ const ON_SubDFaceParameter fp = ON_SubDFaceParameter::CreateFromQuadFaceParameteters(quad_s, quad_t);
+ const ON_2dPoint p = fp.FaceCornerParameters();
+ this->m_p0.f_corner_s = p.x;
+ this->m_p1.f_corner_t = p.y;
+ }
+}
+
+ON_SubDComponentParameter::ON_SubDComponentParameter(
+ const ON_SubDFacePtr quad_fptr,
+ double quad_s,
+ double quad_t
+)
+{
+ if (
+ quad_fptr.IsNotNull()
+ && 4 == quad_fptr.FaceEdgeCount()
+ && Internal_Init(quad_fptr)
+ )
+ {
+ const ON_SubDFaceParameter fp = ON_SubDFaceParameter::CreateFromQuadFaceParameteters(quad_s, quad_t);
+ const ON_2dPoint p = fp.FaceCornerParameters();
+ this->m_p0.f_corner_s = p.x;
+ this->m_p1.f_corner_t = p.y;
+ }
+}
+
+ON_SubDComponentPtr::Type ON_SubDComponentParameter::ComponentType() const
+{
+ return m_cid.ComponentType();
+}
+
+int ON_SubDComponentParameter::CompareComponentTypeAndId(const ON_SubDComponentParameter& lhs, const ON_SubDComponentParameter& rhs)
+{
+ return ON_SubDComponentId::CompareTypeAndId(lhs.m_cid, rhs.m_cid);
+}
+
+int ON_SubDComponentParameter::CompareComponentTypeAndIdAndDirection(const ON_SubDComponentParameter& lhs, const ON_SubDComponentParameter& rhs)
+{
+ return ON_SubDComponentId::CompareTypeAndIdAndDirection(lhs.m_cid, rhs.m_cid);
+}
+
+int ON_SubDComponentParameter::CompareAll(const ON_SubDComponentParameter& lhs, const ON_SubDComponentParameter& rhs)
+{
+ int rc = ON_SubDComponentParameter::CompareComponentTypeAndIdAndDirection(lhs, rhs);
+ if (0 == rc)
+ {
+ switch (lhs.m_cid.ComponentType())
+ {
+ case ON_SubDComponentPtr::Type::Vertex:
+ rc = ON_SubDComponentId::CompareTypeAndIdAndDirection(lhs.m_p0.v_active_e, rhs.m_p0.v_active_e);
+ if (0 == rc)
+ rc = ON_SubDComponentId::CompareTypeAndIdAndDirection(lhs.m_p1.v_active_f, rhs.m_p1.v_active_f);
+ break;
+ case ON_SubDComponentPtr::Type::Edge:
+ rc = ON_DBL::CompareValue(lhs.m_p0.eptr_s, rhs.m_p0.eptr_s);
+ if (0 == rc)
+ rc = ON_SubDComponentId::CompareTypeAndIdAndDirection(lhs.m_p1.e_active_f, rhs.m_p1.e_active_f);
+ break;
+ case ON_SubDComponentPtr::Type::Face:
+ rc = ON_SubDFaceParameter::CompareAll(lhs.FaceParameter(), rhs.FaceParameter());
+ break;
+ default:
+ break;
+ }
+ }
+ return rc;
+}
+
+const ON_SubDComponentId ON_SubDComponentParameter::ComponentIdAndType() const
+{
+ return m_cid;
+}
+
+unsigned ON_SubDComponentParameter::ComponentId() const
+{
+ return m_cid.ComponentId();
+}
+
+unsigned ON_SubDComponentParameter::ComponentDirection() const
+{
+ return m_cid.ComponentDirection();
+}
+
+bool ON_SubDComponentParameter::IsVertexParameter() const
+{
+ return ON_SubDComponentPtr::Type::Vertex == this->ComponentType();
+}
+
+bool ON_SubDComponentParameter::IsEdgeParameter() const
+{
+ return ON_SubDComponentPtr::Type::Edge == this->ComponentType();
+}
+
+bool ON_SubDComponentParameter::IsFaceParameter() const
+{
+ return ON_SubDComponentPtr::Type::Face == this->ComponentType();
+}
+
+const ON_SubDVertex* ON_SubDComponentParameter::Vertex(const ON_SubD* subd) const
+{
+ return this->ComponentPtr(subd).Vertex();
+}
+
+const ON_SubDEdge* ON_SubDComponentParameter::Edge(const ON_SubD* subd) const
+{
+ return this->ComponentPtr(subd).Edge();
+}
+
+const ON_SubDFace* ON_SubDComponentParameter::Face(const ON_SubD* subd) const
+{
+ return this->ComponentPtr(subd).Face();
+}
+
+const ON_SubDComponentPtr ON_SubDComponentParameter::ComponentPtr(const ON_SubD* subd) const
+{
+ return m_cid.ComponentPtr(subd);
+}
+
+const ON_SubDComponentPtr ON_SubDComponentParameter::ComponentPtr(const ON_SubD& subd) const
+{
+ return m_cid.ComponentPtr(subd);
+}
+
+const ON_SubDVertexPtr ON_SubDComponentParameter::VertexPtr(const ON_SubD* subd) const
+{
+ return this->ComponentPtr(subd).VertexPtr();
+}
+
+const ON_SubDEdgePtr ON_SubDComponentParameter::EdgePtr(const ON_SubD* subd) const
+{
+ return this->ComponentPtr(subd).EdgePtr();
+}
+
+const ON_SubDFacePtr ON_SubDComponentParameter::FacePtr(const ON_SubD* subd) const
+{
+ return this->ComponentPtr(subd).FacePtr();
+}
+
+const ON_SubDFaceParameter ON_SubDComponentParameter::FaceParameter() const
+{
+ if (
+ this->IsFaceParameter()
+ && 0.0 <= m_p0.f_corner_s && m_p0.f_corner_s <= 0.5
+ && 0.0 <= m_p1.f_corner_t && m_p1.f_corner_t <= 0.5
+ )
+ {
+ const ON_SubDFaceCornerDex cdex = m_cid.FaceCornerDex();
+ if (cdex.IsSet())
+ {
+ return ON_SubDFaceParameter(cdex, m_p0.f_corner_s, m_p1.f_corner_t);
+ }
+ }
+ return ON_SubDFaceParameter::Nan;
+}
+
+const ON_SubDComponentId ON_SubDComponentParameter::VertexEdge() const
+{
+ return this->IsVertexParameter() ? m_p0.v_active_e : ON_SubDComponentId::Unset;
+}
+
+const ON_SubDComponentId ON_SubDComponentParameter::VertexFace() const
+{
+ return this->IsVertexParameter() ? m_p1.v_active_f : ON_SubDComponentId::Unset;
+}
+
+const ON_SubDComponentId ON_SubDComponentParameter::EdgeFace() const
+{
+ return this->IsEdgeParameter() ? m_p1.e_active_f : ON_SubDComponentId::Unset;
+}
+
+double ON_SubDComponentParameter::EdgeParameter() const
+{
+ return this->IsEdgeParameter() ? m_p0.eptr_s : ON_DBL_QNAN;
+}
+
diff --git a/opennurbs_sun.cpp b/opennurbs_sun.cpp
index 7bf46a49..8143a776 100644
--- a/opennurbs_sun.cpp
+++ b/opennurbs_sun.cpp
@@ -553,6 +553,24 @@ void ON_SunEngine::ConvertSolarVectorToHorizonCoords(const double* dVector, doub
dAzimuth += 360.0;
}
+void ON_SunEngine::GetCurrentLocalDateTime(int& y, int& m, int& d, double& h)
+{
+ const time_t time = ON_SecondsSinceJanOne1970UTC();
+
+ tm ttm = { 0 };
+
+#ifdef ON_RUNTIME_WIN
+ _localtime64_s(&ttm, &time);
+#else
+ ttm = *localtime(&time);
+#endif
+
+ y = ttm.tm_year + 1900;
+ m = ttm.tm_mon + 1;
+ d = ttm.tm_mday;
+ h = ttm.tm_hour + (ttm.tm_min / 60.0) + (ttm.tm_sec / 3600.0);
+}
+
class ON_Sun::CImpl : public ON_InternalXMLImpl
{
public:
@@ -793,11 +811,14 @@ void ON_Sun::CImpl::SetDaylightSavingMinutes(int minutes)
void ON_Sun::CImpl::LocalDateTime(int& year, int& month, int& day, double& hours) const
{
+ int cy = 0, cm = 0, cd = 0; double ch = 0.0;
+ ON_SunEngine::GetCurrentLocalDateTime(cy, cm, cd, ch);
+
const wchar_t* s = XMLPath_Sun();
- year = GetParameter(s, ON_RDK_SUN_DATE_YEAR, 2000);
- month = GetParameter(s, ON_RDK_SUN_DATE_MONTH, 1);
- day = GetParameter(s, ON_RDK_SUN_DATE_DAY, 1);
- hours = GetParameter(s, ON_RDK_SUN_TIME_HOURS, 0.0);
+ year = GetParameter(s, ON_RDK_SUN_DATE_YEAR, cy);
+ month = GetParameter(s, ON_RDK_SUN_DATE_MONTH, cm);
+ day = GetParameter(s, ON_RDK_SUN_DATE_DAY, cd);
+ hours = GetParameter(s, ON_RDK_SUN_TIME_HOURS, ch);
}
bool ON_Sun::CImpl::SetLocalDateTime(int year, int month, int day, double hours)
@@ -1322,10 +1343,13 @@ void ON_Sun::LoadFromXMLNode(const ON_XMLNode& node)
{
ON_XMLParameters p(node);
- const auto y = p.GetParam(ON_RDK_SUN_DATE_YEAR, 2000).AsInteger();
- const auto m = p.GetParam(ON_RDK_SUN_DATE_MONTH, 1).AsInteger();
- const auto d = p.GetParam(ON_RDK_SUN_DATE_DAY, 1).AsInteger();
- const auto h = p.GetParam(ON_RDK_SUN_TIME_HOURS, 12.0).AsDouble();
+ int cy = 0, cm = 0, cd = 0; double ch = 0.0;
+ ON_SunEngine::GetCurrentLocalDateTime(cy, cm, cd, ch);
+
+ const auto y = p.GetParam(ON_RDK_SUN_DATE_YEAR, cy).AsInteger();
+ const auto m = p.GetParam(ON_RDK_SUN_DATE_MONTH, cm).AsInteger();
+ const auto d = p.GetParam(ON_RDK_SUN_DATE_DAY, cd).AsInteger();
+ const auto h = p.GetParam(ON_RDK_SUN_TIME_HOURS, ch).AsDouble();
SetLocalDateTime(y, m, d, h);
SetEnableAllowed (p.GetParam(ON_RDK_SUN_ENABLE_ALLOWED, false));
diff --git a/opennurbs_sun.h b/opennurbs_sun.h
index dbb97ba5..5fd1997b 100644
--- a/opennurbs_sun.h
+++ b/opennurbs_sun.h
@@ -101,6 +101,9 @@ public:
// Helper function; converts unit vector to azimuth and altitude.
static void ConvertSolarVectorToHorizonCoords(const double* dVector, double& dAzimuth, double& dAltitude);
+ // Helper function; gets the current local date and time.
+ static void GetCurrentLocalDateTime(int& y, int& m, int& d, double& h);
+
private:
class CImpl;
CImpl* _impl;
diff --git a/opennurbs_texture.h b/opennurbs_texture.h
index 1defc8b9..c12cef23 100644
--- a/opennurbs_texture.h
+++ b/opennurbs_texture.h
@@ -292,6 +292,12 @@ public:
*/
bool IsWcsBoxProjected() const;
+ /*
+ Description:
+ Evaluates WCS box projection at point pt with normal n.
+ */
+ static ON_3dPoint WcsBoxMapping(const ON_3dPoint& pt, const ON_3dVector& n);
+
// If the m_mapping_channel_id value is one of the built-in
// mappings listed in the MAPPING_CHANNEL enum, then that
// mapping is used. Otherwise, if an object has rendering
diff --git a/opennurbs_texture_mapping.h b/opennurbs_texture_mapping.h
index 7d727c67..df1287c1 100644
--- a/opennurbs_texture_mapping.h
+++ b/opennurbs_texture_mapping.h
@@ -30,6 +30,7 @@ class ON_3dPoint;
typedef int ( *TEXMAP_INTERSECT_LINE_SURFACE )( const ON_Line*, const ON_BrepFace*, ON_SimpleArray& );
typedef bool ( *TEXMAP_BREP_FACE_CLOSEST_POINT )( const ON_BrepFace*, const ON_3dPoint*, ON_3dPoint& );
+typedef ON_TextureMapping ( *GET_TEXMAP_FROM_DOCUMENT )( const class CRhinoDoc&, const ON_MappingChannel* );
class ON_CLASS ON_TextureMapping : public ON_ModelComponent
{
@@ -91,7 +92,9 @@ public:
srf_mapping_primitive = 7, // m_mapping_primitive is an ON_Surface
brep_mapping_primitive = 8, // m_mapping_primitive is an ON_Brep
ocs_mapping = 9, // same as plane_mapping - used to differentiate between OCS and plane mapping in the UI
- false_colors = 10 // some kind of false color mapping used to set per vertex colors.
+ false_colors = 10, // some kind of false color mapping used to set per vertex colors.
+ wcs_projection = 11, // used for ON_MappingTag when creating texture coordinates for WCS projections
+ wcsbox_projection = 12 // used for ON_MappingTag when creating texture coordinates for WCS box projections
};
static ON_TextureMapping::TYPE TypeFromUnsigned(
diff --git a/opennurbs_xml.cpp b/opennurbs_xml.cpp
index 5a8dcfed..9f4737a6 100644
--- a/opennurbs_xml.cpp
+++ b/opennurbs_xml.cpp
@@ -5339,8 +5339,11 @@ void ON_RdkDocumentDefaults::CreateXML(void)
p.SetParam(ON_RDK_SUN_DAYLIGHT_SAVING_ON, false);
p.SetParam(ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, 60);
+ int cy = 0, cm = 0, cd = 0; double ch = 0.0;
+ ON_SunEngine::GetCurrentLocalDateTime(cy, cm, cd, ch);
+
ON_SunEngine engine(ON_SunEngine::Accuracy::Minimum);
- engine.SetLocalDateTime(2000, 1, 1, 12.0);
+ engine.SetLocalDateTime(cy, cm, cd, ch);
int y = 0, m = 0, d = 0; double h = 0.0;
engine.LocalDateTime(y, m, d, h);
p.SetParam(ON_RDK_SUN_DATE_YEAR, y);