diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..dc2d4c42 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,581 @@ +cmake_minimum_required (VERSION 3.16) +set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)") +project(opennurbs CXX) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +if (UNIX AND NOT APPLE) + set(LINUX TRUE) +endif() + +set( OPENNURBS_PUBLIC_HEADERS + opennurbs.h + opennurbs_3dm.h + opennurbs_3dm_attributes.h + opennurbs_3dm_properties.h + opennurbs_3dm_settings.h + opennurbs_annotationbase.h + opennurbs_apple_nsfont.h + opennurbs_arc.h + opennurbs_arccurve.h + opennurbs_archivable_dictionary.h + opennurbs_archive.h + opennurbs_array.h + opennurbs_array_defs.h + opennurbs_base32.h + opennurbs_base64.h + opennurbs_beam.h + opennurbs_bezier.h + opennurbs_bitmap.h + opennurbs_bounding_box.h + opennurbs_box.h + opennurbs_brep.h + opennurbs_circle.h + opennurbs_color.h + opennurbs_compress.h + opennurbs_compstat.h + opennurbs_cone.h + opennurbs_convex_poly.h + opennurbs_crc.h + opennurbs_curve.h + opennurbs_curveonsurface.h + opennurbs_curveproxy.h + opennurbs_cylinder.h + opennurbs_date.h + opennurbs_decals.h + opennurbs_defines.h + opennurbs_detail.h + opennurbs_dimension.h + opennurbs_dimensionformat.h + opennurbs_dimensionstyle.h + opennurbs_dithering.h + opennurbs_ellipse.h + opennurbs_embedded_file.h + opennurbs_error.h + opennurbs_evaluate_nurbs.h + opennurbs_extensions.h + opennurbs_file_utilities.h + opennurbs_font.h + opennurbs_fpoint.h + opennurbs_freetype.h + opennurbs_freetype_include.h + opennurbs_fsp.h + opennurbs_fsp_defs.h + opennurbs_function_list.h + opennurbs_geometry.h + opennurbs_gl.h + opennurbs_ground_plane.h + opennurbs_group.h + opennurbs_hash_table.h + opennurbs_hatch.h + opennurbs_hsort_template.h + opennurbs_instance.h + opennurbs_internal_V2_annotation.h + opennurbs_internal_V5_annotation.h + opennurbs_internal_V5_dimstyle.h + opennurbs_internal_defines.h + opennurbs_internal_glyph.h + opennurbs_intersect.h + opennurbs_ipoint.h + opennurbs_knot.h + opennurbs_layer.h + opennurbs_leader.h + opennurbs_light.h + opennurbs_line.h + opennurbs_linear_workflow.h + opennurbs_linecurve.h + opennurbs_linestyle.h + opennurbs_linetype.h + opennurbs_locale.h + opennurbs_lock.h + opennurbs_lookup.h + opennurbs_mapchan.h + opennurbs_material.h + opennurbs_math.h + opennurbs_matrix.h + opennurbs_md5.h + opennurbs_memory.h + opennurbs_mesh.h + opennurbs_mesh_modifiers.h + opennurbs_model_component.h + opennurbs_model_geometry.h + opennurbs_nurbscurve.h + opennurbs_nurbssurface.h + opennurbs_object.h + opennurbs_object_history.h + opennurbs_objref.h + opennurbs_offsetsurface.h + opennurbs_optimize.h + opennurbs_parse.h + opennurbs_photogrammetry.h + opennurbs_plane.h + opennurbs_planesurface.h + opennurbs_pluginlist.h + opennurbs_point.h + opennurbs_pointcloud.h + opennurbs_pointgeometry.h + opennurbs_pointgrid.h + opennurbs_polycurve.h + opennurbs_polyedgecurve.h + opennurbs_polyline.h + opennurbs_polylinecurve.h + opennurbs_post_effects.h + opennurbs_private_wrap.h + opennurbs_private_wrap_defs.h + opennurbs_progress_reporter.h + opennurbs_qsort_template.h + opennurbs_quacksort_template.h + opennurbs_quaternion.h + opennurbs_rand.h + opennurbs_render_channels.h + opennurbs_render_content.h + opennurbs_rendering.h + opennurbs_revsurface.h + opennurbs_rtree.h + opennurbs_safe_frame.h + opennurbs_sha1.h + opennurbs_skylight.h + opennurbs_sleeplock.h + opennurbs_sphere.h + opennurbs_std_string.h + opennurbs_string.h + opennurbs_string_value.h + opennurbs_subd.h + opennurbs_subd_data.h + opennurbs_sumsurface.h + opennurbs_sun.h + opennurbs_surface.h + opennurbs_surfaceproxy.h + opennurbs_symmetry.h + opennurbs_system.h + opennurbs_system_compiler.h + opennurbs_system_runtime.h + #opennurbs_table.h + opennurbs_terminator.h + opennurbs_testclass.h + opennurbs_text.h + opennurbs_text_style.h + opennurbs_textcontext.h + opennurbs_textdraw.h + opennurbs_textglyph.h + opennurbs_textiterator.h + opennurbs_textlog.h + opennurbs_textobject.h + opennurbs_textrun.h + opennurbs_texture.h + opennurbs_texture_mapping.h + opennurbs_topology.h + opennurbs_torus.h + opennurbs_unicode.h + opennurbs_userdata.h + opennurbs_uuid.h + opennurbs_version.h + opennurbs_version_number.h + opennurbs_viewport.h + opennurbs_wip.h + opennurbs_workspace.h + opennurbs_xform.h + opennurbs_xml.h + opennurbs_zlib.h + ) + +set( OPENNURBS_PUBLIC_SOURCES + opennurbs_3dm_attributes.cpp + opennurbs_3dm_properties.cpp + opennurbs_3dm_settings.cpp + opennurbs_annotationbase.cpp + opennurbs_apple_nsfont.cpp + opennurbs_arc.cpp + opennurbs_arccurve.cpp + opennurbs_archivable_dictionary.cpp + opennurbs_archive.cpp + opennurbs_archive_manifest.cpp + opennurbs_array.cpp + opennurbs_base32.cpp + opennurbs_base64.cpp + opennurbs_beam.cpp + opennurbs_bezier.cpp + opennurbs_beziervolume.cpp + opennurbs_bitmap.cpp + opennurbs_bounding_box.cpp + opennurbs_box.cpp + opennurbs_brep.cpp + opennurbs_brep_extrude.cpp + opennurbs_brep_io.cpp + opennurbs_brep_isvalid.cpp + opennurbs_brep_region.cpp + opennurbs_brep_tools.cpp + opennurbs_brep_v2valid.cpp + opennurbs_calculator.cpp + opennurbs_circle.cpp + opennurbs_color.cpp + opennurbs_compress.cpp + opennurbs_compstat.cpp + opennurbs_cone.cpp + opennurbs_convex_poly.cpp + opennurbs_crc.cpp + opennurbs_curve.cpp + opennurbs_curveonsurface.cpp + opennurbs_curveproxy.cpp + opennurbs_cylinder.cpp + opennurbs_date.cpp + opennurbs_decals.cpp + opennurbs_defines.cpp + opennurbs_detail.cpp + opennurbs_dimension.cpp + opennurbs_dimensionformat.cpp + opennurbs_dimensionstyle.cpp + opennurbs_dithering.cpp + opennurbs_ellipse.cpp + opennurbs_embedded_file.cpp + opennurbs_error.cpp + opennurbs_error_message.cpp + opennurbs_evaluate_nurbs.cpp + opennurbs_extensions.cpp + opennurbs_file_utilities.cpp + opennurbs_font.cpp + opennurbs_freetype.cpp + opennurbs_fsp.cpp + opennurbs_function_list.cpp + opennurbs_geometry.cpp + opennurbs_glyph_outline.cpp + opennurbs_ground_plane.cpp + opennurbs_group.cpp + opennurbs_hash_table.cpp + opennurbs_hatch.cpp + opennurbs_instance.cpp + opennurbs_internal_V2_annotation.cpp + opennurbs_internal_V5_annotation.cpp + opennurbs_internal_V5_dimstyle.cpp + opennurbs_internal_Vx_annotation.cpp + opennurbs_intersect.cpp + opennurbs_ipoint.cpp + opennurbs_knot.cpp + opennurbs_layer.cpp + opennurbs_leader.cpp + opennurbs_light.cpp + opennurbs_line.cpp + opennurbs_linear_workflow.cpp + opennurbs_linecurve.cpp + opennurbs_linetype.cpp + opennurbs_locale.cpp + opennurbs_lock.cpp + opennurbs_lookup.cpp + opennurbs_material.cpp + opennurbs_math.cpp + opennurbs_matrix.cpp + opennurbs_md5.cpp + opennurbs_memory_util.cpp + opennurbs_mesh.cpp + opennurbs_mesh_modifiers.cpp + opennurbs_mesh_ngon.cpp + opennurbs_mesh_tools.cpp + opennurbs_mesh_topology.cpp + opennurbs_model_component.cpp + opennurbs_model_geometry.cpp + opennurbs_morph.cpp + opennurbs_nurbscurve.cpp + opennurbs_nurbssurface.cpp + opennurbs_nurbsvolume.cpp + opennurbs_object.cpp + opennurbs_object_history.cpp + opennurbs_objref.cpp + opennurbs_offsetsurface.cpp + opennurbs_optimize.cpp + opennurbs_parse_angle.cpp + opennurbs_parse_length.cpp + opennurbs_parse_number.cpp + opennurbs_parse_point.cpp + opennurbs_parse_settings.cpp + opennurbs_photogrammetry.cpp + opennurbs_plane.cpp + opennurbs_planesurface.cpp + opennurbs_pluginlist.cpp + opennurbs_point.cpp + opennurbs_pointcloud.cpp + opennurbs_pointgeometry.cpp + opennurbs_pointgrid.cpp + opennurbs_polycurve.cpp + opennurbs_polyedgecurve.cpp + opennurbs_polyline.cpp + opennurbs_polylinecurve.cpp + opennurbs_post_effects.cpp + opennurbs_progress_reporter.cpp + opennurbs_quaternion.cpp + opennurbs_rand.cpp + opennurbs_render_channels.cpp + opennurbs_render_content.cpp + opennurbs_revsurface.cpp + opennurbs_rtree.cpp + opennurbs_safe_frame.cpp + opennurbs_sha1.cpp + opennurbs_skylight.cpp + opennurbs_sleeplock.cpp + opennurbs_sort.cpp + opennurbs_sphere.cpp + opennurbs_statics.cpp + opennurbs_std_string_format.cpp + opennurbs_std_string_utf.cpp + opennurbs_string.cpp + opennurbs_string_compare.cpp + opennurbs_string_format.cpp + opennurbs_string_scan.cpp + opennurbs_string_values.cpp + opennurbs_subd.cpp + opennurbs_subd_archive.cpp + opennurbs_subd_copy.cpp + opennurbs_subd_data.cpp + opennurbs_subd_eval.cpp + opennurbs_subd_fragment.cpp + opennurbs_subd_frommesh.cpp + opennurbs_subd_heap.cpp + opennurbs_subd_iter.cpp + opennurbs_subd_limit.cpp + opennurbs_subd_matrix.cpp + opennurbs_subd_mesh.cpp + opennurbs_subd_ref.cpp + opennurbs_subd_ring.cpp + opennurbs_subd_sector.cpp + opennurbs_subd_texture.cpp + opennurbs_sum.cpp + opennurbs_sumsurface.cpp + opennurbs_sun.cpp + opennurbs_surface.cpp + opennurbs_surfaceproxy.cpp + opennurbs_symmetry.cpp + #opennurbs_table.cpp + opennurbs_terminator.cpp + opennurbs_testclass.cpp + opennurbs_text.cpp + opennurbs_text_style.cpp + opennurbs_textcontext.cpp + opennurbs_textdraw.cpp + opennurbs_textglyph.cpp + opennurbs_textiterator.cpp + opennurbs_textlog.cpp + opennurbs_textobject.cpp + opennurbs_textrun.cpp + opennurbs_topology.cpp + opennurbs_torus.cpp + opennurbs_unicode.cpp + opennurbs_unicode_cpsb.cpp + opennurbs_units.cpp + opennurbs_userdata.cpp + opennurbs_uuid.cpp + opennurbs_version.cpp + opennurbs_version_number.cpp + opennurbs_viewport.cpp + opennurbs_workspace.cpp + opennurbs_wstring.cpp + opennurbs_xform.cpp + opennurbs_xml.cpp + opennurbs_zlib.cpp + opennurbs_zlib_memory.cpp + ) + +if(EXISTS opennurbs_plus.h) +set( OPENNURBS_PLUS_HEADERS + opennurbsRhino.pch + opennurbs_plus.h + opennurbs_plus_crashtest.h + opennurbs_plus_function.h + opennurbs_plus_hiddenline.h + opennurbs_plus_hiddenline_impl.h + opennurbs_plus_idimage.h + opennurbs_plus_massprop.h + opennurbs_plus_mesh_intersection.h + opennurbs_plus_mesh_marker.h + opennurbs_plus_meshbooleans_impl.h + opennurbs_plus_particle.h + opennurbs_plus_rectpack.h + opennurbs_plus_rectpack2.h + opennurbs_plus_registry.h + opennurbs_plus_sections.h + opennurbs_plus_sil.h + opennurbs_plus_sleeplock.h + opennurbs_plus_squish.h + opennurbs_plus_subd.h + opennurbs_plus_trimesh.h + opennurbs_plus_validate.h + opennurbs_plus_x.h + ) + +set( OPENNURBS_PLUS_SOURCES + opennurbs_plus_bezier.cpp + opennurbs_plus_brep.cpp + opennurbs_plus_brep_changesrf.cpp + opennurbs_plus_brep_facegroups.cpp + opennurbs_plus_brep_kinky.cpp + opennurbs_plus_ctree.cpp + opennurbs_plus_fontsub.cpp + opennurbs_plus_fpu.cpp + opennurbs_plus_function.cpp + opennurbs_plus_ginfinity.cpp + opennurbs_plus_hiddenline.cpp + opennurbs_plus_idimage.cpp + opennurbs_plus_massprop.cpp + opennurbs_plus_memory.cpp + opennurbs_plus_memory_new.cpp + opennurbs_plus_mesh_clash.cpp + opennurbs_plus_mesh_intersection.cpp + opennurbs_plus_mesh_marker.cpp + opennurbs_plus_mesh_thickness.cpp + opennurbs_plus_meshbooleans.cpp + opennurbs_plus_mtree.cpp + opennurbs_plus_particle.cpp + opennurbs_plus_rectpack.cpp + opennurbs_plus_rectpack1.cpp + opennurbs_plus_rectpack2.cpp + opennurbs_plus_sections.cpp + opennurbs_plus_sil.cpp + opennurbs_plus_sleeplock.cpp + opennurbs_plus_squish.cpp + opennurbs_plus_stree.cpp + opennurbs_plus_subd.cpp + opennurbs_plus_subd_curve.cpp + opennurbs_plus_subd_eval.cpp + opennurbs_plus_subd_facegroups.cpp + opennurbs_plus_subd_fillet.cpp + opennurbs_plus_subd_limit.cpp + opennurbs_plus_subd_loft.cpp + opennurbs_plus_subd_matrix.cpp + opennurbs_plus_subd_merge.cpp + opennurbs_plus_subd_mesh.cpp + opennurbs_plus_subd_symmetry.cpp + opennurbs_plus_subd_topology.cpp + opennurbs_plus_subd_weld.cpp + opennurbs_plus_trimesh.cpp + opennurbs_plus_validate.cpp + opennurbs_plus_x.cpp + opennurbs_plus_xmesh.cpp + opennurbs_plus_xmeshfast.cpp + opennurbs_plus_xray.cpp + ) +endif() #OpenNURBS plus source files + +if (ANDROID OR LINUX) +add_subdirectory(android_uuid) +endif() + +add_subdirectory(zlib) +add_subdirectory(freetype263) + +## opennurbs static library +add_library( opennurbsStatic STATIC + ${OPENNURBS_PUBLIC_HEADERS} + ${OPENNURBS_PUBLIC_SOURCES} + ${OPENNURBS_PLUS_HEADERS} + ${OPENNURBS_PLUS_SOURCES} + ) + +## opennurbs shared library +add_library( OpenNURBS SHARED + ${OPENNURBS_PUBLIC_HEADERS} + ${OPENNURBS_PUBLIC_SOURCES} + ${OPENNURBS_PLUS_HEADERS} + ${OPENNURBS_PLUS_SOURCES} + ) + + +if( APPLE) + find_library( CORE_GRAPHICS_LIBRARY CoreGraphics) + message( STATUS "CORE_GRAPHICS_LIBRARY is ${CORE_GRAPHICS_LIBRARY}") + find_library( CORE_TEXT_LIBRARY CoreText) + message( STATUS "CORE_TEXT_LIBRARY is ${CORE_TEXT_LIBRARY}") + find_library( FOUNDATION_LIBRARY Foundation) + message( STATUS "FOUNDATION_LIBRARY is ${FOUNDATION_LIBRARY}") + + set( OPENNURBS_APPLE_DEPENDENCIES + ${CORE_GRAPHICS_LIBRARY} + ${CORE_TEXT_LIBRARY} + ${FOUNDATION_LIBRARY} + ) + + set( OPENNURBS_APPLE_DEFINES + ON_COMPILER_CLANG + ON_RUNTIME_APPLE + RHINO_APPLE=1 + _GNU_SOURCE + ) + target_compile_definitions(opennurbsStatic PRIVATE ${OPENNURBS_APPLE_DEFINES}) + target_compile_definitions(OpenNURBS PRIVATE ${OPENNURBS_APPLE_DEFINES}) + + # xcode properties are the same for both static and shared libs + set_target_properties( opennurbsStatic OpenNURBS + PROPERTIES + XCODE_ATTRIBUTE_SDKROOT "macosx" + XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "macosx iphonesimulator iphoneos" + XCODE_ATTRIBUTE_SYMROOT "build" + XCODE_ATTRIBUTE_ALLOW_TARGET_PLATFORM_SPECIALIZATION "YES" + XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf" + XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.3" + XCODE_ATTRIBUTE_DYLIB_COMPATIBILITY_VERSION "1" + XCODE_ATTRIBUTE_DYLIB_CURRENT_VERSION "1" + XCODE_ATTRIBUTE_INFOPLIST_FILE "opennurbsRhinoInfo.plist" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.mcneel.opennurbs" + XCODE_ATTRIBUTE_HEADER_SEARCH_PATHS "$(PROJECT_DIR)/freetype263/include" + XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "1" + XCODE_ATTRIBUTE_MARKETING_VERSION "8 Internal" + XCODE_ATTRIBUTE_GCC_INLINES_ARE_PRIVATE_EXTERN "YES" + XCODE_ATTRIBUTE_GCC_NO_COMMON_BLOCKS "YES" + XCODE_ATTRIBUTE_GCC_INPUT_FILETYPE "sourcecode.cpp.objcpp" + XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES" + XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "$(PROJECT_DIR)/opennurbsRhino.pch" + XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "YES" + XCODE_ATTRIBUTE_ENABLE_STRICT_OBJC_MSGSEND "YES" + ) +endif() + + +if (MSVC) + # warning level 4 and all warnings as errors + add_compile_options(/W4) +else() + # These need to be addressed + add_compile_options(-Wno-inconsistent-missing-override) + add_compile_options(-Wno-defaulted-function-deleted) + add_compile_options(-Wno-switch) + add_compile_options(-Wno-tautological-pointer-compare) + add_compile_options(-Wno-deprecated-declarations) + add_compile_options(-Wno-unsequenced) + add_compile_options(-Wno-parentheses) + add_compile_options(-Wno-writable-strings) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch") +endif() + + +target_compile_definitions(opennurbsStatic PRIVATE ON_COMPILING_OPENNURBS Z_PREFIX MY_ZCALLOC) +target_compile_definitions(OpenNURBS PRIVATE OPENNURBS_EXPORTS Z_PREFIX MY_ZCALLOC) + +target_include_directories(opennurbsStatic PUBLIC .) +target_include_directories( OpenNURBS PUBLIC .) + +install( TARGETS opennurbsStatic DESTINATION "lib") +install( FILES ${OPENNURBS_PUBLIC_HEADERS} DESTINATION "include/opennurbsStatic") + + +if (ANDROID OR LINUX) +target_link_libraries( OpenNURBS ${OPENNURBS_APPLE_DEPENDENCIES} zlib opennurbs_public_freetype android_uuid) +target_link_libraries( opennurbsStatic ${OPENNURBS_APPLE_DEPENDENCIES} zlib opennurbs_public_freetype android_uuid) +else() +target_link_libraries( OpenNURBS ${OPENNURBS_APPLE_DEPENDENCIES} zlib opennurbs_public_freetype) +target_link_libraries( opennurbsStatic ${OPENNURBS_APPLE_DEPENDENCIES} zlib opennurbs_public_freetype) +endif() + +if (APPLE) + set_target_properties( OpenNURBS PROPERTIES +FRAMEWORK TRUE +FRAMEWORK_VERSION A +# MACOSX_FRAMEWORK_IDENTIFIER "com.mcneel.OpenNURBS" +# VERSION "8.0.0" +# SOVERSION "8.0.0" +PUBLIC_HEADER "${OPENNURBS_PUBLIC_HEADERS}" +) + + install( TARGETS OpenNURBS DESTINATION "Frameworks") +else() + install( TARGETS OpenNURBS DESTINATION "lib" ) + install( FILES ${OPENNURBS_PUBLIC_HEADERS} DESTINATION "include/OpenNURBS") +endif() diff --git a/android_uuid/CMakeLists.txt b/android_uuid/CMakeLists.txt new file mode 100644 index 00000000..e707e091 --- /dev/null +++ b/android_uuid/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required (VERSION 3.4) +project( android_uuid C) + + +set( INCLUDE_DIRS +. +) + + +set( SOURCES + copy.c + gen_uuid.c + isnull.c + pack.c + parse.c + unpack.c + unparse.c + uuid_time.c +) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) # -fPIC + +add_library( android_uuid STATIC +${SOURCES} +) + +#target_compile_definitions(android_uuid PRIVATE Z_PREFIX MY_ZCALLOC) + +target_include_directories( android_uuid + PUBLIC +. +) + +install( TARGETS android_uuid DESTINATION "lib") +install( FILES DESTINATION "include/android_uuid") diff --git a/freetype263/CMakeLists.txt b/freetype263/CMakeLists.txt new file mode 100644 index 00000000..ef667a86 --- /dev/null +++ b/freetype263/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required (VERSION 3.16) +project( opennurbs_public_freetype C) + +include_directories("include") + +set( SOURCES + src/autofit/autofit.c + src/base/ftbase.c + src/base/ftbbox.c + src/base/ftbdf.c + src/base/ftbitmap.c + src/base/ftcid.c + src/base/ftdebug.c + src/base/ftfntfmt.c + src/base/ftfstype.c + src/base/ftgasp.c + src/base/ftglyph.c + src/base/ftgxval.c + src/base/ftinit.c + src/base/ftlcdfil.c + src/base/ftmm.c + src/base/ftotval.c + src/base/ftpatent.c + src/base/ftpfr.c + src/base/ftstroke.c + src/base/ftsynth.c + src/base/ftsystem.c + src/base/fttype1.c + src/base/ftwinfnt.c + src/bdf/bdf.c + src/bzip2/ftbzip2.c + src/cache/ftcache.c + src/cff/cff.c + src/cid/type1cid.c + src/gzip/ftgzip.c + src/lzw/ftlzw.c + src/pcf/pcf.c + src/pfr/pfr.c + src/psaux/psaux.c + src/pshinter/pshinter.c + src/psnames/psnames.c + src/raster/raster.c + src/sfnt/sfnt.c + src/smooth/smooth.c + src/truetype/truetype.c + src/type1/type1.c + src/type42/type42.c + src/winfonts/winfnt.c +) + +add_library( opennurbs_public_freetype STATIC + ${SOURCES} +) + + set( OPENNURBS_FREETYPE_DEFINES + FT2_BUILD_LIBRARY + ) + target_compile_definitions(opennurbs_public_freetype PRIVATE ${OPENNURBS_FREETYPE_DEFINES}) + +if( APPLE) + set_target_properties( opennurbs_public_freetype + PROPERTIES + XCODE_ATTRIBUTE_SDKROOT "macosx" + XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "macosx iphonesimulator iphoneos" + XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf" + XCODE_ATTRIBUTE_HEADER_SEARCH_PATHS "$(PROJECT_DIR)/freetype263/include" + ) +endif() + +install( TARGETS opennurbs_public_freetype DESTINATION "lib") +install( FILES ${PUBLIC_HEADERS} DESTINATION "include/opennurbs_public_freetype") diff --git a/opennurbs_3dm_attributes.cpp b/opennurbs_3dm_attributes.cpp index fee14c67..2fe719fe 100644 --- a/opennurbs_3dm_attributes.cpp +++ b/opennurbs_3dm_attributes.cpp @@ -1456,6 +1456,13 @@ bool ON_3dmObjectAttributes::Write( ON_BinaryArchive& file ) const case ON::no_space: uc = 0; break; case ON::model_space: uc = 0; break; case ON::page_space: uc = 1; break; + case ON::uveditor_space: + case ON::blockeditor_space: + { + uc = 2; + ON_ASSERT(false); + break; + } } rc = file.WriteChar(uc); } @@ -1517,14 +1524,13 @@ bool ON_3dmObjectAttributes::Transform(const ON_Geometry* pOriginalGeometry, con { m_object_frame.Transform(xform); } - else if (pOriginalGeometry) + /*else if (pOriginalGeometry) { ON_Plane plane = ON_Plane::World_xy; plane.SetOrigin(pOriginalGeometry->BoundingBox().Center()); - SetObjectFrame(ON_COMPONENT_INDEX::WholeObject, plane); m_object_frame.Transform(xform); - } + }*/ return m_rendering_attributes.Transform(xform); } diff --git a/opennurbs_3dm_settings.cpp b/opennurbs_3dm_settings.cpp index 4d067ad0..bd650a88 100644 --- a/opennurbs_3dm_settings.cpp +++ b/opennurbs_3dm_settings.cpp @@ -950,6 +950,25 @@ ON_3dmRenderSettingsPrivate::ON_3dmRenderSettingsPrivate() _sun (_rdk_document_data), _environments (_rdk_document_data) { + // 26th January 2023 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-71396 + // Populate the RDK document data with defaults. The previous fix for this did it in RdkDocNode() + // which was the wrong place. We have to do it even if the RDK isn't being used. + ON_RdkDocumentDefaults dd(ON::VersionMajor(), ON_RdkDocumentDefaults::ValueSets::All); + dd.CopyDefaultsTo(_rdk_document_data); +} + +ON_3dmRenderSettingsPrivate::ON_3dmRenderSettingsPrivate(const ON_3dmRenderSettingsPrivate& p) + : + _dithering (_rdk_document_data), + _ground_plane (_rdk_document_data), + _linear_workflow(_rdk_document_data), + _render_channels(_rdk_document_data), + _safe_frame (_rdk_document_data), + _skylight (_rdk_document_data), + _sun (_rdk_document_data), + _environments (_rdk_document_data) +{ + operator = (p); } const ON_3dmRenderSettingsPrivate& ON_3dmRenderSettingsPrivate::operator = (const ON_3dmRenderSettingsPrivate& p) @@ -996,16 +1015,22 @@ ON_3dmRenderSettings& ON_3dmRenderSettings::operator = (const ON_3dmRenderSettin if (nullptr != rs.m_private) { if (nullptr == m_private) - m_private = new ON_3dmRenderSettingsPrivate; - - *m_private = *rs.m_private; + { + m_private = new ON_3dmRenderSettingsPrivate(*rs.m_private); + } + else + { + *m_private = *rs.m_private; + } } else { if (nullptr != m_private) { - delete m_private; - m_private = nullptr; + // We can't delete the private because it might have specialized document objects (gp etc). + // So don't delete it, but set it to defaults because the incoming one doesn't exist which + // is essentially the same as the incoming one being an implied default one. + m_private->SetToDefaults(); } } @@ -1062,6 +1087,12 @@ ON_3dmRenderSettings& ON_3dmRenderSettings::operator = (const ON_3dmRenderSettin return *this; } +void ON_3dmRenderSettingsPrivate::SetToDefaults(void) +{ + ON_RdkDocumentDefaults dd(ON::VersionMajor(), ON_RdkDocumentDefaults::ValueSets::All); + dd.CopyDefaultsTo(_rdk_document_data); +} + void ON_3dmRenderSettings::Dump( ON_TextLog& text_log ) const { text_log.Print("m_bCustomImageSize = %s\n",m_bCustomImageSize?"true":"false"); @@ -1548,18 +1579,6 @@ ON_XMLNode& ON_3dmRenderSettings::RdkDocNode(void) const if (nullptr == m_private) m_private = new ON_3dmRenderSettingsPrivate; - if (m_private->_rdk_document_data.FirstChild() == nullptr) - { - // 16th November 2022 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-71396 - // The RDK document data is completely empty. This used to be filled by the RDK itself using - // the CRhRdkPropertyServer class. This worked until someone inside Rhino decided to copy a - // completely default ON_3dmRenderSettings over this one, whereupon the RDK document data got - // deleted instead of simply reverting to defaults. This code is here to fix that. The core - // property server code has been moved to ON_RdkDocumentDefaults. - ON_RdkDocumentDefaults dd(ON::VersionMajor(), ON_RdkDocumentDefaults::ValueSets::All); - dd.CopyDefaultsTo(m_private->_rdk_document_data); - } - return m_private->_rdk_document_data; } @@ -1643,6 +1662,47 @@ void ON_3dmRenderSettings::SetReflectionRenderEnvironment(const ON_UUID& id) m_private->_environments.SetReflectionRenderEnvironment(id); } +extern ON_UUID uuidRenderSettingsPreset_Studio; +extern ON_UUID uuidRenderSettingsPreset_Custom; +extern ON_UUID uuidRenderSettingsPreset_Exterior; +extern ON_UUID uuidRenderSettingsPreset_Interior; + +static const wchar_t* rendering = ON_RDK_DOCUMENT ON_RDK_SLASH ON_RDK_SETTINGS ON_RDK_SLASH ON_RDK_RENDERING; + +ON_UUID ON_3dmRenderSettings::CurrentRenderingPreset(void) const +{ + const auto* node = RdkDocNode().GetNodeAtPath(rendering); + if (nullptr != node) + { + ON_XMLParameters p(*node); + ON_XMLVariant value; + if (p.GetParam(ON_RDK_CURRENT_PRESET, value)) + return value.AsUuid(); + } + + return ON_nil_uuid; +} + +void ON_3dmRenderSettings::SetCurrentRenderingPreset(const ON_UUID& uuid) +{ + ON_ASSERT((uuidRenderSettingsPreset_Studio == uuid) || (uuidRenderSettingsPreset_Custom == uuid) || (uuidRenderSettingsPreset_Exterior == uuid) || (uuidRenderSettingsPreset_Interior == uuid)); + + auto* node = RdkDocNode().GetNodeAtPath(rendering); + if (nullptr != node) + { + ON_XMLParameters p(*node); + p.SetParam(ON_RDK_CURRENT_PRESET, uuid); + } +} + +void ON_3dmRenderSettings::GetRenderingPresets(ON_SimpleArray& presets) const +{ + presets.Append(uuidRenderSettingsPreset_Studio); + presets.Append(uuidRenderSettingsPreset_Exterior); + presets.Append(uuidRenderSettingsPreset_Interior); + presets.Append(uuidRenderSettingsPreset_Custom); +} + ////////////////////////////////////////////////////////////////////////////////////////// // // ON_3dmAnnotationSettings @@ -2980,6 +3040,12 @@ bool ON_3dmView::IsValid(ON_TextLog* text_log) const //} break; + case ON::uveditor_view_type: + break; + + case ON::blockeditor_view_type: + break; + default: if ( text_log ) { diff --git a/opennurbs_3dm_settings.h b/opennurbs_3dm_settings.h index e6a7a9f8..cd138a0f 100644 --- a/opennurbs_3dm_settings.h +++ b/opennurbs_3dm_settings.h @@ -648,7 +648,7 @@ public: // (relative display device coordinates) ON_3dmViewPosition m_position; - ON::view_type m_view_type; // model, page, or nested + ON::view_type m_view_type; // model, page, nested or uveditor // If m_view_type == ON::page_view_type, then the m_page_settings // records the page size. Otherwise, m_page_settings should @@ -826,6 +826,11 @@ public: ON_UUID ReflectionRenderEnvironment(void) const; void SetReflectionRenderEnvironment(const ON_UUID& id); + // Access to rendering presets. + ON_UUID CurrentRenderingPreset(void) const; + void SetCurrentRenderingPreset(const ON_UUID& uuid); + void GetRenderingPresets(ON_SimpleArray& presets) const; + private: unsigned short m_reserved1 = 0; diff --git a/opennurbs_beam.cpp b/opennurbs_beam.cpp index 55182736..fee27311 100644 --- a/opennurbs_beam.cpp +++ b/opennurbs_beam.cpp @@ -2775,7 +2775,9 @@ ON_Brep* ON_Extrusion::BrepForm( ON_Brep* brep, bool bSmoothFaces ) const newbrep->m_T.Reserve((4 + cap_count)*side_count); newbrep->m_C3.Reserve(4*side_count); newbrep->m_E.Reserve(4*side_count); - + // 3 Feb 2023, Mikko, RH-72784: + // Initialize vertex array size to avoid slow reallocs + newbrep->m_V.Reserve(2 * side_count); int vidmap[4] = {0,1,2,3}; int eidmap[4] = {0,1,2,3}; @@ -3223,6 +3225,7 @@ static bool GetBrepFormFaceIndex( && extrusion_profile_parameter < t ) { + t1 = t; break; } t0 = t; @@ -3269,6 +3272,25 @@ bool ON_Extrusion::GetBrepFormComponentIndex( const ON_Brep* brep, ON_COMPONENT_INDEX& brep_ci ) const +{ + return GetBrepFormComponentIndex( + extrusion_ci, + extrusion_profile_parameter, + brep, + false, + brep_ci, + nullptr + ); +} + +bool ON_Extrusion::GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + double extrusion_profile_parameter, + const ON_Brep* brep, + bool bSmoothFaces, + ON_COMPONENT_INDEX& brep_ci, + ON_Interval* profile_subdomain +) const { brep_ci.UnSet(); int face_index = -1; @@ -3286,7 +3308,7 @@ bool ON_Extrusion::GetBrepFormComponentIndex( const bool bClosedProfile = profile0->IsClosed() ? true : false; if ( profile_count > 1 && !bClosedProfile ) return false; - const int edges_per_wall_face = bClosedProfile ? 3 : 4; + const int edges_per_wall_face = 4; const int cap_count = (0 == is_capped || !bClosedProfile) ? 0 : ((3==is_capped)?2:1); int brep_face_count = ( nullptr != brep ) ? brep->m_F.Count() : 0; if ( nullptr != brep && brep_face_count < profile_count + cap_count ) @@ -3294,7 +3316,8 @@ bool ON_Extrusion::GetBrepFormComponentIndex( ON_ERROR("brep_form parameter cannot be extrusion's BrepForm()"); return false; } - bool bCountProfileDiscontinuities = ( brep_face_count > profile_count + cap_count ); + bool bCountProfileDiscontinuities = bSmoothFaces || ( brep_face_count > profile_count + cap_count ); + int side_count = (bCountProfileDiscontinuities) ? GetSmoothSideCount(*this) : profile_count; switch(extrusion_ci.m_type) { @@ -3304,10 +3327,23 @@ bool ON_Extrusion::GetBrepFormComponentIndex( return false; if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) ) return false; - brep_ci.m_index = edges_per_wall_face*face_index; - if ( ON_COMPONENT_INDEX::extrusion_top_profile == extrusion_ci.m_type ) - brep_ci.m_index += 2; + + // first face has edges_per_wall_face edges, the subsequent ones have one less + if (face_index > 0) + brep_ci.m_index = edges_per_wall_face + ((edges_per_wall_face-1) * (face_index-1)); + else + brep_ci.m_index = 0; + + if (ON_COMPONENT_INDEX::extrusion_top_profile == extrusion_ci.m_type) + { + if ( bClosedProfile && face_index == side_count - 1) + brep_ci.m_index += 1; // in closed case the last face has an already existing edge between bottom and top edge + else + brep_ci.m_index += 2; + } brep_ci.m_type = ON_COMPONENT_INDEX::brep_edge; + if (nullptr != profile_subdomain) + *profile_subdomain = face_profile_domain; break; case ON_COMPONENT_INDEX::extrusion_wall_edge: @@ -3322,12 +3358,14 @@ bool ON_Extrusion::GetBrepFormComponentIndex( break; case ON_COMPONENT_INDEX::extrusion_wall_surface: - if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= profile_count ) + if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= side_count ) return false; if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) ) return false; brep_ci.m_index = face_index; brep_ci.m_type = ON_COMPONENT_INDEX::brep_face; + if (nullptr != profile_subdomain) + *profile_subdomain = face_profile_domain; break; case ON_COMPONENT_INDEX::extrusion_cap_surface: diff --git a/opennurbs_beam.h b/opennurbs_beam.h index fbf803ce..63314d8a 100644 --- a/opennurbs_beam.h +++ b/opennurbs_beam.h @@ -155,7 +155,12 @@ public: extrusion_profile_parameter - [in] brep_form - [in] brep created by ON_Extrusion::BrepForm() + bSmoothFaces - [in] + If true and the profiles have kinks, then the faces corresponding + to those profiles are split so they will be G1. brep_ci - [out] + profile_subdomain - [out] + The subdomain of the profile that contributes to the brep form component index Returns: True if successful. False if input is not valid, in which case brep_ci is set by calling ON_COMPONENT_INDEX::UnSet(). @@ -182,6 +187,15 @@ public: ON_COMPONENT_INDEX& brep_ci ) const; + bool GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + double extrusion_profile_parameter, + const ON_Brep* brep_form, + bool bSmoothFaces, + ON_COMPONENT_INDEX& brep_ci, + ON_Interval* profile_subdomain + ) const; + //////////////////////////////////////////////////////////// // // overrides of virtual ON_Surface functions diff --git a/opennurbs_color.cpp b/opennurbs_color.cpp index e91c4664..28d3cdab 100644 --- a/opennurbs_color.cpp +++ b/opennurbs_color.cpp @@ -366,6 +366,8 @@ const ON_wString ON_Color::ToString( case ON_Color::TextFormat::HSVa: format = ON_Color::TextFormat::HSVA; break; + default: + break; } } diff --git a/opennurbs_compstat.cpp b/opennurbs_compstat.cpp index b6e6802e..71b404b3 100644 --- a/opennurbs_compstat.cpp +++ b/opennurbs_compstat.cpp @@ -265,6 +265,10 @@ unsigned int ON_ComponentStatus::ClearStates( s1 &= mask; if (s1 != (m_status_flags & ALL_MASK)) { + // 29 Dec 2022, Mikko, RH-71974: + // This is wrong, selected and highlighted are separate states. A thing can be not selected but + // still highlighted. + /* // If input was selected and highlighted, // and output is not selected, // and hightlight and not explictily cleared, @@ -280,6 +284,7 @@ unsigned int ON_ComponentStatus::ClearStates( // Clear highlight automatically. s1 &= ~HIGHLIGHTED_BIT; } + */ // preserve value of runtime mark bit on m_status_flags const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT); diff --git a/opennurbs_defines.cpp b/opennurbs_defines.cpp index 65e79f39..abf31973 100644 --- a/opennurbs_defines.cpp +++ b/opennurbs_defines.cpp @@ -495,16 +495,11 @@ int ON::CloseAllFiles() // returns number of files closed or EOF for error #if defined(ON_COMPILER_MSC) return _fcloseall(); // ANSI C name -#elif defined(ON_COMPILER_CLANG) - //#error TODO - call clang fcloseall() - //fcloseall is not supported on OS X +#elif defined(ON_RUNTIME_APPLE) || defined(ON_RUNTIME_ANDROID) + //fcloseall is not supported on mac/ios or android return EOF; -#elif defined(ON_COMPILER_GNU) - fcloseall(); #else - // I can't find an fcloseall() or _fcloseall() in - // gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) - return EOF; + return fcloseall(); #endif } @@ -516,10 +511,12 @@ ON::active_space ON::ActiveSpace(int i) switch(i) { - case no_space: as = no_space; break; - case model_space: as = model_space; break; - case page_space: as = page_space; break; - default: as = no_space; break; + case no_space: as = no_space; break; + case model_space: as = model_space; break; + case page_space: as = page_space; break; + case uveditor_space: as = uveditor_space; break; + case blockeditor_space: as = blockeditor_space; break; + default: as = no_space; break; } return as; @@ -1912,9 +1909,11 @@ ON::view_type ON::ViewType(int vt) { switch(vt) { - case model_view_type: return (model_view_type); break; - case page_view_type: return (page_view_type); break; - case nested_view_type: return (nested_view_type); break; + case model_view_type: return (model_view_type); break; + case page_view_type: return (page_view_type); break; + case nested_view_type: return (nested_view_type); break; + case uveditor_view_type: return (uveditor_view_type); break; + case blockeditor_view_type: return (blockeditor_view_type); break; } return (model_view_type); diff --git a/opennurbs_defines.h b/opennurbs_defines.h index f1bd665f..a3bf9789 100644 --- a/opennurbs_defines.h +++ b/opennurbs_defines.h @@ -1149,9 +1149,11 @@ public: // Defines the current working space. enum active_space : unsigned char { - no_space = 0, - model_space = 1, // 3d modeling or "world" space - page_space = 2 // page/layout/paper/printing space + no_space = 0, + model_space = 1, // 3d modeling or "world" space + page_space = 2, // page/layout/paper/printing space + uveditor_space = 3, // UV Editor space + blockeditor_space = 4 // Block Editor space }; static active_space ActiveSpace(int); // convert integer to active_space enum @@ -2037,6 +2039,10 @@ public: nested_view_type = 2, // This view is a "model" view that is nested // in another view. The nesting and parent // information is saved in ON_3dmView. + uveditor_view_type = 3, // This view is a UV Editor view that shows + // UV meshes in a special view port. + blockeditor_view_type = 4, // This view is a Block Editor view that shows + // block definition in a special view port. }; static view_type ViewType(int); // convert integer to display_mode enum diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp index 9cc97728..bf8057d8 100644 --- a/opennurbs_dimensionstyle.cpp +++ b/opennurbs_dimensionstyle.cpp @@ -630,6 +630,8 @@ ON_DimStyle::LengthDisplay ON_DimStyle::LengthDisplayFromUnitsAndFormat( case ON::LengthUnitSystem::Feet: display = ON_DimStyle::LengthDisplay::FeetDecimal; break; + default: + break; } } return display; @@ -4761,11 +4763,13 @@ void ON_DimStyle::SetUnitSystemFromContext( switch (dim_style_units) { - case ON::LengthUnitSystem::None: - case ON::LengthUnitSystem::CustomUnits: - case ON::LengthUnitSystem::Unset: - dim_style_units = ON::LengthUnitSystem::None; - break; + case ON::LengthUnitSystem::None: + case ON::LengthUnitSystem::CustomUnits: + case ON::LengthUnitSystem::Unset: + dim_style_units = ON::LengthUnitSystem::None; + break; + default: + break; } for (int pass = 0; pass < 3; pass++) diff --git a/opennurbs_extensions.cpp b/opennurbs_extensions.cpp index c3059e68..274b8797 100644 --- a/opennurbs_extensions.cpp +++ b/opennurbs_extensions.cpp @@ -4847,9 +4847,14 @@ static const wchar_t* PostEffectTypeString(ON_PostEffect::Types type) { switch (type) { - case ON_PostEffect::Types::Early: return ON_RDK_PEP_TYPE_EARLY; - case ON_PostEffect::Types::ToneMapping: return ON_RDK_PEP_TYPE_TONE; - case ON_PostEffect::Types::Late: return ON_RDK_PEP_TYPE_LATE; + case ON_PostEffect::Types::Early: + return ON_RDK_PEP_TYPE_EARLY; + case ON_PostEffect::Types::ToneMapping: + return ON_RDK_PEP_TYPE_TONE; + case ON_PostEffect::Types::Late: + return ON_RDK_PEP_TYPE_LATE; + case ON_PostEffect::Types::Unset: + break; } ON_ASSERT(false); diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp index 99f81c60..cd3aacd0 100644 --- a/opennurbs_font.cpp +++ b/opennurbs_font.cpp @@ -2005,6 +2005,8 @@ const ON_Font* ON_FontFaceQuartet::Face( return m_italic; case ON_FontFaceQuartet::Member::BoldItalic: return m_bold_italic; + case ON_FontFaceQuartet::Member::Unset: + break; } return nullptr; } @@ -5293,6 +5295,8 @@ const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const case ON_FontFaceQuartet::Member::BoldItalic: bolditalic_faces.Append(f); break; + case ON_FontFaceQuartet::Member::Unset: + break; }; } diff --git a/opennurbs_instance.cpp b/opennurbs_instance.cpp index f192eb58..3d91c383 100644 --- a/opennurbs_instance.cpp +++ b/opennurbs_instance.cpp @@ -1091,9 +1091,12 @@ bool ON_InstanceDefinition::IsLinkedType() const { switch (InstanceDefinitionType()) { - case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: - case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: - return true; + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: + return true; + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static: + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset: + return false; } return false; } diff --git a/opennurbs_internal_defines.h b/opennurbs_internal_defines.h index 3e3e86b5..b4d5bacb 100644 --- a/opennurbs_internal_defines.h +++ b/opennurbs_internal_defines.h @@ -170,10 +170,12 @@ class ON_3dmRenderSettingsPrivate final { public: ON_3dmRenderSettingsPrivate(); - ON_3dmRenderSettingsPrivate(const ON_3dmRenderSettingsPrivate&) = default; + ON_3dmRenderSettingsPrivate(const ON_3dmRenderSettingsPrivate&); const ON_3dmRenderSettingsPrivate& operator = (const ON_3dmRenderSettingsPrivate&); + void SetToDefaults(void); + public: ON_XMLRootNode _rdk_document_data; ON_Dithering _dithering; diff --git a/opennurbs_layer.cpp b/opennurbs_layer.cpp index ee56dfd6..fb278d5f 100644 --- a/opennurbs_layer.cpp +++ b/opennurbs_layer.cpp @@ -300,16 +300,6 @@ void ON_Layer::Dump( ON_TextLog& dump ) const { dump.Print("section hatch index = %d\n", index); } - - const ON_Linetype* lt = CustomLinetype(); - if (nullptr == lt) - { - dump.Print("no custom linetype\n"); - } - else - { - dump.Print("contains custom linetype\n"); - } } // 28 Sept. 2021 @@ -546,17 +536,20 @@ bool ON_Layer::Write( } // custom linetype (1.13) - { - const ON_Linetype* linetype = CustomLinetype(); - if (linetype) - { - c = ON_LayerTypeCodes::CustomLinetype; // 33 - rc = file.WriteChar(c); - if (!rc) break; - rc = linetype->Write(file); - if (!rc) break; - } - } + // 17 Feb 2023 S. Baer + // Custom linetypes were an experiment that ended up making the UI more complicated + // than necessary. Do not write any custom linetype data into the 3dm file. + //{ + // const ON_Linetype* linetype = CustomLinetype(); + // if (linetype) + // { + // c = ON_LayerTypeCodes::CustomLinetype; // 33 + // rc = file.WriteChar(c); + // if (!rc) break; + // rc = linetype->Write(file); + // if (!rc) break; + // } + //} // 0 indicates end of new non-default attributes c = 0; @@ -816,7 +809,11 @@ bool ON_Layer::Read( ON_Linetype lt; rc = lt.Read(file); if (!rc) break; - SetCustomLinetype(lt); + // 17 Feb 2023 S. Baer + // Custom linetype per layer was a short lived experiment + // We still need to read a linetype out of the few 3dm files + // that have them, but we no longer set any sort of custom linetype + //SetCustomLinetype(lt); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -2512,23 +2509,3 @@ void ON_Layer::SetSectionHatchRotation(double rotation) m_private->m_section_hatch_rotation = rotation; } -void ON_Layer::SetCustomLinetype(const ON_Linetype& linetype) -{ - if (nullptr == m_private) - m_private = new ON_LayerPrivate(); - m_private->m_custom_linetype.reset(new ON_Linetype(linetype)); -} - -const ON_Linetype* ON_Layer::CustomLinetype() const -{ - const ON_Linetype* rc = nullptr; - if (m_private) - rc = m_private->m_custom_linetype.get(); - return rc; -} -void ON_Layer::RemoveCustomLinetype() -{ - if (m_private) - m_private->m_custom_linetype.reset(); -} - diff --git a/opennurbs_layer.h b/opennurbs_layer.h index 0a0edec3..4ba1048d 100644 --- a/opennurbs_layer.h +++ b/opennurbs_layer.h @@ -326,29 +326,6 @@ public: */ int LinetypeIndex() const; - /* - Description: - Layers can have optional custom linetypes associated with them. When a custom - linetype is attached to a layer, this linetype is used for a layer instead of - the linetype referenced by the linetype index. This function adds a custom - linetype for this layer. - */ - void SetCustomLinetype(const ON_Linetype& linetype); - - /* - Description: - Layers can have optional custom linetypes associated with them. This function - returns the custom linetype if one exists. If a custom linetype is not - attached to this layer, then an empty shared pointer is returned - */ - const ON_Linetype* CustomLinetype() const; - - /* - Description: - Remove any custom linetype associated with this layer - */ - void RemoveCustomLinetype(); - /* Returns: Returns true if objects on layer are visible. diff --git a/opennurbs_linear_workflow.cpp b/opennurbs_linear_workflow.cpp index 54db32be..19fb95ad 100644 --- a/opennurbs_linear_workflow.cpp +++ b/opennurbs_linear_workflow.cpp @@ -22,12 +22,6 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif -// These settings are inside the ON_RDK_RENDERING section. - -#define ON_RDK_GAMMA L"gamma" -#define ON_RDK_USE_POST_PROCESS_GAMMA L"use-post-process-gamma" -#define ON_RDK_USE_LINEAR_WORKFLOW L"use-linear-workflow" - class ON_LinearWorkflow::CImpl : public ON_InternalXMLImpl { public: @@ -134,6 +128,16 @@ void ON_LinearWorkflow::SetPostProcessGammaOn(bool on) m_impl->SetParameter(XMLPath(), ON_RDK_USE_POST_PROCESS_GAMMA, on); } +bool ON_LinearWorkflow::PostProcessFrameBuffer(void) const +{ + return true; // Always on. For possible future use. +} + +void ON_LinearWorkflow::SetPostProcessFrameBuffer(bool) +{ + // Always on. Ignore the call. +} + float ON_LinearWorkflow::PostProcessGamma(void) const { return PreProcessGamma(); diff --git a/opennurbs_linear_workflow.h b/opennurbs_linear_workflow.h index ad999ddc..d537284c 100644 --- a/opennurbs_linear_workflow.h +++ b/opennurbs_linear_workflow.h @@ -52,6 +52,12 @@ public: // Set post-process gamma enabled state. void SetPostProcessGammaOn(bool on); + // Get post-process frame buffer enabled state. + bool PostProcessFrameBuffer(void) const; + + // Set post-process frame buffer enabled state. + void SetPostProcessFrameBuffer(bool on); + // Returns the post-process gamma for frame buffer. This is not the value applied; it's the value that // appears in the UI. See PostProcessGammaReciprocal(). float PostProcessGamma(void) const; diff --git a/opennurbs_lock.h b/opennurbs_lock.h index 976829e8..338c1437 100644 --- a/opennurbs_lock.h +++ b/opennurbs_lock.h @@ -112,7 +112,7 @@ private: // needs to have dll-interface to be used by clients of class 'ON_Lock' // m_lock_value is private and all code that manages m_lock_value is explicitly implemented in the DLL. private: -#if defined(ON_COMPILER_CLANG) +#if defined(ON_COMPILER_CLANG) || defined(ON_RUNTIME_LINUX) std::atomic m_lock_value; #else std::atomic m_lock_value = ON_Lock::UnlockedValue; diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp index 2bc3c7f9..de48aa56 100644 --- a/opennurbs_material.cpp +++ b/opennurbs_material.cpp @@ -3693,7 +3693,7 @@ public: return *this; } -#if defined(ON_RUNTIME_APPLE) +#if defined(ON_COMPILER_CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winconsistent-missing-override" #endif @@ -3731,7 +3731,7 @@ public: return false; } -#if defined(ON_RUNTIME_APPLE) +#if defined(ON_COMPILER_CLANG) #pragma clang diagnostic pop #endif @@ -5812,6 +5812,24 @@ bool ON_Mesh::SetTextureCoordinatesEx( return rc; } +void ON_Mesh::InvalidateCachedTextureCoordinates(bool bOnlyInvalidateCachedSurfaceParameterMapping) +{ + if (bOnlyInvalidateCachedSurfaceParameterMapping) + { + for (int i = m_TC.Count() - 1; i >= 0; i--) + { + if (m_TC[i].m_tag.m_mapping_type == ON_TextureMapping::TYPE::srfp_mapping) + { + m_TC.Remove(i); + } + } + } + else + { + m_TC.Destroy(); + } +} + ON_MappingChannel::ON_MappingChannel() { Default(); @@ -6930,7 +6948,7 @@ class ON_PhysicallyBasedMaterialUserData : public ON_UserData private: ON_OBJECT_DECLARE(ON_PhysicallyBasedMaterialUserData); -#if defined(ON_RUNTIME_APPLE) +#if defined(ON_COMPILER_CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winconsistent-missing-override" #endif @@ -6939,7 +6957,7 @@ private: description = L"ON_PhysicallyBasedMaterialUserData"; return true; } -#if defined(ON_RUNTIME_APPLE) +#if defined(ON_COMPILER_CLANG) #pragma clang diagnostic pop #endif diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h index 1dc93054..7975d15b 100644 --- a/opennurbs_mesh.h +++ b/opennurbs_mesh.h @@ -3866,6 +3866,19 @@ Returns: bool bSeamCheck = true ); + /* + Description: + Invalidates all cached texture coordinates. Call this + function when you have made changes that will affect + the texture coordinates on the mesh. + Parameters: + bOnlyInvalidateCachedSurfaceParameterMapping - [in] + If true then only cached surface parameter mapping + texture coordinates will be invalidated. Use this + after making changes to the m_S array. + */ + void InvalidateCachedTextureCoordinates(bool bOnlyInvalidateCachedSurfaceParameterMapping = false); + bool EvaluateMeshGeometry( const ON_Surface& ); // evaluate surface at tcoords // to set mesh geometry @@ -5345,21 +5358,26 @@ The map is an array of length m_F.Count(), ngon_map[] ///////////////////////////////////////////////////////////////// - // Implementation - surface parameters and packed texture - // information + // Texture coordinates / surface parameters // - // If m_S.Count() == m_V.Count(), then the mesh is a tesselation - // of a parameteric surface and m_S[j] is the surface parameter at - // m_V[j]. Storing values in m_S[] is OPTIONAL. - // - // If m_srf_scale[] has positive values, then they report + // If m_S.Count() == m_V.Count(), then the mesh has texture + // coordinates, or it is a tesselation of a parameteric surface + // and m_S[j] is the texture coordinate / surface parameter at + // m_V[j]. + // If the values in m_S are changed, then you need to call + // InvalidateCachedTextureCoordinates(). + // Storing values in m_S[] is OPTIONAL. + ON_2dPointArray m_S; + + // Packed texture information + ON_Interval m_srf_domain[2]; // surface evaluation domain. + + // If m_srf_scale[] has positive values, then they represent // the world coordinate size of a rectangle that would // minimize texture distortion if it were mapped to the // mesh using normalized surface evaluation parameters. // This information is used to calculate high quality // packed texture coordinates. - ON_2dPointArray m_S; - ON_Interval m_srf_domain[2]; // surface evaluation domain. double m_srf_scale[2]; @@ -5368,7 +5386,6 @@ The map is an array of length m_F.Count(), ngon_map[] // If either of the m_packed_tex_domain[] intervals is a // proper subinterval of (0,1), then a texture packing // calculation assigned this subrectangle to this mesh. - ON_Interval m_packed_tex_domain[2]; // The m_packed_tex_rotate setting is valid only when diff --git a/opennurbs_mesh_modifiers.cpp b/opennurbs_mesh_modifiers.cpp index de4c1283..45fe2073 100644 --- a/opennurbs_mesh_modifiers.cpp +++ b/opennurbs_mesh_modifiers.cpp @@ -1249,9 +1249,14 @@ static const wchar_t* CapTypeToString(ON_CurvePiping::CapTypes ct) { switch (ct) { - case ON_CurvePiping::CapTypes::Flat: return ON_CURVE_PIPING_FLAT; - case ON_CurvePiping::CapTypes::Box: return ON_CURVE_PIPING_BOX; - case ON_CurvePiping::CapTypes::Dome: return ON_CURVE_PIPING_DOME; + case ON_CurvePiping::CapTypes::Flat: + return ON_CURVE_PIPING_FLAT; + case ON_CurvePiping::CapTypes::Box: + return ON_CURVE_PIPING_BOX; + case ON_CurvePiping::CapTypes::Dome: + return ON_CURVE_PIPING_DOME; + case ON_CurvePiping::CapTypes::None: + return ON_CURVE_PIPING_NONE; } return ON_CURVE_PIPING_NONE; diff --git a/opennurbs_model_component.cpp b/opennurbs_model_component.cpp index 50dcde16..a5fa6278 100644 --- a/opennurbs_model_component.cpp +++ b/opennurbs_model_component.cpp @@ -294,6 +294,14 @@ static ON__UINT64 ON_ModelComponentContentVersionNumberOne( return (runtime_serial_number*0x100000000ULL | s); } +ON_ModelComponent::ON_ModelComponent +( + ON__UINT64 runtime_serial_number +) ON_NOEXCEPT + : m_runtime_serial_number(runtime_serial_number) + , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number)) +{} + ON_ModelComponent::ON_ModelComponent() ON_NOEXCEPT : m_runtime_serial_number(++ON_ModelComponent::Internal_RuntimeSerialNumberGenerator) , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number)) diff --git a/opennurbs_model_component.h b/opennurbs_model_component.h index 89375b9a..8a459751 100644 --- a/opennurbs_model_component.h +++ b/opennurbs_model_component.h @@ -280,6 +280,11 @@ public: ON_wString& valid_name ); +private: + friend class CRhinoObject; + + ON_ModelComponent(ON__UINT64 runtime_serial_number) ON_NOEXCEPT; + public: static const ON_ModelComponent Unset; diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h index 1e27c22b..227787fb 100644 --- a/opennurbs_nurbssurface.h +++ b/opennurbs_nurbssurface.h @@ -582,7 +582,7 @@ public: -#if defined(ON_RUNTIME_APPLE) +#if defined(ON_COMPILER_CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winconsistent-missing-override" #endif @@ -607,7 +607,7 @@ public: double* max_deviation = nullptr ) const; // NOTE: some compilers believe the above is an override and msvc/others do not -#if defined(ON_RUNTIME_APPLE) +#if defined(ON_COMPILER_CLANG) #pragma clang diagnostic pop #endif diff --git a/opennurbs_plane.h b/opennurbs_plane.h index 3d061926..65fdba66 100644 --- a/opennurbs_plane.h +++ b/opennurbs_plane.h @@ -583,9 +583,24 @@ public: ON_UUID m_plane_id; bool m_bEnabled; + // A distance where the clipping plane does not clip geometry. + // By default, distance is ON_DBL_MAX which indicates that there is no + // distance being applied + // A positive value is equivalent to placing another clipping plane at a + // distance from this clipping plane along it's normal and then flipping it + double Distance() const; + void SetDistance(double distance); + void Default(); bool Write( ON_BinaryArchive& ) const; bool Read( ON_BinaryArchive& ); + +private: + char m_reserved[3]; + + // This should be a double, but is a float in order to not change + // the class size. When the Rhino SDK can break, this data type should change. + float m_distance = ON_UNSET_POSITIVE_FLOAT; }; class ON_CLASS ON_ClippingPlane @@ -603,8 +618,23 @@ public: ON_ClippingPlaneInfo ClippingPlaneInfo() const; + // A distance where the clipping plane does not clip geometry. + // By default, distance is a negative value which indicates that there is no + // distance being applied + // A positive value is equivalent to placing another clipping plane at a + // distance from this clipping plane along it's normal and then flipping it + double Distance() const; + void SetDistance(double distance); + bool Read( class ON_BinaryArchive& ); bool Write( class ON_BinaryArchive& ) const; + +private: + char m_reserved[3]; + + // This should be a double, but is a float in order to not change + // the class size. When the Rhino SDK can break, this data type should change. + float m_distance = -1; }; diff --git a/opennurbs_planesurface.cpp b/opennurbs_planesurface.cpp index 78ba9f46..32882770 100644 --- a/opennurbs_planesurface.cpp +++ b/opennurbs_planesurface.cpp @@ -828,14 +828,29 @@ ON_Mesh* ON_PlaneSurface::CreateMesh( return rc; } +double ON_ClippingPlaneInfo::Distance() const +{ + if (ON_IsValidFloat(m_distance)) + return m_distance; + return ON_DBL_MAX; +} +void ON_ClippingPlaneInfo::SetDistance(double distance) +{ + if (ON_IsValid(distance) && distance < ON_UNSET_POSITIVE_FLOAT) + m_distance = (float)distance; + else + m_distance = ON_UNSET_POSITIVE_FLOAT; +} + void ON_ClippingPlaneInfo::Default() { memset(this,0,sizeof(*this)); + m_distance = -1; } bool ON_ClippingPlaneInfo::Write( ON_BinaryArchive& file ) const { - bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2); if (!rc) return false; @@ -850,6 +865,13 @@ bool ON_ClippingPlaneInfo::Write( ON_BinaryArchive& file ) const rc = file.WriteBool(m_bEnabled); if (!rc) break; + // version 1.1 and 1.2 - write distance as a double + // In version 1.1 we wrote -1 as the default value for m_distance. + // This default has changed to ON_UNSET_POSITIVE_FLOAT. Bumping the minor + // version so we can properly handle the default case when reading. + rc = file.WriteDouble(m_distance); + if (!rc) break; + break; } @@ -883,6 +905,22 @@ bool ON_ClippingPlaneInfo::Read( ON_BinaryArchive& file ) rc = file.ReadBool(&m_bEnabled); if (!rc) break; + if (minor_version > 0) + { + double d = -1; + rc = file.ReadDouble(&d); + if (!rc) break; + + if (1 == minor_version) + { + // negative values in 1.1 chunk meant that the distance was unset + if (d < 0.0) + d = ON_UNSET_VALUE; + } + + SetDistance(d); + } + break; } @@ -899,6 +937,7 @@ void ON_ClippingPlane::Default() m_viewport_ids.Empty(); m_plane_id = ON_nil_uuid; m_bEnabled = true; + m_distance = -1; } ON_ClippingPlane::ON_ClippingPlane() @@ -916,9 +955,19 @@ ON_ClippingPlaneInfo ON_ClippingPlane::ClippingPlaneInfo() const info.m_plane_equation = m_plane.plane_equation; info.m_plane_id = m_plane_id; info.m_bEnabled = m_bEnabled; + info.SetDistance(m_distance); return info; } +double ON_ClippingPlane::Distance() const +{ + return m_distance; +} +void ON_ClippingPlane::SetDistance(double distance) +{ + m_distance = (float)distance; +} + bool ON_ClippingPlane::Read( ON_BinaryArchive& file ) { Default(); @@ -957,6 +1006,15 @@ bool ON_ClippingPlane::Read( ON_BinaryArchive& file ) if (!rc) break; } + if (minor_version > 1) + { + double d = -1; + rc = file.ReadDouble(&d); + if (!rc) break; + + m_distance = (float)d; + } + break; } @@ -968,7 +1026,7 @@ bool ON_ClippingPlane::Read( ON_BinaryArchive& file ) bool ON_ClippingPlane::Write( ON_BinaryArchive& file ) const { - bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2); if (!rc) return false; @@ -994,6 +1052,10 @@ bool ON_ClippingPlane::Write( ON_BinaryArchive& file ) const rc = m_viewport_ids.Write(file); if (!rc) break; + //version 1.2 - write distance as double + rc = file.WriteDouble(m_distance); + if (!rc) break; + break; } diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 9f2c4028..4a2ffedb 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -13,11 +13,11 @@ // These are set automatically by the build system as the // first step in each build. // -#define RMA_VERSION_YEAR 2022 -#define RMA_VERSION_MONTH 12 -#define RMA_VERSION_DATE 12 -#define RMA_VERSION_HOUR 12 -#define RMA_VERSION_MINUTE 54 +#define RMA_VERSION_YEAR 2023 +#define RMA_VERSION_MONTH 2 +#define RMA_VERSION_DATE 23 +#define RMA_VERSION_HOUR 15 +#define RMA_VERSION_MINUTE 57 //////////////////////////////////////////////////////////////// // @@ -35,9 +35,9 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,0,22346,12540 -#define VERSION_WITH_PERIODS 8.0.22346.12540 -#define COPYRIGHT "Copyright (C) 1993-2022, Robert McNeel & Associates. All Rights Reserved." +#define VERSION_WITH_COMMAS 8,0,23054,15570 +#define VERSION_WITH_PERIODS 8.0.23054.15570 +#define COPYRIGHT "Copyright (C) 1993-2023, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." #define RMA_VERSION_NUMBER_MAJOR_STRING "8" @@ -47,8 +47,8 @@ #define RMA_VERSION_NUMBER_SR_STRING "SR0" #define RMA_VERSION_NUMBER_SR_WSTRING L"SR0" -#define RMA_VERSION_WITH_PERIODS_STRING "8.0.22346.12540" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.22346.12540" +#define RMA_VERSION_WITH_PERIODS_STRING "8.0.23054.15570" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.23054.15570" diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp index eac32383..0d4526ed 100644 --- a/opennurbs_statics.cpp +++ b/opennurbs_statics.cpp @@ -105,7 +105,8 @@ ON__UINT64 ON_NextContentSerialNumber() // It is critical that ON_ModelComponent::Internal_RuntimeSerialNumberGenerator // be constructed before any instance of a class derived from ON_ModelComponent. // That is why it is above the ClassId stuff in this .cpp file. -std::atomic ON_ModelComponent::Internal_RuntimeSerialNumberGenerator(0); +// Serial numbers below UINT32_MAX + 1 are reserved for Rhino use. +std::atomic ON_ModelComponent::Internal_RuntimeSerialNumberGenerator(UINT32_MAX + 1ULL); std::atomic ON_SubDimple::Internal_RuntimeSerialNumberGenerator; @@ -128,8 +129,14 @@ const ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponent // The default type must be packed, unpacked, zero, or nan and should be packed or upacked. const ON_SubDTextureCoordinateType ON_SubD::DefaultTextureCoordinateType = ON_SubDTextureCoordinateType::Packed; -const double ON_SubDEdge::InfinteSharpness = 5.0; -const double ON_SubDEdge::SharpnessTolerance = 0.01; +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Zero; + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Nan = ON_SubDEdgeSharpness::FromConstant(ON_DBL_QNAN); + +// NOTE WELL: +// It is required that (3 + 2^ON_SubDEdgeSharpness::Maximum) <= ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity +const double ON_SubDEdgeSharpness::Maximum = 4.0; +const double ON_SubDEdgeSharpness::Tolerance = 0.01; 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; @@ -1516,7 +1523,7 @@ static const ON_ModelComponentTypeIterator ON_ModelComponentIterator_Init( const ON_ModelComponentTypeIterator ON_ModelComponentTypeIterator::ExplicitComponentTypes(ON_ModelComponentIterator_Init(1)); const ON_ModelComponentTypeIterator ON_ModelComponentTypeIterator::TableComponentTypes(ON_ModelComponentIterator_Init(2)); -const ON_ModelComponent ON_ModelComponent::Unset; +const ON_ModelComponent ON_ModelComponent::Unset(ON_ModelComponent::Type::Unset, (ON__UINT64) 0); const ON_ModelComponentReference ON_ModelComponentReference::Empty; const ON_ModelComponentWeakReference ON_ModelComponentWeakReference::Empty; diff --git a/opennurbs_string.h b/opennurbs_string.h index 4a3ea3ce..747b185c 100644 --- a/opennurbs_string.h +++ b/opennurbs_string.h @@ -2733,6 +2733,9 @@ public: /// static const wchar_t BlackRecyclingSymbol = (wchar_t)ON_UnicodeCodePoint::ON_BlackRecyclingSymbol; + /// WARNING SIGN U+26A0 (⚠) + static const wchar_t WarningSign = (wchar_t)ON_UnicodeCodePoint::ON_WarningSign; + /// /// REPLACEMENT CHARACTER U+FFFD (�) /// By convention, U+FFFD is used to mark string elements where diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 9a7968ba..1cee6fdc 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -659,6 +659,12 @@ unsigned int ON_SubDEdgePtr::EdgeFaceCount() const return (nullptr != e) ? ((unsigned int)e->m_face_count) : 0U; } +ON_SubDEdgeTag ON_SubDEdgePtr::EdgeTag() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->m_edge_tag : ON_SubDEdgeTag::Unset; +} + bool ON_SubDEdgePtr::EdgeIsSmooth() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); @@ -697,6 +703,13 @@ ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const return ON_SUBD_EDGE_DIRECTION(m_ptr); } + +const ON_3dPoint ON_SubDEdgePtr::SubdivisionPoint() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->SubdivisionPoint() : ON_3dPoint::NanPoint; +} + void ON_SubDEdgePtr::ClearSavedSubdivisionPoints() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); @@ -731,6 +744,23 @@ const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex( return nullptr; } +const ON_3dPoint ON_SubDEdgePtr::RelativeVertexPoint( + int relative_vertex_index, + ON_SubDComponentLocation point_location +) const +{ + const ON_SubDVertex* v = this->RelativeVertex(relative_vertex_index); + return (nullptr != v) ? v->Point(point_location) : ON_3dPoint::NanPoint; +} + + +const ON_3dPoint ON_SubDEdgePtr::RelativeVertexSurfacePoint( + int relative_vertex_index +) const +{ + const ON_SubDVertex* v = this->RelativeVertex(relative_vertex_index); + return (nullptr != v) ? v->SurfacePoint() : ON_3dPoint::NanPoint; +} unsigned ON_SubDEdgePtr::RelativeVertexId(int relative_vertex_index) const { @@ -793,6 +823,48 @@ double ON_SubDEdgePtr::RelativeSectorCoefficient( return ON_SubDSectorType::ErrorSectorCoefficient; } +void ON_SubDEdgePtr::SetRelativeSectorCoefficientForExperts( + int relative_vertex_index, + double relative_sector_coefficient +) const +{ + for (;;) + { + if (relative_vertex_index < 0 || relative_vertex_index>1) + break; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); + if (nullptr == edge) + break; + if (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) + relative_vertex_index = 1 - relative_vertex_index; + edge->m_sector_coefficient[relative_vertex_index] = relative_sector_coefficient; + break; + } +} + + +const ON_SubDEdgeSharpness ON_SubDEdgePtr::RelativeSharpness() const +{ + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); + if (nullptr != edge) + { + const ON_SubDEdgeSharpness s = edge->Sharpness(); + return (0 == ON_SUBD_EDGE_DIRECTION(m_ptr)) ? s : s.Reversed(); + } + return ON_SubDEdgeSharpness::Zero; +} + +void ON_SubDEdgePtr::SetRelativeSharpness(ON_SubDEdgeSharpness relative_sharpness) const +{ + ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); + if (nullptr != edge) + edge->SetSharpnessForExperts( + (0 == ON_SUBD_EDGE_DIRECTION(m_ptr)) + ? relative_sharpness + : relative_sharpness.Reversed() + ); +} + const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const { for (;;) @@ -896,6 +968,14 @@ const ON_SubDEdgePtr ON_SubDEdgePtr::Create( return eptr; } +const ON_SubDEdgePtr ON_SubDEdgePtr::Create( + const class ON_SubDVertex* v0, + const class ON_SubDVertex* v1 +) +{ + return ON_SubDEdge::FromVertices(v0,v1); +} + const ON_SubDEdgePtr ON_SubDEdgePtr::Create( const class ON_SubDComponentPtr& edge_element ) @@ -3304,6 +3384,29 @@ unsigned int ON_SubDVertex::EdgeArrayIndex( return ON_UNSET_UINT_INDEX; } +unsigned int ON_SubDVertex::ReplaceEdgeInArray( + const ON_SubDEdge* old_edge, + const ON_SubDEdgePtr new_edge + ) +{ + if (m_edges == nullptr || m_edge_count == 0) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + unsigned vei = old_edge != nullptr ? EdgeArrayIndex(old_edge) : ON_UNSET_UINT_INDEX; + if (ON_UNSET_UINT_INDEX == vei || m_edges[vei].m_ptr == new_edge.m_ptr) + return ON_UNSET_UINT_INDEX; + if (new_edge.IsNotNull()) + { + m_edges[vei] = new_edge; + } + else + { + const unsigned c = (unsigned)(m_edge_count--); + while (++vei < c) + m_edges[vei - 1] = m_edges[vei]; + } + return vei; +} + unsigned int ON_SubDVertex::MarkedEdgeCount() const { unsigned int mark_count = 0; @@ -3538,6 +3641,13 @@ bool ON_SubDVertex::IsDartOrCrease() const ); } +bool ON_SubDVertex::IsDartOrCorner() const +{ + return ( + ON_SubDVertexTag::Dart == m_vertex_tag + || ON_SubDVertexTag::Corner == m_vertex_tag + ); +} bool ON_SubDVertex::IsSmoothOrDart() const @@ -3545,6 +3655,11 @@ bool ON_SubDVertex::IsSmoothOrDart() const return (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Dart == m_vertex_tag); } +bool ON_SubDVertex::IsSmoothOrDartOrCrease() const +{ + return (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Dart == m_vertex_tag || ON_SubDVertexTag::Crease == m_vertex_tag); +} + bool ON_SubDVertex::IsSmoothOrCrease() const { return (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Crease == m_vertex_tag); @@ -3707,137 +3822,430 @@ bool ON_SubDEdge::IsSmoothX() const return (ON_SubDEdgeTag::SmoothX == m_edge_tag) ? true : false; } - -// These semisharp edge tools need to be out of the protected ON_SUBD_SHARP_EDGES -// in order for public opennurbs to work. They simply provide access to -// ON_SubDEdge::m_sharpness which has been in public opennurbs since 7.0. -double ON_SubDEdge::Sharpness() const +const ON_SubDEdgeSharpness ON_SubDEdge::Sharpness() const { - return Sharpness(ON_DBL_QNAN, 0.0, ON_SubDEdge::InfinteSharpness); + return IsSmooth() ? m_sharpness : ON_SubDEdgeSharpness::Zero; } -double ON_SubDEdge::Sharpness( - double unset_edge_value, - double smooth_edge_value, - double crease_edge_value +double ON_SubDEdge::EndSharpness( + unsigned evi ) const { - if (IsCrease()) - return crease_edge_value; - if (IsSmooth()) - return ((m_sharpness >= 0.0 && m_sharpness < ON_SubDEdge::InfinteSharpness) ? m_sharpness : 0.0); - return unset_edge_value; + return (IsSmooth() && evi >= 0 && evi <= 1) ? m_sharpness[evi] : 0.0; } -void ON_SubDEdge::SetSharpness(double sharpness) +double ON_SubDEdge::EndSharpness( + const ON_SubDVertex* v +) const { - m_sharpness = IsSmooth() ? ON_SubDEdge::SanitizeSharpness(sharpness, false) : 0.0; -} - -double ON_SubDEdge::SanitizeSharpness(double sharpness, bool bPermitInfinteSharpness) -{ - // When sharpness is withing ON_SubDEdge::SharpnessTolerance of an integer value, - // snap to that integer value. - const double f = floor(sharpness); - if (sharpness - f <= ON_SubDEdge::SharpnessTolerance) - sharpness = f; - else if (f + 1.0 - sharpness <= ON_SubDEdge::SharpnessTolerance) - sharpness = f + 1.0; - - if (sharpness >= 0.0 && sharpness < ON_SubDEdge::InfinteSharpness) - return sharpness; - - if (ON_SubDEdge::InfinteSharpness == sharpness && bPermitInfinteSharpness) - return ON_SubDEdge::InfinteSharpness; - + if (nullptr != v) + { + if (v == m_vertex[0]) + return EndSharpness(0U); + if (v == m_vertex[1]) + return EndSharpness(1U); + } return 0.0; } -#if defined(ON_SUBD_SHARP_EDGES) - -bool ON_SubD::HasSemisharpness() const +const ON_SubDEdgeSharpness ON_SubDEdge::SubdivideSharpness( + unsigned evi, + bool bReverse +) const { - ON_SubDEdgeIterator eit = this->EdgeIterator(); - for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) - { - if (e->IsSemisharp()) - return true; - } - return false; + const ON_SubDEdgeSharpness s = Sharpness().Subdivided(evi); + return bReverse ? s.Reversed() : s; } -unsigned int ON_SubD::SemisharpEdgeCount(ON_Interval& sharpness_range) const +const ON_SubDEdgeSharpness ON_SubDEdge::SubdivideSharpness( + const class ON_SubDVertex* end_vertex, + bool bReverse +) const { + if (nullptr != end_vertex) + { + if (end_vertex == this->m_vertex[0]) + return this->SubdivideSharpness(0U, bReverse); + if (end_vertex == this->m_vertex[1]) + return this->SubdivideSharpness(1U, bReverse); + } + return ON_SubDEdgeSharpness::Zero; +} + +bool ON_SubDEdge::ClearSharpnessForExperts() +{ + const bool bChanged = IsSmooth() && (m_sharpness != ON_SubDEdgeSharpness::Zero); + m_sharpness = ON_SubDEdgeSharpness::Zero; + return bChanged; +} + +void ON_SubDEdge::SetSharpnessForExperts(ON_SubDEdgeSharpness sharpness) +{ + m_sharpness = IsSmooth() ? sharpness : ON_SubDEdgeSharpness::Zero; +} + + +bool ON_SubDEdgeSharpness::IsConstant() const +{ + return m_edge_sharpness[0] == m_edge_sharpness[1] && 0.0f <= m_edge_sharpness[0] && m_edge_sharpness[0] <= ((float)ON_SubDEdgeSharpness::Maximum); +} + +bool ON_SubDEdgeSharpness::IsVariable() const +{ + return m_edge_sharpness[0] != m_edge_sharpness[1] && IsValid(); +} + +bool ON_SubDEdgeSharpness::IsZero() const +{ + return (0.0f == m_edge_sharpness[0] && 0.0f == m_edge_sharpness[1]); +} + +bool ON_SubDEdgeSharpness::IsNotZero() const +{ + return (m_edge_sharpness[0] != 0.0f || m_edge_sharpness[1] != 0.0f) && IsValid(); +} + +bool ON_SubDEdgeSharpness::IsValid() const +{ + return + m_edge_sharpness[0] >= 0.0f + && m_edge_sharpness[0] <= ((float)ON_SubDEdgeSharpness::Maximum) + && m_edge_sharpness[1] >= 0.0f + && m_edge_sharpness[1] <= ((float)ON_SubDEdgeSharpness::Maximum) + ; +} + +bool ON_SubDEdgeSharpness::IsNotValid() const +{ + return (false == IsValid()); +} + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::FromConstant( + double sharpness +) +{ + ON_SubDEdgeSharpness s; + s.m_edge_sharpness[0] = (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::Maximum) ? ((float)ON_SubDEdgeSharpness::Sanitize(sharpness, 0.0)) : ON_FLT_QNAN; + s.m_edge_sharpness[1] = s.m_edge_sharpness[0]; + return s; +} + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::FromInterval( + double sharpness0, + double sharpness1 +) +{ + if (sharpness0 >= 0.0 && sharpness0 <= ON_SubDEdgeSharpness::Maximum && sharpness1 >= 0.0 && sharpness1 <= ON_SubDEdgeSharpness::Maximum) + { + ON_SubDEdgeSharpness s; + s.m_edge_sharpness[0] = (float)ON_SubDEdgeSharpness::Sanitize(sharpness0, 0.0); + s.m_edge_sharpness[1] = (float)ON_SubDEdgeSharpness::Sanitize(sharpness1, 0.0); + return s; + } + return ON_SubDEdgeSharpness::Nan; +} + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::FromInterval(const class ON_Interval& sharpness_interval) +{ + return ON_SubDEdgeSharpness::FromInterval(sharpness_interval[0], sharpness_interval[1]); +} + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Union( + const ON_SubDEdgeSharpness& a, + const ON_SubDEdgeSharpness& b +) +{ + float s[4] = {}; + int count = 0; + if (a.m_edge_sharpness[0] > 0.0f || a.m_edge_sharpness[1] > 0.0f) + { + s[0] = a.m_edge_sharpness[0]; + s[1] = a.m_edge_sharpness[1]; + count = 2; + } + if (b.m_edge_sharpness[0] > 0.0f || b.m_edge_sharpness[1] > 0.0f) + { + s[count] = b.m_edge_sharpness[0]; + s[count+1] = b.m_edge_sharpness[1]; + count += 2; + } + ON_SubDEdgeSharpness u = ON_SubDEdgeSharpness::FromConstant(s[0]); + for ( int i = 1; i < count; ++i) + { + float x = s[i]; + if (x < u.m_edge_sharpness[0]) + u.m_edge_sharpness[0] = x; + else if (x > u.m_edge_sharpness[1]) + u.m_edge_sharpness[1] = x; + } + return u; +} + +double ON_SubDEdgeSharpness::Sanitize( + double sharpness, + double invalid_input_result +) +{ + // When sharpness is withing ON_SubDEdgeSharpness::Tolerance of an integer value, + // snap to that integer value. + + if (false == (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::Maximum)) + return invalid_input_result; // when sharpness is nan, invalid_input_result is returned here. + + // When sharpness is close to an integer value, snap to the integer value. + // This results in cleaner looking limit surfaces and faster evaluation. + const double f = floor(sharpness); + if (f >= 0.0 && f <= ON_SubDEdgeSharpness::Maximum) + { + if (sharpness - f <= ON_SubDEdgeSharpness::Tolerance) + sharpness = f; + else if (f + 1.0 - sharpness <= ON_SubDEdgeSharpness::Tolerance && f + 1.0 <= ON_SubDEdgeSharpness::Maximum) + sharpness = f + 1.0; + } + + return sharpness; +} + +double ON_SubDEdgeSharpness::Sanitize( + double sharpness +) +{ + // When edges are subdivided, the call ON_SubDEdgeSharpness::Sanitize(current_sharpness-1.0). + // These critical calls assume this function returns 0.0 when the input is not valid, for example + // negative when 0 <= current_sharpness < 1. + return ON_SubDEdgeSharpness::Sanitize(sharpness, 0.0); +} + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Subdivided(int end_index) const +{ + if (end_index >= 0 && end_index <= 1) + { + const double s[2] = { + ON_SubDEdgeSharpness::Sanitize(((double)(this->m_edge_sharpness[0])) - 1.0), + ON_SubDEdgeSharpness::Sanitize(((double)(this->m_edge_sharpness[1])) - 1.0) + }; + const double mids = (s[0] == s[1]) ? s[0] : ON_SubDEdgeSharpness::Sanitize(0.5 * (s[0] + s[1])); + return (0 == end_index) ? ON_SubDEdgeSharpness::FromInterval(s[0], mids) : ON_SubDEdgeSharpness::FromInterval(mids, s[1]); + } + return ON_SubDEdgeSharpness::Zero; +} + +const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::Reversed() const +{ + ON_SubDEdgeSharpness r; + r.m_edge_sharpness[0] = this->m_edge_sharpness[1]; + r.m_edge_sharpness[1] = this->m_edge_sharpness[0]; + return r; +} + +double ON_SubDEdgeSharpness::operator[](int end_index) const +{ + return this->EndSharpness(end_index); +} + +bool operator!=(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs) +{ + const float* l = (const float*)(&lhs); + const float* r = (const float*)(&rhs); + return l[0] != r[0] || l[1] != r[1]; +} + +bool operator==(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs) +{ + const float* l = (const float*)(&lhs); + const float* r = (const float*)(&rhs); + return l[0] == l[1] && r[0] == r[1]; +} + +double ON_SubDEdgeSharpness::Average() const +{ + return + (m_edge_sharpness[0] == m_edge_sharpness[1]) + ? ((double)m_edge_sharpness[0]) + : ON_SubDEdgeSharpness::Sanitize(0.5 * ( ((double)m_edge_sharpness[0]) + ((double)m_edge_sharpness[1]) )); +} + +double ON_SubDEdgeSharpness::MinimumEndSharpness() const +{ + return (double)((m_edge_sharpness[0] <= m_edge_sharpness[1]) ? m_edge_sharpness[0] : m_edge_sharpness[1]); +} + +double ON_SubDEdgeSharpness::MaximumEndSharpness() const +{ + return (double)((m_edge_sharpness[0] >= m_edge_sharpness[1]) ? m_edge_sharpness[0] : m_edge_sharpness[1]); +} + + +double ON_SubDEdgeSharpness::EndSharpness(int end_index) const +{ + return (end_index >= 0 && end_index <= 1) ? ((double)m_edge_sharpness[end_index]) : ON_DBL_QNAN; +} + +double ON_SubDEdgeSharpness::VertexSharpness( + ON_SubDVertexTag vertex_tag, + unsigned sharp_edge_end_count, + double maximum_edge_end_sharpness +) +{ + // NOTE WELL: + // For edge chains with a contant sharpness, this agrees with DKT SIGGRAPH 1998. + // For edge chains with variable sharpness, having a sharpness interval on the + // edge with end values matching at shared vertices gives nicer looking results + // than assigning a constant sharpness to edges and Chaikin's subdivision rule + // (averaging) at vertices. + + if (sharp_edge_end_count <= 0) + { + // no edges have nonzero end sharpness at this vertex + return 0.0; + } + + if (false == (maximum_edge_end_sharpness > 0.0)) + { + // This happens when the sharp edges attached to the vertex + // have variable sharpness with 0 at the vertex + // and nonzero sharpness at the other ends. + return 0.0; + } + + if (ON_SubDVertexTag::Corner == vertex_tag) + { + // No sharpness bias for corner vertex subdivision. + // At a corner vertex, all subdivision points and the limit point are equal to the level 0 control net point. + // Thus, applying a sharp bias doesn't modify the subdivision point or limit point. + return 0.0; + } + + unsigned crease_edge_count; + if (ON_SubDVertexTag::Smooth == vertex_tag) + crease_edge_count = 0; + else if (ON_SubDVertexTag::Crease == vertex_tag) + crease_edge_count = 2; + else if (ON_SubDVertexTag::Dart == vertex_tag) + crease_edge_count = 1; + else + { + ON_SUBD_ERROR("vertex_tag parameter is invalid or unset."); + return 0.0; + } + + if (sharp_edge_end_count + crease_edge_count < 2U) + { + // 1 sharp edge and no creases + // No sharp bias for the vertex subdivision point. + return 0.0; + } + + return maximum_edge_end_sharpness; +} + +double ON_SubDEdgeSharpness::SharpnessFromNormalizedValue( + double normalized_slider_value +) +{ + return SharpnessFromSliderValue( + ON_Interval::ZeroToOne, + normalized_slider_value, + ON_DBL_QNAN + ); +} + +double ON_SubDEdgeSharpness::SharpnessFromSliderValue( + ON_Interval slider_domain, + double slider_value, + double invalid_input_result +) +{ + if (slider_domain.IsInterval() && slider_value >= slider_domain.Min() && slider_value <= slider_domain.Max()) + { + const double s = (0.0 == slider_domain[0] && 1.0 == slider_domain[1]) ? slider_value : slider_domain.NormalizedParameterAt(slider_value); + if (s >= 0.0 && s <= 1.0) + { + const ON_Interval ZeroToMaximumSharpness(0.0, ON_SubDEdgeSharpness::Maximum); + double sharpness = ON_SubDEdgeSharpness::Sanitize(ZeroToMaximumSharpness.ParameterAt(s), invalid_input_result); + if (sharpness >= 0.0 && sharpness <= ON_SubDEdgeSharpness::Maximum) + return sharpness; + } + } + return invalid_input_result; +} + + + +bool ON_SubD::HasSharpEdges() const +{ + bool bHasSharpEdges = 0 != (ON_ComponentAttributes::EdgeAttributes::InteriorSharp & this->AggregateEdgeAttributes()); + return bHasSharpEdges; + + //ON_SubDEdgeIterator eit = this->EdgeIterator(); + //for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + //{ + // if (e->IsSharp()) + // return true; + //} + //return false; +} + +unsigned int ON_SubD::SharpEdgeCount(ON_SubDEdgeSharpness& sharpness_range) const +{ + sharpness_range = ON_SubDEdgeSharpness::Zero; unsigned int sharp_edge_count = 0; - double mins = 0.0; - double maxs = 0.0; ON_SubDEdgeIterator eit = this->EdgeIterator(); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { - const double s = e->Sharpness(); - if (s > 0.0 && s < ON_SubDEdge::InfinteSharpness) + if (e->IsSharp()) { - if (0 == sharp_edge_count) - { - mins = s; - maxs = s; - } - else if (s < mins) - mins = s; - else if (s > maxs) - maxs = s; + sharpness_range = ON_SubDEdgeSharpness::Union(sharpness_range, e->Sharpness()); ++sharp_edge_count; } } - sharpness_range.Set(mins, maxs); return sharp_edge_count; } -unsigned int ON_SubD::SemisharpEdgeCount() const +unsigned int ON_SubD::SharpEdgeCount() const { - ON_Interval sharpness_range(0.0, 0.0); - return SemisharpEdgeCount(sharpness_range); + ON_SubDEdgeSharpness sharpness_range = ON_SubDEdgeSharpness::Zero; + return SharpEdgeCount(sharpness_range); } -bool ON_SubDVertex::IsSemisharp() const +bool ON_SubDVertex::IsSharp( bool bEndCheck ) const { // This definition of vertex sharpness is from DeRose, Kass, Truong 1998 // If there are exactly two sharp edges adjacent to the vertex, then // the vertex sharpness is the average of the edge sharpnesses. - if (IsSmooth() && nullptr != m_edges) + if (IsSmoothOrDartOrCrease() && nullptr != m_edges) { for (unsigned short vei = 0; vei < m_edge_count; ++vei) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); - if (nullptr != e && e->IsSemisharp()) + if (nullptr != e && e->IsSharp() ) return true; } } return false; } - -unsigned int ON_SubD::ClearSemisharpness() +unsigned int ON_SubD::ClearEdgeSharpness() { - unsigned int semisharp_edge_count = 0; + unsigned int sharp_edge_count = 0; ON_SubDEdgeIterator eit = this->EdgeIterator(); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { - const double s = e->Sharpness(); - if (false == (s== 0.0)) + if (const_cast(e)->ClearSharpnessForExperts()) { - const_cast(e)->SetSharpness(0.0); - ++semisharp_edge_count; + e->ClearSavedSubdivisionPoints(true); + ++sharp_edge_count; } } - if (semisharp_edge_count != 0) + if (sharp_edge_count != 0) this->ChangeGeometryContentSerialNumberForExperts(true); - return semisharp_edge_count; + return sharp_edge_count; } -double ON_SubDVertex::Sharpness() const + +double ON_SubDVertex::VertexSharpness() const { ON_3dPoint sharp_subdivision_point; return GetSharpSubdivisionPoint(sharp_subdivision_point); @@ -3846,265 +4254,159 @@ double ON_SubDVertex::Sharpness() const double ON_SubDVertex::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const { - // This definition of vertex sharpness is from DeRose, Kass, Truong 1998 - // If the vertex is adjacent to zero or one semisharp edges, - // then the ordindary vertex subdivision rule is used and 0.0 is returned. - // If the vertex is adjacent to 3 or more semisharp edges, - // then the corner vertex subdivision rule is used and 10.0 is returned. - // If there are exactly two semisharp edges adjacent to the vertex, - // then the vertex sharpness is the average of the edge sharpnesses. - // If this vertex sharpness is >= 1, then the corner vertex subdivision rule is used. - // Otherwise, 0 < vertex sharpness < 1 and the semisharp vertex subdivision point is - // (1-vertex sharpness)*(ordinary subdivision point) + (vertex sharpness)*(corner subdivision point). - sharp_subdivision_point = ON_3dPoint::NanPoint; + // Below "sharp edge" means an edge attached to this vertex that has a smooth tag + // and a nonzero sharpness at the end attached to this vertex. + // This definition of vertex sharpness is from DeRose, Kass, Truong 1998. + // - If the vertex is smooth and adjacent to zero or one sharp edges, + // then the ordindary vertex subdivision rule is used, + // sharp_subdivision_point is set to NanPoint, + // and 0.0 is returned. + // - If the vertex is adjacent to 3 or more sharp edges and crease edges, + // then the corner vertex subdivision rule is used, + // sharp_subdivision_point = vertex control net point, + // and the maximum edge sharpness at the vertex is returned. + // - If the vertex is adjacent to 2 sharp edges and crease edges, + // then sharp_subdivision_point = crease vertex subdivision point, + // and the average of the edge sharpnesses is returned. + // + // The returned vertex sharpenss is awasy >= 0. + // When the returned vertex sharpness is > 0, the vertex subdivision point + // (1-s)*(ordinary subdivision point) + s*sharp_subdivision_point, + // where s = min(returned vertex sharpness, 1). - if (((this->IsSmoothOrDart() && m_face_count==m_edge_count) || (this->IsCrease() && m_edge_count >= 3 && m_face_count+((unsigned short)1) == m_edge_count)) - && nullptr != m_edges) + if (this->IsSmoothOrDartOrCrease() && nullptr != m_edges) { - // This is a single sector vertex that might have a semisharp edge attached. - - unsigned int crease_edge_count = 0; unsigned int sharp_edge_count = 0; - double sharpness[2] = { 0.0, 0.0 }; - const ON_SubDVertex* other_v[2] = { nullptr,nullptr }; + unsigned other_v_count = 0; + double max_end_sharpeness = 0.0; + const ON_SubDVertex* other_v[2] = {}; + for (unsigned short vei = 0; vei < m_edge_count; ++vei) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); - if (nullptr != e) + if (nullptr == e) + continue; + if (e->IsCrease()) { - const double edge_sharpness = e->Sharpness(ON_DBL_QNAN,0.0,ON_SubDEdge::InfinteSharpness); - if (edge_sharpness > 0.0) + if (other_v_count < 2) + other_v[other_v_count] = e->OtherEndVertex(this); + ++other_v_count; + } + else if ( e->IsSharp() ) + { + // The edge is has a smooth tag and at leas one end has sharpness > 0. + // For variable edge sharpness, s can be zero at an end. + const double s = e->EndSharpness(this); + if (s > 0.0) { - const unsigned n = crease_edge_count + sharp_edge_count; - if (n < 2) - { - other_v[n] = e->OtherEndVertex(this); - sharpness[n] = edge_sharpness; - } - if (ON_SubDEdge::InfinteSharpness == edge_sharpness) - ++crease_edge_count; // dart's crease edge or one of the corner's crease edges - else if (edge_sharpness < ON_SubDEdge::InfinteSharpness) - ++sharp_edge_count; // semisharp edge - else - continue; - if (n >= 2 && sharp_edge_count > 0) - { - sharp_subdivision_point = this->ControlNetPoint(); - return ON_SubDEdge::InfinteSharpness; - } + if (other_v_count < 2) + other_v[other_v_count] = e->OtherEndVertex(this); + ++other_v_count; + if (s > max_end_sharpeness) + max_end_sharpeness = s; + ++sharp_edge_count; } } } - if (2 == crease_edge_count+sharp_edge_count && sharp_edge_count > 0) + const double vertex_sharpness = ON_SubDEdgeSharpness::VertexSharpness(m_vertex_tag, sharp_edge_count, max_end_sharpeness); + if (vertex_sharpness > 0.0) { - if (nullptr != other_v[0] && nullptr != other_v[1]) - sharp_subdivision_point = 0.125 * (6.0 * ControlNetPoint() + other_v[0]->ControlNetPoint() + other_v[1]->ControlNetPoint()); - return ON_SubDEdge::AverageSharpness(sharpness[0], sharpness[1]); + if (2 == other_v_count && nullptr != other_v[0] && nullptr != other_v[1]) + { + // 2 creases and sharps - "crease" subdivision point + sharp_subdivision_point = 0.125 * (6.0 * this->ControlNetPoint() + other_v[0]->ControlNetPoint() + other_v[1]->ControlNetPoint()); + } + else + { + // 3 or more creases and sharps - "corner" subdivision point + sharp_subdivision_point = this->ControlNetPoint(); + } + return vertex_sharpness; } - - sharp_subdivision_point = ON_3dPoint::NanPoint; - return 0.0; } sharp_subdivision_point = ON_3dPoint::NanPoint; return 0.0; } - -double ON_SubDVertex::EvaluationSharpness(ON_3dPoint& sharp_subdivision_point) const -{ - // WARNING: - // This function is temporary and for internal use only. - // It will be removed in the near future. - // Any code referencing this function will unpredictably fail in the near future. - // This is primative and is not thread safe. - double vertex_sharpness; - if (ON_SubDEdge::SemisharpEvaluationIsEnabled()) - { - vertex_sharpness = GetSharpSubdivisionPoint(sharp_subdivision_point); - } - else - { - sharp_subdivision_point = ON_3dPoint::NanPoint; - vertex_sharpness = 0.0; - } - return vertex_sharpness; -} - -unsigned int ON_SubDVertex::SemisharpEdgeCount() const +unsigned int ON_SubDVertex::SharpEdgeCount( bool bEndCheck ) const { ON_Interval sharpness_range; - return SemisharpEdgeCount(sharpness_range); + return SharpEdgeCount(bEndCheck, sharpness_range); } -unsigned int ON_SubDVertex::SemisharpEdgeCount(ON_Interval& sharpness_range) const +unsigned int ON_SubDVertex::SharpEdgeCount( + bool bEndCheck, + ON_Interval& sharpness_range +) const { - unsigned int semisharp_edge_count = 0; + unsigned int sharp_edge_count = 0; double mins = 0.0; double maxs = 0.0; - if (IsSmooth() && nullptr != m_edges) + if (IsSmoothOrDartOrCrease() && nullptr != m_edges) { for (unsigned short vei = 0; vei < m_edge_count; ++vei) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); - const double s = (nullptr != e) ? e->Sharpness(ON_DBL_QNAN, 0.0, ON_SubDEdge::InfinteSharpness) : 0.0; - if (s > 0.0 && s < ON_SubDEdge::InfinteSharpness) + if (nullptr != e ? e->IsSharp() : false) { - if (0 == semisharp_edge_count) + const double s + = bEndCheck + ? e->EndSharpness(this) + : e->Sharpness().MaximumEndSharpness(); + if (s > 0.0) { - mins = s; - maxs = s; + if (0 == sharp_edge_count) + { + mins = s; + maxs = s; + } + else if (s < mins) + mins = s; + else if (s > maxs) + maxs = s; + ++sharp_edge_count; } - else if (s < mins) - mins = s; - else if (s > maxs) - maxs = s; - ++semisharp_edge_count; } } } sharpness_range.Set(mins, maxs); - return semisharp_edge_count; + return sharp_edge_count; } -double ON_SubDEdge::AverageSharpness( - double sharpness0, - double sharpness1 -) +bool ON_SubDEdge::IsSharp() const { - if (sharpness0 >= 0.0 && sharpness0 <= ON_SubDEdge::InfinteSharpness && sharpness1 >= 0.0 && sharpness1 <= ON_SubDEdge::InfinteSharpness) - { - if (sharpness0 == sharpness1) - return sharpness0; - if (sharpness0 == ON_SubDEdge::InfinteSharpness || sharpness1 == ON_SubDEdge::InfinteSharpness) - return ON_SubDEdge::InfinteSharpness; - if (0.0 == sharpness0) - return sharpness1; - if (0.0 == sharpness1) - return sharpness0; - return ON_SubDEdge::SanitizeSharpness(0.5 * (sharpness0 + sharpness1), false); - } - return 0.0; + return + (ON_SubDEdgeTag::Smooth == m_edge_tag || ON_SubDEdgeTag::SmoothX == m_edge_tag) + && m_sharpness.IsNotZero(); } -bool ON_SubDEdge::IsSemisharp() const +bool ON_SubDEdge::IsSmoothNotSharp() const { - return IsSmooth() && Sharpness() > 0.0; + return + (ON_SubDEdgeTag::Smooth == m_edge_tag || ON_SubDEdgeTag::SmoothX == m_edge_tag) + && (false == m_sharpness.IsNotZero()); +} + +bool ON_SubDEdge::IsSmoothNotXNotSharp() const +{ + return ON_SubDEdgeTag::Smooth == m_edge_tag && (false == m_sharpness.IsNotZero()); } double ON_SubDEdge::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const { - const double edge_sharpness = Sharpness(); - if (edge_sharpness > 0.0) + if (IsSharp()) { + const double s = Sharpness().Average(); sharp_subdivision_point = this->ControlNetCenterPoint(); - return edge_sharpness; + return s; } sharp_subdivision_point = ON_3dPoint::NanPoint; return 0.0; } -double ON_SubDEdge::SubdivideSharpness( - unsigned end_index -) const -{ - double sharpness1 = 0.0; - if (end_index >= 0 && end_index <= 1 && nullptr != m_vertex[end_index] && nullptr != m_vertex[end_index]->m_edges ) - { - const double sharpness0 = Sharpness(); - if (0.0 < sharpness0 && sharpness0 < ON_SubDEdge::InfinteSharpness) - { - if (m_vertex[end_index]->IsSmooth()) - { - double other_sharpness0 = 0.0; - const ON_SubDVertex* v = m_vertex[end_index]; - for (unsigned short vei = 0; vei < v->m_edge_count; ++vei) - { - const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); - if (nullptr == e || this == e) - continue; - const double s = e->Sharpness(); - if (0.0 < s && s < ON_SubDEdge::InfinteSharpness) - { - if (0.0 == other_sharpness0) - { - other_sharpness0 = s; - } - else - { - // There are 3 or more semisharp edges at v; - return ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); - } - } - } - if (0.0 < other_sharpness0 && other_sharpness0 < ON_SubDEdge::InfinteSharpness) - { - // Chaikin's curve subdivision rule [DTK1998] Appendix B - sharpness1 = ON_SubDEdge::SanitizeSharpness(((sharpness0 == other_sharpness0) ? sharpness0 : (0.25 * (other_sharpness0 + 3.0 * sharpness0))) - 1.0, false); - //sharpness1 = ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); - } - else - sharpness1 = ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); - } - else - { - // non-smooth vertex - sharpness1 = ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); - } - } - } - - return sharpness1; -} - -bool ON_SubDEdge::SemisharpEvaluationIsEnabled() -{ - // WARNING: - // This function is temporary and for internal use only. - // It will be removed in the near future. - // Any code referencing this function will unpredictably fail in the near future. - // This is primative and is not thread safe. - return ON_SubDEdge::m_bEnableSemisharpEvaluation; -} - -void ON_SubDEdge::SetEnableSemisharpEvaluation(bool bEnableSemisharpEvaluation) -{ - // WARNING: - // This function is temporary and for internal use only. - // It will be removed in the near future. - // Any code referencing this function will unpredictably fail in the near future. - // This is primative and is not thread safe. - ON_SubDEdge::m_bEnableSemisharpEvaluation = bEnableSemisharpEvaluation ? true : false; -} - -double ON_SubDEdge::EvaluationSharpness(ON_3dPoint& sharp_subdivision_point) const -{ - // WARNING: - // This function is temporary and for internal use only. - // It will be removed in the near future. - // Any code referencing this function will unpredictably fail in the near future. - // This is primative and is not thread safe. - double edge_sharpness; - if (ON_SubDEdge::SemisharpEvaluationIsEnabled()) - { - edge_sharpness = GetSharpSubdivisionPoint(sharp_subdivision_point); - } - else - { - sharp_subdivision_point = ON_3dPoint::NanPoint; - edge_sharpness = 0.0; - } - return edge_sharpness; -} - -// This global bool is temporary and is used to determine if subdivision and evaluation code -// pays attention to sharpness. This global bool will be removed in the near future. -// Any code referencing this variable will unpredictably fail in the near future. -bool ON_SubDEdge::m_bEnableSemisharpEvaluation = false; -#endif - bool ON_SubDVertex::IsSingleSectorVertex() const { const bool bIsCreaseOrCorner = IsCreaseOrCorner(); @@ -4587,7 +4889,11 @@ unsigned int ON_SubDEdge::EdgeAttributes() const edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Interior; if (IsSmooth()) + { edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSmooth; + if ( IsSharp() ) + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSharp; + } else if (IsCrease()) edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorCrease; @@ -4772,9 +5078,7 @@ void ON_SubDEdge::CopyFrom( m_edge_tag = src->m_edge_tag; -#if defined(ON_SUBD_SHARP_EDGES) m_sharpness = src->m_sharpness; -#endif unsigned int end0 = bReverseEdge ? 1 : 0; @@ -5036,6 +5340,14 @@ unsigned int ON_SubDEdge::VertexArrayIndex(const ON_SubDVertex * v) const return ON_UNSET_UINT_INDEX; } +const ON_SubDFace* ON_SubDEdgePtr::NeighborFace( + const ON_SubDFace* face, + bool bStopAtCrease +) const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->NeighborFace(face, bStopAtCrease) : nullptr; +} const ON_SubDFace* ON_SubDEdge::NeighborFace( const ON_SubDFace* face, @@ -6084,6 +6396,31 @@ bool ON_SubDFace::ReplaceEdgeInArray( return false; } +bool ON_SubDFace::ReplaceEdgeInArray( + unsigned int fei0, + ON_SubDEdge* edge_to_remove, + ON_SubDEdgePtr edgeptr_to_insert +) +{ + const unsigned int face_edge_count = m_edge_count; + ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + if (fei >= fei0 && edge_to_remove == eptr->Edge() ) + { + *eptr = edgeptr_to_insert; + return true; + } + } + return false; +} + bool ON_SubDFace::RemoveEdgeFromArray( unsigned int i, @@ -7075,7 +7412,7 @@ static bool IsValidSubDEdge( ON_Internal_DamagedMarker dm(edge); - const ON_SubDVertex* edge_vertex[2] = { 0 }; + const ON_SubDVertex* edge_vertex[2] = {}; for (unsigned int i = 0; i < 2; i++) { const ON_SubDVertex* vertex = edge->Vertex(i); @@ -7590,39 +7927,37 @@ unsigned int ON_SubD::DumpTopology( ); } -#if defined(ON_SUBD_SHARP_EDGES) - ON_Interval subd_semisharp_range(0.0, 0.0); - const unsigned int subd_semisharp_edge_count = SemisharpEdgeCount(subd_semisharp_range); - if (subd_semisharp_edge_count > 0) + ON_SubDEdgeSharpness subd_sharpness_range = ON_SubDEdgeSharpness::Zero; + const unsigned int subd_sharp_edge_count = SharpEdgeCount(subd_sharpness_range); + if (subd_sharp_edge_count > 0) { text_log.PushIndent(); - if (subd_semisharp_range[0] == subd_semisharp_range[1]) + if ( subd_sharpness_range.IsConstant() ) { - if (1 == subd_semisharp_edge_count) + if (1 == subd_sharp_edge_count) text_log.Print( - L"1 semisharp edge with sharpness = %g.\n", - subd_semisharp_range[0] + L"1 sharp edge with end sharpness = %g.\n", + subd_sharpness_range[0] ); else text_log.Print( - L"%u semisharp edges with sharpness = %g.\n", - subd_semisharp_edge_count, - subd_semisharp_range[0] + L"%u sharp edges with end sharpness = %g.\n", + subd_sharp_edge_count, + subd_sharpness_range[0] ); } else { text_log.Print( - L"%u semisharp edges with sharpnesses from %g to %g.\n", - subd_semisharp_edge_count, - subd_semisharp_range[0], - subd_semisharp_range[1] + L"%u sharp edges with end sharpnesses from %g to %g.\n", + subd_sharp_edge_count, + subd_sharpness_range[0], + subd_sharpness_range[1] ); } text_log.PopIndent(); } -#endif const ON_SubDHashType htype[] = { ON_SubDHashType::Topology, @@ -7972,7 +8307,14 @@ static void Internal_AccumulateEdgeHash( if (ON_SubDHashType::TopologyAndEdgeCreases == hash_type || ON_SubDHashType::Geometry == hash_type) { // Changing edge crease/smooth attributes often changes the regions used in face packing and exploding. - sha1.AccumulateBool(e->IsCrease()); + const bool bIsCrease = e->IsCrease(); + sha1.AccumulateBool(bIsCrease); + if (false == bIsCrease && ON_SubDHashType::Geometry == hash_type && e->IsSharp()) + { + const ON_SubDEdgeSharpness s = e->Sharpness(); + const double a[2] = { s[0] ,s[1] }; + sha1.AccumulateDoubleArray(2, a); + } } } @@ -10264,16 +10606,41 @@ class ON_SubDEdge* ON_SubD::AddEdge( ); } + +class ON_SubDEdge* ON_SubD::AddEdge( + ON_SubDEdgeTag edge_tag, + ON_SubDVertex* v0, + ON_SubDVertex* v1, + ON_SubDEdgeSharpness sharpness +) +{ + // NO automatic edge tag setting - causes more problems than it helps. + // Users can call ON_SubD::EdgeTagFromContext() if they want to + // preset the edge tag based on context. + + class ON_SubDEdge* e = AddEdgeWithSectorCoefficients( + edge_tag, + v0, + ON_SubDSectorType::UnsetSectorCoefficient, + v1, + ON_SubDSectorType::UnsetSectorCoefficient + ); + if (nullptr != e && e->IsSmooth()) + e->SetSharpnessForExperts(sharpness); + return e; +} + + ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( - ON_SubDEdgeTag edge_tag, - class ON_SubDVertex* v0, - double v0_sector_coefficient, - class ON_SubDVertex* v1, - double v1_sector_coefficient - ) + ON_SubDEdgeTag edge_tag, + class ON_SubDVertex* v0, + double v0_sector_coefficient, + class ON_SubDVertex* v1, + double v1_sector_coefficient +) { ON_SubDimple* subdimple = SubDimple(true); - if (nullptr != subdimple) + if (nullptr != subdimple) return subdimple->AddEdge(edge_tag, v0, v0_sector_coefficient, v1, v1_sector_coefficient); return ON_SUBD_RETURN_ERROR(nullptr); } @@ -12180,23 +12547,16 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ if (nullptr == faces[0] || nullptr == faces[1]) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); -#if defined(ON_SUBD_SHARP_EDGES) - // When the semisharp code is working, change EvaluationSharpness() to Sharpness(). - // Calling EvaluationSharpness() is a temporary hack to make - // the TestSubDSharpSubdivide command work but not break all - // the ordinary evaluation code that relies heavily on - // ordinary subdivision points. ON_3dPoint sharp_subdivision_point; - const double edge_sharpness = this->EvaluationSharpness(sharp_subdivision_point); + const double edge_sharpness = this->GetSharpSubdivisionPoint(sharp_subdivision_point); if (edge_sharpness >= 1.0) { - // Semi-sharp crease with sharpness >= 1 at current level + // sharp crease vertex with sharpness >= 1 at current level subdivision_point[0] = sharp_subdivision_point.x; subdivision_point[1] = sharp_subdivision_point.y; subdivision_point[2] = sharp_subdivision_point.z; return true; } -#endif // for each neighbor face, sum the vertex locations that are not on this edge double facePsum[2][3]; @@ -12250,17 +12610,14 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ subdivision_point[0] = EP[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]); subdivision_point[1] = EP[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]); subdivision_point[2] = EP[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]); - -#if defined(ON_SUBD_SHARP_EDGES) if (edge_sharpness > 0.0) { - // Semi-sharp crease with 0 < sharpness < 1 at the current level + // sharp crease vertex with 0 < sharpness < 1 at the current level const double a = 1.0 - edge_sharpness; subdivision_point[0] = a * subdivision_point[0] + edge_sharpness * sharp_subdivision_point.x; subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.y; subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.z; } -#endif return true; } @@ -12275,16 +12632,14 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ subdivision_point[0] = EP[0] + (0.5*edgePsum[0] + facePsum[0][0] + facePsum[1][0]) / 12.0; subdivision_point[1] = EP[1] + (0.5*edgePsum[1] + facePsum[0][1] + facePsum[1][1]) / 12.0; subdivision_point[2] = EP[2] + (0.5*edgePsum[2] + facePsum[0][2] + facePsum[1][2]) / 12.0; -#if defined(ON_SUBD_SHARP_EDGES) if (edge_sharpness > 0.0) { - // Semi-sharp crease with 0 < sharpness < 1 at the current level + // sharp crease vertex with 0 < sharpness < 1 at the current level const double a = 1.0 - edge_sharpness; subdivision_point[0] = a * subdivision_point[0] + edge_sharpness * sharp_subdivision_point.x; subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.y; subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.z; } -#endif return true; } @@ -12302,16 +12657,14 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ subdivision_point[0] = EP[0] + x * edgePsum[0] + facePsum[0][0] / f0 + facePsum[1][0] / f1; subdivision_point[1] = EP[1] + x * edgePsum[1] + facePsum[0][1] / f0 + facePsum[1][1] / f1; subdivision_point[2] = EP[2] + x * edgePsum[2] + facePsum[0][2] / f0 + facePsum[1][2] / f1; -#if defined(ON_SUBD_SHARP_EDGES) if (edge_sharpness > 0.0) { - // Semi-sharp crease with 0 < sharpness < 1 at the current level + // sharp crease vertex with 0 < sharpness < 1 at the current level const double a = 1.0 - edge_sharpness; subdivision_point[0] = a * subdivision_point[0] + edge_sharpness * sharp_subdivision_point.x; subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.x; subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.x; } -#endif return true; } @@ -12502,6 +12855,26 @@ int FACE_AND_FACE_POINT::CompareFacePointer(const void* a, const void* b) return 0; } +const ON_3dPoint ON_SubDSectorSurfacePoint::Point() const +{ + return ON_3dPoint(m_limitP); +} + +const ON_3dVector ON_SubDSectorSurfacePoint::Normal() const +{ + return ON_3dPoint(m_limitN); +} + +const ON_3dVector ON_SubDSectorSurfacePoint::Tangent(int tangent_index) const +{ + if (0 == tangent_index) + return ON_3dPoint(m_limitT1); + if (1 == tangent_index) + return ON_3dPoint(m_limitT2); + return ON_3dVector::ZeroVector; +} + + bool ON_SubDSectorSurfacePoint::IsUnset() const { return (m_limitP[0] == ON_UNSET_VALUE); @@ -12598,7 +12971,6 @@ void ON_SubDVertex::CopyFrom( m_P[1] = src->m_P[1]; m_P[2] = src->m_P[2]; - if (bCopyLimitPointList) { if ( src->SurfacePointIsSet() ) @@ -12699,14 +13071,8 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( const double* vertexP = vertex->m_P; -#if defined(ON_SUBD_SHARP_EDGES) - // When the semisharp code is working, change EvaluationSharpness() to Sharpness(). - // Calling EvaluationSharpness() is a temporary hack to make - // the TestSubDSharpSubdivide command work but not break all - // the ordinary evaluation code that relies heavily on - // ordinary subdivision points. ON_3dPoint sharp_subdivision_point; - const double vertex_sharpness = vertex->EvaluationSharpness(sharp_subdivision_point); + const double vertex_sharpness = vertex->GetSharpSubdivisionPoint(sharp_subdivision_point); if (vertex_sharpness >= 1.0) { vertex_point[0] = sharp_subdivision_point.x; @@ -12714,13 +13080,11 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( vertex_point[2] = sharp_subdivision_point.z; return true; } -#endif - // It is critical to use the centroids of the neighboring faces // in this step because the number of edges in each face's // boundary may not be constant. - double facePsum[3] = { 0 }; + double facePsum[3] = {}; const ON_SubDFace*const* vertex_faces = vertex->m_faces; for (unsigned int i = 0; i < n; i++) { @@ -12742,7 +13106,7 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( facePsum[2] += vertexP[2]; } - double edgePsum[3] = { 0 }; + double edgePsum[3] = {}; class ON_SubDEdgePtr* edges = vertex->m_edges; for (unsigned int i = 0; i < n; i++) { @@ -12771,8 +13135,6 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( vertex_point[1] = v_weight*vertexP[1] + ef_weight*(edgePsum[1] + facePsum[1]); vertex_point[2] = v_weight*vertexP[2] + ef_weight*(edgePsum[2] + facePsum[2]); - -#if defined(ON_SUBD_SHARP_EDGES) if (vertex_sharpness > 0.0) { // 0 < vertex_sharpness < 1 @@ -12782,7 +13144,6 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( vertex_point[2] = a * vertex_point[2] + vertex_sharpness * sharp_subdivision_point.z; return true; } -#endif return true; } @@ -12807,7 +13168,7 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( if (n < minimum_n || n != vertex->m_face_count || nullptr == vertex->m_faces) return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); - double facePsum[3] = { 0 }; + double facePsum[3] = {}; const ON_SubDFace*const* vertex_faces = vertex->m_faces; const ON_SubDFace* face = vertex_faces[0]; @@ -12872,14 +13233,8 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } -#if defined(ON_SUBD_SHARP_EDGES) - // When the semisharp code is working, change EvaluationSharpness() to Sharpness(). - // Calling EvaluationSharpness() is a temporary hack to make - // the TestSubDSharpSubdivide command work but not break all - // the ordinary evaluation code that relies heavily on - // ordinary subdivision points. ON_3dPoint sharp_subdivision_point = ON_3dPoint::NanPoint; - const double vertex_sharpness = vertex->EvaluationSharpness(sharp_subdivision_point); + const double vertex_sharpness = vertex->GetSharpSubdivisionPoint(sharp_subdivision_point); if (vertex_sharpness >= 1.0) { // use corner subdivision point @@ -12888,9 +13243,8 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( vertex_point[2] = sharp_subdivision_point.z; return true; } -#endif - double edgePsum[3] = { 0 }; + double edgePsum[3] = {}; class ON_SubDEdgePtr* edges = vertex->m_edges; for (unsigned int i = 0; i < n; i++) { @@ -12933,7 +13287,6 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2]; } -#if defined(ON_SUBD_SHARP_EDGES) if (vertex_sharpness > 0.0) { // 0 < vertex_sharpness < 1 @@ -12943,7 +13296,6 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( vertex_point[2] = a * vertex_point[2] + vertex_sharpness * sharp_subdivision_point.z; return true; } -#endif return true; } @@ -13002,9 +13354,7 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin class ON_SubDEdgePtr* edges = m_edges; const ON_SubDVertex* edge0_vertex = nullptr; const ON_SubDVertex* edge1_vertex = nullptr; -#if defined(ON_SUBD_SHARP_EDGES) - double max_sharpness = 0.0; -#endif + bool bSharpEdges = false; for (unsigned int i = 0; i < n; i++) { @@ -13017,11 +13367,8 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin if (ON_SubDEdgeTag::Crease != edge->m_edge_tag) { -#if defined(ON_SUBD_SHARP_EDGES) - const double s = edge->Sharpness(0.0, 0.0, 0.0); - if (s > max_sharpness) - max_sharpness = s; -#endif + if (false == bSharpEdges) + bSharpEdges = edge->IsSharp(); continue; } @@ -13068,50 +13415,49 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin if (nullptr != edge0_vertex && nullptr != edge1_vertex) { // We found the two crease edges that share this crease vertex. - // (The parenthesis around the edgeP sum is to insure this result - // is independent of the order of the edges.) - vertexP = m_P; - -#if defined(ON_SUBD_SHARP_EDGES) - if (ON_SubDEdge::SemisharpEvaluationIsEnabled() && max_sharpness >= 1.0) + ON_3dPoint sharp_subdivision_point = ON_3dPoint::NanPoint; + const double vertex_sharpness + = bSharpEdges + ? this->GetSharpSubdivisionPoint(sharp_subdivision_point) + : 0.0; + if (vertex_sharpness >= 1.0) { - // 2 creases and a semisharp edge with sharpness >= 1 + // 2 creases and a sharp edge with sharpness >= 1 // This modification is not part of the DKT1998 Pixar algorithm. - // It is required to get semisharp creases to evenly end at + // It is required to get sharp edges to evenly end at // ordinary boundaries. It does break crease edge evaluation relying // only on boundary settings, but at this time (Dale Lear Nov 2022), // I'm guessing it is the best option out of 2 nonideal choices. - subdivision_point[0] = vertexP[0]; - subdivision_point[1] = vertexP[1]; - subdivision_point[2] = vertexP[2]; + subdivision_point[0] = sharp_subdivision_point.x; + subdivision_point[1] = sharp_subdivision_point.y; + subdivision_point[2] = sharp_subdivision_point.z; return true; } -#endif + + // (The parenthesis around the edgeP sum is to insure this result + // is independent of the order of the edges.) + vertexP = m_P; const double* edgeP[2] = { edge0_vertex->m_P, edge1_vertex->m_P }; subdivision_point[0] = (vertexP[0] * 6.0 + (edgeP[0][0] + edgeP[1][0])) * 0.125; subdivision_point[1] = (vertexP[1] * 6.0 + (edgeP[0][1] + edgeP[1][1])) * 0.125; subdivision_point[2] = (vertexP[2] * 6.0 + (edgeP[0][2] + edgeP[1][2])) * 0.125; - -#if defined(ON_SUBD_SHARP_EDGES) - if (ON_SubDEdge::SemisharpEvaluationIsEnabled() && max_sharpness > 0.0) + if (vertex_sharpness > 0.0) { - // 2 creases and a semisharp edge with 0 < sharpness < 1 + // 2 creases and a sharp edge with 0 < sharpness < 1 // // This modification is not part of the DKT1998 Pixar algorithm. - // It is required to get semisharp creases to evenly end at - // ordinary boundaries. It does break crease edge evaluation relying + // It is required to get sharp edges to evenly end at ordinary boundaries. + // It does break crease edge evaluation relying // only on boundary settings, but at this time (Dale Lear Nov 2022), // I'm guessing it is the best option out of 2 nonideal choices. // - const double a = 1.0 - max_sharpness; - subdivision_point[0] = a * subdivision_point[0] + max_sharpness * vertexP[0]; - subdivision_point[1] = a * subdivision_point[1] + max_sharpness * vertexP[1]; - subdivision_point[2] = a * subdivision_point[2] + max_sharpness * vertexP[2]; + const double a = 1.0 - vertex_sharpness; + subdivision_point[0] = a * subdivision_point[0] + vertex_sharpness * sharp_subdivision_point.x; + subdivision_point[1] = a * subdivision_point[1] + vertex_sharpness * sharp_subdivision_point.y; + subdivision_point[2] = a * subdivision_point[2] + vertex_sharpness * sharp_subdivision_point.z; return true; } -#endif - return true; } @@ -13366,11 +13712,9 @@ bool ON_SubDimple::LocalSubdivide( // split edges for (unsigned edex = 0; edex < edge_count; ++edex) { - ON_SubDEdge* e = edges[edex]; + ON_SubDEdge* e = edges[edex]; -#if defined(ON_SUBD_SHARP_EDGES) - const double sharpness = e->Sharpness(); -#endif + const ON_SubDEdgeSharpness e_sharpness = e->Sharpness(); e->EdgeModifiedNofification(); const ON_SubDEdge* new_edge = SplitEdge(e, edge_points[edex]); @@ -13379,19 +13723,16 @@ bool ON_SubDimple::LocalSubdivide( new_edge->m_status.ClearRuntimeMark(); e->m_status.SetRuntimeMark(); -#if defined(ON_SUBD_SHARP_EDGES) // LOCAL subdivide is more of an add control points to an existing level // as opposed to a global subdivision level operation. It will be common - // for an input semisharp edge chain crease to have some of its edges - // subdivided and others remain unchanged. + // for the input to include some but not all sharp edges. // Dale Lear's best guess on Nov 9, 2022 is that - // resusing the orginal sharpness is the best way to maximize overall + // reusing the orginal sharpness is the best way to maximize overall // user happiness. This makes some sense because the level of the edges // is not changing in a LOCAL subdivide. // It is a near certainty that some users will not like this in some cases. - const_cast(e)->SetSharpness(sharpness); - const_cast(new_edge)->SetSharpness(sharpness); -#endif + const_cast(e)->SetSharpnessForExperts(ON_SubDEdgeSharpness::FromInterval(e_sharpness[0], e_sharpness.Average())); + const_cast(new_edge)->SetSharpnessForExperts(ON_SubDEdgeSharpness::FromInterval(e_sharpness.Average(), e_sharpness[1])); } ON_SimpleArray fbdry(32); @@ -13521,7 +13862,7 @@ unsigned int ON_SubDimple::GlobalSubdivide() this->ChangeGeometryContentSerialNumber(bChangePreservesSymmetry); // Add face points - unsigned int max_pack_id{ 0 }; + unsigned int max_pack_id = 0U; for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) { if (f0->PackId() > max_pack_id) max_pack_id = f0->PackId(); @@ -13601,30 +13942,19 @@ unsigned int ON_SubDimple::GlobalSubdivide() edge_tag = ON_SubDEdgeTag::Smooth; } -#if defined(ON_SUBD_SHARP_EDGES) - class ON_SubDEdge* e10 = -#endif - AddEdge(edge_tag, end_vertex[0], w[0], mid_vertex, 0.0); - -#if defined(ON_SUBD_SHARP_EDGES) - class ON_SubDEdge* e11 = -#endif - AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); - -#if defined(ON_SUBD_SHARP_EDGES) - const double e0_sharpness - = (ON_SubDEdgeTag::Smooth == edge_tag) - ? e0->Sharpness() - : 0.0; - if (e0_sharpness > 0.0 && e0_sharpness < ON_SubDEdge::InfinteSharpness) + class ON_SubDEdge* e10 = AddEdge(edge_tag, end_vertex[0], w[0], mid_vertex, 0.0); + class ON_SubDEdge* e11 = AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); + if ( e0->IsSharp() ) { - if (nullptr != e10) - e10->SetSharpness(e0->SubdivideSharpness(0)); - if (nullptr != e11) - e11->SetSharpness(e0->SubdivideSharpness(1)); + const ON_SubDEdgeSharpness e0_sharpness = e0->Sharpness(); + if (e0_sharpness.IsNotZero()) + { + if (nullptr != e10) + e10->SetSharpnessForExperts(e0_sharpness.Subdivided(0)); + if (nullptr != e11) + e11->SetSharpnessForExperts(e0_sharpness.Subdivided(1)); + } } -#endif - } for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) @@ -15410,6 +15740,123 @@ const ON_SubDEdgePtr ON_SubD::SplitEdge( return e1ptr; } +const ON_SubDEdge* ON_SubDimple::SpinEdge(ON_SubDEdge* edge, + bool spin_clockwise) { + if (edge == nullptr || !edge->HasInteriorEdgeTopology(true) || + edge->SubdivisionLevel() >= m_levels.UnsignedCount() || + m_levels.UnsignedCount() < 1) { + return ON_SUBD_RETURN_ERROR(nullptr); + } + + unsigned int level0_index{edge->SubdivisionLevel()}; + + if (m_levels[level0_index] == nullptr) return ON_SUBD_RETURN_ERROR(nullptr); + + ClearHigherSubdivisionLevels(level0_index + 1); + if (level0_index + 1 != m_levels.UnsignedCount()) { + return ON_SUBD_RETURN_ERROR(nullptr); + } + + m_active_level = m_levels[level0_index]; + if (m_active_level == nullptr || m_active_level->m_face_count == 0) { + return ON_SUBD_RETURN_ERROR(nullptr); + } + + ON_SubDVertex* vertices[2]{const_cast(edge->Vertex(0)), + const_cast(edge->Vertex(1))}; + + const bool swap_faces{(edge->FaceDirection(0) == 1) != spin_clockwise}; + ON_SubDFace* faces[2]{ + const_cast(swap_faces ? edge->Face(1) : edge->Face(0)), + const_cast(swap_faces ? edge->Face(0) : edge->Face(1))}; + + const unsigned face_edge_indices[2]{faces[0]->EdgeArrayIndex(edge), + faces[1]->EdgeArrayIndex(edge)}; + if (face_edge_indices[0] == ON_UNSET_UINT_INDEX || + face_edge_indices[1] == ON_UNSET_UINT_INDEX) { + return ON_SUBD_RETURN_ERROR(nullptr); + } + ON__UINT_PTR new_directions[2]{faces[0]->EdgeDirection(face_edge_indices[0]), + faces[1]->EdgeDirection(face_edge_indices[1])}; + + const ON_SubDEdgePtr adjacent_edges[2]{ + spin_clockwise + ? faces[1]->VertexEdgePair(vertices[0]).First().EdgePtr() + : faces[1]->VertexEdgePair(vertices[0]).Second().EdgePtr(), + spin_clockwise + ? faces[0]->VertexEdgePair(vertices[1]).First().EdgePtr() + : faces[0]->VertexEdgePair(vertices[1]).Second().EdgePtr()}; + + ON_SubDVertex* new_vertices[2]{ + const_cast( + adjacent_edges[0].RelativeVertex(spin_clockwise ? 0 : 1)), + const_cast( + adjacent_edges[1].RelativeVertex(spin_clockwise ? 0 : 1))}; + if (new_vertices[0] == nullptr || new_vertices[1] == nullptr) { + return ON_SUBD_RETURN_ERROR(nullptr); + } + + ON_SubDEdge* new_edge{edge}; + // Also calls vertices[i].VertexNotifiedModification() + new_edge->EdgeModifiedNofification(); + + for (unsigned int i = 0; i < 2; i++) { + ON_SubDVertex* v = new_vertices[i]; + edge->m_vertex[i] = v; + + bool rc{true}; + rc = rc && vertices[i]->RemoveEdgeFromArray(edge); + if (!m_heap.GrowVertexEdgeArrayByOne(v)) { + v->m_status.SetDamagedState(true); + return ON_SUBD_RETURN_ERROR(nullptr); + } + v->m_edges[v->m_edge_count++] = + ON_SubDEdgePtr::Create(new_edge, (ON__UINT_PTR)i); + + rc = rc && vertices[i]->RemoveFaceFromArray(faces[1 - i]); + if (!m_heap.GrowVertexFaceArrayByOne(v)) { + v->m_status.SetDamagedState(true); + return ON_SUBD_RETURN_ERROR(nullptr); + } + v->m_faces[v->m_face_count++] = faces[i]; + v->VertexModifiedNofification(); + + rc = rc && + (adjacent_edges[i].Edge()->ReplaceFaceInArray(faces[1 - i], faces[i]) < + adjacent_edges[i].EdgeFaceCount()); + + rc = rc && faces[i]->ReplaceEdgeInArray(0, edge, adjacent_edges[i]); + rc = rc && faces[i]->ReplaceEdgeInArray( + 0, adjacent_edges[1 - i].Edge(), + ON_SubDEdgePtr::Create(new_edge, new_directions[i])); + faces[i]->FaceModifiedNofification(); + if (!rc) return ON_SUBD_RETURN_ERROR(nullptr); + } + if (new_edge == nullptr) return ON_SUBD_RETURN_ERROR(nullptr); + + if (nullptr != m_active_level) { + m_active_level->UpdateAllTagsAndSectorCoefficients(true); + } + + ChangeGeometryContentSerialNumber(false); + + return new_edge; +} + +const ON_SubDEdge* ON_SubD::SpinEdge(ON_SubDEdge* edge, bool spin_clockwise) { + ON_SubDimple* subdimple = SubDimple(false); + if (nullptr == subdimple) return ON_SUBD_RETURN_ERROR(nullptr); + return subdimple->SpinEdge(edge, spin_clockwise); +} + +const ON_SubDEdgePtr ON_SubD::SpinEdge(ON_SubDEdgePtr eptr, + bool spin_clockwise) { + ON_SubDEdge* edge = eptr.Edge(); + if (nullptr == edge) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + const ON_SubDEdge* new_edge{SpinEdge(edge, spin_clockwise)}; + if (nullptr == new_edge) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + return ON_SubDEdgePtr::Create(new_edge, eptr.EdgeDirection()); +} const ON_SubDEdge * ON_SubD::SplitFace( ON_SubDFace * face, @@ -16163,8 +16610,7 @@ bool ON_SubDLevel::CopyEvaluationCacheForExperts( ON_SubDHeap& this_heap, const return ON_SUBD_RETURN_ERROR(false); // The built in fragment cache always has adaptive ON_SubDDisplayParameters::DefaultDensity - const unsigned subd_display_density = ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount(ON_SubDDisplayParameters::DefaultDensity,m_face_count); - + unsigned subd_display_density = ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount(ON_SubDDisplayParameters::DefaultDensity,m_face_count); const unsigned this_level_index = this->m_level_index; const unsigned src_level_index = src.m_level_index; @@ -20133,6 +20579,14 @@ unsigned int ON_SubD::SetEdgeTags( ); } +unsigned int ON_SubD::SetEdgeTags( + const ON_SimpleArray& cptr_list, + ON_SubDEdgeTag edge_tag +) +{ + return this->SetEdgeTags(cptr_list.Array(), cptr_list.Count(), edge_tag); +} + unsigned int ON_SubD::SetEdgeTags( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, @@ -20158,24 +20612,24 @@ unsigned int ON_SubD::SetEdgeTags( ON_SubDEdge* edge = cptr_list[i].Edge(); if (nullptr == edge) continue; - const bool bIsSemisharp = -#if defined(ON_SUBD_SHARP_EDGES) - edge->IsSemisharp() -#else - false -#endif - ; - if (bChangeToSmooth == edge->IsSmooth() && false == bIsSemisharp) - continue; - if (bChangeToSmooth && 2 != edge->FaceCount()) - continue; + if (bChangeToSmooth) + { + if ( edge->IsSmoothNotSharp()) + continue; // already smooth + if (2 != edge->FaceCount()) + continue; // can't be smooth + } + else + { + if (edge->IsCrease()) + continue; // already a crease + } edge->EdgeModifiedNofification(); changed_edge_count++; edge->m_edge_tag = edge_tag; - if (bChangeToSmooth && bIsSemisharp) - edge->SetSharpness(0.0); + edge->ClearSharpnessForExperts(); edge->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) { @@ -20262,18 +20716,9 @@ unsigned int ON_SubD::RemoveAllCreases() ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { - const bool bIsSemisharp = -#if defined(ON_SUBD_SHARP_EDGES) - e->IsSemisharp() -#else - false -#endif - ; - if ( (false == e->IsCrease() && false == bIsSemisharp) || 2 != e->m_face_count) + if ( (false == e->IsCrease() && false == e->IsSharp()) || 2 != e->m_face_count) continue; -#if defined(ON_SUBD_SHARP_EDGES) - const_cast(e)->SetSharpness(0.0); -#endif + const_cast(e)->ClearSharpnessForExperts(); const_cast(e)->m_edge_tag = ON_SubDEdgeTag::Smooth; e->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) @@ -21794,7 +22239,7 @@ static bool Internal_VerifyEdgeChain( const ON_SimpleArray& chain, ON_2udex& chaindex, unsigned* edge_count = nullptr) { bool valid_chain{true}; - unsigned valid_edge_count{0}; + unsigned valid_edge_count = 0U; const unsigned jmax{chain.UnsignedCount()}; ON_SubDEdgePtr eptr = chain[chaindex.i]; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); @@ -21843,7 +22288,7 @@ static bool Internal_CountAndVerifyEdgeChains( const ON_SimpleArray& sorted_edges, unsigned* chain_count = nullptr) { if (chain_count != nullptr && chain_count == 0) return false; - unsigned valid_chain_count{0}; + unsigned valid_chain_count = 0U; bool valid_chains{false}; const unsigned edge_count{sorted_edges.UnsignedCount()}; ON_2udex chaindex{0, edge_count}; diff --git a/opennurbs_subd.h b/opennurbs_subd.h index 00fe761e..9437be1a 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -20,8 +20,6 @@ #if !defined(OPENNURBS_SUBD_INC_) #define OPENNURBS_SUBD_INC_ - - /// /// ON_SubDGetControlNetMeshPriority specifies what type of ON_SubD information /// is most important to transfer to the ON_Mesh. @@ -238,16 +236,237 @@ enum class ON_SubDHashType : unsigned char }; #pragma endregion -ON_DECL -ON_SubDHashType ON_SubDHashTypeFromUnsigned( - unsigned int subd_hash_type_as_unsigned -); +/// +/// ON_SubDHash provides a simple way to save a SubD's vertex, edge, and face SHA1 hashes. +/// Typically it is used when a calculation needs to know if the current SubD has is geometrically +/// identical to a previous SubD. When speed is not important, comparing the current value of +/// ON_SubD::GeometryHash() to a previously save value of ON_SubD::GeometryHash() is functionally +/// identical but typically much slower when the SubDs are different. +/// +class ON_CLASS ON_SubDEdgeSharpness +{ +public: + ON_SubDEdgeSharpness() = default; + ~ON_SubDEdgeSharpness() = default; + ON_SubDEdgeSharpness(const ON_SubDEdgeSharpness&) = default; + ON_SubDEdgeSharpness& operator=(const ON_SubDEdgeSharpness&) = default; + +public: + /// + /// An edge sharpness with contant value 0.0. + /// + static const ON_SubDEdgeSharpness Zero; + + /// + /// An edge sharpness with both end values = ON_DBL_QNAN. + /// + static const ON_SubDEdgeSharpness Nan; + + /// True if the sharpness value is valid and contant. + bool IsConstant() const; + + /// True if the sharpness value is valid and variable. + bool IsVariable() const; + + /// True if the sharpness valid is zero. + bool IsZero() const; + + /// True if the sharpness value is valid either end sharpness value is not zero. + bool IsNotZero() const; + + /// True if both end sharpness values are >= 0 and <= ON_SubDEdgeSharpness::Maximum. + bool IsValid() const; + + /// True if IsValid() is false. + bool IsNotValid() const; + + /// + /// Create a constant ON_SubDEdgeSharpness; + /// + /// 0 <= sharpness <= ON_SubDEdgeSharpness::Maximum + /// + /// If the input values is valid, an ON_SubDEdgeSharpness + /// with constant value sharpness is returned. + /// If the input vaue is a nan, ON_SubDEdgeSharpness Nan is returned. + /// Otherwise ON_SubDEdgeSharpness::Nan is returned. + /// + static const ON_SubDEdgeSharpness FromConstant(double sharpness); + + /// + /// Create a variable ON_SubDEdgeSharpness; + /// + /// 0 <= sharpness0 <= ON_SubDEdgeSharpness::Maximum + /// 0 <= sharpness1 <= ON_SubDEdgeSharpness::Maximum + /// + /// If both input values are valid, an edge sharpness + /// with start value sharpness0 and end value sharpness1 is returned. + /// If either end value is a nan, ON_SubDEdgeSharpness Nan is returned. + /// Otherwise ON_SubDEdgeSharpness::Nan is returned. + /// + static const ON_SubDEdgeSharpness FromInterval(double sharpness0, double sharpness1); + + /// + /// Create a variable ON_SubDEdgeSharpness; + /// + /// 0 <= sharpness0 <= ON_SubDEdgeSharpness::Maximum + /// + /// If the interval's values are valid, an edge sharpness + /// with start value sharpness_interval[0] and end value sharpness_interval[1] is returned. + /// If either end value is a nan, ON_SubDEdgeSharpness Nan is returned. + /// Otherwise ON_SubDEdgeSharpness::Nan is returned. + /// + static const ON_SubDEdgeSharpness FromInterval(const class ON_Interval& sharpness_interval); + + /// + /// Return a sharpness interval that is the union of the nonzero input sharpness intervals. + /// + /// + /// + /// + /// If a or b is not zero, then the returned interval is the union of the nonzero + /// input intervals. Otherwise ON_SubDEdgeSharpness::Zero is returned. + /// The returned sharpenss always has sharpness[0] <= sharpness[1]. + /// + static const ON_SubDEdgeSharpness Union( + const ON_SubDEdgeSharpness& a, + const ON_SubDEdgeSharpness& b + ); + + /// + /// Sharpness value for a subdivided edge. + /// + /// + /// Subdivided sharpness or ON_SubDEdgeSharpness::Zero if index is out of range. + const ON_SubDEdgeSharpness Subdivided(int end_index) const; + + const ON_SubDEdgeSharpness Reversed() const; + + /// + /// Convert a user facing slider value to a SubD edge end sharpness value. + /// + /// Non empty slider domain. (Often ON_Interval::ZeroToOne.) + /// A value in the slider_domain. slider_domain[0] returns 0.0. slider_domain[1] returns ON_SubDEdgeSharpness::Maximum. + /// Value to return if the input is not valid. + /// The slider value converted to an ON_SubDEdge sharpness value. + static double SharpnessFromSliderValue( + ON_Interval slider_domain, + double slider_value, + double invalid_input_result + ); + + /// + /// Convert a user facing slider value to a SubD edge end sharpness value. + /// + /// Non empty slider domain. (Often ON_Interval::ZeroToOne.) + /// 0 <= normalized_slider_value <= 1. + /// Value to return if the input is not valid. + /// + /// If 0 <= normalized_slider_value <= 1, the normalized slider value converted to an ON_SubDEdge sharpness value + /// from 0 to ON_SubDEdgeSharpness::Maximum. Otherwise, ON_DBL_QNAN is returned. + /// + static double SharpnessFromNormalizedValue( + double normalized_slider_value + ); + + /// + /// Get the edge sharpness at the start or end. + /// + /// + /// EndSharpness(end_index). + double operator[](int end_index) const; + + /// + /// Return the average of the sharpness interval. + /// + /// 0.5*(sharpness[0]+sharpness[1]) + double Average() const; + + /// + /// Return the minimum of the sharpness interval. + /// + /// The minimum of the sharpness interval + double MinimumEndSharpness() const; + + /// + /// Return the maximum of the sharpness interval. + /// + /// The maximum of the sharpness interval + double MaximumEndSharpness() const; + + + /// + /// Get the sharpness at the start or end. + /// + /// + /// Sharpness or ON_DBL_QNAN if end_index is out of range. + double EndSharpness(int end_index) const; + + /// + /// Calculate the vertex sharpness from the attached sharp edge information. + /// Note that vertices with a corner tag always have zero sharpness. + /// + /// + /// The vertex tag (smooth, crease, dart, corner). + /// For smooth, crease, and dart, this is used to determine the number of attached crease edges. + /// COrner vertices always have sharpness = 0. + /// + /// + /// Number of sharp edges attached to the vertex that have + /// nonzero end sharpness at the vertex. + /// + /// The largest sharp edge end sharpness at the vertex. + /// + /// + static double VertexSharpness( + ON_SubDVertexTag vertex_tag, + unsigned sharp_edge_end_count, + double maximum_edge_end_sharpness + ); + + /// + /// ON_SubDEdgeSharpness::Maximum = 4. + /// SubD edge sharpness values are <= ON_SubDEdgeSharpness::Maximum. + /// + static const double Maximum; + + /// + /// ON_SubDEdgeSharpness::Tolerance = 0.01 + /// If an edge has sharpness within ON_SubDEdgeSharpness::Tolerance of an integer value, + /// the sharpness is set to that integer value. + /// + static const double Tolerance; + + /// + /// Verify 0 <= sharpness <= ON_SubDEdgeSharpness::Maximum and return an integer value when + /// the input sharpenss is within ON_SubDEdgeSharpness::Tolerance of an integer. + /// + /// + /// Value returned when the sharpness paramter is invalid. + /// SubD edge sharpness value that makes sense. + static double Sanitize( + double sharpness, + double invalid_input_result + ); + + /// + /// Verify 0 <= sharpness <= ON_SubDEdgeSharpness::Maximum and return an integer value when + /// the input sharpenss is within ON_SubDEdgeSharpness::Tolerance of an integer. + /// + /// + /// SubD edge sharpness value that makes sense or 0.0 if the input sharpness is invalid. + static double Sanitize( + double sharpness + ); + +private: + float m_edge_sharpness[2] = {}; +}; ON_DECL -const ON_wString ON_SubDHashTypeToString( - ON_SubDHashType subd_hash_type, - bool bVerbose -); +bool operator==(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs); + +ON_DECL +bool operator!=(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs); /// /// ON_SubDHash provides a simple way to save a SubD's vertex, edge, and face SHA1 hashes. @@ -757,6 +976,13 @@ public: Otherwise, 0 is returned. */ unsigned int EdgeFaceCount() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->m_edge_tag is returned. + Otherwise, ON_SubDEdgeTag::Unset is returned. + */ + ON_SubDEdgeTag EdgeTag() const; /* Returns: @@ -834,13 +1060,43 @@ public: 0: return Edge()->Vertex(EdgeDirection()) 1: return Edge()->Vertex(1-EdgeDirection()) Returns: - The requested vertex control net point EdgeDirection() taken into account. + The requested vertex control net point with EdgeDirection() taken into account. ON_3dPoint::NanPoint if relative_vertex_index, Edge() is nullptr, or Edge()->Vertex() is nullptr. */ const ON_3dPoint RelativeControlNetPoint( int relative_vertex_index ) const; + + /* + Parameters: + relative_vertex_index - [in] + 0: return Edge()->Vertex(EdgeDirection())->SurfacePoint() + 1: return Edge()->Vertex(1-EdgeDirection())->SurfacePoint() + Returns: + The requested vertex surface point with EdgeDirection() taken into account. + ON_3dPoint::NanPoint if relative_vertex_index, Edge() is nullptr, or Edge()->Vertex() is nullptr. + */ + const ON_3dPoint RelativeVertexSurfacePoint( + int relative_vertex_index + ) const; + + /* + Parameters: + relative_vertex_index - [in] + 0: return Edge()->Vertex(EdgeDirection())->Point(point_location) + 1: return Edge()->Vertex(1-EdgeDirection())->Point(point_location) + point_location - [in] + Used to select control net or limit surface point. + Returns: + The requested vertex piont with with EdgeDirection() taken into account. + ON_3dPoint::NanPoint if relative_vertex_index, Edge() is nullptr, or Edge()->Vertex() is nullptr. + */ + const ON_3dPoint RelativeVertexPoint( + int relative_vertex_index, + ON_SubDComponentLocation point_location + ) const; + bool RelativeVertexMark( int relative_vertex_index, bool missing_vertex_return_value @@ -856,6 +1112,12 @@ public: const ON_3dVector RelativeControlNetDirection() const; /* + Description: + The sector coefficient is a property of a smooth edge end + that is attached to a dart, crease, or corner vertex. + In all other cases the sector coefficient is ignored. + The value of the sector coefficient is constant throughout subdivision. + Every smooth edge in a sector has the same sector coefficient at the central vertex. Parameters: relative_vertex_index - [in] Returns: @@ -872,6 +1134,16 @@ public: int relative_vertex_index ) const; + void SetRelativeSectorCoefficientForExperts( + int relative_vertex_index, + double relative_sector_coefficient + ) const; + + + const ON_SubDEdgeSharpness RelativeSharpness() const; + + void SetRelativeSharpness(ON_SubDEdgeSharpness relative_sharpness) const; + /* Description: Get the face on the left or right side of an oriented manifold or boundary edge. @@ -893,6 +1165,27 @@ public: int relative_face_index ) const; + /* + Description: + Return the neighboring face. + Parameters: + face - [in] + A face attached to this edge. + bStopAtCrease - [in] + If true and if m_edge_tag = ON_SubDEdgeTag::Crease, + then nullptr is returned. + Returns: + If the m_face_count = 2, + m_edge_tag is smooth or x or passes the crease tag test, + one of m_face2[0,1] points a face, then + the neighboring face is returned. + In any other case, nullptr is returned. + */ + const ON_SubDFace* NeighborFace( + const ON_SubDFace* face, + bool bStopAtCrease + ) const; + /* Returns: this->RelativeFace(relative_face_index)->Mark(); @@ -902,7 +1195,6 @@ public: bool missing_face_return_value ) const; - /* Returns: this->RelativeFace(relative_face_index)->MarkBits(); @@ -931,6 +1223,12 @@ public: */ const ON_SubDEdgePtr Reversed() const; + /// + /// Get the SubD edge Catmull-Clark subdivision point. + /// + /// Catmull-Clark edge subdivision point. + const ON_3dPoint SubdivisionPoint() const; + void ClearSavedSubdivisionPoints() const; /* @@ -970,6 +1268,20 @@ public: ON__UINT_PTR direction ); + /* + Parameters: + v0 - [in] + v1 - [in] + Returns: + If there is an edge connecting v0 and v1, then an ON_SubDEdgePtr pointing to that edge + and oriented from v0 to v1 is returned. Otherwise ON_SubDEdgePtr::Null is returned. + */ + static const ON_SubDEdgePtr Create( + const class ON_SubDVertex* v0, + const class ON_SubDVertex* v1 + ); + + static const ON_SubDEdgePtr Create( const class ON_SubDComponentPtr& edge_component ); @@ -1062,7 +1374,7 @@ public: ) const; ON__UINT8 ClearMarkBits() const; -}; +}; // ON_SubDFace #if defined(ON_DLL_TEMPLATE) ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; @@ -4616,34 +4928,36 @@ public: bool IsEmpty() const; bool IsNotEmpty() const; -#if defined(ON_SUBD_SHARP_EDGES) /// - /// Determine if any edges in this SubD have sem-sharp edges. + /// Determine if this SubD has sharp edges. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// - /// True if the SubD has at lease one semisharp edge. - bool HasSemisharpness() const; + /// True if the SubD has at lease one sharp edge. + bool HasSharpEdges() const; /// - /// Get the range of sharpness values assigned to semisharp edges - /// and return the number of semisharp edges. + /// Get the range of sharpness values assigned to sharp edges + /// and return the number of sharp edges. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// - /// The range of sharpness values is returned here. (0,0) is returned if there are no semisharp edges. - /// Number of semisharp edges. - unsigned int SemisharpEdgeCount(ON_Interval& sharpness_range) const; + /// The range of sharpness values is returned here. (0,0) is returned if there are no sharp edges. + /// Number of sharp edges. + unsigned int SharpEdgeCount(ON_SubDEdgeSharpness& sharpness_range) const; /// - /// Number of semisharp edges. + /// Number of sharp edges. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// - /// The range of sharpness values is returned here. (0,0) is returned if there are no semisharp edges. - /// Number of semisharp edges. - unsigned int SemisharpEdgeCount() const; + /// Number of sharp edges. + unsigned int SharpEdgeCount() const; /// - /// Converts all semisharp edges to smooth edges. + /// Converts all sharp edges to smooth edges. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// - /// Number of semisharp edges converted to smooth edges. - unsigned int ClearSemisharpness(); -#endif + /// Number of sharp edges that were converted to smooth edges. + unsigned int ClearEdgeSharpness(); + /* Description: @@ -5311,6 +5625,52 @@ public: ON_3dPoint vertex_location, unsigned new_edge_end ); + + /* + Description: + Spin an edge's endpoints around the boundary of its neighboring faces. + In a counter-clockwise spin (looking at faces from their shared up orientation): + The edge's start vertex is moved to the next vertex in the boundary + of the face on the right-hand side of the edge. + The edge's end vertex is moved to the next vertex in the boundary + of the face on the left-hand side of the edge. + Note that reversing the input edge does not change the result. + Parameters: + edge - [in] + edge to spin. + spin_clockwise - [in] + false spins the edge counter-clockwise, true spins the edge clockwise + in the adjacent faces. + Returns: + A pointer to the spun edge or nullptr if the input is not valid. + */ + const class ON_SubDEdge* SpinEdge( + class ON_SubDEdge* edge, + bool spin_clockwise = false + ); + + /* + Description: + Spin an edge's endpoints around the boundary of its neighboring faces. + In a counter-clockwise spin (looking at faces from their shared up orientation): + The edge's start vertex is moved to the next vertex in the boundary + of the face on the right-hand side of the edge. + The edge's end vertex is moved to the next vertex in the boundary + of the face on the left-hand side of the edge. + Note that reversing the input edge does not change the result. + Parameters: + edge - [in] + edge to spin. + spin_clockwise - [in] + false spins the edge counter-clockwise, true spins the edge clockwise + in the adjacent faces. + Returns: + A pointer to the spun edge or nullptr if the input is not valid. + */ + const ON_SubDEdgePtr SpinEdge( + ON_SubDEdgePtr eptr, + bool spin_clockwise = false + ); /* Description: @@ -5901,6 +6261,15 @@ public: ON_SubDEdgeTag edge_tag ); + /* + Returns: + number of tags that were changed. + */ + unsigned int SetEdgeTags( + const ON_SimpleArray& cptr_list, + ON_SubDEdgeTag edge_tag + ); + /* Description: @@ -6172,6 +6541,41 @@ public: class ON_SubDVertex* v1 ); + /* + Description: + Add an edge to the subd. + Parameters: + edge_tag - [in] + ON_SubDEdgeTag::Unset + Edge tag is not known at this time. + ON_SubDEdgeTag::Smooth + Smooth edge. If both vertices are tagged as not smooth, the + tag on the returned edge will be ON_SubDEdgeTag::SmoothX. This + tag is changed to ON_SubDEdgeTag::Smooth on the first + subdivision step. + ON_SubDEdgeTag::Crease. + Crease edge. Both vertices must be tagged as not smooth. + v0 - [in] + v1 - [in] + The edge begins at v0 and ends at v1. + The edge will be on the same level as the vertices. + sharpness - [in] + If edge_tag is ON_SubDEdge::Smooth or ON_SubDEdge::SmoothX, then + the the edge's sharpness is set to sharpness. + Otherwise, the sharpness parameter is ignored. + Returns: + Pointer to the allocated edge. + Remarks: + ON_SubD::EdgeTagFromContext() can be used to determine edge + tag values in simple situations. + */ + class ON_SubDEdge* AddEdge( + ON_SubDEdgeTag edge_tag, + class ON_SubDVertex* v0, + class ON_SubDVertex* v1, + ON_SubDEdgeSharpness sharpness + ); + /* Description: Expert use tool to add an edge with precomputed sector coefficients. @@ -6193,7 +6597,7 @@ public: double v0_sector_coefficient, class ON_SubDVertex* v1, double v1_sector_coefficient - ); + ); /* Description: @@ -7798,6 +8202,11 @@ public: Description: Calculates sector coefficient value for the sector type identified by this ON_SubDSectorType. + The sector coefficient is a property of a smooth edge end + that is attached to a dart, crease, or corner vertex. + In all other cases the sector coefficient is ignored. + The value of the sector coefficient is constant throughout subdivision. + Every smooth edge in a sector has the same sector coefficient at the central vertex. Returns: w: 0.0 < w < 1.0 w = sector coefficient value. @@ -7999,6 +8408,12 @@ public: public: /* + Description: + The sector coefficient is a property of a smooth edge end + that is attached to a dart, crease, or corner vertex. + In all other cases the sector coefficient is ignored. + The value of the sector coefficient is constant throughout subdivision. + Every smooth edge in a sector has the same sector coefficient at the central vertex. Returns: ON_SubDSectorType::IgnoredSectorCoefficient */ @@ -8035,6 +8450,35 @@ public: double corner_sector_angle_radians ); + /// + /// Copy the sector coefficent that is currently set on the edge. + /// The sector coefficient is a property of a smooth edge end + /// that is attached to a dart, crease, or corner vertex. + /// In all other cases the sector coefficient is ignored. + /// The value of the sector coefficient is constant throughout subdivision + /// and this function is used to copy edge sector coefficients during subdivision. + /// Every smooth edge in a sector has the same sector coefficient at the central vertex. + /// + /// + /// + /// The vertex identifies which end of the edge to query. + /// + /// + /// If the edge and vertex are not nullptr and attached to each other, + /// edge->IsSmooth() is true, + /// and vertex->IsDartCreaseOrCorner() is true, + /// then the current value of edge->m_sector_coefficient[vertex index] is returned. + /// If the edge and vertex are not nullptr and attached to each other + /// and edge->IsCrease() is true or vertex->IsSmooth() is true, + /// then ON_SubDSectorType::IgnoredSectorCoefficient is returned. + /// In all other cases error_return_value is returned. + /// + static double CopyEdgeSectorCoefficient( + const class ON_SubDEdge* edge, + const class ON_SubDVertex* vertex, + double error_return_value + ); + // This value is is used to set sector angles when the // actual value is not needed. This occurs at both ends // of a creased edge and when the end of a smooth edge @@ -10676,6 +11120,25 @@ public: const ON_Xform& xform ); + /// + /// Get a limit surface point. + /// + /// Limit surface point. + const ON_3dPoint Point() const; + + /// + /// Get a limit surface normal. + /// + /// Limit surface normal vector. + const ON_3dVector Normal() const; + + /// + /// Get a limit surface tangent vector. + /// + /// 0 or 1 + /// Limit surface tangent vector. + const ON_3dVector Tangent(int tangent_index) const; + // limit surface point, tangents and normal double m_limitP[3]; // point double m_limitT1[3]; // first unit tangent @@ -11359,7 +11822,9 @@ public: private: unsigned char m_reserved1 = 0; +private: unsigned short m_reserved2 = 0; +private: unsigned int m_reserved3 = 0; public: @@ -11496,6 +11961,25 @@ public: const ON_SubDEdge* edge ) const; + /* + Description: + Expert user tool to replace reference to old_edge with a reference to new_edge. + Parameters: + old_edge = [in] + Cannot be nullptr. + new_edge = [in] + If new_edge is Null, old_edge is simply removed. + Returns: + If the replacement was successful, then the m_edges[] array index where old_edge/new_edge replacement occured is returned. + Otherwise ON_UNSET_UINT_INDEX is returned. + Remarks: + No modifications are made to old_edge or new_edge. + */ + unsigned int ReplaceEdgeInArray( + const ON_SubDEdge* old_edge, + const ON_SubDEdgePtr new_edge + ); + unsigned int FaceCount() const; const class ON_SubDFace* Face( @@ -11531,70 +12015,102 @@ public: */ bool IsSmooth() const; -#if defined(ON_SUBD_SHARP_EDGES) - /// - /// Semi-sharp vertices are smooth vertices attached to one or more semisharp edges. + /// Sharp vertices are smooth, crease or dart vertices attached to + /// at least one sharp edge. Note that when an edge has variable + /// sharpness it can have zero sharpness at one end + /// and nonzero shaprness at the other end. + /// The bEndCheck parameter controls what type of sharpness query + /// is performed. + /// Note that the vertex subdivision point is affected by attached + /// sharp edges when IsSharp(true) is is true (ON_Vertex::VertexSharpness() > 0). + /// The vertex limit surface point is affected by edge sharpenss + /// when IsSharp(false) is true. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// - /// - /// True if this vertex is smooth and is attached to one or more semisharp edges. - ///< / returns> - bool IsSemisharp() const; - - /// - /// Semi-sharp vertices are smooth vertices attached to one or more semisharp edges. - /// - /// - /// If the vertex is attached to zero or one semisharp edges, 0 is returned. - /// If the vertex is attached to three or more semisharp edges, - /// ON_SubDEdge::InfinteSharpness is returned. - /// If the vertex is attached to two semisharp edges, - /// the average value of the edges' sharpness is returned. - ///< / returns> - double Sharpness() const; - - /// - /// Semi-sharp vertices are smooth vertices attached to one or more semisharp edges. - /// - /// If the returned sharpness is > 0, - /// then the sharp subdivision point is returned. - /// When the returned sharpness is > 0 and < 1, - /// the final subdivision point is a weighted average of - /// sharp_subdivision_point and the ordinary smooth subdivision point. + /// + /// When bEndCheck is false, the check looks for edges with any nonzero sharpness. + /// When bEndCheck is true, the check looks for edges with nonzero sharpness at this vertex. /// /// - /// If the vertex is attached to zero or one semisharp edges, 0 is returned. - /// If the vertex is attached to three or more semisharp edges, - /// ON_SubDEdge::InfinteSharpness is returned. - /// If the vertex is attached to two semisharp edges, - /// the average value of the edges' sharpness is returned. + /// True if this vertex is smooth, dart, or crease and is attached + /// to at least one sharp edge. + /// + bool IsSharp( bool bEndCheck ) const; + + /// + /// Sharp vertices are smooth, crease or dart vertices attached + /// to at least one sharp edge with nonzero end sharpness at the vertex. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. + /// + /// + /// If the vertex is smooth and and two or more attached edges have postive end sharpness + /// at this vertex, then the maximum edge end sharpness at this vertex is returned. + /// If the vertex is a dart or crease and and one or more attached edges have postive end sharpness + /// at this vertex, then the maximum edge end sharpness at this vertex is returned. + /// Otherwise 0.0 is returned. + ///< / returns> + double VertexSharpness() const; + + /// + /// Sharp vertices are smooth, crease or dart vertices attached to + /// at least one sharp edge. Note that the end sharpness at a vertex + /// can be zero. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. + /// + /// + /// When bEndCheck is false, the check looks for edges with any nonzero sharpness. + /// When bEndCheck is true, the check looks for edges with nonzero sharpness at this vertex. + /// + /// Number of sharp edges attached to this vertex. + unsigned int SharpEdgeCount( bool bEndCheck ) const; + + /// + /// Get the range of sharpness values assigned to sharp edges + /// and return the number of sharp edges. + /// Sharp vertices are smooth, crease or dart vertices attached to at least one sharp edge + /// with nonzero sharpness at the vertex. Note that the end sharpness at a vertex + /// can be zero. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. + /// + /// + /// When bEndCheck is false, the check looks for edges with any nonzero sharpness. + /// When bEndCheck is true, the check looks for edges with nonzero sharpness at this vertex. + /// + /// + /// The range of sharpness values is returned here. + /// If bEndCheck is true, the minimum and maximum of nonzero attached_edge->EndSharpness() at this vertex is returned in sharpness_range. + /// If bEndCheck is false, the minimum and maximum of nonzero attached_edge->MaximumEndSharpness() is returned in sharpness_range. + /// If no sharp edges are attached, then (0,0) is returned. + /// + /// + /// If bEndCheck is true, the number of edges with nonzero sharpness at this vertex is returned. + /// If bEndCheck is false, the number of edges attached to this vertex with nonzero sharpness is returned. + /// + unsigned int SharpEdgeCount(bool bEndCheck, ON_Interval& sharpness_range) const; + + /// + /// Sharp vertices are smooth, crease or dart vertices attached to at least one sharp edge + /// with nonzero sharpness at the vertex. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. + /// + /// If the returned sharpness is > 0, + /// then the sharp subdivision point is returned. + /// When the returned sharpness is > 0 and < 1, + /// the final subdivision point is a weighted average of + /// sharp_subdivision_point and the ordinary subdivision point. + /// When the returned sharpness is >= 1, the sharp subdivision point is used + /// in place of the ordinary subdivision point. + /// + /// + /// If the vertex is smooth and and two or more attached edges have postive end sharpness + /// at this vertex, then the maximum edge end sharpness at this vertex is returned. + /// If the vertex is a dart or crease and and one or more attached edges have postive end sharpness + /// at this vertex, then the maximum edge end sharpness at this vertex is returned. + /// Otherwise 0.0 is returned. ///< / returns> double GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const; - /// - /// WARNING: - /// This function is temporary and for internal use only. - /// It will be removed in the near future. - /// Any code referencing this function will unpredictably fail in the near future. - /// This is primative and is not thread safe. - /// - /// (ON_SubDEdge::SemisharpEvaluationIsEnabled() ? Sharpness() : 0.0) - double EvaluationSharpness( - ON_3dPoint& sharp_subdivision_point - ) const; - - unsigned int SemisharpEdgeCount() const; - - /// - /// Get the range of sharpness values assigned to semisharp edges - /// and return the number of semisharp edges. - /// - /// The range of sharpness values is returned here. (0,0) is returned if there are no semisharp edges. - /// Number of semisharp edges. - unsigned int SemisharpEdgeCount(ON_Interval& sharpness_range) const; - -#endif - /* Returns: True if m_vertex_tag is ON_SubDVertexTag::Crease. @@ -11638,11 +12154,22 @@ public: */ bool IsDartOrCrease() const; + /* + Returns: + True if m_vertex_tag is ON_SubDVertexTag::Dart or ON_SubDVertexTag::Corner + */ + bool IsDartOrCorner() const; + /* Returns: True if m_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart. */ bool IsSmoothOrDart() const; + /* + Returns: + True if m_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart or ON_SubDVertexTag::Crease. + */ + bool IsSmoothOrDartOrCrease() const; const ON_SubDVertexEdgeProperties EdgeProperties() const; @@ -12247,6 +12774,20 @@ public: // m_vertex[1] = vertex at the end of the edge. const class ON_SubDVertex* m_vertex[2] = {}; + // NOTE: + // The sector coefficient is a property of a smooth edge end + // that is constant throughout subdivision. It exists at ends of + // smooth edges that are attached to dart, crease, or corner vertices. + // In all other cases the sector coefficient is ignored. + // In particular crease edges and the ends of smooth edges + // attached to smooth vertices do not have a sector coefficient. + // + // The ON_SubDSectorType class provides three static functions that + // calculate sector coefficients: + // ON_SubDSectorType::DartSectorCoefficient() + // ON_SubDSectorType::CreaseSectorCoefficient() + // ON_SubDSectorType::CornerSectorCoefficient() + // // If the value of vertex->m_vertex_tag is not ON_SubDVertexTag::Smooth, // then that vertex is "tagged". // @@ -12326,56 +12867,35 @@ public: mutable double m_sector_coefficient[2] = {}; private: - // Semi-sharp crease sharpness. - // See ON_SubDEdge::Sharpness() comments for details. - double m_sharpness = 0.0; + // For a smooth edge, m_sharpness is the edge's sharpness. + // Edge sharpenss has no meaning for edges with a crease tag. + // ON_SubDEdge::Sharpness() comments for details. + ON_SubDEdgeSharpness m_sharpness = ON_SubDEdgeSharpness::Zero; -#if defined(ON_SUBD_SHARP_EDGES) public: - /// - /// WARNING: - /// This function is temporary and for internal use only. - /// It will be removed in the near future. - /// Any code referencing this function will unpredictably fail in the near future. - /// This is primative and is not thread safe. - /// - /// (ON_SubDEdge::SemisharpEvaluationIsEnabled() ? Sharpness() : 0.0) - double EvaluationSharpness( - ON_3dPoint& sharp_subdivision_point - ) const; + /// + /// Gets the edge's sharp subdivision point and returns the average of the edge's sharpnesses. + /// The final subdivision point is (sharpness >= 1.0) ? sharp_subdivision_point : (1.0-sharpness)(smooth subdivsion point)+sharpness*sharp_subdivision_point. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. + /// + /// If the returned sharpness is > 0, + /// then the sharp subdivision point is returned. + /// When the returned sharpness is > 0 and < 1, + /// the final subdivision point is a weighted average of + /// sharp_subdivision_point and the ordinary smooth subdivision point. + /// When the returned sharpness is >= 1, the sharp subdivision point is used + /// in place of the smooth subdivision point. + /// + /// + /// If the edge is sharp, a value > 0 and < ON_SubDEdgeSharpness::Maximum is returned. + /// If the edge is smooth with zero sharpness, 0 is returned. + /// If the edge is a crease, 0 is returned. + /// double GetSharpSubdivisionPoint( ON_3dPoint& sharp_subdivision_point ) const; - - /// - /// WARNING: - /// This function is temporary and for internal use only. - /// It will be removed in the near future. - /// Any code referencing this function will unpredictably fail in the near future. - /// This is primative and is not thread safe. - /// - /// ON_SubDEdge::m_bEnableSemiharpEvaluation - static bool SemisharpEvaluationIsEnabled(); - - /// - /// WARNING: - /// This function is temporary and for internal use only. - /// It will be removed in the near future. - /// Any code referencing this function will unpredictably fail in the near future. - /// This is primative and is not thread safe. - /// - /// - static void SetEnableSemisharpEvaluation(bool bEnableSemiharpEvaluation); -private: - // This global bool is temporary and is used to determine if subdivision and evaluation code - // pays attention to sharpness. This global bool will be removed in the near future. - // Any code referencing this variable will unpredictably fail in the near future. - // This is primative and is not thread safe. - static bool m_bEnableSemisharpEvaluation; -#endif - private: // Cached limit curve // GetEdgeSurfaceCurveControlPoints( bUseSavedSurfacePoint=true ) can change the value of m_limit_curve. @@ -12688,99 +13208,116 @@ public: */ bool IsSmoothX() const; -#if defined(ON_SUBD_SHARP_EDGES) - /// - /// Semi-sharp edges are tagged as smooth edges with a sharpness > 0 and are - /// a blend between a smooth and a creased edge. + /// Sharp edges are a blend between smooth edges and crease edges. + /// The limit surface has a continuous normal along a sharp edge. + /// A sharp edge has a smooth tag, + /// has sharpness > 0 at at least one end, + /// and has sharpness < ON_SubDEdgeSharpness::Maximum at at least one end. + /// Sharpness has no meaning for edges with crease tags. + /// Both sharpness values are zero for an ordinary smooth edge. + /// Edge sharpness steadily decreases during subdivision and becomes zero after at most ON_SubDEdgeSharpness::Maximum subdivisions. /// - /// True if the edge is tagged as smooth and has Sharpness > 0. - bool IsSemisharp() const; + /// True if the edge is tagged as smooth, h and at least one end with sharpness > 0 and < ON_SubDEdgeSharpness::Maximum. + bool IsSharp() const; /// - /// Determine the sharpness to assign to the subdivided edge. + /// Determine if an edge is smooth and is not sharp. /// - /// 0 or 1 indicating the left or right subdivided edge. - /// Sharpness to assigned to the subdivided edge. - double SubdivideSharpness( - unsigned end_index - ) const; - - static double AverageSharpness( - double sharpness0, - double sharpness1 - ); -#endif + /// (true == IsSmooth() and false == IsSharp()) + bool IsSmoothNotSharp() const; /// - /// Semisharp edges are tagged as smooth edges with a - /// sharpness > 0 and < ON_SubDEdge::InfinteSharpness. + /// An expert user function to determine if an edge tag in ON_SubDEdgeTag::Smooth and is not sharp. + /// + /// (true == IsSmoothNotX() and false == IsSharp()) + bool IsSmoothNotXNotSharp() const; + + /// + /// Get the edge's sharpness. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// /// - /// If the edge is smooth and not semisharp, 0 is returned. - /// If the edge is a crease, ON_SubDEdge::InfinteSharpness is returned. - /// If the edge is semisharp, a sharpness value strictly between 0 and ON_SubDEdge::InfinteSharpness is returned. - /// Otherwise, ON_DBL_QNAN is returned. + /// If the edge is sharp, the interval[0] value is the sharpness at the start of the edge and + /// the interval[1] value is the sharpness at the end of the edge. + /// If the edge is smooth or a crease, (0,0) is returned. + /// Otherwise, (0,0) is returned. /// - /// 0.0 if the edges is smooth and ON_SubDEdge::InfinteSharpness if the edge is a crease. - /// A value strictly between 0 and ON_SubDEdge::InfinteSharpness if the edge is a semisharp. - double Sharpness() const; + const ON_SubDEdgeSharpness Sharpness() const; /// - /// A version of sharpness that allows the return value to be specified for edges that - /// are not explicitly semisharp. This is useful to simplify code branch decisions - /// when smooth, semisharp, and crease edges need to be handled differently. + /// Get the edge's sharpness at the end with the specified vertex. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// - /// - /// Value to return when the edge tag is unset. ON_UNSET_VALUE and ON_DBL_QNAN are good choices. - /// - /// - /// Value to return when the edge is smooth but not semisharp. 0.0 is often a good choice. - /// - /// - /// Value to return when the edge is smooth but not semisharp. - /// ON_SubDEdge::InfinteSharpness is often a good choice. - /// Value of sharpness for the specified situation. - double Sharpness( - double unset_edge_value, - double smooth_edge_value, - double crease_edge_value + /// Vertex at an end of the edge + /// + /// If the edge is sharp, the sharpness at the end with the specified vertex is returned. + /// If the edge is smooth or a crease, 0 is returned. + /// Otherwise, 0.0 is returned. + /// + /// The sharpness at the end of the edge with the specified vertex. + double EndSharpness( + const class ON_SubDVertex* v ) const; /// - /// Semi-sharp edges are tagged as smooth edges with a sharpness > 0 and are - /// a blend between a smooth and a creased edge. Set N = floor(sharpness). - /// For subdivision levels < N, the semisharp edge and attached vertices - /// are subdivided using crease subdivision rules. - /// For subdivision levels >= ceil(sharpness), the semisharp edge and attached vertices - /// are subdivided using smooth subdivision rules. - /// When N < sharpness < ceil(sharpness), at level N the semisharp edge and attached vertices - /// are subdivided using a linear blend of crease and smooth subdivision rules. - /// (1-s)*(smooth rules) * s(crease rules), where s = (sharpness-N). + /// Get the edge's sharpness at the end with the specified vertex. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. /// - /// 0 &le sharpness < ON_SubDEdge::InfiniteSharpness. - void SetSharpness(double sharpness); + /// End index (0=start or 1=end). + /// + /// If the edge is sharp, the sharpness at the end with the specified by evi is returned. + /// If the edge is smooth or a crease, 0 is returned. + /// Otherwise, 0.0 is returned. + /// + /// The sharpness at the end of the edge specified by evi. + double EndSharpness( + unsigned evi + ) const; /// - /// ON_SubDEdge::InfinteSharpness = 5.0 - /// Semi-sharp edges have a Sharpness() > 0.0 and < ON_SubDEdge::InfinteSharpness. + /// Get the edge sharpenss values for the subdivided edge at the specified end of this edge. /// - static const double InfinteSharpness; - + /// Selects the subdivided edge (0 for the left subdivided edge, 1 for the right subdivided edge) + /// Pass true if this edge and the subdividied edge will have opposite orientations. + /// Edge sharpness for the subdivided edge. + const ON_SubDEdgeSharpness SubdivideSharpness( + unsigned evi, + bool bReverseSharpness + ) const; /// - /// ON_SubDEdge::SharpnessTolerance = 0.01 - /// If an edge has sharpness within ON_SubDEdge::SharpnessTolerance of an integer value, - /// the sharpness is set to that integer value. + /// Get the edge sharpenss values for the subdivided edge at the specified end of this edge. /// - static const double SharpnessTolerance; + /// One of this edge's vertices used to select the subdivided edge. + /// Pass true if this edge and the subdividied edge will have opposite orientations. + /// Edge sharpness for the subdivided edge. + const ON_SubDEdgeSharpness SubdivideSharpness( + const class ON_SubDVertex* end_vertex, + bool bReverseSharpness + ) const; - static double SanitizeSharpness( - double sharpness, - bool bPermitInfinteSharpness + /// + /// This tool is for expert users and internal use. + /// A collection of ON_SubD::SetEdgeSharpness() functions provide the easiest way to + /// set and change edge sharpness. + /// Set the edge sharpness values to (sharpness[0],sharpness[1]). + /// The interval values must be >= 0 and <= ON_SubDEdgeSharpness::Maximum. + /// See ON_SubDEdge::IsSharp() for more information about sharp edges. + /// + /// End sharpenss values. + void SetSharpnessForExperts( + ON_SubDEdgeSharpness sharpness ); + /// + /// This tool is for expert users and internal use. + /// Use ON_SubD::ClearEdgeSharpness() to remove all sharp edges from a SubD. + /// Sets the edge sharpness to ON_EdgeSharpness::Zero. + /// + /// True if there were changes to the edge. + bool ClearSharpnessForExperts(); /* Returns: @@ -12827,9 +13364,6 @@ public: /* Parameters: - bUseSavedSubdivisionPoint - [in] - If there is a saved subdivision point and bUseSavedSubdivisionPoint - is true, then the saved value is returned. subdivision_point - [out] Returns: true if successful @@ -12838,9 +13372,10 @@ public: double subdivision_point[3] ) const; - /// - /// The SubD edge Catmull-Clark subdivision point. - /// + /// + /// Get the SubD edge Catmull-Clark subdivision point. + /// + /// Catmull-Clark edge subdivision point. const ON_3dPoint SubdivisionPoint() const; /* @@ -13748,23 +14283,23 @@ public: ON_SubDEdge* edge_to_insert ); - /////* - ////Description: - //// Expert user tool to replace one edge with another in the face's edge array. - ////Parameters: - //// edge_to_remove - [in] - //// edge_to_insert - [in] - //// The inserted edge is assigned the same boundary orientation specified - //// in edgeptr_to_insert. - ////Remarks: - //// Does not modify the edge. The corresponding reference to this face must - //// be removed from the first edge and added to the second edge. - ////*/ - ////bool ReplaceEdgeInArray( - //// unsigned int fei0, - //// ON_SubDEdge* edge_to_remove, - //// ON_SubDEdgePtr edgeptr_to_insert - ////); + /* + Description: + Expert user tool to replace one edge with another in the face's edge array. + Parameters: + edge_to_remove - [in] + edgeptr_to_insert - [in] + The inserted edge is assigned the same boundary orientation specified + in edgeptr_to_insert. + Remarks: + Does not modify the edge. The corresponding reference to this face must + be removed from the first edge and added to the second edge. + */ + bool ReplaceEdgeInArray( + unsigned int fei0, + ON_SubDEdge* edge_to_remove, + ON_SubDEdgePtr edgeptr_to_insert + ); /* Description: @@ -15232,7 +15767,7 @@ public: /* Parameters: center_vertex - [in] - The vertex on initial_face that will be iterated around. + The vertex to iterated around. center_vertex->Face(0) is used to select the sector. Returns: If input is valid, a pointer to the center vertex is returned. @@ -16568,32 +17103,32 @@ public: // The term "standard vertex ring points" is used below. // - // If "C" is an interior vertex (m_vertex_tag is smooth or dart), + // Let "C" be an interior vertex (m_vertex_tag is smooth or dart), // (E[0], ...., E[N-1]) is a radially sorted list of its edges, - // (F[0], ..., F[N-1]) is a radially sorted list of its faces, - // and (P[0], ..., P[N-1]) is a list of the edge vertices opposite C, - // E0type = smooth for a smooth vertex and crease for a dart vertex, - // then C is "standard" if E[0] has type E0type, every other - // edge E[i] is smooth, every outer vertex/ P[i] is smooth, and every - // face F[i] has the stadard facet type (tri or quad) for the subdivision - // algorithm. + // (F[0], ..., F[N-1]) is a radially sorted list of its faces (F[i] is between E[i] and E[(i+1)%N]), + // (P[0], ..., P[N-1]) are the vertices on the edges opposide Cn (P[i] is on E[i]. + // + // C is a "standard" smooth vertex if + // all edges E[i] are smooth with sharpness = 0, + // and all faces f[i] are quads. + // + // C is a "standard" dart vertex if + // edge E[0] is a crease, + // edges E[1], ..., E[N-1] are smooth with sharpness = 0, + // and all faces F[i] are quads. // // If If "C" is a boundary vertex (m_vertex_tag is crease or corner), the conditions // listed above are satisified except - // E[0] and E[N-1] are tagged as crease edges, - // P[0] and P[N-1] are tagged as crease vertices (NOT corners), - // and there are N-2 faces, - // then "C" is a standard boundary vertex. + // E[0] and E[N-1] are crease edges, + // E[1], ..., E[N-2] are smooth edges with sharpness = 0, + // and faces f[0], ..., F[N-2] are quads. // - // If the facet type is triangle and C is a standard interior or boundary vertex, - // then the "standard vertex ring" is the list of N+1 points - // (C, P[0], ...., P[N-1]). - // - // If the facet type is quad, and C is a standard interior vertex, + // If C is a standard interior smooth or dart vertex, // then the "standard vertex ring" is the list of 2*N+1 points - // (C, P[0], Q[0], ...., P[N-1], Q[N-1]), where Q[I] is the vertex of quad F[i] diagonally across from C. + // (C, P[0], Q[0], ...., P[N-1], Q[N-1]), + // where Q[I] is the vertex of quad F[i] diagonally across from C. // - // If the facet type is quad, and C is a standard boundary vertex, + // If C is a standard boundary vertex, // then the "standard vertex ring" is the list of 2*N points // (C, P[0], Q[0], ...., P[N-1]). diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp index f32a9be4..0010ea91 100644 --- a/opennurbs_subd_archive.cpp +++ b/opennurbs_subd_archive.cpp @@ -850,7 +850,7 @@ bool ON_SubDEdge::Write( break; if (!archive.WriteDouble(2,m_sector_coefficient)) break; - if (!archive.WriteDouble(Sharpness(0.0,0.0,0.0))) + if (!archive.WriteDouble(this->m_sharpness[0])) break; if (!Internal_WriteVertexList(2, m_vertex, archive)) break; @@ -865,6 +865,15 @@ bool ON_SubDEdge::Write( return true; } + if (archive.Archive3dmVersion() >= 80) + { + // 2nd sharpness added Jan 2023 to v8 files. + if (!archive.WriteChar((unsigned char)8U)) + break; + if (!archive.WriteDouble(this->m_sharpness[1])) + break; + } + return Internal_FinishWritingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); @@ -888,7 +897,7 @@ bool ON_SubDEdge::Read( unsigned char edge_tag = 0; unsigned short face_count = 0; double sector_coefficient[2] = { 0 }; - double sharpness = 0.0; + double sharpness0 = 0.0; if (!ReadBase(archive,base)) break; @@ -898,7 +907,7 @@ bool ON_SubDEdge::Read( break; if (!archive.ReadDouble(2,sector_coefficient)) break; - if (!archive.ReadDouble(&sharpness)) + if (!archive.ReadDouble(&sharpness0)) break; ON_SubDVertex* v[2] = { 0 }; @@ -923,7 +932,6 @@ bool ON_SubDEdge::Read( e->m_sector_coefficient[0] = sector_coefficient[0]; e->m_sector_coefficient[1] = sector_coefficient[1]; - e->SetSharpness(sharpness); if (!Internal_ReadFacePtrList(archive,face_count,sizeof(e->m_face2)/sizeof(e->m_face2[0]),e->m_face2,e->m_facex_capacity,e->m_facex)) break; @@ -939,6 +947,24 @@ bool ON_SubDEdge::Read( return true; } + if (archive.Archive3dmVersion() >= 80) + { + unsigned char sz; + if (false == archive.ReadChar(&sz)) + break; + if (ON_SubDComponentArchiveAdditionEndMark == sz) + return true; + if (8 != sz) + break; // error + + // 2nd sharpness added Jan 2023 to v8 files. + double sharpness1 = 0.0; + if (false == archive.ReadDouble(&sharpness1)) + break; + if (e->IsSmooth()) + e->SetSharpnessForExperts(ON_SubDEdgeSharpness::FromInterval(sharpness0,sharpness1)); + } + return Internal_FinishReadingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 17914b74..a10d699a 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -155,6 +155,12 @@ public: unsigned char m_boundary_crease_count = 0; bool m_bBoundaryCrease[4] = {}; + // m_sharp_edge_count = number of sharp edges in the quad boundary and m_edge_grid[4][2] + unsigned char m_sharp_edge_count = 0; + +private: + unsigned char m_reserved[5]; + public: const ON_SubDVertex* m_vertex_grid[4][4] = {}; // vertex net m_quad_face corners = ([1][1], [2][1], [2][2], [1][2]) @@ -2079,6 +2085,29 @@ public: unsigned int edge_count, const ON_SubDEdgePtr* edge ); + + /* + Description: + Spin an edge's endpoints around the boundary of its neighboring faces. + In a counter-clockwise spin (looking at faces from their shared up orientation): + The edge's start vertex is moved to the next vertex in the boundary + of the face on the right-hand side of the edge. + The edge's end vertex is moved to the next vertex in the boundary + of the face on the left-hand side of the edge. + Note that reversing the input edge does not change the result. + Parameters: + edge - [in] + edge to spin. + spin_clockwise - [in] + false spins the edge counter-clockwise, true spins the edge clockwise + in the adjacent faces. + Returns: + A pointer to the spun edge or nullptr if the input is not valid. + */ + const ON_SubDEdge* SpinEdge( + ON_SubDEdge* edge, + bool spin_clockwise = false + ); /* Returns: @@ -3019,10 +3048,15 @@ public: ) const; public: + + // NOTE WELL: + // It is required that: + // 1) MaximumControlPointCapacity = 2*MinimumControlPointCapacity+1; + // 2) MaximumControlPointCapacity >= 3 + 2^ON_SubDEdgeSharpness::Maximum enum : unsigned char { - MinimumControlPointCapacity = 5, - MaximumControlPointCapacity = 11 + MinimumControlPointCapacity = 9, + MaximumControlPointCapacity = 19 }; private: diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp index e1e37536..58f937e0 100644 --- a/opennurbs_subd_fragment.cpp +++ b/opennurbs_subd_fragment.cpp @@ -3381,7 +3381,8 @@ unsigned int ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubD( // If this function changes, then a parallel change must be made in // ON_SubDLevel::CopyEvaluationCacheForExperts(const ON_SubDLevel& src, ON_SubDHeap& this_heap) // so it uses the same automatic density value. - return ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount(adaptive_subd_display_density,subd.FaceCount()); + unsigned int display_density = ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount(adaptive_subd_display_density, subd.FaceCount()); + return display_density; } const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp index 7f7b7d16..32f8de6f 100644 --- a/opennurbs_subd_limit.cpp +++ b/opennurbs_subd_limit.cpp @@ -456,7 +456,7 @@ static bool IsOrdinarySmoothQuadCornerVertex( if ( nullptr == e ) return ON_SUBD_RETURN_ERROR(false); // Test for exact tag here - do not call e->IsSmooth() because this is a rare case where X tags need to be rejected. - if ( false == e->IsSmoothNotX() ) + if ( false == e->IsSmoothNotXNotSharp() ) return false; if ( 2 != e->m_face_count) return ON_SUBD_RETURN_ERROR(false); @@ -470,6 +470,9 @@ static bool IsOrdinarySmoothQuadCornerVertex( const double sector_coefficient = e->m_sector_coefficient[outer_vertex_index]; if ( !(0.5 == sector_coefficient) ) return false; + + // If we get this far, then this edge has + // the standared smooth edge Catmull Clark subdivision point. } return true; } @@ -488,6 +491,8 @@ bool ON_SubDQuadNeighborhood::VertexGridIsExactCubicPatch( return false; if (m_boundary_crease_count > 2) return false; + if (m_sharp_edge_count > 0) + return false; int fcount_check; @@ -685,6 +690,34 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( break; } + m_sharp_edge_count = 0; + bool bSharpQuadrant[4] = {}; + for(unsigned int i = 0; i < 4; ++i) + { + if (nullptr != m_center_edges[i] && m_center_edges[i]->IsSharp()) + { + ++m_sharp_edge_count; + bSharpQuadrant[0] = true; + bSharpQuadrant[1] = true; + bSharpQuadrant[2] = true; + bSharpQuadrant[3] = true; + } + if (nullptr != this->m_edge_grid[i][0] && this->m_edge_grid[i][0]->IsSharp()) + { + ++m_sharp_edge_count; + bSharpQuadrant[i] = true; + bSharpQuadrant[(i + 1U) % 4U] = true; + bSharpQuadrant[(i + 3U) % 4U] = true; + } + if (nullptr != this->m_edge_grid[i][1] && this->m_edge_grid[i][1]->IsSharp()) + { + ++m_sharp_edge_count; + bSharpQuadrant[i % 4U] = true; + bSharpQuadrant[(i + 1U) % 4U] = true; + bSharpQuadrant[(i + 2U) % 4U] = true; + } + } + //////////////////////////////////////////////////////////////////////////// // // Set @@ -822,6 +855,8 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( continue; } } + if (bSharpQuadrant[corner_index]) + continue; if (bExtraordinaryCornerVertex[corner_index]) continue; if (bExtraordinaryCornerVertex[(corner_index + 1) % 4]) @@ -1831,26 +1866,34 @@ bool ON_SubDQuadNeighborhood::Internal_GetApproximateCV( return bHaveApproximateCV; } - - -static double ON_SubDQuadFaceTopology_CopySectorWeight( +static bool ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness( const ON_SubDEdge* e0, - const ON_SubDVertex* e0v - ) + const ON_SubDVertex* e0v, + ON_SubDEdge* e1, + const ON_SubDVertex* e1v +) { - if (nullptr == e0 || nullptr == e0v) + if (nullptr == e0 || nullptr == e0v || nullptr == e1 || nullptr == e1v) return ON_SUBD_RETURN_ERROR(false); - if (ON_SubDEdgeTag::Smooth != e0->m_edge_tag && ON_SubDEdgeTag::SmoothX != e0->m_edge_tag ) - return ON_SubDSectorType::IgnoredSectorCoefficient; + if (e0->IsSharp()) + { + const bool bReversed = ((e0->m_vertex[0] == e0v) ? 0 : 1) != ((e1->m_vertex[0] == e1v) ? 0 : 1); + e1->SetSharpnessForExperts(e0->SubdivideSharpness(e0v, bReversed)); + } + else + e1->ClearSharpnessForExperts(); + return true; +} - if (e0v == e0->m_vertex[0]) - return e0->m_sector_coefficient[0]; - - if (e0v == e0->m_vertex[1]) - return e0->m_sector_coefficient[1]; - - return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); +static bool ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness( + const ON_SubDEdge* e0, + const ON_SubDVertex* e0v, + ON_SubDEdgePtr e1, + const ON_SubDVertex* e1v +) +{ + return ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(e0, e0v, e1.Edge(), e1v); } static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( @@ -1878,7 +1921,7 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient; else { - v0_weight = ON_SubDQuadFaceTopology_CopySectorWeight(e0, qv0); + v0_weight = ON_SubDSectorType::CopyEdgeSectorCoefficient(e0, qv0, ON_UNSET_VALUE); if (false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_weight,false)) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); } @@ -1900,6 +1943,7 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( if ( ON_SubDEdgeTag::Smooth != e1->m_edge_tag || ON_SubDEdgeTag::SmoothX != e0->m_edge_tag) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); } + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(e0, qv0, e1, qv1); return ep1; } @@ -1941,10 +1985,12 @@ static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace( ON_SubDEdgePtr e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight); if ( nullptr == e12.Edge()) return ON_SUBD_RETURN_ERROR(nullptr); + // e12 is interior to f0 and has no sharpness ON_SubDEdgePtr e23 = fsh.AllocateEdge(v[2],v2_weight,v[3],v3_weight); if ( nullptr == e23.Edge()) return ON_SUBD_RETURN_ERROR(nullptr); + // e23 is interior to f0 and has no sharpness ON_SubDEdgePtr f1_epts[4] = { e1[0], e12, e23, e1[1].Reversed() }; @@ -2287,10 +2333,11 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[2][1], ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][1], - ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[0], qf0_vertices[1]) + ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[0], qf0_vertices[1], ON_UNSET_VALUE) ); if ( edge_grid1[1][0].IsNull()) return ON_SUBD_RETURN_ERROR(false); + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[0], qf0_vertices[1], edge_grid1[1][0], vertex_grid1[3][1]); edge_grid1[1][1] = fsh.AllocateEdge( bUseFindOrAllocate, @@ -2301,6 +2348,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( ); if (edge_grid1[1][1].IsNull()) return ON_SUBD_RETURN_ERROR(false); + // NOTE: edge_grid1[1][1] is interior to input face f0 and has zero sharpness edge_grid1[2][0] = fsh.AllocateEdge( bUseFindOrAllocate, @@ -2311,16 +2359,18 @@ bool ON_SubDQuadNeighborhood::Subdivide( ); if (edge_grid1[2][0].IsNull()) return ON_SUBD_RETURN_ERROR(false); + // NOTE: edge_grid1[2][0] is interior to input face f0 and has zero sharpness edge_grid1[2][1] = fsh.AllocateEdge( bUseFindOrAllocate, vertex_grid1[1][2], ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[1][3], - ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[3], qf0_vertices[3]) + ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[3], qf0_vertices[3], ON_UNSET_VALUE) ); if (edge_grid1[2][1].IsNull()) return ON_SUBD_RETURN_ERROR(false); + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[3], qf0_vertices[3], edge_grid1[2][1], vertex_grid1[1][3]); // Add the 5 remaining elements to face_grid1[][] ON_SubDEdgePtr fedges[4]; @@ -2343,16 +2393,19 @@ bool ON_SubDQuadNeighborhood::Subdivide( ); if (fedges[0].IsNull()) return ON_SUBD_RETURN_ERROR(false); + // NOTE: fedges[0] is interior to an input face and cannot be sharp. + // m_edge_grid[q0fvi][1] fedges[1] = fsh.AllocateEdge( bUseFindOrAllocate, vertex_grid1[3][0], ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][1], - ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[q0fvi][1], qf0_vertices[1]) + ON_SubDSectorType::CopyEdgeSectorCoefficient(m_edge_grid[q0fvi][1], qf0_vertices[1], ON_UNSET_VALUE) ); if (fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(m_edge_grid[q0fvi][1], qf0_vertices[1], fedges[1], vertex_grid1[3][1]); face_grid1[2][0] = fsh.AllocateQuad(zero_face_id, parent_face_id, fedges[0], @@ -2376,13 +2429,13 @@ bool ON_SubDQuadNeighborhood::Subdivide( fedges[1] = fsh.AllocateEdge( bUseFindOrAllocate, vertex_grid1[3][1], - ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[1]), + ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[1], qf0_vertices[1], ON_UNSET_VALUE), vertex_grid1[3][2], ON_SubDSectorType::IgnoredSectorCoefficient ); if (fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); - + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[1], qf0_vertices[1], fedges[1], vertex_grid1[3][1]); face_grid1[2][1] = fsh.AllocateQuad( zero_face_id, @@ -2409,20 +2462,22 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[3][2], ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][3], - ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[2]) + ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[1], qf0_vertices[2], ON_UNSET_VALUE) ); if (fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[1], qf0_vertices[2], fedges[1], vertex_grid1[3][3]); fedges[2] = fsh.AllocateEdge( bUseFindOrAllocate, vertex_grid1[3][3], - ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[2]), + ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[2], qf0_vertices[2], ON_UNSET_VALUE), vertex_grid1[2][3], ON_SubDSectorType::IgnoredSectorCoefficient ); if (fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[2], qf0_vertices[2], fedges[2], vertex_grid1[3][3]); face_grid1[2][2] = fsh.AllocateQuad( zero_face_id, @@ -2449,10 +2504,11 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[2][3], ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[1][3], - ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[3]) + ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[2], qf0_vertices[3], ON_UNSET_VALUE) ); if (fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[2], qf0_vertices[3], fedges[2], vertex_grid1[1][3]); face_grid1[1][2] = fsh.AllocateQuad( zero_face_id, parent_face_id, @@ -2477,12 +2533,13 @@ bool ON_SubDQuadNeighborhood::Subdivide( fedges[1] = fsh.AllocateEdge( bUseFindOrAllocate, vertex_grid1[1][3], - ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[(q0fvi+3)%4][0], qf0_vertices[3]), + ON_SubDSectorType::CopyEdgeSectorCoefficient(m_edge_grid[(q0fvi+3)%4][0], qf0_vertices[3], ON_UNSET_VALUE), vertex_grid1[0][3], ON_SubDSectorType::IgnoredSectorCoefficient ); if ( fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); + ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(m_edge_grid[(q0fvi + 3) % 4][0], qf0_vertices[3], fedges[1], vertex_grid1[1][3]); fedges[2] = fsh.AllocateEdge( bUseFindOrAllocate, @@ -2493,6 +2550,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( ); if ( fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); + // NOTE: fedges[2] is interior to an input face and cannot be sharp. face_grid1[0][2] = fsh.AllocateQuad( zero_face_id, parent_face_id, @@ -2804,8 +2862,8 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( if ( nullptr == vertex0 ) return ON_SUBD_RETURN_ERROR(false); + ON_SubDEdgePtr edge0_ptr = ON_SubDEdgePtr::Null; const ON_SubDEdge* edge0 = nullptr; - ON__UINT_PTR edge0_dir = 0; ON_SubDVertex* vertex1 = nullptr; ON_SubDEdgePtr edge1 = ON_SubDEdgePtr::Null; @@ -2822,13 +2880,12 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( return ON_SUBD_RETURN_ERROR(false); face_m_edges = face->m_edgex; } - const ON__UINT_PTR edge0_ptr = face_m_edges->m_ptr; + edge0_ptr = *face_m_edges; - edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); + edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr.m_ptr); if (nullptr == edge0 || nullptr == edge0->m_vertex[0] || nullptr == edge0->m_vertex[1]) return ON_SUBD_RETURN_ERROR(false); - edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr); - vertex0 = edge0->m_vertex[edge0_dir]; + vertex0 = edge0_ptr.RelativeVertex(0); if (vertex0->m_edge_count < 2) return ON_SUBD_RETURN_ERROR(false); @@ -2868,6 +2925,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); + // edge1 is interior to face and has no sharpness if ( 0 != edge1.EdgeDirection() ) return ON_SUBD_RETURN_ERROR(false); if ( i+1 != edge1.EdgeId() ) @@ -2892,17 +2950,10 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( { if (4 == i) face_m_edges = face->m_edgex; - const ON__UINT_PTR edge0_ptr = face_m_edges->m_ptr; + ON_SubDEdgePtr prev_edge0_ptr = edge0_ptr; + edge0_ptr = *face_m_edges; + vertex0 = edge0_ptr.RelativeVertex(0); - const ON_SubDEdge* prev_edge0 = edge0; - //const ON__UINT_PTR prev_edge0_dir = edge0_dir; - edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); - edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr); - vertex0 = edge0->m_vertex[edge0_dir]; - - // ring_vertex1[0] = subdivision vertex on input face->Edge(i-1) = prev_edge0 - // ring_vertex1[1] = subdivision vertex on input face->Vertex(i) = vertex0 - // ring_vertex1[2] = subdivision vertex on input face->Edge(i) = edge0 if (0 == i) { ring_vertex1[0] = ring_vertex1[3]; @@ -2923,21 +2974,23 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ring_vertex1[0], ON_SubDSectorType::IgnoredSectorCoefficient, ring_vertex1[1], - ON_SubDQuadFaceTopology_CopySectorWeight(prev_edge0, vertex0) + ON_SubDSectorType::CopyEdgeSectorCoefficient(prev_edge0_ptr.Edge(), vertex0, ON_UNSET_VALUE) ); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); + edge1.SetRelativeSharpness(prev_edge0_ptr.RelativeSharpness().Subdivided(1)); face1_eptrs[1] = edge1; edge1 = m_fsh.AllocateEdge( ring_vertex1[1], - ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), + ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0_ptr.Edge(), vertex0, ON_UNSET_VALUE), ring_vertex1[2], ON_SubDSectorType::IgnoredSectorCoefficient ); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); + edge1.SetRelativeSharpness(edge0_ptr.RelativeSharpness().Subdivided(0)); face1_eptrs[2] = edge1; face1 = m_fsh.AllocateQuad(level_zero_face_id, parent_face_idX, face1_eptrs); @@ -2958,10 +3011,9 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( { if (4 == i) face_m_edges = face->m_edgex; - const ON__UINT_PTR edge0_ptr = face_m_edges->m_ptr; - edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); - edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr); - vertex0 = edge0->m_vertex[edge0_dir]; + edge0_ptr = *face_m_edges; + edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr.m_ptr); + vertex0 = edge0_ptr.RelativeVertex(0); // ring_vertex1[0] = subdivision vertex on input face->Edge(i-1) // ring_vertex1[1] = subdivision vertex on input face->Vertex(i) = vertex0 @@ -2980,7 +3032,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( } - // debugggin checks. + // validity checks. // ring_vertex1[0] counts vary if ( 2 != ring_vertex1[1]->m_edge_count || 1 != ring_vertex1[1]->m_face_count) return ON_SUBD_RETURN_ERROR(false); @@ -3031,7 +3083,8 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ); if ( edge1.IsNull()) return ON_SUBD_RETURN_ERROR(false); - } + // edge1 is interior to neighbor_face0 and has no sharpness + } ///////////////////////////////////////////////////////////////////////////////// // @@ -3211,7 +3264,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( face1_corners[2] = const_cast(edge1_quartet[3]->m_vertex[1]); face1_corners[3] = ring_vertex1[2]; - edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), face1_corners[1], at_crease2_weight); + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE), face1_corners[1], at_crease2_weight); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); face1_eptrs[0] = edge1; @@ -3257,7 +3310,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( } else { - edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), face1_corners[3], at_crease2_weight ); + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE), face1_corners[3], at_crease2_weight ); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); face1_eptrs[3] = edge1.Reversed(); @@ -3458,19 +3511,19 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( switch (j) { case 0: - { - edge0 = sit.CurrentEdge(0); - if (nullptr == edge0) - return ON_SUBD_RETURN_ERROR(false); - edge1 = m_fsh.FindOrAllocateEdge( - face1_corners[0], - ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), - face1_corners[1], - at_crease2_weight - ); - if ( 0 == sit_limit && edge0 == sit_first_edge0 ) - sit_first_eptr1 = edge1; - } + edge0 = sit.CurrentEdge(0); + if (nullptr == edge0) + return ON_SUBD_RETURN_ERROR(false); + edge1 = m_fsh.FindOrAllocateEdge( + face1_corners[0], + ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE), + face1_corners[1], + at_crease2_weight + ); + if ( 0 == sit_limit && edge0 == sit_first_edge0 ) + sit_first_eptr1 = edge1; + if (edge0->IsSharp()) + edge1.SetRelativeSharpness(edge0->SubdivideSharpness(vertex0, vertex0 == edge0->m_vertex[1])); break; case 1: edge1 = m_fsh.FindOrAllocateEdge( @@ -3497,8 +3550,10 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( face1_corners[3], at_crease2_weight, face1_corners[0], - ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0) + ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE) ); + if (edge0->IsSharp()) + edge1.SetRelativeSharpness(edge0->SubdivideSharpness(vertex0, vertex0 == edge0->m_vertex[0])); } break; } diff --git a/opennurbs_subd_ring.cpp b/opennurbs_subd_ring.cpp index 24a6b4d1..9558b123 100644 --- a/opennurbs_subd_ring.cpp +++ b/opennurbs_subd_ring.cpp @@ -23,6 +23,410 @@ #include "opennurbs_subd_data.h" +static void Internal_PointRingSharpSubdivision( + const ON_SubDVertexTag center_vertex_tag, + double sector_coefficient, + const ON_SubDEdgePtr* edges, + size_t edges_stride, + const unsigned int N, + const unsigned int F, + ON_SubDEdgeSharpness* edge_sharpness, + double* point_ring, + size_t point_ring_stride +) +{ + // All ring vertices are either smooth or creases. + // + // The center vertex can be smooth, crease or dart. + // + // In all cases, the sector coefficient is passed in. + // + // All ring faces are quads. + // + // edges[] is used only to get the edge tag. + // In some cases, point_ring[] is the result + // of 1 subdivision from the elements in edges[]. + // // + // This function subdivides the point ring until all sharpness + // values become zero. At that point we can apply the usual + // sector limit surface matrix to calculate limit surface + // point and tangent plane normal. + + if (N < 2) + { + ON_SUBD_ERROR("Invalid input. N must be at least 2."); + return; + } + if (ON_SubDVertexTag::Smooth == center_vertex_tag || ON_SubDVertexTag::Dart == center_vertex_tag) + { + // These checks insure the pointer arithmetic below never dereferences an invalid + // pointer due to invalid input. + if (N != F) + { + ON_SUBD_ERROR("Invalid input. At least on of the center vertex tag, N, or F is wrong."); + return; + } + } + else if(ON_SubDVertexTag::Crease == center_vertex_tag) + { + // These checks insure the pointer arithmetic below never dereferences an invalid + // pointer due to invalid input. + if (N != F + 1) + { + ON_SUBD_ERROR("Invalid input. At least on of the center vertex tag, N, or F is wrong."); + return; + } + if (ON_SubDEdgeTag::Crease != ON_SUBD_EDGE_POINTER(edges->m_ptr)->m_edge_tag) + { + ON_SUBD_ERROR("Invalid input. In a crease sector the first edge must be a crease."); + return; + } + if (ON_SubDEdgeTag::Crease != ON_SUBD_EDGE_POINTER((edges + ((N - 1)* edges_stride) )->m_ptr)->m_edge_tag) + { + ON_SUBD_ERROR("Invalid input. In a crease sector the last edge must be a crease."); + return; + } + } + else + { + ON_SUBD_ERROR("Invalid input. Center vertex tag must be smooth, dart, or crease."); + return; + } + + const unsigned expected_vertex_C_count + = (ON_SubDVertexTag::Crease == center_vertex_tag) + ? 2U + : (ON_SubDVertexTag::Dart == center_vertex_tag ? 1U : 0U); + + if (ON_SubDVertexTag::Smooth == center_vertex_tag) + sector_coefficient = 0.5; + else if (false == (sector_coefficient > 0.0 && sector_coefficient < 1.0)) + { + // Input edges passed to ON_SubD::GetQuadSectorPointRing() + // had incorrect sector coefficients. Using 1/2 will be good enough + // to get some sort of answer. + ON_SUBD_ERROR("An input edges to ON_SubD::GetQuadSectorPointRing() had incorrect sector coeffient value."); + sector_coefficient = 0.5; + } + + const unsigned R = 1U + N + F; + ON_SimpleArray R1_buffer; + ON_3dPoint* R1 = R1_buffer.Reserve(R); + const size_t AQstride = 2 * point_ring_stride; + + // (V0[0],V0[1],V0[2]) = control net position of center vertex + const double* V0 = point_ring; + + // A0 is used to get the ends of the radial edges on the input point ring. + // In this function, "start of edge" means the central vertex control point (V0) + // and "end of edge" means the edge control points on the point ring. + // The stride for the A0 array is AQstride = 2*point_ring_stride. + // (A0[0],A0[1],A0[2]) = control net position of end of 1st edge + const double* A0 = V0 + point_ring_stride; + + // Q0 is used to get the quad points that are diagonally opposite from + // the center vertex on the input point ring. + // The stride for the Q0 array is AQstride = 2*point_ring_stride. + const double* Q0 = A0 + point_ring_stride; + + for (;;) + { + bool bSubdividePointRingAgain = false; + + // Face subdivision points = quad centroids. + for (unsigned i = 0; i < F; ++i) + { + R1 += 2; + const double* A0next = (i+1x = 0.25 * (V0[0] + A0[0] + Q0[0] + A0next[0]); + R1->y = 0.25 * (V0[1] + A0[1] + Q0[1] + A0next[1]); + R1->z = 0.25 * (V0[2] + A0[2] + Q0[2] + A0next[2]); + A0 += AQstride; + Q0 += AQstride; + } + + // reset pointers + R1 = R1_buffer.Array(); + A0 = V0 + point_ring_stride; + Q0 = A0 + point_ring_stride; + + // Edge subdivision points + unsigned vertex_A_count = 0; // number of crease and sharp edges. + unsigned vertex_C_count = 0; // number of crease edges. + ON_3dPoint vertex_A[2] = {}; // end points of the 1st two crease and/or sharp edges + ON_3dPoint vertex_C[2] = {}; // end points of the crease edges (at most two per sector) + double min_vertex_s = 0.0; // "min" means 2nd largest + double max_vertex_s = 0.0; + unsigned vertex_s_count = 0; + + ++R1; // Move P1 to point at end of 1st subdivided edge + const double* A0prev = A0 + (N - 1) * AQstride; + + // If a "lint" detector complains about Q0prev being a nullptr, it's wrong. + // When N != F, it is certain the first edge has a crease tag and Q0prev + // is not dereferenced. By the time Q0 is dereferenced, it is not nullptr. + const double* Q0prev = (N==F) ? (Q0 + (N - 1) * AQstride) : nullptr; + + for (unsigned i = 0; i < N; ++i) + { + if (ON_SubDEdgeTag::Crease == ON_SUBD_EDGE_POINTER(edges->m_ptr)->m_edge_tag) + { + // crease edge + if (vertex_C_count >= 2) + { + ON_SUBD_ERROR("Invalid input. A sector has at most 2 creased edges."); + return; + } + vertex_C[vertex_C_count] = ON_3dPoint(A0); + ++vertex_C_count; + if (vertex_A_count < 2) + vertex_A[vertex_A_count] = ON_3dPoint(A0); + ++vertex_A_count; + + R1->x = 0.5 * (V0[0] + A0[0]); + R1->y = 0.5 * (V0[1] + A0[1]); + R1->z = 0.5 * (V0[2] + A0[2]); + } + else + { + // smooth edge + + const double edge_s = edge_sharpness[i].Average(); + + if (edge_s >= 1.0) + { + // we don't need the ordinary edge subdivision point. + R1->x = 0.0; + R1->y = 0.0; + R1->z = 0.0; + } + else + { + // set R1 = ordinary edge subdivision point + + if (0.5 == sector_coefficient) + { + // smooth vertex or tagged vertex with "ordinary" number of edges. + R1->x = 0.375 * (V0[0] + A0[0]); + R1->y = 0.375 * (V0[1] + A0[1]); + R1->z = 0.375 * (V0[2] + A0[2]); + } + else + { + // tagged vertex with extraordinary number of edges. + const double w1 = 1.0 - sector_coefficient; + R1->x = 0.75 * (sector_coefficient * V0[0] + w1 * A0[0]); + R1->y = 0.75 * (sector_coefficient * V0[1] + w1 * A0[1]); + R1->z = 0.75 * (sector_coefficient * V0[2] + w1 * A0[2]); + } + + if (nullptr != Q0prev) + { + // The "if (nullptr != Q0prev)" check is always true. Dale Lear put the + // "if (nullptr != Q0prev)" check here to suppress incorrect compiler / lint warnings. + // The 2023 lint detectors are not sophisticated enought to figure out + // that if we get here, then Q0prev is not nullptr. + const double* A0next = (i + 1 < N) ? (A0 + AQstride) : (V0 + point_ring_stride); + R1->x += 0.0625 * (A0next[0] + Q0[0] + A0prev[0] + Q0prev[0]); + R1->y += 0.0625 * (A0next[1] + Q0[1] + A0prev[1] + Q0prev[1]); + R1->z += 0.0625 * (A0next[2] + Q0[2] + A0prev[2] + Q0prev[2]); + } + } + + + if (edge_s > 0.0) + { + // This edge is sharp, modify P1 to take account of sharpness. + if (vertex_A_count < 2) + vertex_A[vertex_A_count] = ON_3dPoint(A0); + ++vertex_A_count; + const double vertex_s = edge_sharpness[i][0]; + if (vertex_s > 0.0) + { + // NOTE: "min_vertex_s" is the 2nd largest value, not the actual minimum value + // The value is used only when there are exactly 2 nonzero vertex_s values. + if (0 == vertex_s_count) + { + min_vertex_s = vertex_s; + max_vertex_s = vertex_s; + } + else if (vertex_s > max_vertex_s) + { + min_vertex_s = max_vertex_s; + max_vertex_s = vertex_s; + } + else if (vertex_s < min_vertex_s) + { + min_vertex_s = vertex_s; + } + ++vertex_s_count; + } + + const double M[3] = { + 0.5 * (V0[0] + A0[0]), + 0.5 * (V0[1] + A0[1]), + 0.5 * (V0[2] + A0[2]) + }; + if (edge_s >= 1.0) + { + // crease subdivision point. + R1->x = M[0]; + R1->y = M[1]; + R1->z = M[2]; + } + else + { + // blend of smooth and crease edge subdivision points. + const double r = 1.0 - edge_s; + R1->x = r * R1->x + edge_s * M[0]; + R1->y = r * R1->y + edge_s * M[1]; + R1->z = r * R1->z + edge_s * M[2]; + } + + // sibdivide sharpness + edge_sharpness[i] = edge_sharpness[i].Subdivided(0); + + if (false == bSubdividePointRingAgain) + { + // When bSubdividePointRingAgain is true, we need to subdivide + // the point_ring at least one more time after finishing the + // current subdivision. + bSubdividePointRingAgain = edge_sharpness[i].IsNotZero(); + } + } + } + + // increment pointers for the next edge. + A0prev = A0; + A0 += AQstride; + Q0prev = Q0; + Q0 += AQstride; + edges += edges_stride; + R1 += 2; + } + + if (expected_vertex_C_count != vertex_C_count) + { + ON_SUBD_ERROR("Invalid input. Sector tag and number of crease edges are incompatible."); + return; + } + + // reset pointers + R1 = R1_buffer.Array(); + A0 = V0 + point_ring_stride; + Q0 = A0 + point_ring_stride; + edges -= N * edges_stride; + + const double vertex_s + = (2 == vertex_s_count && min_vertex_s < max_vertex_s) + ? 0.5 * (min_vertex_s + max_vertex_s) + : (vertex_A_count >= 2 ? max_vertex_s : 0.0); + + if (vertex_s >= 1.0) + { + // we don't need the ordinary vertex subdivision point. + R1->x = 0.0; + R1->y = 0.0; + R1->z = 0.0; + } + else + { + // set R1 = ordinary vertex subdivision point + if (2 == vertex_C_count) + { + // crease vertex + R1->x = 0.125 * (6.0 * V0[0] + vertex_C[0].x + vertex_C[1].x); + R1->y = 0.125 * (6.0 * V0[1] + vertex_C[0].y + vertex_C[1].y); + R1->z = 0.125 * (6.0 * V0[2] + vertex_C[0].z + vertex_C[1].z); + } + else + { + // smooth or dart vertex. (We know N = F) + double Asum[3] = {}; // Asum = sum of edge ring points + double Qsum[3] = {}; // Qsum = sum of quad face ring points + for (unsigned i = 0; i < N; ++i) + { + Asum[0] += A0[0]; + Asum[1] += A0[1]; + Asum[2] += A0[2]; + A0 += AQstride; + Qsum[0] += Q0[0]; + Qsum[1] += Q0[1]; + Qsum[2] += Q0[2]; + Q0 += AQstride; + } + A0 -= N * AQstride; + Q0 -= N * AQstride; + const double v = 1.0 - 1.75 / ((double)N); + const double nn = N * N; + const double a = 1.5 / nn; + const double q = 0.25 / nn; + R1->x = v * V0[0] + a * Asum[0] + q * Qsum[0]; + R1->y = v * V0[1] + a * Asum[1] + q * Qsum[1]; + R1->z = v * V0[2] + a * Asum[2] + q * Qsum[2]; + } + } + + if (vertex_s > 0.0) + { + double S[3]; + if (vertex_A_count == 2) + { + // Exactly 2 edges are creases or sharp + S[0] = 0.125 * (6.0 * V0[0] + vertex_A[0].x + vertex_A[1].x); + S[1] = 0.125 * (6.0 * V0[1] + vertex_A[0].y + vertex_A[1].y); + S[2] = 0.125 * (6.0 * V0[2] + vertex_A[0].z + vertex_A[1].z); + } + else + { + // 3 or more edges are creases or sharp (vertex_A_count > 2) + S[0] = V0[0]; + S[1] = V0[1]; + S[2] = V0[2]; + } + if (vertex_s >= 1.0) + { + R1->x = S[0]; + R1->y = S[1]; + R1->z = S[2]; + } + else + { + const double r = 1.0 - vertex_s; + R1->x = r * R1->x + vertex_s * S[0]; + R1->y = r * R1->y + vertex_s * S[1]; + R1->z = r * R1->z + vertex_s * S[2]; + } + } + + // R1 = subdivided point ring with sharpness taken into account + // Copy R1 to point_ring. + for (unsigned i = 0; i < R; ++i) + { + point_ring[0] = R1->x; + point_ring[1] = R1->y; + point_ring[2] = R1->z; + ++R1; + point_ring += point_ring_stride; + } + + if (false == bSubdividePointRingAgain) + { + // The point ring subdivisions have eliminated sharpness + // and the sector limit surface matrix can be applied to + // the point ring to calculate the limit surface point + // and tangent plane normal. + break; + } + + // reset pointers for next subdivision + // Each value in edge_sharpness has been reduced by 1 or is zero. + R1 -= R; + point_ring -= R * point_ring_stride; + } +} + + unsigned int ON_SubD::GetQuadSectorPointRing( bool bFirstPass, bool bSecondPass, @@ -36,6 +440,8 @@ unsigned int ON_SubD::GetQuadSectorPointRing( //// NO VALIDATION CHECKS //// CALLER INSURES INPUT HAS NON-nullptr POINTERS AND CORRECT COUNTS + // Except for internal testing functions, bSecondPass is always true. + double subP[3]; const double* Q = nullptr; @@ -45,27 +451,99 @@ unsigned int ON_SubD::GetQuadSectorPointRing( const double* point_ring1 = point_ring + (point_ring_count*point_ring_stride); + + const ON_SubDVertex* vertex0 = center_vertex; + const size_t element_stride = (nullptr != vertex0) ? 1 : 2; + if (nullptr == vertex0) + { + vertex0 = component_ring[0].Vertex(); + if (nullptr == vertex0) + return ON_SUBD_RETURN_ERROR(0); + } + + const ON_SubDEdgePtr* edges; + + // We need to count sharp edges, crease edges, and save edge sharpnesses. + // edge_sharpness[i].[0] = sharpeness at vertex0 end. + ON_SubDEdgeSharpness* edge_sharpness = nullptr; + double maxs = 0.0; + double sector_coefficient = (vertex0->IsDartOrCreaseOrCorner()) ? 0.0 : 0.5; + + edges = (1 == element_stride) ? vertex0->m_edges : (const ON_SubDEdgePtr*)(component_ring + 1); + for (unsigned i = 0; i < N; ++i, edges += element_stride) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges->m_ptr); + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + const ON_SubDEdgeTag etag = edge->m_edge_tag; + if (ON_SubDEdgeTag::Smooth == etag || ON_SubDEdgeTag::SmoothX == etag) + { + if (0.0 == sector_coefficient) + { + // The sector coefficient for the central vertex is a property + // of the sector and constant on all smooth edges in the sector. + // So, it suffices to harvest it from any smooth edge in the sector. + sector_coefficient = edge->m_sector_coefficient[ON_SUBD_EDGE_DIRECTION(edges->m_ptr)]; + } + ON_SubDEdgeSharpness s = edge->Sharpness(); + if ( s.IsNotZero() ) + { + if (nullptr == edge_sharpness) + edge_sharpness = (ON_SubDEdgeSharpness*)calloc(N, sizeof(edge_sharpness[0])); + if (1 == ON_SUBD_EDGE_DIRECTION(edges->m_ptr)) + s = s.Reversed(); + edge_sharpness[i] = s; + if (maxs < s[0]) + maxs = s[0]; + if (maxs < s[1]) + maxs = s[1]; + } + } + } + + if (nullptr != edge_sharpness && bSecondPass) + { + // Let the subdivision in the 2nd pass do some (or all) of the work. + bFirstPass = false; + } + + // Except for internal testing functions, bSecondPass is always true. + // Sometimes bFirstPass is false when it is already known that + // an ring edge vertex is tagged. for (unsigned int pass = (bFirstPass ? 0U : 1U); pass < (bSecondPass ? 2U : 1U); pass++) { - double* P = point_ring; - - const ON_SubDEdgePtr* edges; - const ON_SubDFacePtr* faces; - size_t element_stride; - - const ON_SubDVertex* vertex0 = center_vertex; - if (nullptr != vertex0) + if (1 == pass && nullptr != edge_sharpness) { - edges = vertex0->m_edges; - faces = (const ON_SubDFacePtr*)(vertex0->m_faces); - element_stride = 1; - } - else - { - vertex0 = component_ring[0].Vertex(); - edges = (const ON_SubDEdgePtr*)(component_ring+1); - faces = (const ON_SubDFacePtr*)(component_ring+2); - element_stride = 2; + if (maxs <= 1.0) + { + // The single subdivision step that occurs during this + // pass will remove all sharpness from the point ring + // and Internal_PointRingSharpSubdivision() doesn't need to be called. + onfree(edge_sharpness); + edge_sharpness = nullptr; + maxs = 0.0; + } + else + { + // we need to subdivide the sharpness values that will get passed + // to Internal_PointRingSharpSubdivision(). + maxs = 0.0; + for (unsigned k = 0; k < N; ++k) + { + const ON_SubDEdgeSharpness s1 = edge_sharpness[k].Subdivided(0); + edge_sharpness[k] = s1; + if (maxs < s1[0]) + maxs = s1[0]; + if (maxs < s1[1]) + maxs = s1[1]; + } + if (0.0 == maxs) + { + // subdivision removed sharpness. + onfree(edge_sharpness); + edge_sharpness = nullptr; + } + } } if (0 == pass) @@ -80,12 +558,17 @@ unsigned int ON_SubD::GetQuadSectorPointRing( Q = subP; } + + double* P = point_ring; P[0] = Q[0]; P[1] = Q[1]; P[2] = Q[2]; P += point_ring_stride; - for ( unsigned int i = 0; i < N; i++, edges += element_stride, faces += element_stride ) + const ON_SubDEdgePtr* edges0 = (1 == element_stride) ? vertex0->m_edges : (const ON_SubDEdgePtr*)(component_ring + 1); + edges = edges0; + const ON_SubDFacePtr* faces = (1 == element_stride) ? ((const ON_SubDFacePtr * )vertex0->m_faces) : (const ON_SubDFacePtr*)(component_ring + 2); + for ( unsigned int i = 0; i < N; ++i, edges += element_stride, faces += element_stride ) { // Get edge point ON__UINT_PTR eptr = edges->m_ptr; @@ -93,29 +576,60 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if (nullptr == edge) return ON_SUBD_RETURN_ERROR(0); eptr = 1 - ON_SUBD_EDGE_DIRECTION(eptr); - const ON_SubDVertex* vertex = edge->m_vertex[eptr]; - if ( nullptr == vertex) + const ON_SubDVertex* vertex1 = edge->m_vertex[eptr]; + if ( nullptr == vertex1) return ON_SUBD_RETURN_ERROR(0); if (0 == pass) { if (ON_SubDEdgeTag::SmoothX == edge->m_edge_tag) break; // need to use subdivision point in 2nd pass - if (ON_SubDVertexTag::Smooth == vertex->m_vertex_tag + + if (ON_SubDVertexTag::Smooth == vertex1->m_vertex_tag || ON_SubDEdgeTag::Crease == edge->m_edge_tag || 0.5 == edge->m_sector_coefficient[eptr] ) { - Q = vertex->m_P; + // We have one of these cases. + // 1) edge is smooth, vertex1 is smooth. + // 2) edge is smooth, vertex1 is creas/corner/dart with an ordinary. + // configuration (0.5 == edge->m_sector_coefficient[eptr]). + // 3) edge is crease. + // + // If the edge is not sharp, then the conditions + // needed to use a matrix to calculate the limit + // surface from the point ring are valid for this edge. + // + // If the edge is sharp, a later call to + // Internal_PointRingSharpSubdivision() will subdivide + // the sharpness away. + Q = vertex1->m_P; } else - break; // need to use subdivision point in 2nd pass + { + // The edge is smooth, vertex1 is a crease/corner/dart, + // and there is a subdivision bias introduced by + // an extraordinary configuration of edges at + // vertex1. The subdivision in the 2nd pass will + // remove these complications so the conditions + // needed to use a matrix to calculate the limit + // surface from the point ring will be valid. + break; + } } else { + // The 2nd pass subdivides the elements in the initial ring + // to insure the edge from the center to the ring point + // is either a smooth edge going to a smooth ring vertex + // or a crease edge going to a crease ring vertex. + // + // If the edge is sharp, a later call to + // Internal_PointRingSharpSubdivision() will subdivide + // the sharpness away. if (false == edge->GetSubdivisionPoint(subP)) return ON_SUBD_RETURN_ERROR(0); - Q = subP; + // Q = subP set above when vertex0 was subdivided. } P[0] = Q[0]; P[1] = Q[1]; @@ -125,6 +639,26 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if (point_ring1 == P) { // success on a sector with crease boundary + if (nullptr != edge_sharpness) + { + // At least one of the smooth edges is sharp. + // Internal_PointRingSharpSubdivision() subdivides + // the ring enough to remove sharpness so the conditions + // needed to use a matrix to calculate the limit + // surface from the point ring will be valid. + Internal_PointRingSharpSubdivision( + vertex0->m_vertex_tag, + sector_coefficient, + edges - (N - 1) * element_stride, + element_stride, + N, + F, + edge_sharpness, + point_ring, + point_ring_stride + ); + onfree(edge_sharpness); + } return point_ring_count; } @@ -132,7 +666,11 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if (0 == pass) { if (4 != face->m_edge_count) - break; // need 2nd pass + { + // face is not a quad. + // We need 2nd pass to subdivide this face so the center vertex is surrounded by quads. + break; + } // find the vertex opposite vertex0 eptr = face->m_edge4[0].m_ptr; @@ -141,9 +679,9 @@ unsigned int ON_SubD::GetQuadSectorPointRing( return ON_SUBD_RETURN_ERROR(0); eptr = ON_SUBD_EDGE_DIRECTION(eptr); if (vertex0 == edge->m_vertex[eptr]) - eptr = 2; // vertex0 = face->Vertex(0), set vertex = face->vertex(2) + eptr = 2; // vertex0 = face->Vertex(0), set vertexQ = face->vertex(2) else if (vertex0 == edge->m_vertex[1-eptr]) - eptr = 3; // vertex0 = face->Vertex(1), set vertex = face->vertex(3) + eptr = 3; // vertex0 = face->Vertex(1), set vertexQ = face->vertex(3) else { eptr = face->m_edge4[2].m_ptr; @@ -152,9 +690,9 @@ unsigned int ON_SubD::GetQuadSectorPointRing( return ON_SUBD_RETURN_ERROR(0); eptr = ON_SUBD_EDGE_DIRECTION(eptr); if (vertex0 == edge->m_vertex[eptr]) - eptr = 0; // vertex0 = face->Vertex(2), set vertex = face->vertex(0) + eptr = 0; // vertex0 = face->Vertex(2), set vertexQ = face->vertex(0) else if (vertex0 == edge->m_vertex[1-eptr]) - eptr = 1; // vertex0 = face->Vertex(3), set vertex = face->vertex(1) + eptr = 1; // vertex0 = face->Vertex(3), set vertexQ = face->vertex(1) else return ON_SUBD_RETURN_ERROR(0); } @@ -163,15 +701,16 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if ( nullptr == edge) return ON_SUBD_RETURN_ERROR(0); eptr = ON_SUBD_EDGE_DIRECTION(eptr); - vertex = edge->m_vertex[eptr]; - if ( nullptr == vertex) + const ON_SubDVertex* vertexQ = edge->m_vertex[eptr]; + if ( nullptr == vertexQ) return ON_SUBD_RETURN_ERROR(0); - Q = vertex->m_P; + Q = vertexQ->m_P; } else { if (false == face->GetSubdivisionPoint(subP)) return ON_SUBD_RETURN_ERROR(0); + // Q = subP set above when vertex0 was subdivided. } P[0] = Q[0]; P[1] = Q[1]; @@ -182,6 +721,26 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if (point_ring1 == P) { // success on a smooth sector + if (nullptr != edge_sharpness) + { + // At least one of the smooth edges is sharp. + // Internal_PointRingSharpSubdivision() subdivides + // the ring enough to remove sharpness so the conditions + // needed to use a matrix to calculate the limit + // surface from the point ring will be valid. + Internal_PointRingSharpSubdivision( + vertex0->m_vertex_tag, + sector_coefficient, + edges - N * element_stride, + element_stride, + N, + F, + edge_sharpness, + point_ring, + point_ring_stride + ); + onfree(edge_sharpness); + } return point_ring_count; } } @@ -330,7 +889,7 @@ unsigned int ON_SubD::GetSectorSubdivsionPointRing( return ON_SUBD_RETURN_ERROR(0); const bool bFirstPass = false; - const bool bSecondPass = true; + const bool bSecondPass = true; // returned ring will be at subdivision level 1 (or greater if there are edges with sharpness > 1) unsigned int rc = GetQuadSectorPointRing(bFirstPass,bSecondPass,nullptr,component_ring,component_ring_count, subd_point_ring, subd_point_ring_stride); if (0 == rc) @@ -384,6 +943,7 @@ unsigned int ON_SubD::GetSectorPointRing( return ON_SUBD_RETURN_ERROR(0); const bool bFirstPass = true; + // Except for internal testing functions, bSubdivideIfNeeded is always true. const bool bSecondPass = bSubdivideIfNeeded; unsigned int rc = GetQuadSectorPointRing(bFirstPass,bSecondPass,nullptr, component_ring,component_ring_count, point_ring,point_ring_stride); @@ -456,23 +1016,6 @@ unsigned int ON_SubD::GetSectorPointRing( return ON_SUBD_RETURN_ERROR(0); } -static double Subdivide_CenterVertexSectorWeight( - const ON_SubDEdge* edge0, - const ON_SubDVertex* vertex0 - ) -{ - if ( ON_SubDEdgeTag::Crease == edge0->m_edge_tag) - return ON_SubDSectorType::IgnoredSectorCoefficient; - if (ON_SubDEdgeTag::Smooth == edge0->m_edge_tag || ON_SubDEdgeTag::SmoothX == edge0->m_edge_tag) - { - if (vertex0 == edge0->m_vertex[0]) - return edge0->m_sector_coefficient[0]; - if (vertex0 == edge0->m_vertex[1]) - return edge0->m_sector_coefficient[1]; - } - return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); -} - const ON_SubDVertex* ON_SubD::SubdivideSector( const ON_SubDVertex* center_vertex, const ON_SubDComponentPtr* component_ring, @@ -567,7 +1110,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( = ON_SubDEdgeTag::Crease == edge1_tag ? ON_SubDSectorType::CreaseSectorCoefficient(5-K) : ON_SubDSectorType::IgnoredSectorCoefficient; - ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorCoefficient ); + ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0,vertex0,ON_UNSET_VALUE), vertex1, ON_SubDSectorType::IgnoredSectorCoefficient ); if (edge1.IsNull()) return ON_SUBD_RETURN_ERROR(nullptr); edge1.Edge()->m_edge_tag = edge1_tag; @@ -602,7 +1145,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( v1[K] = fsh.AllocateVertex(edge0); if (nullptr == v1[K]) return ON_SUBD_RETURN_ERROR(nullptr); - e1[K] = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0, vertex0), v1[K], ON_SubDSectorType::IgnoredSectorCoefficient); + e1[K] = fsh.AllocateEdge(v1[0], ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE), v1[K], ON_SubDSectorType::IgnoredSectorCoefficient); if (e1[K].IsNull()) return ON_SUBD_RETURN_ERROR(nullptr); e1[K].Edge()->m_edge_tag = edge1_tag; diff --git a/opennurbs_subd_sector.cpp b/opennurbs_subd_sector.cpp index e0b3c15e..34e73cb5 100644 --- a/opennurbs_subd_sector.cpp +++ b/opennurbs_subd_sector.cpp @@ -522,6 +522,37 @@ double ON_SubDSectorType::CornerSectorCoefficient( return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient); } +double ON_SubDSectorType::CopyEdgeSectorCoefficient( + const class ON_SubDEdge* edge, + const class ON_SubDVertex* vertex, + double error_return_value +) +{ + if ( nullptr != edge && nullptr != vertex ) + { + const int evi = (vertex == edge->m_vertex[0]) ? 0 : (vertex == edge->m_vertex[1] ? 1 : 2); + if (evi < 2) + { + if (edge->IsSmooth()) + { + if ( vertex->IsDartOrCreaseOrCorner() ) + return edge->m_sector_coefficient[evi]; + if ( vertex->IsSmooth() ) + return edge->m_sector_coefficient[evi]; + } + else if ( edge->IsCrease() ) + { + return ON_SubDSectorType::IgnoredSectorCoefficient; + } + } + } + + // null pointers, unset tags, vertex and edge are not attached, + // or other conditions where returning a m_sector_coefficient[] + // is impossible or makes no sense. + return error_return_value; +} + static int CompareUnsigned(unsigned int a, unsigned int b) { if (a < b) diff --git a/opennurbs_system_runtime.h b/opennurbs_system_runtime.h index 04800941..a3c43796 100644 --- a/opennurbs_system_runtime.h +++ b/opennurbs_system_runtime.h @@ -43,7 +43,7 @@ #define ON_RUNTIME_WIN #endif -#elif defined(__ANDROID__) || defined(__EMSCRIPTEN__) +#elif defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(ANDROID) // __EMSCRIPTEN__ is for a web assembly compile which currently compiles with the // same settings as an android build. We will need to add an ON_RUNTIME_WASM once // the __EMSCRIPTEN__ compile stabilizes @@ -142,28 +142,43 @@ #endif #elif defined(ON_RUNTIME_ANDROID) +// Android is Linux +#define ON_RUNTIME_LINUX -#if !defined(ON_SIZEOF_WCHAR_T) -#define ON_SIZEOF_WCHAR_T 4 -#endif - -#elif defined(ON_RUNTIME_LINUX) - -#if defined(__x86_64__) +#if defined(__x86_64__) || defined(__LP64__) || defined(__ppc64__) #define ON_64BIT_RUNTIME #else #define ON_32BIT_RUNTIME #endif +#if !defined(ON_SIZEOF_WCHAR_T) +#define ON_SIZEOF_WCHAR_T 4 +#endif + +// It looks like all android compiles are little endian. +// If there is a better test, please add it here +#if !defined(ON_LITTLE_ENDIAN) +#define ON_LITTLE_ENDIAN +#endif + +#elif defined(ON_RUNTIME_LINUX) #if !defined(ON_SIZEOF_WCHAR_T) #define ON_SIZEOF_WCHAR_T 4 #endif -#if !defined(ON_LITTLE_ENDIAN) -#if defined( __x86_64__ ) -#define ON_LITTLE_ENDIAN +#include +#if INTPTR_MAX == INT64_MAX +#define ON_64BIT_RUNTIME +#elif INTPTR_MAX == INT32_MAX +#define ON_32BIT_RUNTIME #endif + +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ON_LITTLE_ENDIAN +#else +#define ON_BIG_ENDIAN #endif #endif diff --git a/opennurbs_texture.h b/opennurbs_texture.h index 7aaa35c9..acc2a0ce 100644 --- a/opennurbs_texture.h +++ b/opennurbs_texture.h @@ -233,21 +233,20 @@ public: // list of pre-defined channel ids enum class MAPPING_CHANNEL : unsigned int { - tc_channel = 0U, // Use the texture coordinate values - // currently on the geometric object. - default_channel = 1U, // Use either default mappingU, or the "Custom" mapping applied to the object + tc_channel = 0U, // Deprecated. Use 'default_channel' instead. + default_channel = 1U, // Use either default mapping, or the "Custom" mapping applied to the object - screen_based_channel = 0xFFFFFFF1U, - wcs_channel = 0xFFFFFFF2U, - wcs_box_channel = 0xFFFFFFF3U, - environment_map_box_channel = 0xFFFFFFF4U, - environment_map_light_probe_channel = 0xFFFFFFF5U, - environment_map_spherical_channel = 0xFFFFFFF6U, - environment_map_cube_map_channel = 0xFFFFFFF7U, + screen_based_channel = 0xFFFFFFF1U, + wcs_channel = 0xFFFFFFF2U, + wcs_box_channel = 0xFFFFFFF3U, + environment_map_box_channel = 0xFFFFFFF4U, + environment_map_light_probe_channel = 0xFFFFFFF5U, + environment_map_spherical_channel = 0xFFFFFFF6U, + environment_map_cube_map_channel = 0xFFFFFFF7U, environment_map_vcross_cube_map_channel = 0xFFFFFFF8U, environment_map_hcross_cube_map_channel = 0xFFFFFFF9U, - environment_map_hemispherical_channel = 0xFFFFFFFAU, - environment_map_emap_channel = 0xFFFFFFFFU, + environment_map_hemispherical_channel = 0xFFFFFFFAU, + environment_map_emap_channel = 0xFFFFFFFFU, srfp_channel = 0xFFFFFFFEU, // Use surface parameterization. emap_channel = 0xFFFFFFFFU // Environment map the geometric object - deprecated. Use environment_map_emap_channel instead @@ -262,22 +261,24 @@ public: const ON_SHA1_Hash ContentHash() const; - // 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 - // attributes with an ON_MappingChannel entry that has a - // matching m_mapping_channel_id value, then the mapping - // identified by ON_MappingChannel::m_mapping_id is used. - // A value of zero means no mapping is supplied - // and the texture coordinates on the mesh are - // used. void SetBuiltInMappingChannel( ON_Texture::MAPPING_CHANNEL built_in_mapping_channel_as_unsigned ); void SetMappingChannel( unsigned int mapping_channel_id ); - unsigned int m_mapping_channel_id = 0; + + // 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 + // attributes with an ON_MappingChannel entry that has a + // matching m_mapping_channel_id value, then the mapping + // identified by ON_MappingChannel::m_mapping_id is used. + // If a matching ON_MappingChannel::m_mapping_id is not + // found, then an attempt will be made to find a mapping + // with a ON_MappingChannel::m_mapping_id of 1. If that also + // fails, then surface parameter mapping will be used. + unsigned int m_mapping_channel_id = 1; // Image file // If m_image_file_reference is set and m_image_file_reference.FullPath() diff --git a/opennurbs_topology.h b/opennurbs_topology.h index 25cc51cb..4e1fcb6c 100644 --- a/opennurbs_topology.h +++ b/opennurbs_topology.h @@ -137,6 +137,11 @@ public: /// InteriorSlit = 16384, + /// + /// The InteriorSharp edge has two faces with a guaranteed surface tangent continuity. + /// An InteriorSharp edge is an InteriorSmooth edge with added SubD sharpness. + /// + InteriorSharp = 32768, /// /// ON_ComponentAttributes::EdgeAttributes::Mask can be used to isolate EdgeAttributes bits diff --git a/opennurbs_unicode.h b/opennurbs_unicode.h index 216331f7..b981fa1f 100644 --- a/opennurbs_unicode.h +++ b/opennurbs_unicode.h @@ -309,13 +309,15 @@ enum ON_UnicodeCodePoint /// ON_RecyclingSymbol = 0x2672, - /// /// BLACK UNIVERSAL RECYCLING SYMBOL U+267B (♻) /// This is a good cold point for testing glyph substitution. /// ON_BlackRecyclingSymbol = 0x267B, + /// WARNING SIGN U+26A0 (⚠) + ON_WarningSign = 0x26A0, + /// /// REPLACEMENT CHARACTER U+FFFD (�) /// By convention, U+FFFD is used to mark string elements where diff --git a/opennurbs_uuid.cpp b/opennurbs_uuid.cpp index 1bde5ab9..5318bfc7 100644 --- a/opennurbs_uuid.cpp +++ b/opennurbs_uuid.cpp @@ -174,7 +174,7 @@ bool ON_CreateUuid( ON_UUID& new_uuid ) // identifies the user and some // customers may object. return true; -#elif defined(ON_COMPILER_CLANG) +#elif defined(ON_RUNTIME_APPLE) // Header: #include if ( ON::endian::little_endian == ON::Endian() ) { diff --git a/opennurbs_viewport.cpp b/opennurbs_viewport.cpp index 9413373c..7d8544b3 100644 --- a/opennurbs_viewport.cpp +++ b/opennurbs_viewport.cpp @@ -4474,12 +4474,23 @@ bool ON_Viewport::SetViewScale( double x, double y, double z) // solution for supporting RCP viewports. What this does do is let us have users // experiment with a -1.0 horizontal scale in a custom display mode and tell // us if this is the desired display that they are after. + // 5 Jan 2023 S. Baer (RH-71044) + // Always allow case where the (x,y,z) = (1,1,1) in order to reset the scale + // to 1 across the board + bool validInput = fabs(x) > ON_ZERO_TOLERANCE && ON_IsValid(x) + && fabs(y) > ON_ZERO_TOLERANCE && ON_IsValid(y) + && fabs(z) > ON_ZERO_TOLERANCE && ON_IsValid(z); + if (!validInput) + return false; + + bool allOnes = fabs(x - 1.0) < ON_EPSILON + && fabs(y - 1.0) < ON_EPSILON + && fabs(z - 1.0) < ON_EPSILON; + if (allOnes) + return SetClipModXform(ON_Xform::IdentityTransformation); + bool rc = false; - if ( IsParallelProjection() - && fabs(x) > ON_ZERO_TOLERANCE && ON_IsValid(x) - && fabs(y) > ON_ZERO_TOLERANCE && ON_IsValid(y) - && fabs(z) > ON_ZERO_TOLERANCE && ON_IsValid(z) - ) + if ( IsParallelProjection() ) { ON_Xform xform(ON_Xform::IdentityTransformation); xform.m_xform[0][0] = x; diff --git a/opennurbs_xform.cpp b/opennurbs_xform.cpp index c386df9e..520df9dc 100644 --- a/opennurbs_xform.cpp +++ b/opennurbs_xform.cpp @@ -1463,6 +1463,12 @@ int ON_Xform::IsRigid(double tolerance) const double dist = ApproxDist2Ortho(L); rval = (dist < tolerance); + if (rval) + { + double det = L.Determinant(); + if (det < 0) + rval = -1; + } } return rval; } diff --git a/opennurbs_xml.cpp b/opennurbs_xml.cpp index 53cb30b9..f3d8ea48 100644 --- a/opennurbs_xml.cpp +++ b/opennurbs_xml.cpp @@ -5155,12 +5155,15 @@ bool ON_RunXMLTests(const wchar_t* test_folder) #pragma warning (pop) //------------------------------------------------------------------------------------------------------------------- -ON_UUID uuidPostEffect_ToneMapper_Clamp = { 0xacb8d258, 0xc1d6, 0x499d, { 0xaa, 0x23, 0x02, 0xdc, 0xde, 0xa2, 0xb0, 0xa2 } }; -ON_UUID uuidPostEffect_Gamma = { 0x84c0798d, 0xc43a, 0x4402, { 0x88, 0x91, 0xe0, 0xc8, 0x08, 0x8e, 0x67, 0xca } }; -ON_UUID chanRGBA = { 0x453a9a1c, 0x9307, 0x4976, { 0xb2, 0x82, 0x4e, 0xad, 0x4d, 0x53, 0x98, 0x79 } }; -ON_UUID chanDistanceFromCamera = { 0xb752ce0b, 0xc219, 0x4bdd, { 0xb1, 0x34, 0x26, 0x42, 0x5e, 0x1c, 0x43, 0x31 } }; -ON_UUID uuidRenderSettingsPreset_Studio = { 0x5898cc05, 0x4202, 0x4dfb, { 0x83, 0xfe, 0x8f, 0xa8, 0x8f, 0x91, 0xc7, 0xd6 } }; -ON_UUID uuidRenderSettingsPreset_Custom = { 0xc89a74fb, 0x1451, 0x4a9b, { 0xb8, 0x7d, 0xe3, 0x0f, 0xf3, 0x51, 0x0f, 0x96 } }; +ON_UUID uuidPostEffect_ToneMapper_Clamp = { 0xacb8d258, 0xc1d6, 0x499d, { 0xaa, 0x23, 0x02, 0xdc, 0xde, 0xa2, 0xb0, 0xa2 } }; +ON_UUID uuidPostEffect_Gamma = { 0x84c0798d, 0xc43a, 0x4402, { 0x88, 0x91, 0xe0, 0xc8, 0x08, 0x8e, 0x67, 0xca } }; +ON_UUID chanRGBA = { 0x453a9a1c, 0x9307, 0x4976, { 0xb2, 0x82, 0x4e, 0xad, 0x4d, 0x53, 0x98, 0x79 } }; +ON_UUID chanDistanceFromCamera = { 0xb752ce0b, 0xc219, 0x4bdd, { 0xb1, 0x34, 0x26, 0x42, 0x5e, 0x1c, 0x43, 0x31 } }; +ON_UUID uuidRenderSettingsPreset_Studio = { 0x5898cc05, 0x4202, 0x4dfb, { 0x83, 0xfe, 0x8f, 0xa8, 0x8f, 0x91, 0xc7, 0xd6 } }; +ON_UUID uuidRenderSettingsPreset_Custom = { 0xc89a74fb, 0x1451, 0x4a9b, { 0xb8, 0x7d, 0xe3, 0x0f, 0xf3, 0x51, 0x0f, 0x96 } }; +ON_UUID uuidRenderSettingsPreset_Exterior = { 0x1346FE79, 0xBF49, 0x4BB6, { 0x86, 0xF4, 0xF2, 0xC2, 0x81, 0xD1, 0xD5, 0x5A } }; +ON_UUID uuidRenderSettingsPreset_Interior = { 0x14A1D7E9, 0xC75D, 0x464D, { 0xBB, 0x81, 0x38, 0x1C, 0xA2, 0xF1, 0xC9, 0x58 } }; + //------------------------------------------------------------------------------------------------------------------- #pragma ON_PRAGMA_WARNING_PUSH diff --git a/zlib/CMakeLists.txt b/zlib/CMakeLists.txt new file mode 100644 index 00000000..4cfd86ac --- /dev/null +++ b/zlib/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required (VERSION 3.4) +project( zlib C) + + +set( INCLUDE_DIRS +. +) + +set( PROJECT_HEADERS +crc32.h +deflate.h +inffast.h +inffixed.h +inflate.h +inftrees.h +trees.h +zconf.h +zlib.h +zutil.h +) + +set( SOURCES +adler32.c +compress.c +crc32.c +deflate.c +infback.c +inffast.c +inflate.c +inftrees.c +trees.c +uncompr.c +zutil.c +) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) # -fPIC +add_library( zlib STATIC +${SOURCES} +${PROJECT_HEADERS} +${RESOURCES} +) +target_compile_definitions(zlib PRIVATE Z_PREFIX MY_ZCALLOC) + +target_include_directories( zlib + PUBLIC +. +) + +install( TARGETS zlib DESTINATION "lib") +install( FILES DESTINATION "include/zlib")