Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions source/MRMesh/MRObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "MRBox.h"
#include "MRHeapBytes.h"
#include "MRphmap.h"
#include "MRViewportProperty.h"
#include "MRPch/MRJson.h"
#include "MRPch/MRSpdlog.h"

Expand Down Expand Up @@ -169,6 +170,31 @@ size_t ObjectChildrenHolder::heapBytes() const
return res;
}

struct Object::Data
{
ViewportProperty<AffineXf3f> xf;
};

Object::DataPtr::DataPtr() : p_( std::make_unique<Data>() )
{
}

Object::DataPtr::DataPtr( const DataPtr& b ) : p_( std::make_unique<Data>( *b.p_ ) )
{
}

Object::DataPtr& Object::DataPtr::operator =( const DataPtr& b )
{
p_ = std::make_unique<Data>( *b.p_ );
return *this;
}

Object::DataPtr::DataPtr( DataPtr&& b ) noexcept = default;

Object::DataPtr& Object::DataPtr::operator =( DataPtr&& b ) noexcept = default;

Object::DataPtr::~DataPtr() = default;

std::shared_ptr<const Object> Object::find( const std::string_view & name ) const
{
for ( const auto & child : children_ )
Expand All @@ -177,40 +203,50 @@ std::shared_ptr<const Object> Object::find( const std::string_view & name ) cons
return {}; // not found among recognized children
}

const AffineXf3f& Object::xf( ViewportId id, bool* isDef ) const
{
return data_->xf.get( id, isDef );
}

void Object::setXf( const AffineXf3f& xf, ViewportId id )
{
if ( xf_.get( id ) == xf )
if ( data_->xf.get( id ) == xf )
return;
if ( xf.A.det() == 0 )
{
assert( false && "Object transform is degenerate" );
spdlog::warn( "Object transform is degenerate" );
return;
}
xf_.set( xf, id );
data_->xf.set( xf, id );
sendWorldXfChangedSignal_();
needRedraw_ = true;
}

void Object::resetXf( ViewportId id )
{
if ( !xf_.reset( id ) )
if ( !data_->xf.reset( id ) )
return;
sendWorldXfChangedSignal_();
needRedraw_ = true;
}

const ViewportProperty<AffineXf3f>& Object::xfsForAllViewports() const
{
return data_->xf;
}

void Object::setXfsForAllViewports( ViewportProperty<AffineXf3f> xf )
{
if ( xf_ == xf )
if ( data_->xf == xf )
return;
xf_ = std::move( xf );
data_->xf = std::move( xf );
needRedraw_ = true;
}

AffineXf3f Object::worldXf( ViewportId id, bool * isDef ) const
{
auto xf = xf_.get( id, isDef );
auto xf = data_->xf.get( id, isDef );
auto parent = this->parent();
while ( parent )
{
Expand All @@ -225,7 +261,7 @@ AffineXf3f Object::worldXf( ViewportId id, bool * isDef ) const

void Object::setWorldXf( const AffineXf3f& worldxf, ViewportId id )
{
setXf( xf_.get( id ) * worldXf( id ).inverse() * worldxf );
setXf( data_->xf.get( id ) * worldXf( id ).inverse() * worldxf );
}

void Object::applyScale( float )
Expand Down Expand Up @@ -544,7 +580,7 @@ void Object::serializeFields_( Json::Value& root ) const
root["ParentLocked"] = parentLocked_;

// xf
serializeToJson( xf_.get(), root["XF"] );
serializeToJson( data_->xf.get(), root["XF"] );

// Type
root["Type"].append( Object::StaticTypeName() ); // will be appended in derived calls
Expand Down Expand Up @@ -581,7 +617,7 @@ void Object::deserializeFields_( const Json::Value& root )
if ( root["Selected"].isBool() )
selected_ = root["Selected"].asBool();
if ( !root["XF"].isNull() )
deserializeFromJson( root["XF"], xf_.get() );
deserializeFromJson( root["XF"], data_->xf.get() );
if ( root["Locked"].isBool() )
locked_ = root["Locked"].asBool();
if ( const auto& json = root["ParentLocked"]; json.isBool() )
Expand Down
32 changes: 26 additions & 6 deletions source/MRMesh/MRObject.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
#pragma once

#include "MRAffineXf3.h"
#include "MRExpected.h"
#include "MRProgressCallback.h"
#include "MRSignal.h"
#include "MRViewportProperty.h"
#include "MRViewportId.h"

#include <array>
#include <filesystem>
#include <future>
#include <memory>
Expand Down Expand Up @@ -102,13 +100,13 @@ class MRMESH_CLASS Object : public ObjectChildrenHolder

/// this space to parent space transformation (to world space if no parent) for default or given viewport
/// \param isDef receives true if the object has default transformation in this viewport (same as xf() returns)
const AffineXf3f & xf( ViewportId id = {}, bool * isDef = nullptr ) const { return xf_.get( id, isDef ); }
MRMESH_API const AffineXf3f & xf( ViewportId id = {}, bool * isDef = nullptr ) const;
MRMESH_API virtual void setXf( const AffineXf3f& xf, ViewportId id = {} );
/// forgets specific transform in given viewport (or forgets all specific transforms for {} input)
MRMESH_API virtual void resetXf( ViewportId id = {} );

/// returns xfs for all viewports, combined into a single object
const ViewportProperty<AffineXf3f> & xfsForAllViewports() const { return xf_; }
MRMESH_API const ViewportProperty<AffineXf3f> & xfsForAllViewports() const;
/// modifies xfs for all viewports at once
MRMESH_API virtual void setXfsForAllViewports( ViewportProperty<AffineXf3f> xf );

Expand Down Expand Up @@ -312,7 +310,6 @@ class MRMESH_CLASS Object : public ObjectChildrenHolder
MRMESH_API virtual void deserializeFields_( const Json::Value& root );

std::string name_;
ViewportProperty<AffineXf3f> xf_;
ViewportMask visibilityMask_ = ViewportMask::all(); // Prefer to not read directly. Use the getter, as it can be overridden.
bool locked_ = false;
bool parentLocked_ = false;
Expand All @@ -329,6 +326,29 @@ class MRMESH_CLASS Object : public ObjectChildrenHolder
// Emits `worldXfChangedSignal`, but derived classes can add additional behavior to it.
MRMESH_API virtual void onWorldXfChanged_();

private:
struct Data;

/// std::unique_ptr<Data> with value semantics, which keeps Object's copy and move
/// operations defaulted despite Data being incomplete here
class DataPtr
{
public:
MRMESH_API DataPtr();
MRMESH_API DataPtr( const DataPtr& b );
MRMESH_API DataPtr& operator =( const DataPtr& b );
MRMESH_API DataPtr( DataPtr&& b ) noexcept;
MRMESH_API DataPtr& operator =( DataPtr&& b ) noexcept;
MRMESH_API ~DataPtr();
Data& operator *() { return *p_; }
const Data& operator *() const { return *p_; }
Data* operator ->() { return p_.get(); }
const Data* operator ->() const { return p_.get(); }
private:
std::unique_ptr<Data> p_;
};
DataPtr data_;

private:
struct MapSharedObjects;
Expected<std::vector<std::future<Expected<void>>>> serializeRecursive_( const std::filesystem::path& path, Json::Value& root,
Expand Down
1 change: 1 addition & 0 deletions source/MRMesh/MRVisualObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "MRColor.h"
#include "MRUniquePtr.h"
#include "MREnums.h"
#include "MRViewportProperty.h"

#include <optional>
#include <typeindex>
Expand Down
26 changes: 26 additions & 0 deletions source/MRTest/MRObjectTests.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "MRMesh/MRObject.h"
#include "MRMesh/MRAffineXf3.h"
#include <gtest/gtest.h>

namespace MR
Expand Down Expand Up @@ -33,4 +34,29 @@ TEST( MRMesh, DataModelRemoveChild )
EXPECT_EQ( parent, nullptr );
}

TEST( MRMesh, ObjectCloneAndSwap )
{
Object a;
a.setName( "a" );
a.setXf( AffineXf3f::translation( { 1.f, 2.f, 3.f } ) );
a.select( true );
a.setLocked( true );

auto clone = a.clone();
EXPECT_EQ( clone->name(), "a" );
EXPECT_EQ( clone->xf(), a.xf() );
EXPECT_TRUE( clone->isSelected() );
EXPECT_TRUE( clone->isLocked() );

Object b;
b.setName( "b" );
a.swap( b );
EXPECT_EQ( a.name(), "b" );
EXPECT_EQ( b.name(), "a" );
EXPECT_EQ( a.xf(), AffineXf3f() );
EXPECT_EQ( b.xf(), clone->xf() );
EXPECT_FALSE( a.isSelected() );
EXPECT_TRUE( b.isSelected() );
}

} //namespace MR
Loading