Skip to content

Override visual object colors with tags #4920

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Aug 5, 2025
Merged
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
42 changes: 1 addition & 41 deletions source/MRCommonPlugins/ViewerButtons/MRAddCustomTheme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,47 +282,7 @@ std::string AddCustomThemePlugin::save_()
{
auto visualObjs = getAllObjectsInTree<VisualObject>( &SceneRoot::get() );
for ( auto obj : visualObjs )
{
obj->setFrontColor( SceneColors::get( SceneColors::SelectedObjectMesh ), true );
obj->setFrontColor( SceneColors::get( SceneColors::UnselectedObjectMesh ), false );
obj->setBackColor( SceneColors::get( SceneColors::BackFaces ) );
MR_SUPPRESS_WARNING_PUSH
MR_SUPPRESS_WARNING( "-Wdeprecated-declarations", 4996 )
obj->setLabelsColor( SceneColors::get( SceneColors::Labels ) );
MR_SUPPRESS_WARNING_POP
#ifndef MESHLIB_NO_VOXELS
if ( auto objVoxels = std::dynamic_pointer_cast< ObjectVoxels >( obj ) )
{
objVoxels->setFrontColor( SceneColors::get( SceneColors::SelectedObjectVoxels ), true );
objVoxels->setFrontColor( SceneColors::get( SceneColors::UnselectedObjectVoxels ), false );
}
else
#endif
if ( auto objDM = std::dynamic_pointer_cast< ObjectDistanceMap >( obj ) )
{
objDM->setFrontColor( SceneColors::get( SceneColors::SelectedObjectDistanceMap ), true );
objDM->setFrontColor( SceneColors::get( SceneColors::UnselectedObjectDistanceMap ), false );
}
else if ( auto meshObj = std::dynamic_pointer_cast< ObjectMesh >( obj ) )
{
meshObj->setFrontColor( SceneColors::get( SceneColors::SelectedObjectMesh ), true );
meshObj->setFrontColor( SceneColors::get( SceneColors::UnselectedObjectMesh ), false );
meshObj->setSelectedFacesColor( SceneColors::get( SceneColors::SelectedFaces ) );
meshObj->setSelectedEdgesColor( SceneColors::get( SceneColors::SelectedEdges ) );
meshObj->setEdgesColor( SceneColors::get( SceneColors::Edges ) );
meshObj->setPointsColor( SceneColors::get( SceneColors::Points ) );
}
else if ( auto objPoints = std::dynamic_pointer_cast< ObjectPoints >( obj ) )
{
objPoints->setFrontColor( SceneColors::get( SceneColors::SelectedObjectPoints ), true );
objPoints->setFrontColor( SceneColors::get( SceneColors::UnselectedObjectPoints ), false );
}
else if ( auto objLines = std::dynamic_pointer_cast< ObjectLines >( obj ) )
{
objLines->setFrontColor( SceneColors::get( SceneColors::SelectedObjectLines ), true );
objLines->setFrontColor( SceneColors::get( SceneColors::UnselectedObjectLines ), false );
}
}
obj->resetColors();
}
updateThemeNames_();
return {};
Expand Down
9 changes: 9 additions & 0 deletions source/MRMesh/MRObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,11 @@ void Object::serializeFields_( Json::Value& root ) const

// Type
root["Type"].append( Object::TypeName() ); // will be appended in derived calls

// tags
auto& tagsJson = root["Tags"] = Json::arrayValue;
for ( const auto& tag : tags_ )
tagsJson.append( tag );
}

Expected<void> Object::deserializeModel_( const std::filesystem::path&, ProgressCallback progressCb )
Expand Down Expand Up @@ -463,6 +468,10 @@ void Object::deserializeFields_( const Json::Value& root )
locked_ = root["Locked"].asBool();
if ( const auto& json = root["ParentLocked"]; json.isBool() )
parentLocked_ = json.asBool();
if ( const auto& tagsJson = root["Tags"]; tagsJson.isArray() )
for ( const auto& tagJson : tagsJson )
if ( tagJson.isString() )
tags_.emplace( tagJson.asString() );
}

void Object::sendWorldXfChangedSignal_()
Expand Down
21 changes: 15 additions & 6 deletions source/MRMesh/MRObject.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#pragma once

#include "MRAffineXf3.h"
#include "MRBox.h"
#include "MRBitSet.h"
#include "MRViewportProperty.h"
#include "MRProgressCallback.h"
#include "MRBox.h"
#include "MRExpected.h"
#include "MRProgressCallback.h"
#include "MRSignal.h"
#include <memory>
#include <vector>
#include "MRViewportProperty.h"

#include <array>
#include <future>
#include <filesystem>
#include <future>
#include <memory>
#include <unordered_set>
#include <vector>

namespace Json
{
Expand Down Expand Up @@ -250,6 +252,12 @@ class MRMESH_CLASS Object : public ObjectChildrenHolder
/// e.g. ObjectMesh has valid mesh() or ObjectPoints has valid pointCloud()
[[nodiscard]] virtual bool hasModel() const { return false; }

/// provides read-only access to the tag storage
/// the storage is a set of unique strings
const std::unordered_set<std::string>& tags() const { return tags_; }
/// provides read-write access to the tag storage
std::unordered_set<std::string>& varTags() { return tags_; }

/// returns the amount of memory this object occupies on heap
[[nodiscard]] MRMESH_API virtual size_t heapBytes() const;

Expand Down Expand Up @@ -297,6 +305,7 @@ class MRMESH_CLASS Object : public ObjectChildrenHolder
bool selected_{ false };
bool ancillary_{ false };
mutable bool needRedraw_{false};
std::unordered_set<std::string> tags_;

// This calls `onWorldXfChanged_()` for all children recursively, which in turn emits `worldXfChangedSignal`.
// This isn't virtual because it wouldn't be very useful, because it doesn't call itself on the children
Expand Down
6 changes: 6 additions & 0 deletions source/MRMesh/MRObjectDistanceMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ Expected<std::future<Expected<void>>> ObjectDistanceMap::serializeModel_( const
} );
}

void ObjectDistanceMap::resetFrontColor()
{
// cannot implement in the opposite way to keep `setDefaultColors_()` non-virtual
setDefaultColors_();
}

void ObjectDistanceMap::setDefaultColors_()
{
setFrontColor( SceneColors::get( SceneColors::SelectedObjectDistanceMap ), true );
Expand Down
3 changes: 3 additions & 0 deletions source/MRMesh/MRObjectDistanceMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class MRMESH_CLASS ObjectDistanceMap : public ObjectMeshHolder

MRMESH_API virtual Expected<std::future<Expected<void>>> serializeModel_( const std::filesystem::path& path ) const override;

/// reset basic object colors to their default values from the current theme
MRMESH_API void resetFrontColor() override;

private:
std::shared_ptr<DistanceMap> dmap_;
AffineXf3f dmap2local_;
Expand Down
6 changes: 6 additions & 0 deletions source/MRMesh/MRObjectLinesHolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ float ObjectLinesHolder::totalLength() const
return *totalLength_;
}

void ObjectLinesHolder::resetFrontColor()
{
// cannot implement in the opposite way to keep `setDefaultColors_()` non-virtual
setDefaultColors_();
}

bool ObjectLinesHolder::supportsVisualizeProperty( AnyVisualizeMaskEnum type ) const
{
return VisualObject::supportsVisualizeProperty( type ) || type.tryGet<LinesVisualizePropertyType>().has_value();
Expand Down
3 changes: 3 additions & 0 deletions source/MRMesh/MRObjectLinesHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class MRMESH_CLASS ObjectLinesHolder : public VisualObject
/// return cached total length
[[nodiscard]] MRMESH_API float totalLength() const;

/// reset basic object colors to their default values from the current theme
MRMESH_API void resetFrontColor() override;

protected:
ObjectLinesHolder( const ObjectLinesHolder& other ) = default;

Expand Down
12 changes: 12 additions & 0 deletions source/MRMesh/MRObjectMeshHolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,18 @@ void ObjectMeshHolder::setSerializeFormat( const char * newFormat )
serializeFormat_ = newFormat;
}

void ObjectMeshHolder::resetFrontColor()
{
setFrontColor( SceneColors::get( SceneColors::SelectedObjectMesh ), true );
setFrontColor( SceneColors::get( SceneColors::UnselectedObjectMesh ), false );
}

void ObjectMeshHolder::resetColors()
{
// cannot implement in the opposite way to keep `setDefaultColors_()` non-virtual
setDefaultColors_();
}

size_t ObjectMeshHolder::numUndirectedEdges() const
{
if ( !numUndirectedEdges_ )
Expand Down
5 changes: 5 additions & 0 deletions source/MRMesh/MRObjectMeshHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ class MRMESH_CLASS ObjectMeshHolder : public VisualObject
/// nullptr means serialize in defaultSerializeMeshFormat()
MRMESH_API void setSerializeFormat( const char * newFormat );

/// reset basic object colors to their default values from the current theme
MRMESH_API void resetFrontColor() override;
/// reset all object colors to their default values from the current theme
MRMESH_API void resetColors() override;

/// signal about face selection changing, triggered in selectFaces
using SelectionChangedSignal = Signal<void()>;
SelectionChangedSignal faceSelectionChangedSignal;
Expand Down
12 changes: 12 additions & 0 deletions source/MRMesh/MRObjectPointsHolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,18 @@ void ObjectPointsHolder::setSerializeFormat( const char * newFormat )
serializeFormat_ = newFormat;
}

void ObjectPointsHolder::resetFrontColor()
{
setFrontColor( SceneColors::get( SceneColors::SelectedObjectPoints ), true );
setFrontColor( SceneColors::get( SceneColors::UnselectedObjectPoints ), false );
}

void ObjectPointsHolder::resetColors()
{
// cannot implement in the opposite way to keep `setDefaultColors_()` non-virtual
setDefaultColors_();
}

void ObjectPointsHolder::swapBase_( Object& other )
{
if ( auto otherPointsHolder = other.asType<ObjectPointsHolder>() )
Expand Down
5 changes: 5 additions & 0 deletions source/MRMesh/MRObjectPointsHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ class MRMESH_CLASS ObjectPointsHolder : public VisualObject
MRMESH_API void setSerializeFormat( const char * newFormat );
[[deprecated]] MR_BIND_IGNORE void setSavePointsFormat( const char * newFormat ) { setSerializeFormat( newFormat ); }

/// reset basic object colors to their default values from the current theme
MRMESH_API void resetFrontColor() override;
/// reset all object colors to their default values from the current theme
MRMESH_API void resetColors() override;

/// signal about points selection changing, triggered in selectPoints
using SelectionChangedSignal = Signal<void()>;
SelectionChangedSignal pointsSelectionChangedSignal;
Expand Down
13 changes: 13 additions & 0 deletions source/MRMesh/MRString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ void replaceInplace( std::string& target, std::string_view from, std::string_vie
target = replace( std::move( target ), from, to );
}

std::string_view trim( std::string_view str )
{
return trimRight( trimLeft( str ) );
}

std::string_view trimLeft( std::string_view str )
{
size_t pos = 0;
while ( pos < str.size() && std::isspace( str[pos] ) )
++pos;
return str.substr( pos );
}

std::string_view trimRight( std::string_view str )
{
auto l = str.size();
Expand Down
6 changes: 6 additions & 0 deletions source/MRMesh/MRString.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ bool split( std::string_view str, std::string_view sep, F&& func )
/// Replaces \param from with \param to in \param target (in-place), zero or more times.
MRMESH_API void replaceInplace( std::string& target, std::string_view from, std::string_view to );

/// Removes all whitespace character (detected by std::isspace) at the beginning and the end of string view
[[nodiscard]] MRMESH_API std::string_view trim( std::string_view str );

/// Removes all whitespace character (detected by std::isspace) at the beginning of string view
[[nodiscard]] MRMESH_API std::string_view trimLeft( std::string_view str );

/// Removes all whitespace character (detected by std::isspace) at the end of string view
[[nodiscard]] MRMESH_API std::string_view trimRight( std::string_view str );

Expand Down
17 changes: 17 additions & 0 deletions source/MRMesh/MRVisualObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,23 @@ std::vector<std::string> VisualObject::getInfoLines() const
return res;
}

void VisualObject::resetFrontColor()
{
setFrontColor( SceneColors::get( SceneColors::SelectedObjectMesh ), true );
setFrontColor( SceneColors::get( SceneColors::UnselectedObjectMesh ), false );
}

void VisualObject::resetColors()
{
resetFrontColor();

setBackColor( SceneColors::get( SceneColors::BackFaces ) );
MR_SUPPRESS_WARNING_PUSH
MR_SUPPRESS_WARNING( "-Wdeprecated-declarations", 4996 )
setLabelsColor( SceneColors::get( SceneColors::Labels ) );
MR_SUPPRESS_WARNING_POP
}

void VisualObject::boundingBoxToInfoLines_( std::vector<std::string> & res ) const
{
auto bbox = getBoundingBox();
Expand Down
5 changes: 5 additions & 0 deletions source/MRMesh/MRVisualObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ class MRMESH_CLASS VisualObject : public Object
MRMESH_API void setUseDefaultScenePropertiesOnDeserialization( bool useDefaultScenePropertiesOnDeserialization )
{ useDefaultScenePropertiesOnDeserialization_ = useDefaultScenePropertiesOnDeserialization; }

/// reset basic object colors to their default values from the current theme
MRMESH_API virtual void resetFrontColor();
/// reset all object colors to their default values from the current theme
MRMESH_API virtual void resetColors();

protected:
VisualObject( const VisualObject& obj ) = default;

Expand Down
2 changes: 2 additions & 0 deletions source/MRViewer/MRViewer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
<ClCompile Include="MRModalDialog.cpp" />
<ClCompile Include="MRRenderImGui.cpp" />
<ClCompile Include="MRRenderToImage.cpp" />
<ClCompile Include="MRVisualObjectTag.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="MRAlphaSortGL.h" />
Expand Down Expand Up @@ -309,6 +310,7 @@
<ClInclude Include="MRModalDialog.h" />
<ClInclude Include="MRRenderImGui.h" />
<ClInclude Include="MRRenderToImage.h" />
<ClInclude Include="MRVisualObjectTag.h" />
</ItemGroup>
<ItemGroup>
<None Include="$(ProjectDir)..\.editorconfig" />
Expand Down
6 changes: 6 additions & 0 deletions source/MRViewer/MRViewer.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@
<ClCompile Include="MRRenderToImage.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="MRVisualObjectTag.cpp">
<Filter>Render</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ImGuiHelpers.h">
Expand Down Expand Up @@ -955,6 +958,9 @@
<ClInclude Include="MRRenderToImage.cpp">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="MRVisualObjectTag.cpp">
<Filter>Render</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="$(ProjectDir)..\.editorconfig" />
Expand Down
15 changes: 15 additions & 0 deletions source/MRViewer/MRViewerSettingsManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "MRMesh/MRSerializer.h"
#include "MRPch/MRSpdlog.h"
#include "MRRibbonSceneObjectsListDrawer.h"
#include "MRVisualObjectTag.h"
#include "MRMesh/MRObjectMesh.h"
#include "MRMesh/MRObjectPointsHolder.h"
#include "MRVoxels/MRObjectVoxels.h"
Expand Down Expand Up @@ -69,6 +70,7 @@ const std::string cMruInnerPointsFormat = "mruInner.pointsFormat";
const std::string cMruInnerVoxelsFormat = "mruInner.voxelsFormat";
const std::string cSortDroppedFiles = "sortDroppedFiles";
const std::string cScrollForceConfigKey = "scrollForce";
const std::string cVisualObjectTags = "visualObjectTags";
}

namespace Defaults
Expand Down Expand Up @@ -526,6 +528,12 @@ void ViewerSettingsManager::loadSettings( Viewer& viewer )
format = loadString( cMruInnerVoxelsFormat, ".vdb" );
setDefaultSerializeVoxelsFormat( format );
}

if ( cfg.hasJsonValue( cVisualObjectTags ) )
{
auto& manager = VisualObjectTagManager::instance();
deserializeFromJson( cfg.getJsonValue( cVisualObjectTags ), manager );
}
}

void ViewerSettingsManager::saveSettings( const Viewer& viewer )
Expand Down Expand Up @@ -674,6 +682,13 @@ void ViewerSettingsManager::saveSettings( const Viewer& viewer )
saveString( cMruInnerPointsFormat, defaultSerializePointsFormat() );
saveString( cMruInnerVoxelsFormat, defaultSerializeVoxelsFormat() );
}

{
Json::Value visualObjectTagsJson;
const auto& manager = VisualObjectTagManager::instance();
serializeToJson( manager, visualObjectTagsJson );
cfg.setJsonValue( cVisualObjectTags, visualObjectTagsJson );
}
}

const std::string & ViewerSettingsManager::getLastExtention( ObjType objType )
Expand Down
Loading
Loading