diff --git a/CMakeLists.txt b/CMakeLists.txt index 223de362..a59cb622 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,6 +387,7 @@ set( OPENNURBS_PLUS_HEADERS opennurbs_plus_hiddenline.h opennurbs_plus_hiddenline_impl.h opennurbs_plus_idimage.h + opennurbs_plus_implicitfn.h opennurbs_plus_massprop.h opennurbs_plus_mesh_intersection.h opennurbs_plus_mesh_marker.h @@ -418,6 +419,7 @@ set( OPENNURBS_PLUS_SOURCES opennurbs_plus_ginfinity.cpp opennurbs_plus_hiddenline.cpp opennurbs_plus_idimage.cpp + opennurbs_plus_implicitfn.cpp opennurbs_plus_massprop.cpp opennurbs_plus_memory.cpp opennurbs_plus_memory_new.cpp diff --git a/opennurbsRhino_iOS.xcodeproj/project.pbxproj b/opennurbsRhino_iOS.xcodeproj/project.pbxproj index a1afcde0..72c48c5f 100644 --- a/opennurbsRhino_iOS.xcodeproj/project.pbxproj +++ b/opennurbsRhino_iOS.xcodeproj/project.pbxproj @@ -440,6 +440,10 @@ D6B06A951BC57E52000B5948 /* opennurbs_archive_manifest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6B06A941BC57E52000B5948 /* opennurbs_archive_manifest.cpp */; }; D6F232111C0086D700D1B680 /* opennurbs_file_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = D6F232101C0086D700D1B680 /* opennurbs_file_utilities.h */; }; D6F232131C0086E500D1B680 /* opennurbs_file_utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6F232121C0086E500D1B680 /* opennurbs_file_utilities.cpp */; }; + DE0517582BC6E45E00653EF9 /* opennurbs_plus_implicitfn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEE5EF022BC668C5008B7FC3 /* opennurbs_plus_implicitfn.cpp */; }; + DE05175B2BC6E47700653EF9 /* opennurbs_plus_implicitfn.h in Headers */ = {isa = PBXBuildFile; fileRef = DEE5EEFE2BC6689D008B7FC3 /* opennurbs_plus_implicitfn.h */; }; + DEE5EF012BC6689D008B7FC3 /* opennurbs_plus_implicitfn.h in Headers */ = {isa = PBXBuildFile; fileRef = DEE5EEFE2BC6689D008B7FC3 /* opennurbs_plus_implicitfn.h */; }; + DEE5EF032BC668C6008B7FC3 /* opennurbs_plus_implicitfn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEE5EF022BC668C5008B7FC3 /* opennurbs_plus_implicitfn.cpp */; }; DF2447021BE96D2600FD193A /* opennurbs_md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2446FE1BE96D2600FD193A /* opennurbs_md5.cpp */; }; DF2447031BE96D2600FD193A /* opennurbs_md5.h in Headers */ = {isa = PBXBuildFile; fileRef = DF2446FF1BE96D2600FD193A /* opennurbs_md5.h */; }; DF2447041BE96D2600FD193A /* opennurbs_sha1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2447001BE96D2600FD193A /* opennurbs_sha1.cpp */; }; @@ -1129,6 +1133,8 @@ D6B06A941BC57E52000B5948 /* opennurbs_archive_manifest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_archive_manifest.cpp; sourceTree = ""; }; D6F232101C0086D700D1B680 /* opennurbs_file_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_file_utilities.h; sourceTree = ""; }; D6F232121C0086E500D1B680 /* opennurbs_file_utilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_file_utilities.cpp; sourceTree = ""; }; + DEE5EEFE2BC6689D008B7FC3 /* opennurbs_plus_implicitfn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_plus_implicitfn.h; sourceTree = ""; }; + DEE5EF022BC668C5008B7FC3 /* opennurbs_plus_implicitfn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_plus_implicitfn.cpp; sourceTree = ""; }; DF2446FE1BE96D2600FD193A /* opennurbs_md5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_md5.cpp; sourceTree = ""; }; DF2446FF1BE96D2600FD193A /* opennurbs_md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_md5.h; sourceTree = ""; }; DF2447001BE96D2600FD193A /* opennurbs_sha1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sha1.cpp; sourceTree = ""; }; @@ -1358,6 +1364,7 @@ D69DB7151A957AA10080DA91 /* opennurbs_plus_hiddenline_impl.h */, D69DB7161A957AA10080DA91 /* opennurbs_plus_hiddenline.h */, D6184CAB1B0F83560099E507 /* opennurbs_plus_idimage.h */, + DEE5EEFE2BC6689D008B7FC3 /* opennurbs_plus_implicitfn.h */, DFFC2022169E4D36007E9A98 /* opennurbs_plus_massprop.h */, 1D0BCB481D7F6F2A00B5F9CD /* opennurbs_plus_mesh_intersection.h */, E7DDF94D22958CEF00B22F2C /* opennurbs_plus_meshbooleans_impl.h */, @@ -1576,6 +1583,7 @@ 10A930DE0B2F16980022B2F6 /* opennurbs_plus_ginfinity.cpp */, D69DB7191A957ABA0080DA91 /* opennurbs_plus_hiddenline.cpp */, D66DBD861A67505A00125759 /* opennurbs_plus_idimage.cpp */, + DEE5EF022BC668C5008B7FC3 /* opennurbs_plus_implicitfn.cpp */, DFFC2028169E4D6C007E9A98 /* opennurbs_plus_massprop.cpp */, DFB8D1C92183D5DC00A0307C /* opennurbs_plus_memory_apple.cpp */, 10D7CFE609E04F0A0056FF9C /* opennurbs_plus_memory_new.cpp */, @@ -1850,6 +1858,7 @@ 369B20181CE520E000A5CB6E /* opennurbs_annotationbase.h in Headers */, 10D7D0F909E0523C0056FF9C /* opennurbs_pluginlist.h in Headers */, 10D7D0FA09E0523C0056FF9C /* opennurbs_point.h in Headers */, + DEE5EF012BC6689D008B7FC3 /* opennurbs_plus_implicitfn.h in Headers */, 10D7D0FB09E0523C0056FF9C /* opennurbs_pointcloud.h in Headers */, 10D7D0FC09E0523C0056FF9C /* opennurbs_pointgeometry.h in Headers */, D69DB7181A957AA10080DA91 /* opennurbs_plus_hiddenline.h in Headers */, @@ -1952,6 +1961,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + DE05175B2BC6E47700653EF9 /* opennurbs_plus_implicitfn.h in Headers */, 1D741C1621B9C5DC00AA10E5 /* opennurbs_sleeplock.h in Headers */, 99284D062800FD4B00CA9E82 /* opennurbs_render_channels.h in Headers */, A165DED527C953020006F184 /* opennurbs_xml.h in Headers */, @@ -2284,6 +2294,7 @@ 1D496A012354FE1700BFB6AA /* opennurbs_plus_subd_facegroups.cpp in Sources */, 10D7D05E09E04F820056FF9C /* opennurbs_zlib_memory.cpp in Sources */, 10D7D05F09E04F820056FF9C /* opennurbs_zlib.cpp in Sources */, + DEE5EF032BC668C6008B7FC3 /* opennurbs_plus_implicitfn.cpp in Sources */, 1DB1AA931ED7B864007648CC /* opennurbs_plus_memory.cpp in Sources */, 10A930E50B2F16980022B2F6 /* opennurbs_plus_ctree.cpp in Sources */, 10A930E60B2F16980022B2F6 /* opennurbs_plus_ginfinity.cpp in Sources */, @@ -2532,6 +2543,7 @@ 1D496A022354FE1700BFB6AA /* opennurbs_plus_subd_facegroups.cpp in Sources */, DF6D39021F2A72DF00D997E4 /* opennurbs_zlib_memory.cpp in Sources */, DF6D39031F2A72DF00D997E4 /* opennurbs_zlib.cpp in Sources */, + DE0517582BC6E45E00653EF9 /* opennurbs_plus_implicitfn.cpp in Sources */, DF6D39041F2A72DF00D997E4 /* opennurbs_plus_memory.cpp in Sources */, DF6D39051F2A72DF00D997E4 /* opennurbs_plus_ctree.cpp in Sources */, DF6D39061F2A72DF00D997E4 /* opennurbs_plus_ginfinity.cpp in Sources */, 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..ec116cca 100644 --- a/opennurbs_linecurve.cpp +++ b/opennurbs_linecurve.cpp @@ -186,7 +186,18 @@ 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; + bool rc = true; + if (m_t[0] > m_t[1]) + { + if (text_log) text_log->Print(L"Line domain not valid."); + rc = false; + } + if (m_line.from.IsCoincident(m_line.to)) + { + if (text_log) text_log->Print(L"Line points are coincident."); + rc = false; + } + return rc; } 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..e56722f7 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()); + + const ON_TextureCoordinates* pTC = pMesh->SetCachedTextureCoordinatesEx(mapping, pObjectXform, true, false); + + if(pTC && 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 a707df95..51211a1d 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,8 +15,8 @@ // #define RMA_VERSION_YEAR 2024 #define RMA_VERSION_MONTH 4 -#define RMA_VERSION_DATE 10 -#define RMA_VERSION_HOUR 5 +#define RMA_VERSION_DATE 11 +#define RMA_VERSION_HOUR 13 #define RMA_VERSION_MINUTE 0 //////////////////////////////////////////////////////////////// @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,6,24101,5000 -#define VERSION_WITH_PERIODS 8.6.24101.05000 +#define VERSION_WITH_COMMAS 8,7,24102,13000 +#define VERSION_WITH_PERIODS 8.7.24102.13000 #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.24101.05000" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.6.24101.05000" +#define RMA_VERSION_WITH_PERIODS_STRING "8.7.24102.13000" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.7.24102.13000" 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..86293782 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] /// @@ -7887,7 +8616,7 @@ public: /* Description: - Clear all cached evaluation information (meshes, surface points, boundiang boxes, ...) + Clear all cached evaluation information (meshes, surface points, bounding boxes, ...) that depends on edge tags, vertex tags, and the location of vertex control points. */ void ClearEvaluationCache() const; @@ -10614,7 +11343,54 @@ public: const ON_3dVector VertexNormal( unsigned grid_point_index ) const; - + + /// + /// Get the SubD face evaluation parameter for the + /// SubD surface point where this vertex is located. + /// + /// + /// 0 <= grid2dex_i <= m_grid.SideSegmentCount() + /// + /// 0 <= grid2dex_j <= m_grid.SideSegmentCount() + /// + /// + /// The SubD face evaluation parameter at this grid vertex. + /// + const ON_SubDFaceParameter VertexSubDFaceParameter( + unsigned grid2dex_i, + unsigned grid2dex_j + ) const; + + /// + /// Get the SubD face evaluation parameter for the + /// SubD surface point where this vertex is located. + /// + /// + /// 0 <= grid2dex.i <= m_grid.SideSegmentCount() + /// + /// 0 <= grid2dex.j <= m_grid.SideSegmentCount() + /// + /// + /// The SubD face evaluation parameter at this grid vertex. + /// + const ON_SubDFaceParameter VertexSubDFaceParameter( + ON_2udex grid2dex + ) const; + + /// + /// Get the SubD face evaluation parameter for the + /// SubD surface point where this vertex is located. + /// + /// + /// 0 <= grid_point_index <= m_grid.GridPointCount() + /// + /// + /// The SubD face evaluation parameter at this grid vertex. + /// + const ON_SubDFaceParameter VertexSubDFaceParameter( + unsigned grid_point_index + ) const; + /* Parameters: grid2dex_i - [in] @@ -10905,24 +11681,35 @@ public: ON_ComponentStatus Status() const; /// - /// This simple version - /// transforms the points and normals and unconditionally - /// makes no changes to the curvatures, texture coordinates and colors. + /// This simple version transforms the points and normals and attempts possible + /// transformations to the curvatures, texture coordinates and colors: + /// - Normals are transformed with the inverse transpose of xform, then flipped + /// if the transformation is mirroring, and unitized if the transformation has + /// a scale component. + /// - Texture coordinates are unchanged in value, + /// ON_SubDimple::Transform(xform) will update the texture coordinate tag + /// with subdimple.m_texture_mapping_tag.Transform(xform) + /// - Colors are unchanged in value if xform is an isometry, deleted otherwise. + /// ON_SubDimple::Transform(xform) will update the colors tag + /// with subdimple.m_color_mapping_tag.Transform(xform) + /// - Curvatures are scaled by 1/det(xform), if xform is a Similarity tranformation, + /// deleted otherwise. /// - /// Typically lots of fragments are being transformed and - /// the type and context of the transformation determines - /// if texture coordinate, curvature and color inforation should be - /// preserved or destroyed. It is better to determine the answers to these - /// questions and call the version of Transform with - /// the bKeepTextures, bKeepCurvatures and bKeepColors parameters. - /// For example if the transformation is an isometry and the colors - /// are set from the curvatures, then curvatures and colors should be - /// kept. If the transformation is not an isometry, the curvatures should - /// be destroyed. - /// If the texture coordinates are set from grid location - /// (fake surface paramaters), the the texture coordinates should be kept. + /// Typically lots of fragments are being transformed and the type and context + /// of the transformation determines if and how normals, texture coordinate, + /// curvature and color inforation should be preserved, transformed, or destroyed. + /// It is better to determine the answers to these questions and call the version + /// of Transform with the bKeepTextures, bKeepCurvatures and bKeepColors parameters. + /// + /// For example if the transformation is an isometry and the colors are set from + /// the curvatures, then curvatures and colors should be kept. If the transformation + /// is a similarity, the curvatures should be scaled and the colors destroyed. If + /// the transformation is not a similarity both colors and curvatures should be destroyed. + /// + /// If the texture coordinates are set from grid location (fake surface paramaters), + /// then the texture coordinates should be kept. /// If transform is not an identity and the texture coordinates come from a - /// world object mapping, the should generally be destroyed. + /// world object mapping, they should generally be destroyed. /// /// /// @@ -10931,7 +11718,20 @@ public: ); /// - /// + /// This version transforms the points and normals and attempts possible + /// transformations to the curvatures, texture coordinates and colors, if they are kept: + /// - Normals are transformed with the inverse transpose of xform, then flipped + /// if the transformation is mirroring, and unitized if the transformation has a scale component. + /// - Texture coordinates are unchanged in value, ON_SubDimple::Transform(xform) will update + /// them with subdimple.m_texture_mapping_tag.Transform(xform). + /// If bKeepTextures is false, texture coordinates are destroyed. + /// - Curvatures are either: + /// - scaled by 1/det(xform), if xform is a Similarity tranformation + /// - unchanged in value, if not. + /// If bKeepCurvatures is false, curvatures are destroyed. + /// - Colors are unchanged in value, ON_SubDimple::Transform(xform) will update + /// them with subdimple.m_color_mapping_tag.Transform(xform) + /// If bKeepColors is false, colors are destroyed. /// /// /// @@ -10945,6 +11745,37 @@ public: const ON_Xform& xform ); + /// + /// This version transforms the points, normals, curvatures, texture coordinates, + /// and colors, if they are kept. + /// - Normals are transformed with xformNormals. + /// - Texture coordinates are transformed with xformTextures. + /// If bKeepTextures is false, texture coordinates are destroyed. + /// - Curvatures are transformed with xformCurvatures. + /// If bKeepCurvatures is false, curvatures are destroyed. + /// - Colors are transformed with xformColors. + /// If bKeepColors is false, colors are destroyed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + bool Transform( + bool bKeepTextures, + bool bKeepCurvatures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformTextures, + const ON_Xform& xformCurvatures, + const ON_Xform& xformColors + ); + ON_SubDMeshFragment* m_next_fragment; ON_SubDMeshFragment* m_prev_fragment; @@ -12092,10 +12923,21 @@ public: ON_SubDMesh& b ); - bool Transform( + bool Transform( const ON_Xform& xform ); + bool Transform( + bool bKeepCurvatures, + bool bKeepTextures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformCurvatures, + const ON_Xform& xformTextures, + const ON_Xform& xformColors + ); + ON_DEPRECATED_MSG("AbsoluteSubDDisplayDensity") unsigned int DisplayDensity() const; @@ -12282,6 +13124,11 @@ public: const ON_Xform& xform ); + bool Transform( + const ON_Xform& xform, + const ON_Xform& xformNormals + ); + /// /// Get a limit surface point. /// @@ -12940,7 +13787,7 @@ public: /* Description: - Apply a tranxfomration matrix to vertex geometry information. + Apply a transformation matrix to vertex geometry information. Parameters: bTransformationSavedSubdivisionPoint - [in] If the transformation is being applied to every vertex, edge and @@ -12959,6 +13806,12 @@ public: const class ON_Xform& xform ); + bool Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform, + const class ON_Xform& xformNormals + ); + bool SetControlNetPoint( ON_3dPoint control_net_point, bool bClearNeighborhoodCache @@ -14030,7 +14883,7 @@ public: /* Description: - Apply a tranxfomration matrix to vertex geometry information. + Apply a transformation matrix to edge geometry information. Parameters: bTransformationSavedSubdivisionPoint - [in] @@ -14971,7 +15824,7 @@ public: /* Description: - Apply a tranxfomration matrix to vertex geometry information. + Apply a transformation matrix to face geometry information. Parameters: bTransformationSavedSubdivisionPoint - [in] @@ -14991,6 +15844,50 @@ public: const class ON_Xform& xform ); + /* + Description: + Apply a transformation matrix to face geometry information. + This version transforms the points, normals, curvatures, texture coordinates, + and colors, if they are kept. + - Normals are transformed with xformNormals. + - Texture coordinates are transformed with xformTextures. + If bKeepTextures is false, texture coordinates are destroyed. + - Curvatures are transformed with xformCurvatures. + If bKeepCurvatures is false, curvatures are destroyed. + - Colors are transformed with xformColors. + If bKeepColors is false, colors are destroyed. + + Parameters: + bTransformationSavedSubdivisionPoint - [in] + If the transformation is being applied to every vertex, edge and + face in every level of a subdivision object, and the transformation + is an orientation preserving isometry (rotation, translation, ...), + then set bTransformationSavedSubdivisionPoint = true to apply the + transformation to saved subdivision and saved limit point information. + In all other cases, set bTransformationSavedSubdivisionPoint = false + and any saved subdivision points or saved limit points will be + deleted. When in doubt, pass false. + bKeepTExtures - [in] + bKeepCurvatures - [in] + bKeepColors - [in] + xform - [in] + xformNormals - [in] + xformTextures - [in] + xformCurvatures - [in] + xformColors - [in] + */ + bool Transform( + bool bTransformationSavedSubdivisionPoint, + bool bKeepCurvatures, + bool bKeepTextures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformCurvatures, + const ON_Xform& xformTextures, + const ON_Xform& xformColors + ); + const ON_BoundingBox ControlNetBoundingBox() const; /// diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp index 14b3e832..6b12d171 100644 --- a/opennurbs_subd_data.cpp +++ b/opennurbs_subd_data.cpp @@ -573,7 +573,7 @@ std::shared_ptr ON_SubDLevel::FaceArray() const ////////////////////////////////////////////////////////////////////////// // -// ON_SubD::Tranxform +// ON_SubD::Transform // ON_SubDimple::Transform // ON_SubDLevel::Transform // ON_SubDVertex::Transform @@ -632,6 +632,18 @@ bool ON_SubDSectorSurfacePoint::Transform( return rc; } +bool ON_SubDSectorSurfacePoint::Transform( + const ON_Xform& xform, + const ON_Xform& xformNormals +) +{ + TransformPoint(&xform.m_xform[0][0], m_limitP); + TransformVector(&xform.m_xform[0][0], m_limitT1); + TransformVector(&xform.m_xform[0][0], m_limitT2); + TransformVector(&xformNormals.m_xform[0][0], m_limitN); + return true; +} + bool ON_SubDVertex::Transform( bool bTransformationSavedSubdivisionPoint, @@ -671,6 +683,46 @@ bool ON_SubDVertex::Transform( return true; } + +bool ON_SubDVertex::Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform, + const class ON_Xform& xformNormals + ) +{ + TransformPoint(&xform.m_xform[0][0],m_P); + + if (bTransformationSavedSubdivisionPoint) + { + // Transform saved subdivision point + Internal_TransformComponentBase(bTransformationSavedSubdivisionPoint, xform); + + // NOTE WELL: + // If the vertex + // is tagged as ON_SubDVertexTag::Corner + // and bTransformationSavedSubdivisionPoint is true, + // and the corner sector(s) contains interior smooth edges, + // and the transformation changes the angle between a corner sector's crease boundary, + // then the sector's interior smooth edge's m_sector_coefficient[] could change + // and invalidate the subdivison points and limit points. + // This is only possible for uncommon (in practice) transformations + // and corner sectors and will require a fair bit of testing for + // now it's easier to simply set bTransformationSavedSubdivisionPoint to false + // at a higher level when these types of transformations are encountered. + if (bTransformationSavedSubdivisionPoint && Internal_SurfacePointFlag()) + { + for (const ON_SubDSectorSurfacePoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point) + const_cast(lp)->Transform(xform, xformNormals); + } + else + Internal_ClearSurfacePointFlag(); + } + else + this->ClearSavedSubdivisionPoints(); + + return true; +} + void ON_SubDVertex::UnsetControlNetPoint() { m_P[0] = ON_DBL_QNAN; @@ -836,6 +888,41 @@ bool ON_SubDFace::Transform( bool bTransformationSavedSubdivisionPoint, const class ON_Xform& xform ) +{ + ON_Xform xformNormals{ ON_Xform::IdentityTransformation }; + const double det{ xform.GetSurfaceNormalXform(xformNormals) }; + ON_Xform xformCurvatures{ ON_Xform::IdentityTransformation }; + if (abs(det) > ON_EPSILON) { + xformNormals = xformNormals * ON_Xform::ScaleTransformation(ON_3dPoint::Origin, 1. / det); + xformCurvatures = xformCurvatures * ON_Xform::ScaleTransformation(ON_3dPoint::Origin, 1. / det); + } + const bool bKeepCurvatures{ xform.IsSimilarity() != 0 }; + const bool bKeepTextures{ true }; + const bool bKeepColors{ xform.IsRigid() != 0 }; + + + // bTransformationSavedSubdivisionPoint = true should mean that + // xform is a similarity. + // If it's more complicated than this, the calling code should + // reset or adjust colors as needed based on information in the + // SubD's texture coordinate mapping tag and color mapping tag. + // Note that both of those tags have their transformation updated + // so intelligent decisions can be made at a higher level where + // there is enough context to make the correct decision. + return Transform(bTransformationSavedSubdivisionPoint, bKeepCurvatures, bKeepTextures, bKeepColors, xform, xformNormals, xformCurvatures, ON_Xform::IdentityTransformation, ON_Xform::IdentityTransformation); +} + +bool ON_SubDFace::Transform( + bool bTransformationSavedSubdivisionPoint, + bool bKeepCurvatures, + bool bKeepTextures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformCurvatures, + const ON_Xform& xformTextures, + const ON_Xform& xformColors + ) { if (bTransformationSavedSubdivisionPoint) { @@ -843,15 +930,8 @@ bool ON_SubDFace::Transform( if (Internal_SurfacePointFlag()) { - // bTransformationSavedSubdivisionPoint = true means xform is an isometry. - // If its more complicated than this, the calling code should - // reset or addjut colors as needed based on information in the - // SubD's texture coordinate mapping tag and color mapping tag. - // Note that both of those tags have their transformation updated - // so intelligent decisions can be made at a higher level where - // there is enough context to make the correct decision. for (ON_SubDMeshFragment* f = m_mesh_fragments; nullptr != f; f = f->m_next_fragment) - f->Transform(true, true, true, xform); + f->Transform(bKeepCurvatures, bKeepTextures, bKeepColors, xform, xformNormals, xformCurvatures, xformTextures, xformColors); } else Internal_ClearSurfacePointFlag(); @@ -868,12 +948,25 @@ bool ON_SubDLevel::Transform( ) { bool rc = true; + ON_Xform xformNormals{ ON_Xform::IdentityTransformation }; + const double det{ xform.GetSurfaceNormalXform(xformNormals) }; + ON_Xform xformCurvatures{ ON_Xform::IdentityTransformation }; + if (abs(det) > ON_EPSILON) { + xformNormals = xformNormals * ON_Xform::ScaleTransformation(ON_3dPoint::Origin, 1. / det); + xformCurvatures = xformCurvatures * ON_Xform::ScaleTransformation(ON_3dPoint::Origin, 1. / det); + } + else { + rc = false; + } + const bool bKeepCurvatures{ xform.IsSimilarity() != 0 }; + const bool bKeepTextures{ true }; + const bool bKeepColors{ xform.IsRigid() != 0 }; m_aggregates.m_bDirtyBoundingBox = true; for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { - if (false == const_cast(vertex)->Transform(bTransformationSavedSubdivisionPoint, xform)) + if (false == const_cast(vertex)->Transform(bTransformationSavedSubdivisionPoint, xform, xformNormals)) rc = false; } @@ -885,14 +978,23 @@ bool ON_SubDLevel::Transform( for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { - if (false == const_cast(face)->Transform(bTransformationSavedSubdivisionPoint, xform)) + if (false == const_cast(face)->Transform( + bTransformationSavedSubdivisionPoint, bKeepCurvatures, bKeepTextures, bKeepColors, + xform, xformNormals, xformCurvatures, ON_Xform::IdentityTransformation, ON_Xform::IdentityTransformation + )) rc = false; } - if (false == m_surface_mesh.Transform(xform)) + if (false == m_surface_mesh.Transform( + bKeepCurvatures, bKeepTextures, bKeepColors, + xform, xformNormals, xformCurvatures, ON_Xform::IdentityTransformation, ON_Xform::IdentityTransformation + )) rc = false; - if (false == m_control_net_mesh.Transform(xform)) + if (false == m_control_net_mesh.Transform( + bKeepCurvatures, bKeepTextures, bKeepColors, + xform, xformNormals, xformCurvatures, ON_Xform::IdentityTransformation, ON_Xform::IdentityTransformation + )) rc = false; if (rc) @@ -918,6 +1020,30 @@ bool ON_SubDMesh::Transform( return impl->Transform(xform); } + +bool ON_SubDMesh::Transform( + bool bKeepCurvatures, + bool bKeepTextures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformCurvatures, + const ON_Xform& xformTextures, + const ON_Xform& xformColors + ) +{ + if (false == xform.IsValid()) + return false; + if (xform.IsIdentity()) + return true; + if (xform.IsZero()) + return false; + ON_SubDMeshImpl* impl = m_impl_sp.get(); + if ( nullptr == impl ) + return true; // transform applied to empty mesh is true on purpose. Changing to false will break other code. + return impl->Transform(bKeepCurvatures, bKeepTextures, bKeepColors, xform, xformNormals, xformCurvatures, xformTextures, xformColors); +} + bool ON_SubDimple::Transform( const ON_Xform& xform ) @@ -949,14 +1075,14 @@ bool ON_SubDimple::Transform( // If // 1) The transformation is being applied to every vertex, edge and // face in every level of a subdivision object, and - // 2) the transformation is an isometry (rotation, translation, ...), + // 2) the transformation is an isometry (rotation, translation, mirror, ...), // a uniform scale, or a composition of these types, // then set bTransformationSavedSubdivisionPoint = true to apply the // transformation to saved subdivision and saved limit point information. // In all other cases, set bTransformationSavedSubdivisionPoint = false // and any saved subdivision points or saved limit points will be // deleted. - const bool bTransformationSavedSubdivisionPoint = (1 == xform.IsRigid()); + const bool bTransformationSavedSubdivisionPoint = (0 != xform.IsSimilarity()); bool bHasTextures = false; bool bHasColors = false; @@ -1058,15 +1184,47 @@ bool ON_SubDMeshFragment::Transform( const ON_Xform& xform ) { - return this->Transform(true, true, true, xform); + const bool bKeepCurvatures{ xform.IsSimilarity() != 0 }; + const bool bKeepTextures{ true }; + const bool bKeepColors{ xform.IsRigid() != 0 }; + return this->Transform(bKeepCurvatures, bKeepTextures, bKeepColors, xform); } bool ON_SubDMeshFragment::Transform( bool bKeepCurvatures, - bool bKeepTextureCoordinates, + bool bKeepTextures, bool bKeepColors, const ON_Xform& xform ) +{ + ON_Xform xformNormals{ON_Xform::IdentityTransformation}; + const double det{xform.GetSurfaceNormalXform(xformNormals)}; + ON_Xform xformCurvatures{ON_Xform::IdentityTransformation}; + if (abs(det) > ON_EPSILON) { + xformNormals = xformNormals * ON_Xform::ScaleTransformation(ON_3dPoint::Origin, 1. / det); + xformCurvatures = xformCurvatures * ON_Xform::ScaleTransformation(ON_3dPoint::Origin, 1. / det); + } + else { + return ON_SUBD_RETURN_ERROR(false); + } + + return this->Transform( + bKeepCurvatures, bKeepTextures, bKeepColors, + xform, xformNormals, xformCurvatures, ON_Xform::IdentityTransformation, ON_Xform::IdentityTransformation + ); +} + +bool ON_SubDMeshFragment::Transform( + bool bKeepCurvatures, + bool bKeepTextures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformCurvatures, + const ON_Xform& xformTextures, + const ON_Xform& xformColors + +) { const unsigned count = PointCount(); if (0 == count) @@ -1078,7 +1236,7 @@ bool ON_SubDMeshFragment::Transform( return ON_SUBD_RETURN_ERROR(false); if (count == NormalCount()) { - if (false == ON_TransformVectorList(3, count, (int)m_N_stride, m_N, xform)) + if (false == ON_TransformVectorList(3, count, (int)m_N_stride, m_N, xformNormals)) return ON_SUBD_RETURN_ERROR(false); } if (0 != (ON_SubDMeshFragment::EtcControlNetQuadBit & m_vertex_count_etc)) @@ -1102,9 +1260,7 @@ bool ON_SubDMeshFragment::Transform( const ON_3dVector A(m_ctrlnetN); if (A.IsNotZero()) { - ON_3dVector B = xform * A; - if ( A.IsUnitVector() && false == B.IsUnitVector() ) - B = B.UnitVector(); + ON_3dVector B = xformNormals * A; m_ctrlnetN[0] = B.x; m_ctrlnetN[1] = B.y; m_ctrlnetN[2] = B.z; @@ -1112,43 +1268,106 @@ bool ON_SubDMeshFragment::Transform( } ON_GetPointListBoundingBox(3,0,count,(int)m_P_stride,m_P,&m_surface_bbox.m_min.x,&m_surface_bbox.m_max.x,false); - if (false == bKeepTextureCoordinates) - { - this->SetTextureCoordinatesExistForExperts(false); - double* p = &this->m_ctrlnetT[0][0]; - double* p1 = p + sizeof(this->m_ctrlnetT) / sizeof(this->m_ctrlnetT[0][0]); - while (p < p1) - *p++ = ON_DBL_QNAN; + if (TextureCoordinatesExistForExperts()) { + this->SetTextureCoordinatesExistForExperts(bKeepTextures); + if (bKeepTextures) { + if (xformTextures.IsNotIdentity()) { + double* p = &this->m_ctrlnetT[0][0]; + constexpr unsigned dim = sizeof(this->m_ctrlnetT[0]) / sizeof(this->m_ctrlnetT[0][0]); + constexpr unsigned tcount = sizeof(this->m_ctrlnetT) / sizeof(this->m_ctrlnetT[0]); + if (false == ON_TransformPointList(dim, false, tcount, dim, p, xformTextures)) + return ON_SUBD_RETURN_ERROR(false); + } + } + else { + double* p = &this->m_ctrlnetT[0][0]; + const double* p1 = p + sizeof(this->m_ctrlnetT) / sizeof(this->m_ctrlnetT[0][0]); + while (p < p1) + *p++ = ON_DBL_QNAN; + } } - if (false == bKeepCurvatures) - { - this->SetCurvaturesExistForExperts(false); - this->m_ctrlnetK[0] = ON_SurfaceCurvature::Nan; - this->m_ctrlnetK[1] = ON_SurfaceCurvature::Nan; - this->m_ctrlnetK[2] = ON_SurfaceCurvature::Nan; - this->m_ctrlnetK[3] = ON_SurfaceCurvature::Nan; + + if (CurvaturesExistForExperts()) { + this->SetCurvaturesExistForExperts(bKeepCurvatures); + if (bKeepCurvatures) { + if (xformCurvatures.IsNotIdentity()) { + constexpr unsigned dim{sizeof(ON_SurfaceCurvature) / sizeof(double)}; + constexpr unsigned ccount{sizeof(m_ctrlnetK) / sizeof(m_ctrlnetK[0])}; + if (false == ON_TransformVectorList(dim, ccount, dim, (double*)m_ctrlnetK, xformCurvatures)) + return ON_SUBD_RETURN_ERROR(false); + } + } + else { + this->m_ctrlnetK[0] = ON_SurfaceCurvature::Nan; + this->m_ctrlnetK[1] = ON_SurfaceCurvature::Nan; + this->m_ctrlnetK[2] = ON_SurfaceCurvature::Nan; + this->m_ctrlnetK[3] = ON_SurfaceCurvature::Nan; + } } - if (false == bKeepColors) - { - this->SetColorsExistForExperts(false); - this->m_ctrlnetC[0] = ON_Color::UnsetColor; - this->m_ctrlnetC[1] = ON_Color::UnsetColor; - this->m_ctrlnetC[2] = ON_Color::UnsetColor; - this->m_ctrlnetC[3] = ON_Color::UnsetColor; + + if (ColorsExistForExperts()) { + this->SetColorsExistForExperts(bKeepColors); + if (bKeepColors) { + if (xformColors.IsNotIdentity()) { + for (int i = 0; i < 4; ++i) { + ON_Color& color{ m_ctrlnetC[i] }; + ON_4dPoint rgba{ (double)color.Red(), (double)color.Green(), (double)color.Blue(), (double)color.Alpha() }; + rgba.Transform(xformColors); + m_ctrlnetC->SetRGBA((int)rgba[0], (int)rgba[1], (int)rgba[2], (int)rgba[3]); + } + } + } + else { + this->m_ctrlnetC[0] = ON_Color::UnsetColor; + this->m_ctrlnetC[1] = ON_Color::UnsetColor; + this->m_ctrlnetC[2] = ON_Color::UnsetColor; + this->m_ctrlnetC[3] = ON_Color::UnsetColor; + } } + return true; } bool ON_SubDMeshImpl::Transform( const ON_Xform& xform ) +{ + m_bbox = ON_BoundingBox::EmptyBoundingBox; + ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; + for (const ON_SubDMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + { + if (false == const_cast(fragment)->Transform(xform)) + return ON_SUBD_RETURN_ERROR(false); + if (fragment == m_first_fragment) + bbox = fragment->m_surface_bbox; + else + bbox.Union(fragment->m_surface_bbox); + } + m_bbox = bbox; + ChangeContentSerialNumber(); + return true; +} + +bool ON_SubDMeshImpl::Transform( + bool bKeepCurvatures, + bool bKeepTextures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformCurvatures, + const ON_Xform& xformTextures, + const ON_Xform& xformColors + ) { const bool bIsometry = (1 == xform.IsRigid()); m_bbox = ON_BoundingBox::EmptyBoundingBox; ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; for ( const ON_SubDMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) { - if ( false == const_cast(fragment)->Transform(bIsometry, bIsometry, bIsometry, xform) ) + if ( false == const_cast(fragment)->Transform( + bKeepCurvatures, bKeepTextures, bKeepColors, + xform, xformNormals, xformCurvatures, xformTextures, xformColors + ) ) return ON_SUBD_RETURN_ERROR(false); if ( fragment == m_first_fragment ) bbox = fragment->m_surface_bbox; diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 8d072571..2842dfbf 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -3000,9 +3000,20 @@ public: void ClearTree(); - bool Transform( + bool Transform( const ON_Xform& xform ); + + bool Transform( + bool bKeepCurvatures, + bool bKeepTextures, + bool bKeepColors, + const ON_Xform& xform, + const ON_Xform& xformNormals, + const ON_Xform& xformCurvatures, + const ON_Xform& xformTextures, + const ON_Xform& xformColors + ); bool GetTightBoundingBox( diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp index 90a64716..4aff27ca 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), quad_face_t, 1.0 - 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 <= 0.5 && 0.0 <= m_t && m_t <= 0.5; +} + +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_subd_fragment.cpp b/opennurbs_subd_fragment.cpp index 83912c0b..6da0addd 100644 --- a/opennurbs_subd_fragment.cpp +++ b/opennurbs_subd_fragment.cpp @@ -1801,6 +1801,84 @@ const ON_3dVector ON_SubDMeshFragment::VertexNormal( ; } + +const ON_SubDFaceParameter ON_SubDMeshFragment::VertexSubDFaceParameter( + ON_2udex grid2dex +) const +{ + return VertexSubDFaceParameter(grid2dex.i, grid2dex.j); +} + +const ON_SubDFaceParameter ON_SubDMeshFragment::VertexSubDFaceParameter( + unsigned grid2dex_i, + unsigned grid2dex_j +) const +{ + for (;;) + { + const ON_3dPoint debugP = VertexPoint(grid2dex_i, grid2dex_j); + if (false == debugP.IsValid()) + break; + + + const unsigned gc = m_grid.SideSegmentCount(); + if (gc < 1) + break; + if (grid2dex_i > gc || grid2dex_j > gc) + break; + + // quad faces have m_face_fragment_count = 1; + // n-gon faces have m_face_fragment_count = n (n = 3 or n >= 5) + const unsigned face_edge_count + = (1 == m_face_fragment_count) + ? 4u + : ((unsigned)m_face_fragment_count); + + if (face_edge_count < 3) + break; + + if (4 == face_edge_count) + { + // quad face + if (0 != m_face_fragment_index) + break; + // quad parameters run from 0.0 to 1.0. + const double gdelta = 1.0 / ((double)gc); + const double quad_s = gdelta * grid2dex_i; + const double quad_t = gdelta * grid2dex_j; + const ON_SubDFaceParameter p = ON_SubDFaceParameter::CreateFromQuadFaceParameteters(quad_s, quad_t); + if (p.IsNotSet()) + break; + return p; + } + + // ngon face + if (0 != m_face_fragment_index >= face_edge_count) + break; + // corner parameters run from 0.0 to 0.5. + const double gdelta = 0.5 / ((double)gc); + const double corner_s = gdelta * grid2dex_i; + const double corner_t = gdelta * grid2dex_j; + const ON_SubDFaceCornerDex cdex(m_face_fragment_index, face_edge_count); + const ON_SubDFaceParameter p(cdex, corner_s, corner_t); + if (p.IsNotSet()) + break; + return p; + } + + return ON_SubDFaceParameter::Nan; +} + +const ON_SubDFaceParameter ON_SubDMeshFragment::VertexSubDFaceParameter( + unsigned grid_point_index +) const +{ + return + (grid_point_index < VertexCount()) + ? VertexSubDFaceParameter(m_grid.Grid2dexFromPointIndex(grid_point_index)) + : ON_SubDFaceParameter::Nan; +} + const class ON_SubDEdge* ON_SubDMeshFragment::SubDEdge( unsigned int grid_side_index ) const 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);