diff --git a/source/MRMesh/MRChangeMeshDataAction.cpp b/source/MRMesh/MRChangeMeshDataAction.cpp new file mode 100644 index 000000000000..b28cb290af71 --- /dev/null +++ b/source/MRMesh/MRChangeMeshDataAction.cpp @@ -0,0 +1,28 @@ +#include "MRChangeMeshDataAction.h" +#include "MRMesh.h" +#include "MRPartialChangeMeshAction.h" +#include "MRChangeMeshAction.h" +#include "MRChangeVertsColorMapAction.h" +#include "MRChangeColoringActions.h" +#include "MRChangeSelectionAction.h" + +namespace MR +{ + +PartialChangeMeshDataAction::PartialChangeMeshDataAction( std::string name, const std::shared_ptr& obj, ObjectMeshData&& newData ) +{ + std::vector> actions; + actions.push_back( std::make_shared( "mesh", obj, setNew, std::move( newData.mesh ) ) ); + + actions.push_back( std::make_shared( "setUVCoords", obj, std::move( newData.uvCoordinates ) ) ); + actions.push_back( std::make_shared( "setTexturePerFace", obj, std::move( newData.texturePerFace ) ) ); + actions.push_back( std::make_shared>( "setVertsColorMap", obj, std::move( newData.vertColors ) ) ); + actions.push_back( std::make_shared( "setFacesColorMap", obj, std::move( newData.faceColors ) ) ); + actions.push_back( std::make_shared( "faceSelection", obj, std::move( newData.selectedFaces ) ) ); + actions.push_back( std::make_shared( "edgeSelection", obj, std::move( newData.selectedEdges ) ) ); + actions.push_back( std::make_shared( "creases", obj, std::move( newData.creases ) ) ); + + combinedAction_ = std::make_unique( std::move( name ), actions ); +} + +} \ No newline at end of file diff --git a/source/MRMesh/MRChangeMeshDataAction.h b/source/MRMesh/MRChangeMeshDataAction.h index ecad320d712d..361463995fba 100644 --- a/source/MRMesh/MRChangeMeshDataAction.h +++ b/source/MRMesh/MRChangeMeshDataAction.h @@ -1,6 +1,6 @@ #pragma once -#include "MRHistoryAction.h" +#include "MRCombinedHistoryAction.h" #include "MRObjectMesh.h" namespace MR @@ -75,6 +75,39 @@ class ChangeMeshDataAction : public HistoryAction std::string name_; }; +/// Undo action for ObjectMeshData change partially +class MRMESH_CLASS PartialChangeMeshDataAction : public HistoryAction +{ +public: + /// use this constructor to remember object's data and immediately set new data + MRMESH_API PartialChangeMeshDataAction( std::string name, const std::shared_ptr& obj, ObjectMeshData&& newData ); + + virtual std::string name() const override + { + if ( combinedAction_ ) + return combinedAction_->name(); + assert( false ); + return "##empty_PartialChangeMeshDataAction"; + } + + virtual void action( HistoryAction::Type type) override + { + if ( combinedAction_ ) + combinedAction_->action( type ); + } + + [[nodiscard]] virtual size_t heapBytes() const override + { + if ( combinedAction_ ) + return combinedAction_->heapBytes() + sizeof( CombinedHistoryAction ); + else + return 0; + } + +private: + std::unique_ptr combinedAction_; +}; + /// \} } // namespace MR diff --git a/source/MRMesh/MRMesh.vcxproj b/source/MRMesh/MRMesh.vcxproj index 1134524568d0..d1db49e23680 100644 --- a/source/MRMesh/MRMesh.vcxproj +++ b/source/MRMesh/MRMesh.vcxproj @@ -438,6 +438,7 @@ + @@ -620,6 +621,7 @@ + diff --git a/source/MRMesh/MRMesh.vcxproj.filters b/source/MRMesh/MRMesh.vcxproj.filters index 399298c9040a..69d6e5c468d1 100644 --- a/source/MRMesh/MRMesh.vcxproj.filters +++ b/source/MRMesh/MRMesh.vcxproj.filters @@ -2237,6 +2237,12 @@ Source Files\System + + Source Files\MeshAlgorithm + + + Source Files\History + diff --git a/source/MRMesh/MRProjectionMeshAttribute.cpp b/source/MRMesh/MRProjectionMeshAttribute.cpp new file mode 100644 index 000000000000..2e1dbaaebe3b --- /dev/null +++ b/source/MRMesh/MRProjectionMeshAttribute.cpp @@ -0,0 +1,98 @@ +#include "MRProjectionMeshAttribute.h" +#include "MRObjectMeshData.h" +#include "MRRegionBoundary.h" +#include "MRBitSet.h" + +namespace MR +{ + +Expected projectObjectMeshData( const ObjectMeshData& oldMeshData, ObjectMeshData& newMeshData, + const FaceBitSet* region /*= nullptr*/, const ProjectAttributeParams& params /*= {} */ ) +{ + if ( !newMeshData.mesh || !oldMeshData.mesh ) + { + assert( false ); + return unexpected( "No mesh for projecting attributes" ); + } + const auto& newMesh = *newMeshData.mesh; + const auto& oldVertColors = oldMeshData.vertColors; + const auto& oldUVCoords = oldMeshData.uvCoordinates; + const auto& oldFaceColorMap = oldMeshData.faceColors; + const auto& oldTexturePerFace = oldMeshData.texturePerFace; + const auto& oldFaceSelection = oldMeshData.selectedFaces; + + if ( !oldUVCoords.empty() ) + { + newMeshData.uvCoordinates = oldUVCoords; + newMeshData.uvCoordinates.resize( size_t( newMesh.topology.lastValidVert() + 1 ) ); + } + if ( !oldVertColors.empty() ) + { + newMeshData.vertColors = oldVertColors; + newMeshData.vertColors.resize( size_t( newMesh.topology.lastValidVert() + 1 ) ); + } + if ( !oldFaceColorMap.empty() ) + { + newMeshData.faceColors = oldFaceColorMap; + newMeshData.faceColors.resize( size_t( newMesh.topology.lastValidFace() + 1 ) ); + } + if ( !oldTexturePerFace.empty() ) + { + newMeshData.texturePerFace = oldTexturePerFace; + newMeshData.texturePerFace.resize( size_t( newMesh.topology.lastValidFace() + 1 ) ); + } + bool haveFaceSelection = oldFaceSelection.any(); + if ( haveFaceSelection ) + { + newMeshData.selectedFaces = oldFaceSelection; + newMeshData.selectedFaces.resize( size_t( newMesh.topology.lastValidFace() + 1 ) ); + } + + const bool hasFaceAttribs = !oldFaceColorMap.empty() || !oldTexturePerFace.empty() || haveFaceSelection; + const bool hasVertAttribs = !oldVertColors.empty() || !oldUVCoords.empty(); + + auto faceFunc = [&] ( FaceId id, const MeshProjectionResult& res ) + { + if ( !oldFaceColorMap.empty() ) + newMeshData.faceColors[id] = oldFaceColorMap[res.proj.face]; + if ( !oldTexturePerFace.empty() ) + newMeshData.texturePerFace[id] = oldTexturePerFace[res.proj.face]; + if ( haveFaceSelection ) + newMeshData.selectedFaces.set( id, oldFaceSelection.test( res.proj.face ) ); + }; + + ProjectAttributeParams localParams = params; + + if ( hasVertAttribs ) + { + auto vertFunc = [&] ( VertId id, const MeshProjectionResult& res, VertId v1, VertId v2, VertId v3 ) + { + if ( !oldVertColors.empty() ) + newMeshData.vertColors[id] = res.mtp.bary.interpolate( oldVertColors[v1], oldVertColors[v2], oldVertColors[v3] ); + if ( !oldUVCoords.empty() ) + newMeshData.uvCoordinates[id] = res.mtp.bary.interpolate( oldUVCoords[v1], oldUVCoords[v2], oldUVCoords[v3] ); + }; + + VertBitSet vertRegion; + MeshVertPart mvp( newMesh ); + if ( region ) + { + vertRegion = getIncidentVerts( newMesh.topology, *region ); + mvp.region = &vertRegion; + } + + + localParams.progressCb = subprogress( params.progressCb, 0.0f, hasFaceAttribs ? 0.5f : 1.0f ); + if ( !projectVertAttribute( mvp, *oldMeshData.mesh, vertFunc, localParams ) ) + return unexpectedOperationCanceled(); + + } + + localParams.progressCb = subprogress( params.progressCb, hasVertAttribs ? 0.5f : 0.0f, 1.0f ); + if ( hasFaceAttribs && !projectFaceAttribute( MeshPart( newMesh, region ), *oldMeshData.mesh, faceFunc, localParams ) ) + return unexpectedOperationCanceled(); + + return {}; +} + +} \ No newline at end of file diff --git a/source/MRMesh/MRProjectionMeshAttribute.h b/source/MRMesh/MRProjectionMeshAttribute.h index 33280b41b646..073d66af14b3 100644 --- a/source/MRMesh/MRProjectionMeshAttribute.h +++ b/source/MRMesh/MRProjectionMeshAttribute.h @@ -1,6 +1,8 @@ #pragma once +#include "MRMeshFwd.h" #include "MRAffineXf.h" +#include "MRMatrix3.h" #include "MRMesh.h" #include "MRBitSetParallelFor.h" #include "MRMeshProject.h" @@ -9,22 +11,34 @@ namespace MR { +/// this structure contains transformation for projection from one mesh to another and progress callback struct ProjectAttributeParams { MeshProjectionTransforms xfs; ProgressCallback progressCb; }; -// projecting the vertex attributes of the old onto the new one -// returns false if canceled by progress bar +/// projecting the vertex attributes of the old onto the new one +/// returns false if canceled by progress bar template bool projectVertAttribute( const MeshVertPart& mp, const Mesh& oldMesh, F&& func, const ProjectAttributeParams& params = {} ); -// projecting the face attributes of the old onto the new one -// returns false if canceled by progress bar +/// projecting the face attributes of the old onto the new one +/// returns false if canceled by progress bar template bool projectFaceAttribute( const MeshPart& mp, const Mesh& oldMesh, F&& func, const ProjectAttributeParams& params = {} ); +/// +/// finds attributes of new mesh by projecting faces/vertices on old mesh +/// \note for now clears edges attributes +/// +/// old mesh along with input attributes +/// new mesh along with outpuyt attributes +/// optional input region for projecting (usefull if newMesh is changed part of old mesh) +/// parameters of prohecting +[[nodiscard]] MRMESH_API Expected projectObjectMeshData( + const ObjectMeshData& oldMeshData, ObjectMeshData& newMeshData, const FaceBitSet* region = nullptr, + const ProjectAttributeParams& params = {} ); template bool projectVertAttribute( const MeshVertPart& mp, const Mesh& oldMesh, F&& func, const ProjectAttributeParams& params ) diff --git a/source/MRViewer/MRProjectMeshAttributes.cpp b/source/MRViewer/MRProjectMeshAttributes.cpp deleted file mode 100644 index 3e79b8af939a..000000000000 --- a/source/MRViewer/MRProjectMeshAttributes.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "MRProjectMeshAttributes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace MR -{ - -std::optional projectMeshAttributes( - const ObjectMesh& objectMesh, - const MeshPart& mp, - const AffineXf3f* newMeshXf, - ProgressCallback cb ) -{ - const auto& newMesh = mp.mesh; - const auto& oldVertColors = objectMesh.getVertsColorMap(); - const auto& oldUVCoords = objectMesh.getUVCoords(); - const auto& oldFaceColorMap = objectMesh.getFacesColorMap(); - const auto& oldTexturePerFace = objectMesh.getTexturePerFace(); - - MeshAttributes newAttribute; - if ( !oldUVCoords.empty() ) - { - newAttribute.uvCoords = oldUVCoords; - newAttribute.uvCoords.resize( size_t( newMesh.topology.lastValidVert() + 1 ) ); - } - if ( !oldVertColors.empty() ) - { - newAttribute.colorMap = oldVertColors; - newAttribute.colorMap.resize( size_t( newMesh.topology.lastValidVert() + 1 ) ); - } - if ( !oldFaceColorMap.empty() ) - { - newAttribute.faceColors = oldFaceColorMap; - newAttribute.faceColors.resize( size_t( newMesh.topology.lastValidFace() + 1 ) ); - } - if ( !oldTexturePerFace.empty() ) - { - newAttribute.texturePerFace = oldTexturePerFace; - newAttribute.texturePerFace.resize( size_t( newMesh.topology.lastValidFace() + 1 ) ); - } - - const bool hasFaceAttribs = !oldFaceColorMap.empty() || !oldTexturePerFace.empty(); - const bool hasVertAttribs = !oldVertColors.empty() || !oldUVCoords.empty(); - - auto faceFunc = [&] ( FaceId id, const MeshProjectionResult& res ) - { - if ( !oldFaceColorMap.empty() ) - newAttribute.faceColors[id] = oldFaceColorMap[res.proj.face]; - if ( !oldTexturePerFace.empty() ) - newAttribute.texturePerFace[id] = oldTexturePerFace[res.proj.face]; - }; - - AffineXf3f storeXf; - ProjectAttributeParams params; - auto treeWXf = objectMesh.worldXf(); - params.xfs = createProjectionTransforms( storeXf, newMeshXf, &treeWXf ); - - if ( hasVertAttribs ) - { - auto vertFunc = [&] ( VertId id, const MeshProjectionResult& res, VertId v1, VertId v2, VertId v3 ) - { - if ( !oldVertColors.empty() ) - newAttribute.colorMap[id] = res.mtp.bary.interpolate( oldVertColors[v1], oldVertColors[v2], oldVertColors[v3] ); - if ( !oldUVCoords.empty() ) - newAttribute.uvCoords[id] = res.mtp.bary.interpolate( oldUVCoords[v1], oldUVCoords[v2], oldUVCoords[v3] ); - }; - - VertBitSet vertRegion; - MeshVertPart mvp( newMesh ); - if ( mp.region ) - { - vertRegion = getIncidentVerts( newMesh.topology, *mp.region ); - mvp.region = &vertRegion; - } - - params.progressCb = subprogress( cb, 0.0f, hasVertAttribs ? 0.5f : 1.0f ); - if ( !projectVertAttribute( mvp, *objectMesh.mesh(), vertFunc, params ) ) - return {}; - - } - - params.progressCb = subprogress( cb, hasVertAttribs ? 0.0f : 0.5f, 1.0f ); - if ( hasFaceAttribs && !projectFaceAttribute( mp, *objectMesh.mesh(), faceFunc, params ) ) - return {}; - - return newAttribute; -} - -void emplaceMeshAttributes( - std::shared_ptr objectMesh, - MeshAttributes&& newAttribute ) -{ - if ( !newAttribute.uvCoords.empty() ) - { - Historian htpf( "setUVCoords", objectMesh, std::move( newAttribute.uvCoords ) ); - } - if ( !newAttribute.texturePerFace.empty() ) - { - Historian htpf( "setTexturePerFace", objectMesh, std::move( newAttribute.texturePerFace ) ); - } - if ( !newAttribute.colorMap.empty() ) - { - Historian> htpf( "setVertsColorMap", objectMesh, std::move( newAttribute.colorMap ) ); - } - if ( !newAttribute.faceColors.empty() ) - { - Historian htpf( "setFacesColorMap", objectMesh, std::move( newAttribute.faceColors ) ); - } -} - -} diff --git a/source/MRViewer/MRProjectMeshAttributes.h b/source/MRViewer/MRProjectMeshAttributes.h deleted file mode 100644 index cdcd6ce8c4ff..000000000000 --- a/source/MRViewer/MRProjectMeshAttributes.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include "MRViewerFwd.h" -#include -#include "MRMesh/MRVector.h" - -#include - -namespace MR -{ - -struct MeshAttributes -{ - VertUVCoords uvCoords; - VertColors colorMap; - - TexturePerFace texturePerFace; - FaceColors faceColors; -}; - -/// finds attributes of new mesh part by projecting region's faces/vertices on old mesh -/// returns nullopt if canceled by progress bar -[[nodiscard]] MRVIEWER_API std::optional projectMeshAttributes( - const ObjectMesh& oldMeshObj, - const MeshPart& newMeshPart, - const AffineXf3f* newMeshXf = nullptr, - ProgressCallback cb = {} ); - -/// set new mesh attributes and saving the history of changing mesh attributes -MRVIEWER_API void emplaceMeshAttributes( - std::shared_ptr objectMesh, - MeshAttributes&& newAttribute ); - -} diff --git a/source/MRViewer/MRSurfaceManipulationWidget.cpp b/source/MRViewer/MRSurfaceManipulationWidget.cpp index 0d1c0f6ca389..cd4e87f0b275 100644 --- a/source/MRViewer/MRSurfaceManipulationWidget.cpp +++ b/source/MRViewer/MRSurfaceManipulationWidget.cpp @@ -5,7 +5,6 @@ #include "MRAppendHistory.h" #include "MRMouse.h" #include "MRPalette.h" -#include "MRProjectMeshAttributes.h" #include "MRViewer/MRGladGlfw.h" #include "MRMesh/MRObjectMesh.h" #include "MRMesh/MRMesh.h" @@ -32,6 +31,8 @@ #include "MRSceneCache.h" #include "MRMesh/MRAABBTreePoints.h" #include "MRMesh/MRPointsProject.h" +#include "MRMesh/MRProjectionMeshAttribute.h" +#include "MRMesh/MRChangeMeshDataAction.h" namespace MR { @@ -429,12 +430,18 @@ bool SurfaceManipulationWidget::onMouseUp_( Viewer::MouseButton button, int /*mo // newFaces include both faces inside the patch and subdivided faces around const FaceBitSet newFaces = newMesh->topology.getValidFaces() - oldFaces; - auto meshAttribs = projectMeshAttributes( *obj_, MeshPart( *newMesh, &newFaces ) ); - - appendMeshChangeHistory_( std::move( newMesh ), newFaces ); - - if ( meshAttribs ) - emplaceMeshAttributes( obj_, std::move( *meshAttribs ) ); + ObjectMeshData newMeshData; + newMeshData.mesh = newMesh; + auto projRes = projectObjectMeshData( obj_->data(), newMeshData, &newFaces ); + if ( projRes.has_value() ) + { + // appendMeshChangeHistory_( std::move( newMesh ), newFaces ); -> Partial + AppendHistory( "mesh data", obj_, std::move( newMeshData ) ); + } + else + { + assert( false ); + } reallocData_( obj_->mesh()->topology.lastValidVert() + 1 ); sameValidVerticesAsInOriginMesh_ = originalMesh_->topology.getValidVerts() == obj_->mesh()->topology.getValidVerts(); diff --git a/source/MRViewer/MRViewer.vcxproj b/source/MRViewer/MRViewer.vcxproj index 30d16e5280da..1fb5c141b55c 100644 --- a/source/MRViewer/MRViewer.vcxproj +++ b/source/MRViewer/MRViewer.vcxproj @@ -44,7 +44,6 @@ - @@ -190,7 +189,6 @@ - diff --git a/source/MRViewer/MRViewer.vcxproj.filters b/source/MRViewer/MRViewer.vcxproj.filters index 103ae532d994..3b852a19c56f 100644 --- a/source/MRViewer/MRViewer.vcxproj.filters +++ b/source/MRViewer/MRViewer.vcxproj.filters @@ -436,9 +436,6 @@ Source Files - - Helpers - Viewer @@ -909,9 +906,6 @@ Source Files - - Helpers - IO