diff --git a/source/MRMesh/MRMesh.vcxproj b/source/MRMesh/MRMesh.vcxproj index 8e948f8493a3..ccc6bd59c3cd 100644 --- a/source/MRMesh/MRMesh.vcxproj +++ b/source/MRMesh/MRMesh.vcxproj @@ -257,6 +257,8 @@ + + @@ -459,6 +461,7 @@ + diff --git a/source/MRMesh/MRMesh.vcxproj.filters b/source/MRMesh/MRMesh.vcxproj.filters index 143b3846d8cd..886fc545462d 100644 --- a/source/MRMesh/MRMesh.vcxproj.filters +++ b/source/MRMesh/MRMesh.vcxproj.filters @@ -604,7 +604,7 @@ Source Files\AABBTree - Source Files\PointCloud + Source Files\Relax Source Files\Math @@ -733,7 +733,7 @@ Source Files\Mesh - Source Files\MeshAlgorithm + Source Files\Relax Source Files\MeshAlgorithm @@ -843,6 +843,12 @@ Source Files\History + + Source Files\Relax + + + Source Files\Relax + @@ -1427,6 +1433,9 @@ Source Files\Polyline + + Source Files\Polyline + diff --git a/source/MRMesh/MRMeshRelax.h b/source/MRMesh/MRMeshRelax.h index f9b52723128a..6f89d55d9145 100644 --- a/source/MRMesh/MRMeshRelax.h +++ b/source/MRMesh/MRMeshRelax.h @@ -1,6 +1,7 @@ #pragma once #include "MRMeshFwd.h" #include "MRProgressCallback.h" +#include "MRRelaxParams.h" namespace MR { @@ -9,16 +10,6 @@ namespace MR /// \ingroup MeshAlgorithmGroup /// \{ -struct RelaxParams -{ - /// number of iterations - int iterations{ 1 }; - /// region to relax - const VertBitSet* region{ nullptr }; - /// speed of relaxing, typical values (0.0, 0.5] - float force{ 0.5f }; -}; - struct MeshRelaxParams : RelaxParams { /// smooth tetrahedron verts (with complete three edges ring) to base triangle (based on its edges destinations) @@ -34,12 +25,6 @@ MRMESH_API bool relax( Mesh& mesh, const MeshRelaxParams& params = {}, ProgressC /// \return true if was finished successfully, false if was interrupted by progress callback MRMESH_API bool relaxKeepVolume( Mesh& mesh, const MeshRelaxParams& params = {}, ProgressCallback cb = {} ); -enum class RelaxApproxType -{ - Planar, - Quadric -}; - struct MeshApproxRelaxParams : MeshRelaxParams { /// radius to find neighbors by surface diff --git a/source/MRMesh/MRPointCloudRelax.h b/source/MRMesh/MRPointCloudRelax.h index 89ea45e4404e..21488a75ac5b 100644 --- a/source/MRMesh/MRPointCloudRelax.h +++ b/source/MRMesh/MRPointCloudRelax.h @@ -1,6 +1,7 @@ #pragma once #include "MRMeshFwd.h" -#include "MRMeshRelax.h" +#include "MRProgressCallback.h" +#include "MRRelaxParams.h" namespace MR { diff --git a/source/MRMesh/MRPolylineRelax.cpp b/source/MRMesh/MRPolylineRelax.cpp new file mode 100644 index 000000000000..783e84c1da49 --- /dev/null +++ b/source/MRMesh/MRPolylineRelax.cpp @@ -0,0 +1,130 @@ +#include "MRPolylineRelax.h" +#include "MRBitSetParallelFor.h" +#include "MRPolyline.h" +#include "MRTimer.h" +#include "MRVector2.h" +#include "MRWriter.h" + +namespace MR +{ + +template +bool relax( Polyline &polyline, const RelaxParams ¶ms, ProgressCallback cb ) +{ + if ( params.iterations <= 0 ) + return true; + + MR_TIMER + MR_WRITER(polyline) + + Vector newPoints; + const auto& zone = polyline.topology.getVertIds( params.region ); + + bool keepGoing = true; + for ( int i = 0; i < params.iterations; ++i ) + { + ProgressCallback internalCb; + if ( cb ) + { + internalCb = [&]( float p ) + { + return cb(( float( i ) + p ) / float( params.iterations )); + }; + } + + newPoints = polyline.points; + keepGoing = BitSetParallelFor( zone, [&]( VertId v ) + { + auto e0 = polyline.topology.edgeWithOrg( v ); + assert( !polyline.topology.isLoneEdge( e0 ) ); + auto e1 = polyline.topology.next( e0 ); + if ( e0 == e1 ) + return; + + auto mp = ( polyline.destPnt( e0 ) + polyline.destPnt( e1 ) ) / 2.f; + + auto& np = newPoints[v]; + auto pushForce = params.force * ( mp - np ); + np += pushForce; + }, internalCb ); + polyline.points.swap( newPoints ); + if ( !keepGoing ) + break; + } + return keepGoing; +} + +template +bool relaxKeepArea( Polyline &polyline, const RelaxParams ¶ms, ProgressCallback cb ) +{ + if ( params.iterations <= 0 ) + return true; + + MR_TIMER + MR_WRITER(polyline) + + Vector newPoints; + const auto& zone = polyline.topology.getVertIds( params.region ); + std::vector vertPushForces( zone.size() ); + + bool keepGoing = true; + for ( int i = 0; i < params.iterations; ++i ) + { + ProgressCallback internalCb1, internalCb2; + if ( cb ) + { + internalCb1 = [&] ( float p ) + { + return cb( ( float( i ) + p * 0.5f ) / float( params.iterations ) ); + }; + internalCb2 = [&] ( float p ) + { + return cb( ( float( i ) + p * 0.5f + 0.5f ) / float( params.iterations ) ); + }; + } + + keepGoing = BitSetParallelFor( zone, [&]( VertId v ) + { + auto e0 = polyline.topology.edgeWithOrg( v ); + assert( !polyline.topology.isLoneEdge( e0 ) ); + auto e1 = polyline.topology.next( e0 ); + if ( e0 == e1 ) + return; + + auto mp = ( polyline.destPnt( e0 ) + polyline.destPnt( e1 ) ) / 2.f; + + vertPushForces[v] = params.force * ( mp - polyline.points[v] ); + }, internalCb1 ); + if ( !keepGoing ) + break; + + newPoints = polyline.points; + keepGoing = BitSetParallelFor( zone, [&]( VertId v ) + { + auto e0 = polyline.topology.edgeWithOrg( v ); + assert( !polyline.topology.isLoneEdge( e0 ) ); + auto e1 = polyline.topology.next( e0 ); + if ( e0 == e1 ) + return; + + auto& np = newPoints[v]; + np += vertPushForces[v]; + auto modifier = 1.0f / 2.0f; + np -= ( vertPushForces[polyline.topology.dest( e0 )] * modifier ); + np -= ( vertPushForces[polyline.topology.dest( e1 )] * modifier ); + }, internalCb2 ); + polyline.points.swap( newPoints ); + if ( !keepGoing ) + break; + } + + return keepGoing; +} + +template bool relax<>( Polyline2& polyline, const RelaxParams& params, ProgressCallback cb ); +template bool relax<>( Polyline3& polyline, const RelaxParams& params, ProgressCallback cb ); + +template bool relaxKeepArea<>( Polyline2 &polyline, const RelaxParams ¶ms, ProgressCallback cb ); +template bool relaxKeepArea<>( Polyline3 &polyline, const RelaxParams ¶ms, ProgressCallback cb ); + +} // namespace MR \ No newline at end of file diff --git a/source/MRMesh/MRPolylineRelax.h b/source/MRMesh/MRPolylineRelax.h new file mode 100644 index 000000000000..d182433f96c4 --- /dev/null +++ b/source/MRMesh/MRPolylineRelax.h @@ -0,0 +1,25 @@ +#pragma once +#include "MRMeshFwd.h" +#include "MRProgressCallback.h" +#include "MRRelaxParams.h" + +namespace MR +{ + +/// \addtogroup PolylineGroup +/// \{ + +/// applies given number of relaxation iterations to the whole polyline ( or some region if it is specified ) +/// \return true if was finished successfully, false if was interrupted by progress callback +template +MRMESH_API bool relax( Polyline& polyline, const RelaxParams& params = {}, ProgressCallback cb = {} ); + +/// applies given number of relaxation iterations to the whole polyline ( or some region if it is specified ) +/// do not really keeps area but tries hard +/// \return true if was finished successfully, false if was interrupted by progress callback +template +MRMESH_API bool relaxKeepArea( Polyline& polyline, const RelaxParams& params = {}, ProgressCallback cb = {} ); + +/// \} + +} // namespace MR diff --git a/source/MRMesh/MRRelaxParams.h b/source/MRMesh/MRRelaxParams.h new file mode 100644 index 000000000000..e3a764156901 --- /dev/null +++ b/source/MRMesh/MRRelaxParams.h @@ -0,0 +1,23 @@ +#pragma once +#include "MRMeshFwd.h" + +namespace MR +{ + +struct RelaxParams +{ + /// number of iterations + int iterations{1}; + /// region to relax + const VertBitSet *region{nullptr}; + /// speed of relaxing, typical values (0.0, 0.5] + float force{0.5f}; +}; + +enum class RelaxApproxType +{ + Planar, + Quadric, +}; + +} // namespace MR \ No newline at end of file