From 5b70d59a73d840dae0b9dd16bdcc13de3d421955 Mon Sep 17 00:00:00 2001 From: dlyr Date: Fri, 15 Jul 2022 09:58:31 +0200 Subject: [PATCH 01/19] [git] Add compile_commands.json .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 26bf0dcc869..0a4f7641d72 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ *.swp *.old \#*\# +compile_commands.json + # All dot files, if you wish to track one, just add it .* From d8504c9fe7e9ad85bcabf9ee22a9cf6a7e823afb Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Mon, 4 Jul 2022 17:37:15 +0200 Subject: [PATCH 02/19] [tests] Add brush radius getter in renderer --- src/Engine/Rendering/Renderer.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Engine/Rendering/Renderer.hpp b/src/Engine/Rendering/Renderer.hpp index 888a01a5e4c..553e7a2e2c5 100644 --- a/src/Engine/Rendering/Renderer.hpp +++ b/src/Engine/Rendering/Renderer.hpp @@ -273,6 +273,8 @@ class RA_ENGINE_API Renderer inline void setBrushRadius( Scalar brushRadius ); + inline Scalar getBrushRadius() { return m_brushRadius; } + /// Tell if the renderer has an usable light. bool hasLight() const; From 0c680955b391a6e6ceceb101a3cdc4e69d806b49 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Mon, 4 Jul 2022 18:27:29 +0200 Subject: [PATCH 03/19] [tests] Add curve editor example --- examples/CMakeLists.txt | 1 + examples/CurveEditor/CMakeLists.txt | 101 +++++ examples/CurveEditor/CurveComponent.cpp | 92 ++++ examples/CurveEditor/CurveComponent.hpp | 21 + examples/CurveEditor/CurveEditor.cpp | 409 ++++++++++++++++++ examples/CurveEditor/CurveEditor.hpp | 68 +++ examples/CurveEditor/CurveFactory.hpp | 26 ++ examples/CurveEditor/Gui/MainWindow.cpp | 240 ++++++++++ examples/CurveEditor/Gui/MainWindow.hpp | 77 ++++ examples/CurveEditor/Gui/MainWindow.ui | 37 ++ examples/CurveEditor/Gui/MyViewer.cpp | 9 + examples/CurveEditor/Gui/MyViewer.hpp | 21 + examples/CurveEditor/Painty/CubicBezier.hpp | 279 ++++++++++++ .../Painty/CubicBezierApproximation.hpp | 358 +++++++++++++++ .../CurveEditor/Painty/LeastSquareSystem.hpp | 204 +++++++++ examples/CurveEditor/PointComponent.cpp | 66 +++ examples/CurveEditor/PointComponent.hpp | 26 ++ examples/CurveEditor/PointFactory.hpp | 33 ++ examples/CurveEditor/README.md | 12 + examples/CurveEditor/StrokeComponent.cpp | 58 +++ examples/CurveEditor/StrokeComponent.hpp | 19 + examples/CurveEditor/StrokeFactory.hpp | 30 ++ examples/CurveEditor/main.cpp | 60 +++ 23 files changed, 2247 insertions(+) create mode 100644 examples/CurveEditor/CMakeLists.txt create mode 100644 examples/CurveEditor/CurveComponent.cpp create mode 100644 examples/CurveEditor/CurveComponent.hpp create mode 100644 examples/CurveEditor/CurveEditor.cpp create mode 100644 examples/CurveEditor/CurveEditor.hpp create mode 100644 examples/CurveEditor/CurveFactory.hpp create mode 100644 examples/CurveEditor/Gui/MainWindow.cpp create mode 100644 examples/CurveEditor/Gui/MainWindow.hpp create mode 100644 examples/CurveEditor/Gui/MainWindow.ui create mode 100644 examples/CurveEditor/Gui/MyViewer.cpp create mode 100644 examples/CurveEditor/Gui/MyViewer.hpp create mode 100644 examples/CurveEditor/Painty/CubicBezier.hpp create mode 100644 examples/CurveEditor/Painty/CubicBezierApproximation.hpp create mode 100644 examples/CurveEditor/Painty/LeastSquareSystem.hpp create mode 100644 examples/CurveEditor/PointComponent.cpp create mode 100644 examples/CurveEditor/PointComponent.hpp create mode 100644 examples/CurveEditor/PointFactory.hpp create mode 100644 examples/CurveEditor/README.md create mode 100644 examples/CurveEditor/StrokeComponent.cpp create mode 100644 examples/CurveEditor/StrokeComponent.hpp create mode 100644 examples/CurveEditor/StrokeFactory.hpp create mode 100644 examples/CurveEditor/main.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c17881c905b..dc33dbdf160 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,6 +14,7 @@ add_custom_target(Install_${PROJECT_NAME}) foreach( APP CoreExample + CurveEditor CustomCameraManipulator DrawPrimitives EntityAnimation diff --git a/examples/CurveEditor/CMakeLists.txt b/examples/CurveEditor/CMakeLists.txt new file mode 100644 index 00000000000..714d1166e4a --- /dev/null +++ b/examples/CurveEditor/CMakeLists.txt @@ -0,0 +1,101 @@ +cmake_minimum_required(VERSION 3.16) +cmake_policy(SET CMP0071 NEW) +cmake_policy(SET CMP0042 NEW) + +project(CurveEditor VERSION 1.0.0) + +# ------------------------------------------------------------------------------ +# set wanted application defaults for cmake settings +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() +# Set default install location to installed- folder in build dir we do not want to +# install to /usr by default +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX + "${CMAKE_CURRENT_BINARY_DIR}/installed-${CMAKE_CXX_COMPILER_ID}-${CMAKE_BUILD_TYPE}" + CACHE PATH "Install path prefix, prepended onto install directories." FORCE + ) + message("Set install prefix to ${CMAKE_INSTALL_PREFIX}") +endif() +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) + +# ------------------------------------------------------------------------------ +find_package(Radium REQUIRED Core Engine Gui IO) + +find_qt_package(COMPONENTS Core Widgets REQUIRED) +set(Qt_LIBRARIES Qt::Core Qt::Widgets) +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# ------------------------------------------------------------------------------ + +set(app_sources main.cpp CurveComponent.cpp StrokeComponent.cpp PointComponent.cpp + Gui/MainWindow.cpp Gui/MyViewer.cpp CurveEditor.cpp +) + +set(app_headers + Gui/MainWindow.hpp + Gui/MyViewer.hpp + CurveComponent.hpp + StrokeComponent.hpp + CurveFactory.hpp + PointComponent.hpp + PointFactory.hpp + Painty/CubicBezier.hpp + Painty/CubicBezierApproximation.hpp + Painty/LeastSquareSystem.hpp + CurveEditor.hpp +) + +set(app_uis) +qt_wrap_ui(app_uis_moc ${app_uis}) + +set(app_resources) + +# to install the app as a redistribuable bundle on macos, add MACOSX_BUNDLE when calling +# add_executable +add_executable( + ${PROJECT_NAME} MACOSX_BUNDLE ${app_sources} ${app_headers} ${app_uis} ${app_resources} +) + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + target_compile_options( + ${PROJECT_NAME} + PRIVATE /MP + /W4 + /wd4251 + /wd4592 + /wd4127 + /Zm200 + $<$: + /Gw + /GS- + /GL + /GF + > + PUBLIC + ) +endif() + +get_target_property(USE_ASSIMP Radium::IO IO_ASSIMP) +if(${USE_ASSIMP}) + target_compile_definitions(${PROJECT_NAME} PRIVATE "-DIO_USE_ASSIMP") +endif() + +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) +target_include_directories( + ${PROJECT_NAME} PRIVATE ${RADIUM_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} # Moc + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries( + ${PROJECT_NAME} PUBLIC Radium::Core Radium::Engine Radium::Gui Radium::IO ${Qt_LIBRARIES} +) + +# configure the application +configure_radium_app( + NAME ${PROJECT_NAME} PREFIX Examples RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/Assets" +) diff --git a/examples/CurveEditor/CurveComponent.cpp b/examples/CurveEditor/CurveComponent.cpp new file mode 100644 index 00000000000..6a9146e9986 --- /dev/null +++ b/examples/CurveEditor/CurveComponent.cpp @@ -0,0 +1,92 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef IO_USE_ASSIMP +# include +#endif + +#include + +using namespace Ra; +using namespace Ra::Core; +using namespace Ra::Core::Utils; +using namespace Ra::Core::Geometry; +using namespace Ra::Engine; +using namespace Ra::Engine::Rendering; +using namespace Ra::Engine::Data; +using namespace Ra::Engine::Scene; + +/** + * This file contains a minimal radium/qt application which shows the geometrical primitives + * supported by Radium + */ + +CurveComponent::CurveComponent( Ra::Engine::Scene::Entity* entity, + Vector3Array ctrlPts, + const std::string& name ) : + Ra::Engine::Scene::Component( name, entity ), m_ctrlPts( ctrlPts ) {} + +/// This function is called when the component is properly +/// setup, i.e. it has an entity. +void CurveComponent::initialize() { + auto plainMaterial = make_shared( "Plain Material" ); + plainMaterial->m_perVertexColor = true; + + auto bezier = CubicBezier( Vector2( m_ctrlPts[0].x(), m_ctrlPts[0].z() ), + Vector2( m_ctrlPts[1].x(), m_ctrlPts[1].z() ), + Vector2( m_ctrlPts[2].x(), m_ctrlPts[2].z() ), + Vector2( m_ctrlPts[3].x(), m_ctrlPts[3].z() ) ); + + auto bezierVertices = Vector3Array(); + auto bezierColors = Vector4Array(); + for ( unsigned int i = 0; i <= 100; i++ ) { + float u = float( i ) / 100.f; + auto fu = bezier.f( u ); + bezierVertices.push_back( Vector3( fu.x(), 0, fu.y() ) ); + bezierColors.push_back( { 1, 0, 0, 1 } ); + } + + // Render mesh + auto renderObject1 = + RenderObject::createRenderObject( "PolyMesh", + this, + RenderObjectType::Geometry, + DrawPrimitives::LineStrip( bezierVertices, bezierColors ), + Ra::Engine::Rendering::RenderTechnique {} ); + renderObject1->setMaterial( plainMaterial ); + addRenderObject( renderObject1 ); + + auto renderObject2 = RenderObject::createRenderObject( + "PolyMesh1", + this, + RenderObjectType::Geometry, + DrawPrimitives::LineStrip( { m_ctrlPts[0], m_ctrlPts[1] }, { { 0, 0, 0, 1 } } ), + Ra::Engine::Rendering::RenderTechnique {} ); + renderObject2->setMaterial( plainMaterial ); + addRenderObject( renderObject2 ); + + auto renderObject3 = RenderObject::createRenderObject( + "PolyMesh1", + this, + RenderObjectType::Geometry, + DrawPrimitives::LineStrip( { m_ctrlPts[2], m_ctrlPts[3] }, { { 0, 0, 0, 1 } } ), + Ra::Engine::Rendering::RenderTechnique {} ); + renderObject3->setMaterial( plainMaterial ); + addRenderObject( renderObject3 ); +} diff --git a/examples/CurveEditor/CurveComponent.hpp b/examples/CurveEditor/CurveComponent.hpp new file mode 100644 index 00000000000..9a8365d608b --- /dev/null +++ b/examples/CurveEditor/CurveComponent.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include + +class CurveComponent : public Ra::Engine::Scene::Component +{ + + public: + CurveComponent( Ra::Engine::Scene::Entity* entity, + Ra::Core::Vector3Array ctrlPts, + const std::string& name ); + + /// This function is called when the component is properly + /// setup, i.e. it has an entity. + void initialize() override; + + Ra::Core::Vector3Array m_ctrlPts; +}; diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp new file mode 100644 index 00000000000..ef6f9c0880e --- /dev/null +++ b/examples/CurveEditor/CurveEditor.cpp @@ -0,0 +1,409 @@ +#include +#include +#include +#include +#include +#include + +#include "CurveEditor.hpp" +#include "Painty/CubicBezier.hpp" +#include "Painty/CubicBezierApproximation.hpp" + +CurveEditor::CurveEditor( std::vector polyline, MyViewer* viewer ) : + Ra::Engine::Scene::Entity( "Curve Editor" ), m_viewer( viewer ) { + m_entityMgr = Ra::Engine::RadiumEngine::getInstance()->getEntityManager(); + m_roMgr = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager(); + + // Approximate polyline with bezier + CubicBezierApproximation2f approximator; + approximator.init( polyline ); + approximator.compute(); + auto solution = approximator.getSolution(); + auto solutionPts = solution.getCtrlPoints(); + + // Create and render solution entities + CurveFactory factory; + std::vector allCtrlPts; + m_viewer->makeCurrent(); + for ( unsigned int i = 0; i < solutionPts.size() - 3; i += 3 ) { + Ra::Core::Vector3Array ctrlPts; + ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i].x(), 0, solutionPts[i].y() ) ); + ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i + 1].x(), 0, solutionPts[i + 1].y() ) ); + ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i + 2].x(), 0, solutionPts[i + 2].y() ) ); + ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i + 3].x(), 0, solutionPts[i + 3].y() ) ); + std::string name = "Curve_" + std::to_string( i / 3 ); + auto e = factory.createCurveComponent( this, ctrlPts, name ); + m_curveEntities.push_back( e ); + allCtrlPts.push_back( ctrlPts ); + } + + std::string namePt = "CtrlPt_" + std::to_string( 0 ); + auto e = PointFactory::createPointComponent( + this, allCtrlPts[0][0], { 0 }, namePt, Ra::Core::Utils::Color::Blue() ); + m_pointEntities.push_back( e ); + int nameIndex = 1; + for ( unsigned int i = 0; i < allCtrlPts.size(); i++ ) { + for ( unsigned int j = 1; j < allCtrlPts[i].size() - 1; j++ ) { + namePt = "CtrlPt_" + std::to_string( nameIndex ); + e = PointFactory::createPointComponent( this, allCtrlPts[i][j], { i }, namePt ); + m_pointEntities.push_back( e ); + nameIndex++; + } + namePt = "CtrlPt_" + std::to_string( nameIndex ); + if ( i == allCtrlPts.size() - 1 ) + e = PointFactory::createPointComponent( + this, allCtrlPts[i][3], { i }, namePt, Ra::Core::Utils::Color::Blue() ); + else + e = PointFactory::createPointComponent( + this, allCtrlPts[i][3], { i, i + 1 }, namePt, Ra::Core::Utils::Color::Blue() ); + m_pointEntities.push_back( e ); + nameIndex++; + } + m_viewer->doneCurrent(); + m_viewer->getRenderer()->buildAllRenderTechniques(); + m_viewer->needUpdate(); +} + +CurveEditor::~CurveEditor() {} + +unsigned int CurveEditor::getPointIndex( unsigned int curveIdSize, + unsigned int currentPtIndex, + const Ra::Core::Vector3& firstCtrlPt, + const Ra::Core::Vector3& currentPt ) { + unsigned int pointIndex; + if ( curveIdSize > 1 || currentPtIndex == m_pointEntities.size() - 1 ) { + pointIndex = ( firstCtrlPt == currentPt ) ? 0 : 3; + } + else { pointIndex = ( currentPtIndex % 3 ); } + return pointIndex; +} + +void CurveEditor::updateCurve( unsigned int curveId, + unsigned int curveIdSize, + PointComponent* pointComponent, + const Ra::Core::Vector3& oldPoint, + unsigned int currentPoint ) { + auto component = m_curveEntities[curveId]; + auto ctrlPts = component->m_ctrlPts; + int pointIndex = getPointIndex( curveIdSize, currentPoint, ctrlPts[0], oldPoint ); + + auto ro = m_roMgr->getRenderObject( m_pointEntities[currentPoint]->getRenderObjects()[0] ); + auto transform = ro->getTransform(); + ctrlPts[pointIndex] = transform * pointComponent->m_defaultPoint; + pointComponent->m_point = ctrlPts[pointIndex]; + + std::string name = m_curveEntities[curveId]->getName(); + removeComponent( name ); + auto e = CurveFactory::createCurveComponent( this, ctrlPts, name ); + m_curveEntities[curveId] = e; +} + +void CurveEditor::refreshPoint( unsigned int pointIndex, const Ra::Core::Vector3& newPoint ) { + auto pointName = m_pointEntities[pointIndex]->getName(); + auto pointComponent = m_pointEntities[pointIndex]; + auto pointColor = pointComponent->m_color; + auto pointCurve = pointComponent->m_curveId; + Ra::Core::Vector3 point; + if ( newPoint == Ra::Core::Vector3::Zero() ) + point = pointComponent->m_point; + else + point = newPoint; + removeComponent( m_pointEntities[pointIndex]->getName() ); + auto pointEntity = + PointFactory::createPointComponent( this, point, pointCurve, pointName, pointColor ); + m_pointEntities[pointIndex] = pointEntity; +} + +void CurveEditor::updateCurves( bool onRelease ) { + + if ( m_currentPoint < 0 ) return; + + auto pointComponent = m_pointEntities[m_currentPoint]; + auto oldPoint = pointComponent->m_point; + unsigned int curveIdSize = pointComponent->m_curveId.size(); + + CurveFactory factory; + m_viewer->makeCurrent(); + + for ( unsigned int i = 0; i < curveIdSize; i++ ) { + auto curveId = pointComponent->m_curveId[i]; + updateCurve( curveId, curveIdSize, pointComponent, oldPoint, m_currentPoint ); + } + + for ( unsigned int i = 0; i < m_tangentPoints.size(); i++ ) { + auto tangentPtComponent = m_pointEntities[m_tangentPoints[i]]; + Ra::Core::Vector3 tangentPt = tangentPtComponent->m_point; + auto symCurveId = tangentPtComponent->m_curveId[0]; + updateCurve( symCurveId, 1, tangentPtComponent, tangentPt, m_tangentPoints[i] ); + } + + // Necessary for transform re-initialization + if ( onRelease ) { + refreshPoint( m_currentPoint ); + for ( unsigned int i = 0; i < m_tangentPoints.size(); i++ ) { + refreshPoint( m_tangentPoints[i] ); + } + m_tangentPoints.clear(); + } + + m_viewer->doneCurrent(); + m_viewer->getRenderer()->buildAllRenderTechniques(); + m_viewer->needUpdate(); +} + +Ra::Core::Transform CurveEditor::computePointTransform( PointComponent* pointCmp, + const Ra::Core::Vector3& midPoint, + const Ra::Core::Vector3& worldPos ) { + auto transform = Ra::Core::Transform::Identity(); + Ra::Core::Vector3 diff = ( midPoint - worldPos ); + if ( m_symetry ) { transform.translate( ( midPoint + diff ) - pointCmp->m_defaultPoint ); } + else { + diff.normalize(); + auto actualdiff = diff * ( midPoint - pointCmp->m_point ).norm(); + transform.translate( ( midPoint + actualdiff ) - pointCmp->m_defaultPoint ); + } + return transform; +} + +inline float CurveEditor::distanceSquared( const Ra::Core::Vector3f& pointA, + const Ra::Core::Vector3f& pointB ) { + float dx = pointA.x() - pointB.x(); + float dy = pointA.y() - pointB.y(); + float dz = pointA.z() - pointB.z(); + return dx * dx + dy * dy + dz * dz; +} + +// De Casteljau algorithm +void CurveEditor::subdivisionBezier( int vertexIndex, + unsigned int curveIndex, + const Ra::Core::Vector3Array& ctrlPts ) { + auto bezier = + Ra::Core::Geometry::CubicBezier( Ra::Core::Vector2( ctrlPts[0].x(), ctrlPts[0].z() ), + Ra::Core::Vector2( ctrlPts[1].x(), ctrlPts[1].z() ), + Ra::Core::Vector2( ctrlPts[2].x(), ctrlPts[2].z() ), + Ra::Core::Vector2( ctrlPts[3].x(), ctrlPts[3].z() ) ); + float u = float( vertexIndex ) / 100.f; + + Ra::Core::Vector2 fu = bezier.f( u ); + auto clickedPoint = Ra::Core::Vector3( fu.x(), 0, fu.y() ); + Ra::Core::Vector3 firstPoint = Ra::Core::Math::linearInterpolate( ctrlPts[0], ctrlPts[1], u ); + Ra::Core::Vector3 sndPoint = Ra::Core::Math::linearInterpolate( ctrlPts[1], ctrlPts[2], u ); + Ra::Core::Vector3 thirdPoint = Ra::Core::Math::linearInterpolate( ctrlPts[2], ctrlPts[3], u ); + Ra::Core::Vector3 fourthPoint = Ra::Core::Math::linearInterpolate( firstPoint, sndPoint, u ); + Ra::Core::Vector3 fifthPoint = Ra::Core::Math::linearInterpolate( sndPoint, thirdPoint, u ); + + auto newCtrlPts = ctrlPts; + newCtrlPts[1] = firstPoint; + newCtrlPts[2] = fourthPoint; + newCtrlPts[3] = clickedPoint; + auto nameCurve = m_curveEntities[curveIndex]->getName(); + removeComponent( nameCurve ); + m_curveEntities[curveIndex] = CurveFactory::createCurveComponent( this, newCtrlPts, nameCurve ); + + refreshPoint( curveIndex * 3 + 1, firstPoint ); + refreshPoint( curveIndex * 3 + 2, thirdPoint ); + + auto firstInsertionIdx = curveIndex * 3 + 2; + std::string namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); + auto ptE = PointFactory::createPointComponent( this, + clickedPoint, + { curveIndex, curveIndex + 1 }, + namePt, + Ra::Core::Utils::Color::Blue() ); + m_pointEntities.insert( m_pointEntities.begin() + firstInsertionIdx, ptE ); + namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); + ptE = PointFactory::createPointComponent( this, fourthPoint, { curveIndex }, namePt ); + m_pointEntities.insert( m_pointEntities.begin() + firstInsertionIdx, ptE ); + + namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); + ptE = PointFactory::createPointComponent( this, fifthPoint, { curveIndex + 1 }, namePt ); + m_pointEntities.insert( m_pointEntities.begin() + ( ( curveIndex + 1 ) * 3 + 1 ), ptE ); + + Ra::Core::Vector3Array newCtrlPts1; + newCtrlPts1.push_back( clickedPoint ); + newCtrlPts1.push_back( fifthPoint ); + newCtrlPts1.push_back( thirdPoint ); + newCtrlPts1.push_back( ctrlPts[3] ); + + nameCurve = "Curve_" + std::to_string( m_curveEntities.size() ); + auto curveE = CurveFactory::createCurveComponent( this, newCtrlPts1, nameCurve ); + + m_curveEntities.insert( m_curveEntities.begin() + curveIndex + 1, curveE ); + + for ( unsigned int i = ( ( curveIndex + 1 ) * 3 + 2 ); i < m_pointEntities.size(); i++ ) { + auto ptCmp = m_pointEntities[i]; + for ( unsigned int j = 0; j < ptCmp->m_curveId.size(); j++ ) { + ptCmp->m_curveId[j] += 1; + } + } +} + +void CurveEditor::addPointAtEnd( const Ra::Core::Vector3& worldPos ) { + Ra::Core::Vector3Array ctrlPts; + auto lastIndex = m_pointEntities.size() - 1; + auto beforeLast = m_pointEntities[lastIndex - 1]; + auto last = m_pointEntities[lastIndex]; + Ra::Core::Vector3 diff = ( last->m_point - beforeLast->m_point ); + diff.normalize(); + Ra::Core::Vector3 secondPt = ( last->m_point + diff ); + Ra::Core::Vector3 thirdDiff = ( last->m_point - worldPos ); + thirdDiff.normalize(); + Ra::Core::Vector3 thirdPt = ( worldPos - diff ); + ctrlPts.push_back( last->m_point ); + ctrlPts.push_back( secondPt ); + ctrlPts.push_back( thirdPt ); + ctrlPts.push_back( worldPos ); + std::string name = "Curve_" + std::to_string( m_curveEntities.size() ); + auto e = CurveFactory::createCurveComponent( this, ctrlPts, name ); + m_curveEntities.push_back( e ); + + unsigned int pointIndex = lastIndex + 1; + last->m_curveId.push_back( ( pointIndex / 3 ) ); + std::string namePt = "CtrlPt_" + std::to_string( pointIndex ); + auto ptC = + PointFactory::createPointComponent( this, ctrlPts[1], { ( pointIndex / 3 ) }, namePt ); + m_pointEntities.push_back( ptC ); + namePt = "CtrlPt_" + std::to_string( pointIndex + 1 ); + auto eb = + PointFactory::createPointComponent( this, ctrlPts[2], { ( pointIndex / 3 ) }, namePt ); + m_pointEntities.push_back( eb ); + namePt = "CtrlPt_" + std::to_string( pointIndex + 2 ); + ptC = PointFactory::createPointComponent( + this, ctrlPts[3], { ( pointIndex / 3 ) }, namePt, Ra::Core::Utils::Color::Blue() ); + m_pointEntities.push_back( ptC ); +} + +void CurveEditor::addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX, int mouseY ) { + auto camera = m_viewer->getCameraManipulator()->getCamera(); + auto radius = int( m_viewer->getRenderer()->getBrushRadius() ); + bool found = false; + Ra::Engine::Rendering::Renderer::PickingResult pres; + for ( int i = mouseX - radius; i < mouseX + radius && !found; i++ ) { + for ( int j = mouseY - radius; j < mouseY + radius; j++ ) { + // m_viewer->cursor().setPos(m_viewer->mapToGlobal(QPoint(i, j))); + Ra::Engine::Rendering::Renderer::PickingQuery query { + Ra::Core::Vector2( i, m_viewer->height() - j ), + Ra::Engine::Rendering::Renderer::SELECTION, + Ra::Engine::Rendering::Renderer::RO }; + Ra::Engine::Data::ViewingParameters renderData { + camera->getViewMatrix(), camera->getProjMatrix(), 0 }; + + pres = m_viewer->getRenderer()->doPickingNow( query, renderData ); + if ( pres.getRoIdx().isValid() && m_roMgr->exists( pres.getRoIdx() ) ) { + auto ro = m_roMgr->getRenderObject( pres.getRoIdx() ); + if ( ro->getMesh()->getNumVertices() == 2 ) continue; + pres.removeDuplicatedIndices(); + auto e = ro->getComponent(); + + int curveIndex = -1; + for ( int i = 0; i < m_curveEntities.size(); i++ ) { + if ( m_curveEntities[i] == e ) { + curveIndex = i; + break; + } + } + if ( curveIndex < 0 ) continue; + + auto meshPtr = ro->getMesh().get(); + auto mesh = dynamic_cast( meshPtr ); + auto curveCmp = static_cast( ro->getComponent() ); + auto ctrlPts = curveCmp->m_ctrlPts; + + if ( mesh != nullptr ) { + float bestV = std::numeric_limits::max(); + int vIndex = -1; + for ( unsigned int w = 0; w < mesh->getNumVertices(); w++ ) { + auto dist = + distanceSquared( worldPos, mesh->getCoreGeometry().vertices()[w] ); + if ( dist < bestV ) { + bestV = dist; + vIndex = w; + } + } + + subdivisionBezier( vIndex, curveIndex, ctrlPts ); + } + found = true; + break; + } + } + } +} + +bool CurveEditor::processHover( std::shared_ptr ro ) { + auto e = ro->getComponent(); + + // if not a control point -> do nothing + for ( int i = 0; i < m_pointEntities.size(); i++ ) { + if ( e == m_pointEntities[i] ) { + m_currentPoint = i; + break; + } + } + if ( m_currentPoint < 0 ) return false; + + ro->getMaterial()->getParameters().addParameter( "material.color", + Ra::Core::Utils::Color::Red() ); + m_selectedRo = ro; + return true; +} + +void CurveEditor::processUnhovering() { + auto pointCmp = static_cast( m_selectedRo->getComponent() ); + m_selectedRo->getMaterial()->getParameters().addParameter( "material.color", + pointCmp->m_color ); + m_selectedRo = nullptr; + m_currentPoint = -1; +} + +void CurveEditor::processPicking( const Ra::Core::Vector3& worldPos ) { + if ( m_currentPoint < 0 ) return; + auto pointComponent = static_cast( m_selectedRo->getComponent() ); + auto point = pointComponent->m_point; + + auto transformTranslate = Ra::Core::Transform::Identity(); + transformTranslate.translate( worldPos - point ); + + auto transform = Ra::Core::Transform::Identity(); + transform = m_selectedRo->getLocalTransform() * transformTranslate; + m_selectedRo->setLocalTransform( transform ); + + if ( m_smooth && !( m_currentPoint == 0 || m_currentPoint == 1 || + m_currentPoint == m_pointEntities.size() - 2 || + m_currentPoint == m_pointEntities.size() - 1 ) ) { + + if ( m_currentPoint % 3 == 2 ) { + auto pointMid = m_pointEntities[m_currentPoint + 1]; + pointComponent = m_pointEntities[m_currentPoint + 2]; + auto symTransform = + computePointTransform( pointComponent, pointMid->m_point, worldPos ); + auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); + ro->setLocalTransform( symTransform ); + if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint + 2 ); } + } + else if ( m_currentPoint % 3 == 1 ) { + auto pointMid = m_pointEntities[m_currentPoint - 1]; + pointComponent = m_pointEntities[m_currentPoint - 2]; + auto symTransform = + computePointTransform( pointComponent, pointMid->m_point, worldPos ); + auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); + ro->setLocalTransform( symTransform ); + if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint - 2 ); } + } + else if ( m_currentPoint % 3 == 0 ) { + auto leftRo = m_roMgr->getRenderObject( + m_pointEntities[m_currentPoint - 1]->getRenderObjects()[0] ); + auto leftTransform = leftRo->getTransform() * transformTranslate; + leftRo->setLocalTransform( leftTransform ); + auto rightRo = m_roMgr->getRenderObject( + m_pointEntities[m_currentPoint + 1]->getRenderObjects()[0] ); + auto rightTransform = rightRo->getTransform() * transformTranslate; + rightRo->setLocalTransform( rightTransform ); + if ( m_tangentPoints.empty() ) { + m_tangentPoints.push_back( m_currentPoint - 1 ); + m_tangentPoints.push_back( m_currentPoint + 1 ); + } + } + } + updateCurves(); +} diff --git a/examples/CurveEditor/CurveEditor.hpp b/examples/CurveEditor/CurveEditor.hpp new file mode 100644 index 00000000000..35f2fd7f4ce --- /dev/null +++ b/examples/CurveEditor/CurveEditor.hpp @@ -0,0 +1,68 @@ +#include "CurveFactory.hpp" +#include "Gui/MyViewer.hpp" +#include "PointFactory.hpp" +#include +#include +#include + +class CurveEditor : public Ra::Engine::Scene::Entity +{ + + public: + CurveEditor( std::vector polyline, MyViewer* viewer ); + ~CurveEditor(); + + void addPointAtEnd( const Ra::Core::Vector3& worldPos ); + void addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX, int mouseY ); + + void updateCurves( bool onRelease = false ); + + bool processHover( std::shared_ptr ro ); + void processUnhovering(); + void processPicking( const Ra::Core::Vector3& worldPos ); + + std::shared_ptr getSelected() { return m_selectedRo; } + + void resetSelected() { + m_selectedRo = nullptr; + m_currentPoint = -1; + } + + void setSmooth( bool smooth ) { m_smooth = smooth; } + void setSymetry( bool symetry ) { m_symetry = symetry; } + + private: + inline float distanceSquared( const Ra::Core::Vector3f& pointA, + const Ra::Core::Vector3f& pointB ); + unsigned int getPointIndex( unsigned int curveIdSize, + unsigned int currentPtIndex, + const Ra::Core::Vector3& firstCtrlPt, + const Ra::Core::Vector3& currentPt ); + void updateCurve( unsigned int curveId, + unsigned int curveIdSize, + PointComponent* pointComponent, + const Ra::Core::Vector3& currentPt, + unsigned int currentPoint ); + Ra::Core::Transform computePointTransform( PointComponent* pointCmp, + const Ra::Core::Vector3& midPoint, + const Ra::Core::Vector3& worldPos ); + void subdivisionBezier( int vertexIndex, + unsigned int curveIndex, + const Ra::Core::Vector3Array& ctrlPts ); + void refreshPoint( unsigned int pointIndex, + const Ra::Core::Vector3& newPoint = Ra::Core::Vector3::Zero() ); + + private: + Ra::Engine::Scene::EntityManager* m_entityMgr; + int m_currentPoint { -1 }; + std::shared_ptr m_selectedRo { nullptr }; + std::vector m_pointEntities; + std::vector m_curveEntities; + std::vector m_tangentPoints; + Ra::Engine::Rendering::RenderObjectManager* m_roMgr; + + bool m_smooth { false }; + bool m_symetry { false }; + + MyViewer* m_viewer { nullptr }; +}; diff --git a/examples/CurveEditor/CurveFactory.hpp b/examples/CurveEditor/CurveFactory.hpp new file mode 100644 index 00000000000..7aa26e2edd0 --- /dev/null +++ b/examples/CurveEditor/CurveFactory.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "CurveComponent.hpp" +#include +#include + +class CurveFactory +{ + public: + static CurveComponent* createCurveComponent( Ra::Engine::Scene::Entity* e, + Ra::Core::Vector3Array ctrlPts, + const std::string& name ) { + auto c = new CurveComponent( e, ctrlPts, name ); + c->initialize(); + return c; + } + static Ra::Engine::Scene::Entity* createCurveEntity( Ra::Core::Vector3Array ctrlPts, + const std::string& name ) { + auto engine = Ra::Engine::RadiumEngine::getInstance(); + Ra::Engine::Scene::Entity* e = engine->getEntityManager()->createEntity( name ); + createCurveComponent( e, ctrlPts, name ); + return e; + } + + private: +}; diff --git a/examples/CurveEditor/Gui/MainWindow.cpp b/examples/CurveEditor/Gui/MainWindow.cpp new file mode 100644 index 00000000000..5d5f0c50681 --- /dev/null +++ b/examples/CurveEditor/Gui/MainWindow.cpp @@ -0,0 +1,240 @@ +#include "MainWindow.hpp" +#include "Painty/CubicBezierApproximation.hpp" +#include "PointFactory.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Ra::Gui; +using namespace Ra::Engine; +using namespace Ra::Engine::Rendering; +using namespace Ra::Core::Utils; + +MainWindow::MainWindow( QWidget* parent ) : MainWindowInterface( parent ) { + if ( objectName().isEmpty() ) setObjectName( QString::fromUtf8( "RadiumSimpleWindow" ) ); + + // Initialize the minimum tools for a Radium-Guibased Application + m_viewer = new MyViewer(); + m_viewer->setObjectName( QStringLiteral( "m_viewer" ) ); + m_viewer->setBackgroundColor( Color::White() ); + m_engine = Ra::Engine::RadiumEngine::getInstance(); + + // Initialize the scene interactive representation + m_sceneModel = new Ra::Gui::ItemModel( Ra::Engine::RadiumEngine::getInstance(), this ); + m_selectionManager = new Ra::Gui::SelectionManager( m_sceneModel, this ); + + // initialize Gui for the application + auto viewerWidget = QWidget::createWindowContainer( m_viewer ); + viewerWidget->setAutoFillBackground( false ); + setCentralWidget( viewerWidget ); + setWindowTitle( QString( "Radium player" ) ); + setMinimumSize( 800, 600 ); + + // Dock widget + QDockWidget* dockWidget = new QDockWidget( "Dock", this ); + QWidget* myWidget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(); + m_editCurveButton = new QPushButton( "Edit stroke" ); + m_button = new QPushButton( "smooth" ); + m_button->setCheckable( true ); + m_hideStrokeButton = new QPushButton( "Hide initial stroke" ); + m_hideStrokeButton->setCheckable( true ); + m_symetryButton = new QPushButton( "Symetry" ); + m_symetryButton->setCheckable( true ); + m_symetryButton->setEnabled( false ); + layout->addWidget( m_hideStrokeButton ); + layout->addWidget( m_editCurveButton ); + layout->addWidget( m_button ); + layout->addWidget( m_symetryButton ); + myWidget->setLayout( layout ); + dockWidget->setWidget( myWidget ); + dockWidget->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); + addDockWidget( Qt::LeftDockWidgetArea, dockWidget ); + m_dockWidget = dockWidget; + createConnections(); +} + +MainWindow::~MainWindow() = default; + +MyViewer* MainWindow::getViewer() { + return m_viewer; +} + +Ra::Gui::SelectionManager* MainWindow::getSelectionManager() { + return m_selectionManager; +} + +Ra::Gui::Timeline* MainWindow::getTimeline() { + return nullptr; +} + +void MainWindow::updateUi( Ra::Plugins::RadiumPluginInterface* plugin ) { + QString name; + if ( plugin->doAddWidget( name ) ) { m_dockWidget->setWidget( plugin->getWidget() ); } +} + +void MainWindow::onFrameComplete() {} + +void MainWindow::addRenderer( const std::string&, + std::shared_ptr e ) { + e->enableDebugDraw( false ); + m_viewer->addRenderer( e ); +} +void MainWindow::prepareDisplay() { + m_selectionManager->clear(); + if ( m_viewer->prepareDisplay() ) { emit frameUpdate(); } + m_viewer->getRenderer()->toggleDrawDebug(); +} + +void MainWindow::cleanup() { + m_viewer = nullptr; +} + +void MainWindow::createConnections() { + connect( getViewer(), &MyViewer::toggleBrushPicking, this, &MainWindow::toggleCirclePicking ); + connect( getViewer(), &MyViewer::onMouseMove, this, &MainWindow::handleMouseMoveEvent ); + connect( getViewer(), &MyViewer::onMouseRelease, this, &MainWindow::handleMouseReleaseEvent ); + connect( getViewer(), &MyViewer::onMousePress, this, &MainWindow::handleMousePressEvent ); + connect( + m_editCurveButton, &QPushButton::pressed, this, &MainWindow::onEditStrokeButtonPressed ); + connect( + m_hideStrokeButton, &QPushButton::clicked, this, &MainWindow::onHideStrokeButtonClicked ); + connect( m_button, &QPushButton::clicked, this, &MainWindow::onSmoothButtonClicked ); + connect( m_symetryButton, &QPushButton::clicked, this, &MainWindow::onSymetryButtonClicked ); + connect( getViewer(), + &MyViewer::onMouseDoubleClick, + this, + &MainWindow::handleMouseDoubleClickEvent ); +} + +void MainWindow::onSmoothButtonClicked() { + m_symetryButton->setEnabled( m_button->isChecked() ); + m_curveEditor->setSmooth( m_button->isChecked() ); +} + +void MainWindow::onSymetryButtonClicked() { + m_curveEditor->setSymetry( m_symetryButton->isChecked() ); +} + +void MainWindow::onHideStrokeButtonClicked() { + auto roMgr = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager(); + auto ro = roMgr->getRenderObject( m_initialStroke->getComponents()[0]->getRenderObjects()[0] ); + if ( m_hideStrokeButton->isChecked() ) { ro->setVisible( false ); } + else + ro->setVisible( true ); + m_viewer->needUpdate(); +} + +void MainWindow::onEditStrokeButtonPressed() { + if ( m_polyline.empty() || m_edited ) return; + m_edited = true; + m_curveEditor = new CurveEditor( m_polyline, m_viewer ); +} + +void MainWindow::handleMousePressEvent( QMouseEvent* event ) { + if ( event->button() == Qt::RightButton ) m_clicked = true; +} + +void MainWindow::displayHelpDialog() { + m_viewer->displayHelpDialog(); +} + +void MainWindow::toggleCirclePicking( bool on ) { + m_isTracking = on; + centralWidget()->setMouseTracking( on ); +} + +Ra::Core::Vector3 MainWindow::getWorldPos( int x, int y ) { + const auto r = m_viewer->getCameraManipulator()->getCamera()->getRayFromScreen( { x, y } ); + std::vector t; + // {0,0,0} a point and {0,1,0} the normal + if ( Ra::Core::Geometry::RayCastPlane( r, { 0, 0, 0 }, { 0, 1, 0 }, t ) ) { + // vsPlane return at most one intersection t + return r.pointAt( t[0] ); + } + return Ra::Core::Vector3(); +} + +void MainWindow::handleMouseReleaseEvent( QMouseEvent* event ) { + m_clicked = false; + if ( m_curveEditor == nullptr ) return; + if ( m_curveEditor->getSelected() && event->button() == Qt::RightButton ) { + m_curveEditor->updateCurves( true ); + m_curveEditor->resetSelected(); + m_hovering = false; + } +} + +void MainWindow::handleMouseDoubleClickEvent( QMouseEvent* event ) { + if ( m_curveEditor == nullptr ) return; + auto worldPos = getWorldPos( event->x(), event->y() ); + + auto modifiers = event->modifiers(); + + m_viewer->makeCurrent(); + if ( modifiers == Qt::ShiftModifier ) { + m_curveEditor->addPointAtEnd( worldPos ); + m_viewer->doneCurrent(); + m_viewer->getRenderer()->buildAllRenderTechniques(); + m_viewer->needUpdate(); + return; + } + + m_curveEditor->addPointInCurve( worldPos, event->pos().x(), event->pos().y() ); + + m_viewer->doneCurrent(); + m_viewer->getRenderer()->buildAllRenderTechniques(); + m_viewer->needUpdate(); +} + +void MainWindow::handleMouseMoveEvent( QMouseEvent* event ) { + if ( m_curveEditor == nullptr ) return; + int mouseX = event->pos().x(); + int mouseY = event->pos().y(); + handleHover( mouseX, mouseY ); + handlePicking( mouseX, mouseY ); +} + +void MainWindow::handleHover( int mouseX, int mouseY ) { + m_viewer->makeCurrent(); + auto camera = m_viewer->getCameraManipulator()->getCamera(); + Ra::Engine::Rendering::Renderer::PickingQuery query { + Ra::Core::Vector2( mouseX, height() - mouseY ), + Ra::Engine::Rendering::Renderer::MANIPULATION, + Ra::Engine::Rendering::Renderer::RO }; + Ra::Engine::Data::ViewingParameters renderData { + camera->getViewMatrix(), camera->getProjMatrix(), 0 }; + + auto pres = m_viewer->getRenderer()->doPickingNow( query, renderData ); + m_viewer->doneCurrent(); + + if ( pres.getRoIdx() >= 0 && ( !m_hovering ) ) { + auto ro = m_engine->getRenderObjectManager()->getRenderObject( pres.getRoIdx() ); + m_hovering = m_curveEditor->processHover( ro ); + } + else if ( m_hovering && !( m_clicked ) ) { + m_curveEditor->processUnhovering(); + m_hovering = false; + } +} + +void MainWindow::handlePicking( int mouseX, int mouseY ) { + if ( m_curveEditor->getSelected() != nullptr && m_clicked ) { + auto worldPos = getWorldPos( mouseX, mouseY ); + m_curveEditor->processPicking( worldPos ); + } +} diff --git a/examples/CurveEditor/Gui/MainWindow.hpp b/examples/CurveEditor/Gui/MainWindow.hpp new file mode 100644 index 00000000000..6a3eb5aac49 --- /dev/null +++ b/examples/CurveEditor/Gui/MainWindow.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +#include "CurveEditor.hpp" +#include "MyViewer.hpp" +#include "PointComponent.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// This class manages most of the GUI of the application : +/// top menu, side toolbar and side dock. +class MainWindow : public Ra::Gui::MainWindowInterface +{ + Q_OBJECT + public: + /// Constructor and destructor. + explicit MainWindow( QWidget* parent = nullptr ); + virtual ~MainWindow() override; + MyViewer* getViewer() override; + Ra::Gui::SelectionManager* getSelectionManager() override; + Ra::Gui::Timeline* getTimeline() override; + void updateUi( Ra::Plugins::RadiumPluginInterface* plugin ) override; + void onFrameComplete() override; + void addRenderer( const std::string& name, + std::shared_ptr e ) override; + void toggleCirclePicking( bool on ); + Ra::Core::Vector3 getWorldPos( int x, int y ); + void handleHover( int mouseX, int mouseY ); + void handlePicking( int mouseX, int mouseY ); + void setPolyline( std::vector polyline ) { m_polyline = polyline; } + void setInitialStroke( Ra::Engine::Scene::Entity* e ) { m_initialStroke = e; } + + public slots: + void prepareDisplay() override; + void cleanup() override; + // Display help dialog about Viewer key-bindings + void displayHelpDialog() override; + void handleMouseMoveEvent( QMouseEvent* event ); + void handleMouseReleaseEvent( QMouseEvent* event ); + void handleMousePressEvent( QMouseEvent* event ); + void handleMouseDoubleClickEvent( QMouseEvent* event ); + void onEditStrokeButtonPressed(); + void onHideStrokeButtonClicked(); + void onSmoothButtonClicked(); + void onSymetryButtonClicked(); + signals: + void frameUpdate(); + + private: + void createConnections(); + MyViewer* m_viewer; + Ra::Gui::SelectionManager* m_selectionManager; + Ra::Gui::ItemModel* m_sceneModel; + Ra::Engine::RadiumEngine* m_engine; + QDockWidget* m_dockWidget; + QPushButton* m_button; + QPushButton* m_editCurveButton; + QPushButton* m_hideStrokeButton; + QPushButton* m_symetryButton; + bool m_clicked = false; + bool m_isTracking { false }; + bool m_hovering { false }; + bool m_edited { false }; + std::vector m_polyline; + Ra::Engine::Scene::Entity* m_initialStroke; + + CurveEditor* m_curveEditor { nullptr }; +}; diff --git a/examples/CurveEditor/Gui/MainWindow.ui b/examples/CurveEditor/Gui/MainWindow.ui new file mode 100644 index 00000000000..d28c894e56a --- /dev/null +++ b/examples/CurveEditor/Gui/MainWindow.ui @@ -0,0 +1,37 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 0 + 0 + 800 + 22 + + + + + About + + + + + + + + + diff --git a/examples/CurveEditor/Gui/MyViewer.cpp b/examples/CurveEditor/Gui/MyViewer.cpp new file mode 100644 index 00000000000..15ffd98244b --- /dev/null +++ b/examples/CurveEditor/Gui/MyViewer.cpp @@ -0,0 +1,9 @@ +#include "MyViewer.hpp" + +MyViewer::MyViewer() : Ra::Gui::Viewer() {} + +void MyViewer::mouseDoubleClickEvent( QMouseEvent* event ) { + emit onMouseDoubleClick( event ); +} + +MyViewer::~MyViewer() {} diff --git a/examples/CurveEditor/Gui/MyViewer.hpp b/examples/CurveEditor/Gui/MyViewer.hpp new file mode 100644 index 00000000000..1591e34f3ac --- /dev/null +++ b/examples/CurveEditor/Gui/MyViewer.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +class MyViewer : public Ra::Gui::Viewer +{ + Q_OBJECT; + + public: + MyViewer(); + virtual ~MyViewer(); + + signals: + void onMouseDoubleClick( QMouseEvent* event ); + + public slots: + void mouseDoubleClickEvent( QMouseEvent* event ) override; + + private: + /* data */ +}; diff --git a/examples/CurveEditor/Painty/CubicBezier.hpp b/examples/CurveEditor/Painty/CubicBezier.hpp new file mode 100644 index 00000000000..452b75f1ae4 --- /dev/null +++ b/examples/CurveEditor/Painty/CubicBezier.hpp @@ -0,0 +1,279 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef Ra::Core::Vector2f ControlPoint; + +/** + * @brief Cubic Bézier segment + */ +template +class CubicBezier +{ + public: + using ControlPoint = T; + + /** + * @brief Cubic Bezier segment, control polygon has 4 control points + * @param control polygon (needs to be of size >= 4) + * @param index of the first control point in the polygon (optional) + */ + CubicBezier( const std::vector& cpoints, int _start = 0 ) { + m_cpoints.insert( + m_cpoints.begin(), cpoints.begin() + _start, cpoints.begin() + _start + 4 ); + } + + /** + * @brief Computes a sample point in the bezier curve + * @param t parameter of the sample, should be in [0,1] + * @param deriv derivative order of the sampling + * @return coordinates of the sample point + */ + ControlPoint pointAt( float t, int deriv = 0 ) const { + std::vector b = bernsteinCoefsAt( t, deriv ); + ControlPoint p; + p.fill( 0. ); + return std::inner_product( b.begin(), b.end(), m_cpoints.cbegin(), p ); + } + + const std::vector& getCtrlPoints() const { return m_cpoints; } + + void setCtrlPoints( const std::vector& cpoints, int _start = 0 ) { + m_cpoints.insert( + m_cpoints.begin(), cpoints.begin() + _start, cpoints.begin() + _start + 4 ); + } + + /** + * @brief Computes the cubic Bernstein coefficients for parameter t + * @param t parameter of the coefficients + * @param deriv derivative order + * @return a vector of 4 scalar coefficients + */ + static std::vector bernsteinCoefsAt( float t, int deriv = 0 ) { + if ( deriv == 2 ) + return { 6 * ( 1 - t ), 6 * ( -2 + 3 * t ), 6 * ( 1 - 3 * t ), 6 * t }; + else if ( deriv == 1 ) + return { -3 * powf( 1 - t, 2 ), + 3 * ( 1 - t ) * ( 1 - 3 * t ), + 3 * t * ( 2 - 3 * t ), + 3 * powf( t, 2 ) }; + else + return { powf( 1 - t, 3 ), + 3 * t * powf( 1 - t, 2 ), + 3 * powf( t, 2 ) * ( 1 - t ), + powf( t, 3 ) }; + } + + /** + * @brief get a list of curviline abscisses + * @param distance in cm accross the curve that separate two params value + * @param step sampling [0, 1] + * @return list of params [0, 1] + */ + std::vector getArcLengthParameterization( float resolution, float epsilon ) const { + std::vector params; + float start = 0.0f; + float end = 1.0f; + float curParam = start; + float curDist = 0.0f; + + params.push_back( curParam ); + + ControlPoint p0 = pointAt( curParam ); + curParam += epsilon; + + while ( curParam <= end ) { + ControlPoint p1 = pointAt( curParam ); + curDist += sqrt( pow( p0.x() - p1.x(), 2 ) + pow( p0.y() - p1.y(), 2 ) ); + if ( curDist >= resolution ) { + params.push_back( curParam ); + curDist = 0.0f; + } + p0 = p1; + curParam += epsilon; + } + + // push last sample point to the end to ensure bounds [0, 1] + params[params.size() - 1] = end; + + return params; + } + + protected: + std::vector m_cpoints; // Vector of control points of the Bezier segment +}; + +template +class CubicBezierSpline +{ + public: + using ControlPoint = T; + using CubicBz = CubicBezier; + + CubicBezierSpline() {} + + /** + * @brief Spline of cubic Bézier segments. Construction guarantees C0 continuity. + * ie extremities of successive segments share the same coordinates + * @param vector of control points, should be 3*n+1 points where n is the number of segments + */ + CubicBezierSpline( const std::vector& cpoints ) { setCtrlPoints( cpoints ); } + + CubicBezierSpline( const CubicBezierSpline& other ) { setCtrlPoints( other.getCtrlPoints() ); } + + int getNbBezier() const { return spline.size(); } + + const std::vector getSplines() const { return spline; } + + /** + * @brief Computes a sample point in the bezier spline + * @param u global parameter of the sample, should be in [0,nbz] + * integer part of u represents the id of the Bézier segment + * while decimal part of u represents the local Bézier parameter + * @param deriv derivative order of the sampling + * @return coordinates of the sample point + */ + ControlPoint pointAt( float u, int deriv = 0 ) const { + using namespace Ra::Core::Utils; + std::pair locpar { getLocalParameter( u ) }; + + if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { + LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; + ControlPoint p; + p.fill( 0 ); + return p; + } + + return spline[locpar.first].pointAt( locpar.second, deriv ); + } + + /** + * @brief Computes a list of samples points in the bezier spline + * @param list of u global parameter of the sample, should be in [0,nbz] + * integer part of u represents the id of the Bézier segment + * while decimal part of u represents the local Bézier parameter + * @param deriv derivative order of the sampling + * @return coordinates of the sample point + */ + std::vector pointsAt( std::vector params, int deriv = 0 ) const { + std::vector controlPoints; + + for ( int i = 0; i < (int)params.size(); ++i ) { + controlPoints.push_back( pointAt( params[i], deriv ) ); + } + + return controlPoints; + } + + std::vector getCtrlPoints() const { + std::vector cp; + cp.reserve( 3 * getNbBezier() + 1 ); + for ( const CubicBz& bz : spline ) { + cp.insert( cp.end(), bz.getCtrlPoints().begin(), bz.getCtrlPoints().end() - 1 ); + } + if ( !spline.empty() ) { cp.emplace_back( spline.back().getCtrlPoints().back() ); } + + return cp; + } + + void setCtrlPoints( const std::vector& cpoints ) { + int nbz { (int)( ( cpoints.size() - 1 ) / 3 ) }; + spline.clear(); + spline.reserve( nbz ); + for ( int b = 0; b < nbz; ++b ) { + spline.emplace_back( CubicBz( cpoints, 3 * b ) ); + } + } + + /** + * @brief Decomposes a spline global parameter into the local Bézier parameters (static) + * @param global parameter + * @param number of segments in the spline + * @return a pair (b,t) where b is the index of the bezier segment, and t the local parameter in + * the segment + */ + static std::pair getLocalParameter( float u, int nbz ) { + int b { (int)( std::floor( u ) ) }; + float t { u - b }; + + if ( ( b == nbz ) && ( t == 0 ) ) { + b = nbz - 1; + t = 1; + } + return { b, t }; + } + + /** + * @brief Map a normalized parameter for the spline to a global parameter + * @param normalized parameter [0, 1] + * @param number of segments in the spline + * @return a global parameter t [0, nbz] + */ + static float getGlobalParameter( float u, int nbz ) { return u * nbz; } + + /** + * @brief equivalent to linspace function + * @param number of param + * @return a list of parameters t [0, nbz] + */ + std::vector getUniformParameterization( int nbSamples ) const { + std::vector params; + params.resize( nbSamples ); + + float delta = { 1.0f / (float)( nbSamples - 1 ) }; + float acc = 0.0f; + int nbz = getNbBezier(); + + params[0] = getGlobalParameter( 0.0f, nbz ); + for ( int i = 1; i < nbSamples; ++i ) { + acc += delta; + params[i] = getGlobalParameter( acc, nbz ); + } + + params[params.size() - 1] = getGlobalParameter( 1.0f, nbz ); + + return params; + } + + /** + * @brief get a list of curviline abscisses + * @param distance in cm accross the curve that separate two params value + * @param step sampling [0, 1] + * @return list of params [0, nbz] + */ + std::vector getArcLengthParameterization( float resolution, float epsilon ) const { + + std::vector params; + int nbz = getNbBezier(); + + if ( nbz <= 0 ) return params; + + params = spline[0].getArcLengthParameterization( resolution, epsilon ); + + for ( int i = 1; i < nbz; ++i ) { + std::vector tmpParams = + spline[i].getArcLengthParameterization( resolution, epsilon ); + std::transform( tmpParams.begin(), + tmpParams.end(), + tmpParams.begin(), + [&]( auto const& elem ) { return elem + i; } ); + params.insert( params.end(), tmpParams.begin() + 1, tmpParams.end() ); + } + + return params; + } + + private: + std::vector spline; // Vector of Bézier segments in the spline + + std::pair getLocalParameter( float u ) const { + return getLocalParameter( u, getNbBezier() ); + } +}; + +using CubicBezierSpline2f = CubicBezierSpline; +using CubicBezierSpline3f = CubicBezierSpline; diff --git a/examples/CurveEditor/Painty/CubicBezierApproximation.hpp b/examples/CurveEditor/Painty/CubicBezierApproximation.hpp new file mode 100644 index 00000000000..60f134f38c6 --- /dev/null +++ b/examples/CurveEditor/Painty/CubicBezierApproximation.hpp @@ -0,0 +1,358 @@ +#pragma once + +#include "CubicBezier.hpp" +#include "LeastSquareSystem.hpp" +#include +#include + +#include + +template +class CubicBezierApproximation +{ + public: + using ControlPoint = T; + using CubicBz = CubicBezier; + using CubicBzSpline = CubicBezierSpline; + + CubicBezierApproximation() {} + + void init( const std::vector& data, + int nb_min_bz = 1, + float epsilon = 0.1, + std::ofstream* logfile = nullptr ) { + if ( logfile != nullptr ) { + ( *logfile ) << "# Initialising optimization with epsilon " << epsilon << std::endl; + ( *logfile ) << "eps= " << epsilon << ";" << std::endl; + } + + m_data = data; + m_distThreshold = epsilon; + m_logfile = logfile; + + recomputeJunctions( nb_min_bz + 1 ); + if ( !data.empty() ) { m_dim = data[0].rows(); } + + updateParameters(); + + m_step = 0; + + m_isInitialized = true; + m_hasComputed = false; + } + + bool compute() { + using namespace Ra::Core::Utils; + + if ( !m_isInitialized ) { + LOG( logERROR ) << "CubicBezierApproximation is not initialized"; + return false; + } + + if ( m_hasComputed ) { + LOG( logERROR ) << "CubicBezierApproximation has already a solution"; + return false; + } + + if ( m_data.size() < 4 ) { + auto okflag = computeDegeneratedSolution(); + if ( !okflag ) { return false; } + m_hasComputed = true; + return true; + } + + ++m_step; + printPolygonMatlab( m_data, "P_" + std::to_string( m_step ) ); + + auto okflag = computeLeastSquareSolution(); + if ( !okflag ) { return false; } + + printPolygonMatlab( m_curSol.getCtrlPoints(), "B_" + std::to_string( m_step ) ); + + float err = evaluateSolution(); + + if ( err > m_distThreshold ) { + bool stopFlag { recomputeJunctions( m_bzjunctions.size() + 1 ) }; + + if ( !stopFlag ) { return false; } + + return compute(); + } + + m_hasComputed = true; + + return true; + } + + bool compute( int nbz ) { + using namespace Ra::Core::Utils; + + if ( !m_isInitialized ) { + LOG( logERROR ) << "CubicBezierApproximation is not initialized"; + return false; + } + + if ( m_hasComputed ) { + LOG( logERROR ) << "CubicBezierApproximation has already a solution"; + return false; + } + + if ( m_data.size() < 4 ) { + auto okflag = computeDegeneratedSolution(); + if ( !okflag ) { return false; } + m_hasComputed = true; + return true; + } + + ++m_step; + printPolygonMatlab( m_data, "P_" + std::to_string( m_step ) ); + + auto okflag = computeLeastSquareSolution(); + if ( !okflag ) { return false; } + + printPolygonMatlab( m_curSol.getCtrlPoints(), "B_" + std::to_string( m_step ) ); + + if ( m_curSol.getNbBezier() < nbz ) { + bool stopFlag { recomputeJunctions( m_bzjunctions.size() + 1 ) }; + + if ( !stopFlag ) { return false; } + + return compute( nbz ); + } + + m_hasComputed = true; + + return true; + } + + CubicBzSpline getSolution() const { return m_curSol; } + + int getNstep() const { return m_step; } + + private: + bool m_isInitialized { false }; + bool m_hasComputed { false }; + int m_dim { 0 }; + int m_step { 0 }; + float m_distThreshold; + std::vector m_data; + std::vector m_params; + std::set m_bzjunctions; + CubicBzSpline m_curSol; + + std::ofstream* m_logfile { nullptr }; + + void updateParameters() { + if ( m_bzjunctions.size() == 0 ) return; + + m_params.resize( m_data.size() ); + + auto b { m_bzjunctions.cbegin() }; + int d1 { *b }; + int numseg { 0 }; + while ( ( ++b ) != m_bzjunctions.cend() ) { + int d0 { d1 }; + d1 = *b; + + float totalDist { 0. }; + m_params[d0] = 0; + for ( int i = d0 + 1; i <= d1; ++i ) { + float dist { ( m_data[i] - m_data[i - 1] ).norm() }; + m_params[i] = m_params[i - 1] + dist; + totalDist += dist; + } + + for ( int i = d0; i <= d1; ++i ) { + m_params[i] = ( m_params[i] / totalDist ) + numseg; + } + ++numseg; + } + } + + float errorAt( int i ) { return ( m_data[i] - m_curSol.pointAt( m_params[i] ) ).squaredNorm(); } + + bool recomputeJunctions( int nb_junctions ) { + m_bzjunctions.clear(); + + int a { 0 }; + int b { (int)( m_data.size() - 1 ) }; + float h = ( b - a ) / (float)( nb_junctions - 1 ); + std::vector xs( nb_junctions ); + typename std::vector::iterator x; + float val; + for ( x = xs.begin(), val = a; x != xs.end(); ++x, val += h ) + *x = (int)val; + + for ( int x : xs ) { + m_bzjunctions.insert( x ); + } + + updateParameters(); + + return true; + } + + float evaluateSolution() { + float errMax { -1 }; + using namespace Ra::Core::Utils; + // LOG(logDEBUG) << "Evaluation"; + for ( int i = 0; i < (int)m_data.size(); ++i ) { + // LOG(logDEBUG) << i << " : " << errorAt(i); + errMax = std::max( errMax, errorAt( i ) ); + } + return errMax; + } + + void computeConstraints( LeastSquareSystem& lss ) { + int nbz { (int)( m_bzjunctions.size() ) - 1 }; + + auto computePointDistanceConstraint = [nbz]( float u ) { + std::map A_cstr; + auto locpar = CubicBzSpline::getLocalParameter( u, nbz ); + auto bcoefs = CubicBz::bernsteinCoefsAt( locpar.second ); + int bi = locpar.first; + + for ( int i = 0; i < 4; ++i ) { + A_cstr[3 * bi + i] = bcoefs[i]; + } + + return A_cstr; + }; + + std::set::const_iterator bzit = m_bzjunctions.cbegin(); + for ( int b = 0; b < nbz; ++b ) { + int s0 { *bzit }; + int s1 { *( ++bzit ) }; + int nb_pts_in_bz { s1 - s0 + 1 }; + + if ( nb_pts_in_bz >= 4 ) { + // Bezier segment is constrained by at least 4 points => well-posed system + for ( int s = s0; s <= s1; ++s ) { + if ( ( s == s0 ) && ( s0 != 0 ) ) { continue; } + // The problem is over (or perfectly) constrained : for each sample in between + // the Bezier junction we minimize the distance with the optimized curve at the + // associated parameter + lss.addConstraint( computePointDistanceConstraint( m_params[s] ), m_data[s] ); + } + } + else { + // Bezier segment is constrained by less than 4 points => degenerated case + std::vector data( m_data.cbegin() + s0, m_data.cbegin() + s1 + 1 ); + std::vector cpts; + + // The problem is underconstrained : we compute one solution that perfectly fits the + // data And we constrain the control points of the solution to match this solution + computeDegeneratedSolution( data, cpts ); + + for ( int k = 0; k < 4; ++k ) { + if ( ( k == 0 ) && ( s0 != 0 ) ) { continue; } + lss.addConstraint( { { 3 * b + k, 1 } }, cpts[k] ); + } + } + } + } + + bool computeLeastSquareSolution() { + int nbz { (int)( m_bzjunctions.size() ) - 1 }; + int nvar { 3 * nbz + 1 }; + + if ( nbz < 1 ) { + using namespace Ra::Core::Utils; + LOG( logERROR ) << "Error while approximating stroke : not enough points"; + return false; + } + + LeastSquareSystem lss( nvar, m_dim ); + computeConstraints( lss ); + + Eigen::MatrixXf sol; + if ( !lss.solve( sol ) ) { return false; } + + std::vector cpts( nvar ); + for ( int i = 0; i < nvar; ++i ) { + cpts[i] = ControlPoint( sol.row( i ) ); + } + + m_curSol.setCtrlPoints( cpts ); + + return true; + } + + bool computeDegeneratedSolution( const std::vector& data, + std::vector& cpts ) { + + int npts { (int)data.size() }; + if ( ( npts == 0 ) || ( npts >= 4 ) ) { return false; } + + cpts.resize( 4 ); + if ( npts == 1 ) { + // One point in the stroke : the Bezier curve is degenerated + cpts[0] = data[0]; + cpts[1] = data[0]; + cpts[2] = data[0]; + cpts[3] = data[0]; + } + if ( npts == 2 ) { + // Two points in the stroke : straight line segment + cpts[0] = data[0]; + cpts[1] = ( 2. / 3. ) * data[0] + ( 1. / 3. ) * data[1]; + cpts[2] = ( 1. / 3. ) * data[0] + ( 2. / 3. ) * data[1]; + cpts[3] = data[1]; + } + if ( npts == 3 ) { + // Three points in the stroke : we find the quadratic bezier best fitting solution + // and raise the degree to 3 + float l0 { ( data[1] - data[0] ).norm() }; + float l1 { ( data[2] - data[1] ).norm() }; + float tau { l0 / ( l0 + l1 ) }; // parameter of the middle point in the curve + + /** === Details of the computation + * p0,p1,p2 the data to approximate + * t : parameter for p1 in the fitted bezier + * + * best fitting quadratic bezier has control points : + * { p0, (1/(2t(1-t))*(p1 - (t^2)p2 - ((1-t)^2)p0), p2 } + * + * we can raise degree of the bezier by applying : + * { b0, b0 + (2/3)*(b1-b0), b2 + (2/3)*(b1-b2), b2 } + * where {b0, b1, b2} are the control points of the quadratic + * + * hence the control points of the best fitting cubic bezier + * { p0, p0 + (2/3)*( (1/(2t(1-t))*(p1 - (t^2)p2 - ((1-t)^2)p0) - p0) + * , p2 + (2/3)*( (1/(2t(1-t))*(p1 - (t^2)p2 - ((1-t)^2)p0) - p0), p2} + * with some simplifications, we get + * { p0, (1/(3t(1-t))*(p1 - (t^2)p2 + (-1+2t)*(1-t)p0) + * , (1/(3t(1-t))*(p1 + t(1-2t)p2 - (1-t)^2)p0), p2} + * + **/ + + float omt { 1 - tau }; + float fact { 1.f / ( 3 * tau * omt ) }; + cpts[0] = data[0]; + cpts[1] = fact * ( ( -1 + 2 * tau ) * omt * data[0] + data[1] - tau * tau * data[2] ); + cpts[2] = fact * ( -omt * omt * data[0] + data[1] + ( 1 - 2 * tau ) * tau * data[2] ); + cpts[3] = data[2]; + } + + return true; + } + + bool computeDegeneratedSolution() { + std::vector cpts; + if ( !computeDegeneratedSolution( m_data, cpts ) ) { return false; } + m_curSol.setCtrlPoints( cpts ); + return true; + } + + void printPolygonMatlab( const std::vector& poly, const std::string& varname ) { + if ( m_logfile == nullptr ) { return; } + ( *m_logfile ) << varname << "= ["; + for ( const auto& p : poly ) { + ( *m_logfile ) << "[" << p[0] << ";" << p[1] << "] "; + } + ( *m_logfile ) << "];" << std::endl; + } +}; + +using CubicBezierApproximation2f = CubicBezierApproximation; +using CubicBezierApproximation3f = CubicBezierApproximation; diff --git a/examples/CurveEditor/Painty/LeastSquareSystem.hpp b/examples/CurveEditor/Painty/LeastSquareSystem.hpp new file mode 100644 index 00000000000..8bef5ea4a98 --- /dev/null +++ b/examples/CurveEditor/Painty/LeastSquareSystem.hpp @@ -0,0 +1,204 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +using Eigen::MatrixXf; +using Eigen::SparseMatrix; +using Eigen::Triplet; +using Eigen::VectorXf; + +/** + * @brief The LeastSquareSystem class allows + * setting up and solving an n-dimensional least square system + * of the form Ax=b. + */ +class LeastSquareSystem +{ + public: + using CoefTriplet = Triplet; + using SparseMat = SparseMatrix; + + /** + * @brief LeastSquareSystem of the dimension given by the parameters + * @param number of variables of the system + * @param dimension of the variables (optional) + * @param expected number of constraints (optional) + */ + LeastSquareSystem( int nvar, int dim = 1, int exp_ncstr = 0 ) : m_nvar( nvar ), m_dim( dim ) { + m_constrainedVar.resize( m_nvar, false ); + if ( exp_ncstr > 0 ) { m_bval.reserve( exp_ncstr ); } + } + + /** + * @brief Adding a constraint to the system + * @param coefs of the corresponding row in the matrix A, + * only non zero coefs will be stored in the sparse matrix + * coefs are 1D : same for all dimension of the variable + * @param value of the corresponding row in b (n-dimensional) + * @param weight of the constraint (optional) + * @return 0 if everything went ok, -1 otherwise (the constraint is not added in this case) + */ + int addConstraint( std::map coefs, VectorXf res, float w = 1 ) { + using namespace Ra::Core::Utils; + if ( coefs.empty() || ( res.rows() != m_dim ) ) { + LOG( logERROR ) << "Constraint has wrong dimension"; + return -1; + } + + bool anyNonZeroCoef { false }; + + for ( const auto& [id_var, coef] : coefs ) { + if ( w * fabs( coef ) < 1e-8 ) { continue; } + anyNonZeroCoef = true; + for ( int d = 0; d < m_dim; ++d ) { + m_tripletList.emplace_back( + CoefTriplet( m_dim * m_ncstr + d, m_dim * id_var + d, w * coef ) ); + } + m_constrainedVar[id_var] = true; + } + + if ( anyNonZeroCoef ) { + m_bval.push_back( w * res ); + ++m_ncstr; + return 0; + } + + return -1; + } + + /** + * @brief Call this once all constraints have been added to the system. + * Sets up and solves the LSS if it is solvable. + * @param writes the solution in a (nvar x ndim) matrix (a row = a variable) + * @return true if everything went ok, false if the system is unsolvable or badly built + */ + bool solve( MatrixXf& solution ) const { + using namespace Ra::Core::Utils; + + if ( !isSolvable() ) { + LOG( logERROR ) << "Least square system unsolvable"; + return false; + } + + SparseMat A; + VectorXf b; + bool setupFlag { this->setUpSystem( A, b ) }; + if ( !setupFlag ) { + LOG( logERROR ) << "Error while setting up least square system"; + return false; + } + + VectorXf flat_solution; + bool solveFlag { jacobiSVD( A, b, flat_solution ) }; + if ( !solveFlag ) { + LOG( logERROR ) << "Error while solving the least square system"; + return false; + } + + solution = flat_solution.reshaped( m_nvar, m_dim ); + + return true; + } + + /** + * @brief Prints details of the current state of the system + * @param output log level (optional) + */ + void log( Ra::Core::Utils::TLogLevel logL = Ra::Core::Utils::logINFO ) const { + using namespace Ra::Core::Utils; + + LOG( logL ) << "LSS with " << m_nvar << " variables of dimension " << m_dim << ", and " + << m_ncstr << " constraints"; + + LOG( logL ) << "Triplets (" << m_tripletList.size() << ") : "; + for ( const auto& t : m_tripletList ) { + LOG( logL ) << "(" << t.row() << "," << t.col() << ") = " << t.value(); + } + + LOG( logL ) << "Values (" << m_bval.size() << ")"; + for ( const auto& b : m_bval ) { + LOG( logL ) << b.transpose(); + } + + std::stringstream logss; + logss << "Constrained variables :"; + int v = -1; + for ( const auto& c : m_constrainedVar ) { + logss << "(" << ++v << ": " << c << ") "; + } + LOG( logL ) << logss.str(); + logss.str( "" ); + } + + private: + int m_nvar { 0 }; // number of variables in the system + int m_dim { 1 }; // dimension of the variables (n) + int m_ncstr { 0 }; // number of constraints in the system + + std::vector m_constrainedVar; // constrained variables + std::list m_tripletList; // coefs of the sparse A matrix + std::vector m_bval; // values of the b vector (each coef is n-dimensional) + + /** + * @brief Setting up the matrices of the system + * @param output A sparse (nconstr*ndim) x (nvar*ndim) matrix + * @param output b (nconstr*ndim) vector + * @return true if the system was built properly, false otherwise + */ + bool setUpSystem( SparseMat& A, VectorXf& b ) const { + if ( ( m_ncstr != (int)m_bval.size() ) ) { return false; } + + A.resize( m_ncstr * m_dim, m_nvar * m_dim ); + A.setFromTriplets( m_tripletList.begin(), m_tripletList.end() ); + A.makeCompressed(); + + b.resize( m_ncstr * m_dim ); + for ( int c = 0; c < m_ncstr; ++c ) { + for ( int d = 0; d < m_dim; ++d ) { + b( m_dim * c + d ) = m_bval[c]( d ); + } + } + return true; + } + + /** + * @brief Check if system is solvable. + * Only check if all variable are constrained and if more constraints than variables + * Does not guarantee the rank of the A matrix will be sufficient + * @return true if solvable, false otherwise + */ + bool isSolvable() const { + using namespace Ra::Core::Utils; + if ( m_ncstr < m_nvar ) { + LOG( logERROR ) << "LSS - Underconstrained problem"; + return false; + } + + auto unconstr = std::find( m_constrainedVar.cbegin(), m_constrainedVar.cend(), false ); + if ( unconstr != m_constrainedVar.cend() ) { + LOG( logERROR ) << "LSS - Unconstrained variable(s)"; + return false; + } + + return true; + } + + bool simpleCholesky( const SparseMat& A, const VectorXf& b, VectorXf& x ) const { + Eigen::SimplicialCholesky chol( A.transpose() * A ); + if ( chol.info() != Eigen::Success ) { return false; } + x = chol.solve( A.transpose() * b ); + return true; + } + + bool jacobiSVD( const SparseMat& A, const VectorXf& b, VectorXf& x ) const { + MatrixXf denseA( A ); + Eigen::JacobiSVD svd( denseA, Eigen::ComputeThinU | Eigen::ComputeThinV ); + x = svd.solve( b ); + return true; + } +}; diff --git a/examples/CurveEditor/PointComponent.cpp b/examples/CurveEditor/PointComponent.cpp new file mode 100644 index 00000000000..fc1f6361282 --- /dev/null +++ b/examples/CurveEditor/PointComponent.cpp @@ -0,0 +1,66 @@ +#include "PointComponent.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef IO_USE_ASSIMP +# include +#endif + +#include + +using namespace Ra; +using namespace Ra::Core; +using namespace Ra::Core::Utils; +using namespace Ra::Core::Geometry; +using namespace Ra::Engine; +using namespace Ra::Engine::Rendering; +using namespace Ra::Engine::Data; +using namespace Ra::Engine::Scene; + +/** + * This file contains a minimal radium/qt application which shows the geometrical primitives + * supported by Radium + */ + +PointComponent::PointComponent( Ra::Engine::Scene::Entity* entity, + Ra::Core::Vector3 point, + const std::vector& curveId, + const std::string& name, + Color color ) : + Ra::Engine::Scene::Component( name, entity ), + m_point( point ), + m_defaultPoint( point ), + m_curveId( curveId ), + m_color( color ) {} + +/// This function is called when the component is properly +/// setup, i.e. it has an entity. +void PointComponent::initialize() { + auto plainMaterial = make_shared( "Plain Material" ); + plainMaterial->m_color = m_color; + plainMaterial->m_perVertexColor = false; + + auto circle = RenderObject::createRenderObject( + "contourPt_circle", + this, + RenderObjectType::Geometry, + DrawPrimitives::Disk( m_point, { 0_ra, 1_ra, 0_ra }, 0.1, 64, Color::White() ), + {} ); + circle->setMaterial( plainMaterial ); + addRenderObject( circle ); +} diff --git a/examples/CurveEditor/PointComponent.hpp b/examples/CurveEditor/PointComponent.hpp new file mode 100644 index 00000000000..31fe302b5d0 --- /dev/null +++ b/examples/CurveEditor/PointComponent.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include + +class PointComponent : public Ra::Engine::Scene::Component +{ + + public: + PointComponent( Ra::Engine::Scene::Entity* entity, + Ra::Core::Vector3 point, + const std::vector& curveId, + const std::string& name, + Ra::Core::Utils::Color color ); + + /// This function is called when the component is properly + /// setup, i.e. it has an entity. + void initialize() override; + + Ra::Core::Vector3 m_point; + Ra::Core::Vector3 m_defaultPoint; + Ra::Core::Utils::Color m_color; + std::vector m_curveId; +}; diff --git a/examples/CurveEditor/PointFactory.hpp b/examples/CurveEditor/PointFactory.hpp new file mode 100644 index 00000000000..27c3d436f12 --- /dev/null +++ b/examples/CurveEditor/PointFactory.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "PointComponent.hpp" +#include +#include +#include + +class PointFactory +{ + public: + static PointComponent* + createPointComponent( Ra::Engine::Scene::Entity* e, + Ra::Core::Vector3 point, + const std::vector& curveId, + const std::string& name, + Ra::Core::Utils::Color color = Ra::Core::Utils::Color::Black() ) { + auto c = new PointComponent( e, point, curveId, name, color ); + c->initialize(); + return c; + } + static Ra::Engine::Scene::Entity* + createPointEntity( Ra::Core::Vector3 point, + const std::string& name, + const std::vector& curveId, + Ra::Core::Utils::Color color = Ra::Core::Utils::Color::Grey() ) { + auto engine = Ra::Engine::RadiumEngine::getInstance(); + Ra::Engine::Scene::Entity* e = engine->getEntityManager()->createEntity( name ); + createPointComponent( e, point, curveId, name, color ); + return e; + } + + private: +}; diff --git a/examples/CurveEditor/README.md b/examples/CurveEditor/README.md new file mode 100644 index 00000000000..374fcef23aa --- /dev/null +++ b/examples/CurveEditor/README.md @@ -0,0 +1,12 @@ +# PaintyStrokes + +```{.bash} +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DRadium_DIR={RADIUM_DIR}/Bundle-GNU/lib/cmake/Radium/ ../ +./src/DrawStrokes +``` + +Disclaimer: +You have to run DrawStrokes from the ./build directory since the Assets directory +to read/load XML files is relative to the execution directory diff --git a/examples/CurveEditor/StrokeComponent.cpp b/examples/CurveEditor/StrokeComponent.cpp new file mode 100644 index 00000000000..b1a00ee85a9 --- /dev/null +++ b/examples/CurveEditor/StrokeComponent.cpp @@ -0,0 +1,58 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef IO_USE_ASSIMP +# include +#endif + +#include + +using namespace Ra; +using namespace Ra::Core; +using namespace Ra::Core::Utils; +using namespace Ra::Core::Geometry; +using namespace Ra::Engine; +using namespace Ra::Engine::Rendering; +using namespace Ra::Engine::Data; +using namespace Ra::Engine::Scene; + +/** + * This file contains a minimal radium/qt application which shows the geometrical primitives + * supported by Radium + */ + +StrokeComponent::StrokeComponent( Ra::Engine::Scene::Entity* entity, Vector3Array strokePoints ) : + Ra::Engine::Scene::Component( "Stroke Component", entity ), m_strokePts( strokePoints ) {} + +/// This function is called when the component is properly +/// setup, i.e. it has an entity. +void StrokeComponent::initialize() { + auto plainMaterial = make_shared( "Plain Material" ); + plainMaterial->m_perVertexColor = true; + + // Render mesh + auto renderObject1 = RenderObject::createRenderObject( + "PolyMesh", + this, + RenderObjectType::Geometry, + DrawPrimitives::LineStrip( m_strokePts, + Vector4Array { m_strokePts.size(), { 0, 0, 0.7, 1 } } ), + Ra::Engine::Rendering::RenderTechnique {} ); + renderObject1->setMaterial( plainMaterial ); + addRenderObject( renderObject1 ); +} diff --git a/examples/CurveEditor/StrokeComponent.hpp b/examples/CurveEditor/StrokeComponent.hpp new file mode 100644 index 00000000000..83a7726add4 --- /dev/null +++ b/examples/CurveEditor/StrokeComponent.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include +#include + +class StrokeComponent : public Ra::Engine::Scene::Component +{ + + public: + StrokeComponent( Ra::Engine::Scene::Entity* entity, Ra::Core::Vector3Array strokePoints ); + + /// This function is called when the component is properly + /// setup, i.e. it has an entity. + void initialize() override; + + Ra::Core::Vector3Array m_strokePts; +}; diff --git a/examples/CurveEditor/StrokeFactory.hpp b/examples/CurveEditor/StrokeFactory.hpp new file mode 100644 index 00000000000..62e9741fc5f --- /dev/null +++ b/examples/CurveEditor/StrokeFactory.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "StrokeComponent.hpp" +#include +#include +#include + +class StrokeFactory +{ + public: + static StrokeComponent* createStrokeComponent( Ra::Engine::Scene::Entity* e, + Ra::Core::Vector3Array strokePts ) { + auto c = new StrokeComponent( e, strokePts ); + c->initialize(); + return c; + } + static Ra::Engine::Scene::Entity* createCurveEntity( std::vector polyline, + const std::string& name ) { + auto engine = Ra::Engine::RadiumEngine::getInstance(); + Ra::Engine::Scene::Entity* e = engine->getEntityManager()->createEntity( name ); + Ra::Core::Vector3Array strokePts; + for ( auto& pt : polyline ) { + strokePts.push_back( Ra::Core::Vector3( pt.x(), 0, pt.y() ) ); + } + createStrokeComponent( e, strokePts ); + return e; + } + + private: +}; diff --git a/examples/CurveEditor/main.cpp b/examples/CurveEditor/main.cpp new file mode 100644 index 00000000000..9a8541fb784 --- /dev/null +++ b/examples/CurveEditor/main.cpp @@ -0,0 +1,60 @@ +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include "Gui/MainWindow.hpp" +#include "StrokeFactory.hpp" + +class MainWindowFactory : public Ra::Gui::BaseApplication::WindowFactory +{ + public: + using Ra::Gui::BaseApplication::WindowFactory::WindowFactory; + Ra::Gui::MainWindowInterface* createMainWindow() const { return new MainWindow(); } +}; + +int main( int argc, char* argv[] ) { + + // Create app and show viewer window + Ra::Gui::BaseApplication app( argc, argv ); + app.initialize( MainWindowFactory() ); + app.setContinuousUpdate( false ); + + // Create polyline from stroke points + std::vector polyline = { + { -7.07583, -7.77924 }, { -6.4575, -7.69091 }, { -5.35333, -7.33757 }, + { -3.27749, -6.54257 }, { -1.68749, -5.39423 }, { -0.671654, -4.20173 }, + { 0.123348, -3.18589 }, { 0.697517, -1.94923 }, { 1.44835, -0.270889 }, + { 1.84585, 1.14245 }, { 1.93419, 3.04162 }, { 1.84585, 4.36662 }, + { 1.36002, 5.82412 }, { 0.874184, 7.10496 }, { 0.167517, 8.2533 }, + { -0.759985, 9.3133 }, { -1.37832, 9.93163 }, { -1.68749, 10.0641 }, + { -2.21749, 10.1083 }, { -2.70333, 9.75497 }, { -3.23333, 9.00413 }, + { -3.49832, 8.16496 }, { -3.71916, 6.70746 }, { -3.71916, 5.38246 }, + { -3.54249, 3.96912 }, { -3.32166, 2.64412 }, { -2.74749, 0.612446 }, + { -1.73166, -1.81673 }, { -0.804153, -3.93673 }, { 0.0350161, -5.7034 }, + { 0.830018, -6.94007 }, { 1.36002, -7.47007 }, { 2.02252, -7.86757 }, + { 3.03835, -8.26508 }, { 3.70086, -8.39758 }, { 4.14253, -8.30924 }, + { 4.80503, -8.00007 } }; + auto window = static_cast( app.m_mainWindow.get() ); + window->setPolyline( polyline ); + + auto initialStroke = StrokeFactory::createCurveEntity( polyline, "Initial stroke" ); + window->setInitialStroke( initialStroke ); + + app.m_mainWindow->prepareDisplay(); + + app.m_mainWindow->getViewer()->setCameraManipulator( new Ra::Gui::RotateAroundCameraManipulator( + *( app.m_mainWindow->getViewer()->getCameraManipulator() ), + app.m_mainWindow->getViewer() ) ); + + return app.exec(); +} From 6b5b7d37a10811c26784ff652c93052101fb1e09 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Tue, 5 Jul 2022 17:50:58 +0200 Subject: [PATCH 04/19] [tests] Add CubicBezierSpline to Curves and adapt approximation accordingly --- examples/CurveEditor/CMakeLists.txt | 1 - examples/CurveEditor/CurveEditor.cpp | 9 +- examples/CurveEditor/CurveEditor.hpp | 66 ++++- examples/CurveEditor/Gui/MainWindow.hpp | 24 +- examples/CurveEditor/Painty/CubicBezier.hpp | 279 ------------------ .../Painty/CubicBezierApproximation.hpp | 70 +++-- examples/CurveEditor/StrokeFactory.hpp | 7 +- examples/CurveEditor/main.cpp | 4 +- src/Core/Geometry/Curve2D.hpp | 271 +++++++++++++++++ 9 files changed, 413 insertions(+), 318 deletions(-) delete mode 100644 examples/CurveEditor/Painty/CubicBezier.hpp diff --git a/examples/CurveEditor/CMakeLists.txt b/examples/CurveEditor/CMakeLists.txt index 714d1166e4a..6188dd357cc 100644 --- a/examples/CurveEditor/CMakeLists.txt +++ b/examples/CurveEditor/CMakeLists.txt @@ -44,7 +44,6 @@ set(app_headers CurveFactory.hpp PointComponent.hpp PointFactory.hpp - Painty/CubicBezier.hpp Painty/CubicBezierApproximation.hpp Painty/LeastSquareSystem.hpp CurveEditor.hpp diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index ef6f9c0880e..a989f6dbadf 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -6,18 +6,21 @@ #include #include "CurveEditor.hpp" -#include "Painty/CubicBezier.hpp" #include "Painty/CubicBezierApproximation.hpp" -CurveEditor::CurveEditor( std::vector polyline, MyViewer* viewer ) : +CurveEditor::CurveEditor( + const Ra::Core::VectorArray& polyline, + MyViewer* viewer ) : Ra::Engine::Scene::Entity( "Curve Editor" ), m_viewer( viewer ) { m_entityMgr = Ra::Engine::RadiumEngine::getInstance()->getEntityManager(); m_roMgr = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager(); // Approximate polyline with bezier - CubicBezierApproximation2f approximator; + CubicBezierApproximation approximator; approximator.init( polyline ); + std::cout << "INITED" << std::endl; approximator.compute(); + std::cout << "COMPUTED" << std::endl; auto solution = approximator.getSolution(); auto solutionPts = solution.getCtrlPoints(); diff --git a/examples/CurveEditor/CurveEditor.hpp b/examples/CurveEditor/CurveEditor.hpp index 35f2fd7f4ce..38ccf25d25b 100644 --- a/examples/CurveEditor/CurveEditor.hpp +++ b/examples/CurveEditor/CurveEditor.hpp @@ -1,6 +1,6 @@ #include "CurveFactory.hpp" #include "Gui/MyViewer.hpp" -#include "PointFactory.hpp" +#include #include #include #include @@ -64,5 +64,65 @@ class CurveEditor : public Ra::Engine::Scene::Entity bool m_smooth { false }; bool m_symetry { false }; - MyViewer* m_viewer { nullptr }; -}; + class CurveEditor : public Ra::Engine::Scene::Entity + { + + public: + CurveEditor( const Ra::Core::VectorArray& polyline, + MyViewer* viewer ); + ~CurveEditor(); + + void addPointAtEnd( const Ra::Core::Vector3& worldPos ); + void addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX, int mouseY ); + + void updateCurves( bool onRelease = false ); + + bool processHover( std::shared_ptr ro ); + void processUnhovering(); + void processPicking( const Ra::Core::Vector3& worldPos ); + + std::shared_ptr getSelected() { return m_selectedRo; } + + void resetSelected() { + m_selectedRo = nullptr; + m_currentPoint = -1; + } + + void setSmooth( bool smooth ) { m_smooth = smooth; } + void setSymetry( bool symetry ) { m_symetry = symetry; } + + private: + inline float distanceSquared( const Ra::Core::Vector3f& pointA, + const Ra::Core::Vector3f& pointB ); + unsigned int getPointIndex( unsigned int curveIdSize, + unsigned int currentPtIndex, + const Ra::Core::Vector3& firstCtrlPt, + const Ra::Core::Vector3& currentPt ); + void updateCurve( unsigned int curveId, + unsigned int curveIdSize, + PointComponent* pointComponent, + const Ra::Core::Vector3& currentPt, + unsigned int currentPoint ); + Ra::Core::Transform computePointTransform( PointComponent* pointCmp, + const Ra::Core::Vector3& midPoint, + const Ra::Core::Vector3& worldPos ); + void subdivisionBezier( int vertexIndex, + unsigned int curveIndex, + const Ra::Core::Vector3Array& ctrlPts ); + void refreshPoint( unsigned int pointIndex, + const Ra::Core::Vector3& newPoint = Ra::Core::Vector3::Zero() ); + + private: + Ra::Engine::Scene::EntityManager* m_entityMgr; + int m_currentPoint { -1 }; + std::shared_ptr m_selectedRo { nullptr }; + std::vector m_pointEntities; + std::vector m_curveEntities; + std::vector m_tangentPoints; + Ra::Engine::Rendering::RenderObjectManager* m_roMgr; + + bool m_smooth { false }; + bool m_symetry { false }; + + MyViewer* m_viewer { nullptr }; + }; diff --git a/examples/CurveEditor/Gui/MainWindow.hpp b/examples/CurveEditor/Gui/MainWindow.hpp index 6a3eb5aac49..c2394743509 100644 --- a/examples/CurveEditor/Gui/MainWindow.hpp +++ b/examples/CurveEditor/Gui/MainWindow.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -36,7 +38,9 @@ class MainWindow : public Ra::Gui::MainWindowInterface Ra::Core::Vector3 getWorldPos( int x, int y ); void handleHover( int mouseX, int mouseY ); void handlePicking( int mouseX, int mouseY ); - void setPolyline( std::vector polyline ) { m_polyline = polyline; } + void setPolyline( Ra::Core::VectorArray polyline ) { + m_polyline = polyline; + } void setInitialStroke( Ra::Engine::Scene::Entity* e ) { m_initialStroke = e; } public slots: @@ -55,6 +59,24 @@ class MainWindow : public Ra::Gui::MainWindowInterface signals: void frameUpdate(); + private: + void createConnections(); + MyViewer* m_viewer; + Ra::Gui::SelectionManager* m_selectionManager; + Ra::Gui::ItemModel* m_sceneModel; + Ra::Engine::RadiumEngine* m_engine; + QDockWidget* m_dockWidget; + QPushButton* m_button; + QPushButton* m_editCurveButton; + QPushButton* m_hideStrokeButton; + QPushButton* m_symetryButton; + bool m_clicked = false; + bool m_isTracking { false }; + bool m_hovering { false }; + bool m_edited { false }; + Ra::Core::VectorArray m_polyline; + Ra::Engine::Scene::Entity* m_initialStroke; + private: void createConnections(); MyViewer* m_viewer; diff --git a/examples/CurveEditor/Painty/CubicBezier.hpp b/examples/CurveEditor/Painty/CubicBezier.hpp deleted file mode 100644 index 452b75f1ae4..00000000000 --- a/examples/CurveEditor/Painty/CubicBezier.hpp +++ /dev/null @@ -1,279 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -typedef Ra::Core::Vector2f ControlPoint; - -/** - * @brief Cubic Bézier segment - */ -template -class CubicBezier -{ - public: - using ControlPoint = T; - - /** - * @brief Cubic Bezier segment, control polygon has 4 control points - * @param control polygon (needs to be of size >= 4) - * @param index of the first control point in the polygon (optional) - */ - CubicBezier( const std::vector& cpoints, int _start = 0 ) { - m_cpoints.insert( - m_cpoints.begin(), cpoints.begin() + _start, cpoints.begin() + _start + 4 ); - } - - /** - * @brief Computes a sample point in the bezier curve - * @param t parameter of the sample, should be in [0,1] - * @param deriv derivative order of the sampling - * @return coordinates of the sample point - */ - ControlPoint pointAt( float t, int deriv = 0 ) const { - std::vector b = bernsteinCoefsAt( t, deriv ); - ControlPoint p; - p.fill( 0. ); - return std::inner_product( b.begin(), b.end(), m_cpoints.cbegin(), p ); - } - - const std::vector& getCtrlPoints() const { return m_cpoints; } - - void setCtrlPoints( const std::vector& cpoints, int _start = 0 ) { - m_cpoints.insert( - m_cpoints.begin(), cpoints.begin() + _start, cpoints.begin() + _start + 4 ); - } - - /** - * @brief Computes the cubic Bernstein coefficients for parameter t - * @param t parameter of the coefficients - * @param deriv derivative order - * @return a vector of 4 scalar coefficients - */ - static std::vector bernsteinCoefsAt( float t, int deriv = 0 ) { - if ( deriv == 2 ) - return { 6 * ( 1 - t ), 6 * ( -2 + 3 * t ), 6 * ( 1 - 3 * t ), 6 * t }; - else if ( deriv == 1 ) - return { -3 * powf( 1 - t, 2 ), - 3 * ( 1 - t ) * ( 1 - 3 * t ), - 3 * t * ( 2 - 3 * t ), - 3 * powf( t, 2 ) }; - else - return { powf( 1 - t, 3 ), - 3 * t * powf( 1 - t, 2 ), - 3 * powf( t, 2 ) * ( 1 - t ), - powf( t, 3 ) }; - } - - /** - * @brief get a list of curviline abscisses - * @param distance in cm accross the curve that separate two params value - * @param step sampling [0, 1] - * @return list of params [0, 1] - */ - std::vector getArcLengthParameterization( float resolution, float epsilon ) const { - std::vector params; - float start = 0.0f; - float end = 1.0f; - float curParam = start; - float curDist = 0.0f; - - params.push_back( curParam ); - - ControlPoint p0 = pointAt( curParam ); - curParam += epsilon; - - while ( curParam <= end ) { - ControlPoint p1 = pointAt( curParam ); - curDist += sqrt( pow( p0.x() - p1.x(), 2 ) + pow( p0.y() - p1.y(), 2 ) ); - if ( curDist >= resolution ) { - params.push_back( curParam ); - curDist = 0.0f; - } - p0 = p1; - curParam += epsilon; - } - - // push last sample point to the end to ensure bounds [0, 1] - params[params.size() - 1] = end; - - return params; - } - - protected: - std::vector m_cpoints; // Vector of control points of the Bezier segment -}; - -template -class CubicBezierSpline -{ - public: - using ControlPoint = T; - using CubicBz = CubicBezier; - - CubicBezierSpline() {} - - /** - * @brief Spline of cubic Bézier segments. Construction guarantees C0 continuity. - * ie extremities of successive segments share the same coordinates - * @param vector of control points, should be 3*n+1 points where n is the number of segments - */ - CubicBezierSpline( const std::vector& cpoints ) { setCtrlPoints( cpoints ); } - - CubicBezierSpline( const CubicBezierSpline& other ) { setCtrlPoints( other.getCtrlPoints() ); } - - int getNbBezier() const { return spline.size(); } - - const std::vector getSplines() const { return spline; } - - /** - * @brief Computes a sample point in the bezier spline - * @param u global parameter of the sample, should be in [0,nbz] - * integer part of u represents the id of the Bézier segment - * while decimal part of u represents the local Bézier parameter - * @param deriv derivative order of the sampling - * @return coordinates of the sample point - */ - ControlPoint pointAt( float u, int deriv = 0 ) const { - using namespace Ra::Core::Utils; - std::pair locpar { getLocalParameter( u ) }; - - if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { - LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; - ControlPoint p; - p.fill( 0 ); - return p; - } - - return spline[locpar.first].pointAt( locpar.second, deriv ); - } - - /** - * @brief Computes a list of samples points in the bezier spline - * @param list of u global parameter of the sample, should be in [0,nbz] - * integer part of u represents the id of the Bézier segment - * while decimal part of u represents the local Bézier parameter - * @param deriv derivative order of the sampling - * @return coordinates of the sample point - */ - std::vector pointsAt( std::vector params, int deriv = 0 ) const { - std::vector controlPoints; - - for ( int i = 0; i < (int)params.size(); ++i ) { - controlPoints.push_back( pointAt( params[i], deriv ) ); - } - - return controlPoints; - } - - std::vector getCtrlPoints() const { - std::vector cp; - cp.reserve( 3 * getNbBezier() + 1 ); - for ( const CubicBz& bz : spline ) { - cp.insert( cp.end(), bz.getCtrlPoints().begin(), bz.getCtrlPoints().end() - 1 ); - } - if ( !spline.empty() ) { cp.emplace_back( spline.back().getCtrlPoints().back() ); } - - return cp; - } - - void setCtrlPoints( const std::vector& cpoints ) { - int nbz { (int)( ( cpoints.size() - 1 ) / 3 ) }; - spline.clear(); - spline.reserve( nbz ); - for ( int b = 0; b < nbz; ++b ) { - spline.emplace_back( CubicBz( cpoints, 3 * b ) ); - } - } - - /** - * @brief Decomposes a spline global parameter into the local Bézier parameters (static) - * @param global parameter - * @param number of segments in the spline - * @return a pair (b,t) where b is the index of the bezier segment, and t the local parameter in - * the segment - */ - static std::pair getLocalParameter( float u, int nbz ) { - int b { (int)( std::floor( u ) ) }; - float t { u - b }; - - if ( ( b == nbz ) && ( t == 0 ) ) { - b = nbz - 1; - t = 1; - } - return { b, t }; - } - - /** - * @brief Map a normalized parameter for the spline to a global parameter - * @param normalized parameter [0, 1] - * @param number of segments in the spline - * @return a global parameter t [0, nbz] - */ - static float getGlobalParameter( float u, int nbz ) { return u * nbz; } - - /** - * @brief equivalent to linspace function - * @param number of param - * @return a list of parameters t [0, nbz] - */ - std::vector getUniformParameterization( int nbSamples ) const { - std::vector params; - params.resize( nbSamples ); - - float delta = { 1.0f / (float)( nbSamples - 1 ) }; - float acc = 0.0f; - int nbz = getNbBezier(); - - params[0] = getGlobalParameter( 0.0f, nbz ); - for ( int i = 1; i < nbSamples; ++i ) { - acc += delta; - params[i] = getGlobalParameter( acc, nbz ); - } - - params[params.size() - 1] = getGlobalParameter( 1.0f, nbz ); - - return params; - } - - /** - * @brief get a list of curviline abscisses - * @param distance in cm accross the curve that separate two params value - * @param step sampling [0, 1] - * @return list of params [0, nbz] - */ - std::vector getArcLengthParameterization( float resolution, float epsilon ) const { - - std::vector params; - int nbz = getNbBezier(); - - if ( nbz <= 0 ) return params; - - params = spline[0].getArcLengthParameterization( resolution, epsilon ); - - for ( int i = 1; i < nbz; ++i ) { - std::vector tmpParams = - spline[i].getArcLengthParameterization( resolution, epsilon ); - std::transform( tmpParams.begin(), - tmpParams.end(), - tmpParams.begin(), - [&]( auto const& elem ) { return elem + i; } ); - params.insert( params.end(), tmpParams.begin() + 1, tmpParams.end() ); - } - - return params; - } - - private: - std::vector spline; // Vector of Bézier segments in the spline - - std::pair getLocalParameter( float u ) const { - return getLocalParameter( u, getNbBezier() ); - } -}; - -using CubicBezierSpline2f = CubicBezierSpline; -using CubicBezierSpline3f = CubicBezierSpline; diff --git a/examples/CurveEditor/Painty/CubicBezierApproximation.hpp b/examples/CurveEditor/Painty/CubicBezierApproximation.hpp index 60f134f38c6..9c67de44298 100644 --- a/examples/CurveEditor/Painty/CubicBezierApproximation.hpp +++ b/examples/CurveEditor/Painty/CubicBezierApproximation.hpp @@ -1,23 +1,23 @@ #pragma once -#include "CubicBezier.hpp" +#include +#include +// #include "CubicBezier.hpp" #include "LeastSquareSystem.hpp" #include #include #include -template +using namespace Ra::Core; +using namespace Ra::Core::Geometry; + class CubicBezierApproximation { public: - using ControlPoint = T; - using CubicBz = CubicBezier; - using CubicBzSpline = CubicBezierSpline; - CubicBezierApproximation() {} - void init( const std::vector& data, + void init( const VectorArray& data, int nb_min_bz = 1, float epsilon = 0.1, std::ofstream* logfile = nullptr ) { @@ -26,7 +26,8 @@ class CubicBezierApproximation ( *logfile ) << "eps= " << epsilon << ";" << std::endl; } - m_data = data; + m_data = data; + std::cout << "DATA SIZE: " << m_data.size() << std::endl; m_distThreshold = epsilon; m_logfile = logfile; @@ -43,6 +44,7 @@ class CubicBezierApproximation bool compute() { using namespace Ra::Core::Utils; + std::cout << "INIT COMPUTE" << std::endl; if ( !m_isInitialized ) { LOG( logERROR ) << "CubicBezierApproximation is not initialized"; @@ -63,16 +65,24 @@ class CubicBezierApproximation ++m_step; printPolygonMatlab( m_data, "P_" + std::to_string( m_step ) ); + std::cout << "END M_DATA PRING" << std::endl; + std::cout << "COMPUTE LSS" << std::endl; auto okflag = computeLeastSquareSolution(); if ( !okflag ) { return false; } + std::cout << "END COMPUTE LSS" << std::endl; + std::cout << "START M_CUR_SOL PRINT" << std::endl; + std::cout << m_curSol.getCtrlPoints().size() << std::endl; printPolygonMatlab( m_curSol.getCtrlPoints(), "B_" + std::to_string( m_step ) ); + std::cout << "END M_CUR_SOL PRINT" << std::endl; float err = evaluateSolution(); if ( err > m_distThreshold ) { + std::cout << "COMPUTE JUNC" << std::endl; bool stopFlag { recomputeJunctions( m_bzjunctions.size() + 1 ) }; + std::cout << "END JUNC" << std::endl; if ( !stopFlag ) { return false; } @@ -81,6 +91,7 @@ class CubicBezierApproximation m_hasComputed = true; + std::cout << "END COMPUTE" << std::endl; return true; } @@ -106,10 +117,13 @@ class CubicBezierApproximation ++m_step; printPolygonMatlab( m_data, "P_" + std::to_string( m_step ) ); + std::cout << "END POLYGON DATA" << std::endl; + std::cout << "COMPUTE LSS" << std::endl; auto okflag = computeLeastSquareSolution(); if ( !okflag ) { return false; } + std::cout << "START M_CUR_SOL PRINT" << std::endl; printPolygonMatlab( m_curSol.getCtrlPoints(), "B_" + std::to_string( m_step ) ); if ( m_curSol.getNbBezier() < nbz ) { @@ -125,7 +139,7 @@ class CubicBezierApproximation return true; } - CubicBzSpline getSolution() const { return m_curSol; } + CubicBezierSpline getSolution() const { return m_curSol; } int getNstep() const { return m_step; } @@ -135,10 +149,10 @@ class CubicBezierApproximation int m_dim { 0 }; int m_step { 0 }; float m_distThreshold; - std::vector m_data; + VectorArray m_data; std::vector m_params; std::set m_bzjunctions; - CubicBzSpline m_curSol; + CubicBezierSpline m_curSol; std::ofstream* m_logfile { nullptr }; @@ -169,7 +183,7 @@ class CubicBezierApproximation } } - float errorAt( int i ) { return ( m_data[i] - m_curSol.pointAt( m_params[i] ) ).squaredNorm(); } + float errorAt( int i ) { return ( m_data[i] - m_curSol.f( m_params[i] ) ).squaredNorm(); } bool recomputeJunctions( int nb_junctions ) { m_bzjunctions.clear(); @@ -208,8 +222,8 @@ class CubicBezierApproximation auto computePointDistanceConstraint = [nbz]( float u ) { std::map A_cstr; - auto locpar = CubicBzSpline::getLocalParameter( u, nbz ); - auto bcoefs = CubicBz::bernsteinCoefsAt( locpar.second ); + auto locpar = CubicBezierSpline::getLocalParameter( u, nbz ); + auto bcoefs = CubicBezier::bernsteinCoefsAt( locpar.second ); int bi = locpar.first; for ( int i = 0; i < 4; ++i ) { @@ -237,8 +251,8 @@ class CubicBezierApproximation } else { // Bezier segment is constrained by less than 4 points => degenerated case - std::vector data( m_data.cbegin() + s0, m_data.cbegin() + s1 + 1 ); - std::vector cpts; + VectorArray data( m_data.cbegin() + s0, m_data.cbegin() + s1 + 1 ); + VectorArray cpts; // The problem is underconstrained : we compute one solution that perfectly fits the // data And we constrain the control points of the solution to match this solution @@ -255,6 +269,7 @@ class CubicBezierApproximation bool computeLeastSquareSolution() { int nbz { (int)( m_bzjunctions.size() ) - 1 }; int nvar { 3 * nbz + 1 }; + std::cout << "nvar: " << nvar << std::endl; if ( nbz < 1 ) { using namespace Ra::Core::Utils; @@ -268,18 +283,19 @@ class CubicBezierApproximation Eigen::MatrixXf sol; if ( !lss.solve( sol ) ) { return false; } - std::vector cpts( nvar ); + VectorArray cpts( nvar ); for ( int i = 0; i < nvar; ++i ) { - cpts[i] = ControlPoint( sol.row( i ) ); + cpts[i] = Curve2D::Vector( sol.row( i ) ); } m_curSol.setCtrlPoints( cpts ); + std::cout << "cpts size: " << cpts.size() << std::endl; return true; } - bool computeDegeneratedSolution( const std::vector& data, - std::vector& cpts ) { + bool computeDegeneratedSolution( const VectorArray& data, + VectorArray& cpts ) { int npts { (int)data.size() }; if ( ( npts == 0 ) || ( npts >= 4 ) ) { return false; } @@ -338,21 +354,21 @@ class CubicBezierApproximation } bool computeDegeneratedSolution() { - std::vector cpts; + VectorArray cpts; if ( !computeDegeneratedSolution( m_data, cpts ) ) { return false; } m_curSol.setCtrlPoints( cpts ); return true; } - void printPolygonMatlab( const std::vector& poly, const std::string& varname ) { + void printPolygonMatlab( const VectorArray& poly, + const std::string& varname ) { if ( m_logfile == nullptr ) { return; } ( *m_logfile ) << varname << "= ["; - for ( const auto& p : poly ) { - ( *m_logfile ) << "[" << p[0] << ";" << p[1] << "] "; + for ( unsigned int i = 0; i < poly.size(); i++ ) { + std::cout << "POINT:" << std::endl; + std::cout << poly[i].x() << " " << poly[i].y() << std::endl; + ( *m_logfile ) << "[" << poly[i].x() << ";" << poly[i].y() << "] "; } ( *m_logfile ) << "];" << std::endl; } }; - -using CubicBezierApproximation2f = CubicBezierApproximation; -using CubicBezierApproximation3f = CubicBezierApproximation; diff --git a/examples/CurveEditor/StrokeFactory.hpp b/examples/CurveEditor/StrokeFactory.hpp index 62e9741fc5f..d3085a8aed0 100644 --- a/examples/CurveEditor/StrokeFactory.hpp +++ b/examples/CurveEditor/StrokeFactory.hpp @@ -1,6 +1,6 @@ #pragma once -#include "StrokeComponent.hpp" +#include #include #include #include @@ -14,8 +14,9 @@ class StrokeFactory c->initialize(); return c; } - static Ra::Engine::Scene::Entity* createCurveEntity( std::vector polyline, - const std::string& name ) { + static Ra::Engine::Scene::Entity* + createCurveEntity( Ra::Core::VectorArray polyline, + const std::string& name ) { auto engine = Ra::Engine::RadiumEngine::getInstance(); Ra::Engine::Scene::Entity* e = engine->getEntityManager()->createEntity( name ); Ra::Core::Vector3Array strokePts; diff --git a/examples/CurveEditor/main.cpp b/examples/CurveEditor/main.cpp index 9a8541fb784..8267c031d06 100644 --- a/examples/CurveEditor/main.cpp +++ b/examples/CurveEditor/main.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include #include @@ -30,7 +32,7 @@ int main( int argc, char* argv[] ) { app.setContinuousUpdate( false ); // Create polyline from stroke points - std::vector polyline = { + Ra::Core::VectorArray polyline = { { -7.07583, -7.77924 }, { -6.4575, -7.69091 }, { -5.35333, -7.33757 }, { -3.27749, -6.54257 }, { -1.68749, -5.39423 }, { -0.671654, -4.20173 }, { 0.123348, -3.18589 }, { 0.697517, -1.94923 }, { 1.44835, -0.270889 }, diff --git a/src/Core/Geometry/Curve2D.hpp b/src/Core/Geometry/Curve2D.hpp index debe2eda546..1800c8d1226 100644 --- a/src/Core/Geometry/Curve2D.hpp +++ b/src/Core/Geometry/Curve2D.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace Ra { @@ -67,6 +68,73 @@ class CubicBezier : public Curve2D inline Vector df( Scalar u ) const override; inline Vector fdf( Scalar t, Vector& grad ) const override; + /** + * @brief Computes the cubic Bernstein coefficients for parameter t + * @param t parameter of the coefficients + * @param deriv derivative order + * @return a vector of 4 scalar coefficients + */ + static std::vector bernsteinCoefsAt( float u, int deriv = 0 ) { + if ( deriv == 2 ) { + return { 6 * ( 1 - u ), 6 * ( -2 + 3 * u ), 6 * ( 1 - 3 * u ), 6 * u }; + } + else if ( deriv == 1 ) + return { -3 * powf( 1 - u, 2 ), + 3 * ( 1 - u ) * ( 1 - 3 * u ), + 3 * u * ( 2 - 3 * u ), + 3 * powf( u, 2 ) }; + else + return { powf( 1 - u, 3 ), + 3 * u * powf( 1 - u, 2 ), + 3 * powf( u, 2 ) * ( 1 - u ), + powf( u, 3 ) }; + } + + /** + * @brief get a list of curviline abscisses + * @param distance in cm accross the curve that separate two params value + * @param step sampling [0, 1] + * @return list of params [0, 1] + */ + std::vector getArcLengthParameterization( float resolution, float epsilon ) const { + std::vector params; + float start = 0.0f; + float end = 1.0f; + float curParam = start; + float curDist = 0.0f; + + params.push_back( curParam ); + + Vector p0 = f( curParam ); + curParam += epsilon; + + while ( curParam <= end ) { + Vector p1 = f( curParam ); + curDist += sqrt( pow( p0.x() - p1.x(), 2 ) + pow( p0.y() - p1.y(), 2 ) ); + if ( curDist >= resolution ) { + params.push_back( curParam ); + curDist = 0.0f; + } + p0 = p1; + curParam += epsilon; + } + + // push last sample point to the end to ensure bounds [0, 1] + params[params.size() - 1] = end; + + return params; + } + + const VectorArray getCtrlPoints() const { + VectorArray ctrlPts; + ctrlPts.reserve( 4 ); + ctrlPts.push_back( m_points[0] ); + ctrlPts.push_back( m_points[1] ); + ctrlPts.push_back( m_points[2] ); + ctrlPts.push_back( m_points[3] ); + return ctrlPts; + } + private: Vector m_points[4]; }; @@ -107,6 +175,209 @@ class SplineCurve : public Curve2D Core::VectorArray m_points; }; +class CubicBezierSpline : public Curve2D +{ + public: + CubicBezierSpline() {} + + /** + * @brief Spline of cubic Bézier segments. Construction guarantees C0 continuity. + * ie extremities of successive segments share the same coordinates + * @param vector of control points, should be 3*n+1 points where n is the number of segments + */ + CubicBezierSpline( const Core::VectorArray& cpoints ) { setCtrlPoints( cpoints ); } + + CubicBezierSpline( const CubicBezierSpline& other ) { setCtrlPoints( other.getCtrlPoints() ); } + + int getNbBezier() const { return spline.size(); } + + const std::vector getSplines() const { return spline; } + + /** + * @brief Computes a sample point in the bezier spline + * @param u global parameter of the sample, should be in [0,nbz] + * integer part of u represents the id of the Bézier segment + * while decimal part of u represents the local Bézier parameter + * @param deriv derivative order of the sampling + * @return coordinates of the sample point + */ + inline Vector f( float u ) const override { + using namespace Ra::Core::Utils; + std::pair locpar { getLocalParameter( u ) }; + + if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { + LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; + Vector p; + p.fill( 0 ); + return p; + } + + return spline[locpar.first].f( locpar.second ); + } + + inline Vector df( float u ) const override { + using namespace Ra::Core::Utils; + std::pair locpar { getLocalParameter( u ) }; + + if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { + LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; + Vector p; + p.fill( 0 ); + return p; + } + + return spline[locpar.first].df( locpar.second ); + } + + inline Vector fdf( Scalar t, Vector& grad ) const override { + using namespace Ra::Core::Utils; + std::pair locpar { getLocalParameter( t ) }; + + if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { + LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; + Vector p; + p.fill( 0 ); + return p; + } + + return spline[locpar.first].fdf( locpar.second, grad ); + } + + /** + * @brief Computes a list of samples points in the bezier spline + * @param list of u global parameter of the sample, should be in [0,nbz] + * integer part of u represents the id of the Bézier segment + * while decimal part of u represents the local Bézier parameter + * @param deriv derivative order of the sampling + * @return coordinates of the sample point + */ + VectorArray f( std::vector params ) const { + VectorArray controlPoints; + + for ( int i = 0; i < (int)params.size(); ++i ) { + controlPoints.push_back( f( params[i] ) ); + } + + return controlPoints; + } + + VectorArray getCtrlPoints() const { + VectorArray cp; + std::cout << "nb bezier: " << getNbBezier() << std::endl; + cp.reserve( 3 * getNbBezier() + 1 ); + std::cout << "spline size: " << spline.size() << std::endl; + std::cout << cp.size() << std::endl; + for ( unsigned int i = 0; i < spline.size(); i++ ) { + cp.push_back( spline[i].getCtrlPoints()[0] ); + cp.push_back( spline[i].getCtrlPoints()[1] ); + cp.push_back( spline[i].getCtrlPoints()[2] ); + } + if ( !spline.empty() ) { cp.push_back( spline[spline.size() - 1].getCtrlPoints()[3] ); } + std::cout << "cp size: " << cp.size() << std::endl; + + return cp; + } + + void setCtrlPoints( const VectorArray& cpoints ) { + int nbz { (int)( ( cpoints.size() - 1 ) / 3 ) }; + spline.clear(); + spline.reserve( nbz ); + for ( int b = 0; b < nbz; ++b ) { + spline.emplace_back( CubicBezier( + cpoints[3 * b], cpoints[3 * b + 1], cpoints[3 * b + 2], cpoints[3 * b + 3] ) ); + } + } + + inline void addPoint( const Vector p ) override { + + }; + + /** + * @brief Decomposes a spline global parameter into the local Bézier parameters (static) + * @param global parameter + * @param number of segments in the spline + * @return a pair (b,t) where b is the index of the bezier segment, and t the local parameter in + * the segment + */ + static std::pair getLocalParameter( float u, int nbz ) { + int b { (int)( std::floor( u ) ) }; + float t { u - b }; + + if ( ( b == nbz ) && ( t == 0 ) ) { + b = nbz - 1; + t = 1; + } + return { b, t }; + } + + /** + * @brief Map a normalized parameter for the spline to a global parameter + * @param normalized parameter [0, 1] + * @param number of segments in the spline + * @return a global parameter t [0, nbz] + */ + static float getGlobalParameter( float u, int nbz ) { return u * nbz; } + + /** + * @brief equivalent to linspace function + * @param number of param + * @return a list of parameters t [0, nbz] + */ + std::vector getUniformParameterization( int nbSamples ) const { + std::vector params; + params.resize( nbSamples ); + + float delta = { 1.0f / (float)( nbSamples - 1 ) }; + float acc = 0.0f; + int nbz = getNbBezier(); + + params[0] = getGlobalParameter( 0.0f, nbz ); + for ( int i = 1; i < nbSamples; ++i ) { + acc += delta; + params[i] = getGlobalParameter( acc, nbz ); + } + + params[params.size() - 1] = getGlobalParameter( 1.0f, nbz ); + + return params; + } + + /** + * @brief get a list of curviline abscisses + * @param distance in cm accross the curve that separate two params value + * @param step sampling [0, 1] + * @return list of params [0, nbz] + */ + std::vector getArcLengthParameterization( float resolution, float epsilon ) const { + + std::vector params; + int nbz = getNbBezier(); + + if ( nbz <= 0 ) return params; + + params = spline[0].getArcLengthParameterization( resolution, epsilon ); + + for ( int i = 1; i < nbz; ++i ) { + std::vector tmpParams = + spline[i].getArcLengthParameterization( resolution, epsilon ); + std::transform( tmpParams.begin(), + tmpParams.end(), + tmpParams.begin(), + [&]( auto const& elem ) { return elem + i; } ); + params.insert( params.end(), tmpParams.begin() + 1, tmpParams.end() ); + } + + return params; + } + + private: + std::vector spline; // Vector of Bézier segments in the spline + + std::pair getLocalParameter( float u ) const { + return getLocalParameter( u, getNbBezier() ); + } +}; + /*--------------------------------------------------*/ void CubicBezier::addPoint( const Curve2D::Vector p ) { From 6017afa1e0f0f3f5bbd0c944f857f523dc34b2c4 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Wed, 6 Jul 2022 12:14:28 +0200 Subject: [PATCH 05/19] [tests] Refactor CubicBezierSpline --- src/Core/Geometry/Curve2D.cpp | 88 +++++++++++++++ src/Core/Geometry/Curve2D.hpp | 200 ++++++++++++---------------------- src/Core/filelist.cmake | 1 + 3 files changed, 160 insertions(+), 129 deletions(-) create mode 100644 src/Core/Geometry/Curve2D.cpp diff --git a/src/Core/Geometry/Curve2D.cpp b/src/Core/Geometry/Curve2D.cpp new file mode 100644 index 00000000000..38ab9c3011f --- /dev/null +++ b/src/Core/Geometry/Curve2D.cpp @@ -0,0 +1,88 @@ +#include +#include + +using namespace Ra::Core; +using namespace Ra::Core::Geometry; + +std::pair CubicBezierSpline::getLocalParameter( float u, int nbz ) { + int b { (int)( std::floor( u ) ) }; + float t { u - b }; + + if ( ( b == nbz ) && ( t == 0 ) ) { + b = nbz - 1; + t = 1; + } + return { b, t }; +} + +float CubicBezierSpline::getGlobalParameter( float u, int nbz ) { + return u * nbz; +} + +VectorArray CubicBezierSpline::getCtrlPoints() const { + VectorArray cp; + cp.reserve( 3 * getNbBezier() + 1 ); + for ( unsigned int i = 0; i < spline.size(); i++ ) { + cp.push_back( spline[i].getCtrlPoints()[0] ); + cp.push_back( spline[i].getCtrlPoints()[1] ); + cp.push_back( spline[i].getCtrlPoints()[2] ); + } + if ( !spline.empty() ) { cp.push_back( spline[spline.size() - 1].getCtrlPoints()[3] ); } + + return cp; +} + +void CubicBezierSpline::setCtrlPoints( const VectorArray& cpoints ) { + int nbz { (int)( ( cpoints.size() - 1 ) / 3 ) }; + spline.clear(); + spline.reserve( nbz ); + for ( int b = 0; b < nbz; ++b ) { + spline.emplace_back( CubicBezier( + cpoints[3 * b], cpoints[3 * b + 1], cpoints[3 * b + 2], cpoints[3 * b + 3] ) ); + } +} + +std::vector CubicBezierSpline::getUniformParameterization( int nbSamples ) const { + std::vector params; + params.resize( nbSamples ); + + float delta = { 1.0f / (float)( nbSamples - 1 ) }; + float acc = 0.0f; + int nbz = getNbBezier(); + + params[0] = getGlobalParameter( 0.0f, nbz ); + for ( int i = 1; i < nbSamples; ++i ) { + acc += delta; + params[i] = getGlobalParameter( acc, nbz ); + } + + params[params.size() - 1] = getGlobalParameter( 1.0f, nbz ); + + return params; +} + +std::vector CubicBezierSpline::getArcLengthParameterization( float resolution, + float epsilon ) const { + + std::vector params; + int nbz = getNbBezier(); + + if ( nbz <= 0 ) return params; + + params = spline[0].getArcLengthParameterization( resolution, epsilon ); + + for ( int i = 1; i < nbz; ++i ) { + std::vector tmpParams = + spline[i].getArcLengthParameterization( resolution, epsilon ); + std::transform( tmpParams.begin(), + tmpParams.end(), + tmpParams.begin(), + [&]( auto const& elem ) { return elem + i; } ); + params.insert( params.end(), tmpParams.begin() + 1, tmpParams.end() ); + } + return params; +} + +std::pair CubicBezierSpline::getLocalParameter( float u ) const { + return getLocalParameter( u, getNbBezier() ); +} diff --git a/src/Core/Geometry/Curve2D.hpp b/src/Core/Geometry/Curve2D.hpp index 1800c8d1226..f56975a2314 100644 --- a/src/Core/Geometry/Curve2D.hpp +++ b/src/Core/Geometry/Curve2D.hpp @@ -201,47 +201,11 @@ class CubicBezierSpline : public Curve2D * @param deriv derivative order of the sampling * @return coordinates of the sample point */ - inline Vector f( float u ) const override { - using namespace Ra::Core::Utils; - std::pair locpar { getLocalParameter( u ) }; - - if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { - LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; - Vector p; - p.fill( 0 ); - return p; - } - - return spline[locpar.first].f( locpar.second ); - } - - inline Vector df( float u ) const override { - using namespace Ra::Core::Utils; - std::pair locpar { getLocalParameter( u ) }; - - if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { - LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; - Vector p; - p.fill( 0 ); - return p; - } - - return spline[locpar.first].df( locpar.second ); - } - - inline Vector fdf( Scalar t, Vector& grad ) const override { - using namespace Ra::Core::Utils; - std::pair locpar { getLocalParameter( t ) }; + inline Vector f( float u ) const override; - if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { - LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; - Vector p; - p.fill( 0 ); - return p; - } + inline Vector df( float u ) const override; - return spline[locpar.first].fdf( locpar.second, grad ); - } + inline Vector fdf( Scalar t, Vector& grad ) const override; /** * @brief Computes a list of samples points in the bezier spline @@ -251,46 +215,13 @@ class CubicBezierSpline : public Curve2D * @param deriv derivative order of the sampling * @return coordinates of the sample point */ - VectorArray f( std::vector params ) const { - VectorArray controlPoints; - - for ( int i = 0; i < (int)params.size(); ++i ) { - controlPoints.push_back( f( params[i] ) ); - } - - return controlPoints; - } - - VectorArray getCtrlPoints() const { - VectorArray cp; - std::cout << "nb bezier: " << getNbBezier() << std::endl; - cp.reserve( 3 * getNbBezier() + 1 ); - std::cout << "spline size: " << spline.size() << std::endl; - std::cout << cp.size() << std::endl; - for ( unsigned int i = 0; i < spline.size(); i++ ) { - cp.push_back( spline[i].getCtrlPoints()[0] ); - cp.push_back( spline[i].getCtrlPoints()[1] ); - cp.push_back( spline[i].getCtrlPoints()[2] ); - } - if ( !spline.empty() ) { cp.push_back( spline[spline.size() - 1].getCtrlPoints()[3] ); } - std::cout << "cp size: " << cp.size() << std::endl; + inline VectorArray f( std::vector params ) const; - return cp; - } - - void setCtrlPoints( const VectorArray& cpoints ) { - int nbz { (int)( ( cpoints.size() - 1 ) / 3 ) }; - spline.clear(); - spline.reserve( nbz ); - for ( int b = 0; b < nbz; ++b ) { - spline.emplace_back( CubicBezier( - cpoints[3 * b], cpoints[3 * b + 1], cpoints[3 * b + 2], cpoints[3 * b + 3] ) ); - } - } + inline void addPoint( const Vector p ) override; - inline void addPoint( const Vector p ) override { + VectorArray getCtrlPoints() const; - }; + void setCtrlPoints( const VectorArray& cpoints ); /** * @brief Decomposes a spline global parameter into the local Bézier parameters (static) @@ -299,16 +230,7 @@ class CubicBezierSpline : public Curve2D * @return a pair (b,t) where b is the index of the bezier segment, and t the local parameter in * the segment */ - static std::pair getLocalParameter( float u, int nbz ) { - int b { (int)( std::floor( u ) ) }; - float t { u - b }; - - if ( ( b == nbz ) && ( t == 0 ) ) { - b = nbz - 1; - t = 1; - } - return { b, t }; - } + static std::pair getLocalParameter( float u, int nbz ); /** * @brief Map a normalized parameter for the spline to a global parameter @@ -316,31 +238,14 @@ class CubicBezierSpline : public Curve2D * @param number of segments in the spline * @return a global parameter t [0, nbz] */ - static float getGlobalParameter( float u, int nbz ) { return u * nbz; } + static float getGlobalParameter( float u, int nbz ); /** * @brief equivalent to linspace function * @param number of param * @return a list of parameters t [0, nbz] */ - std::vector getUniformParameterization( int nbSamples ) const { - std::vector params; - params.resize( nbSamples ); - - float delta = { 1.0f / (float)( nbSamples - 1 ) }; - float acc = 0.0f; - int nbz = getNbBezier(); - - params[0] = getGlobalParameter( 0.0f, nbz ); - for ( int i = 1; i < nbSamples; ++i ) { - acc += delta; - params[i] = getGlobalParameter( acc, nbz ); - } - - params[params.size() - 1] = getGlobalParameter( 1.0f, nbz ); - - return params; - } + std::vector getUniformParameterization( int nbSamples ) const; /** * @brief get a list of curviline abscisses @@ -348,34 +253,12 @@ class CubicBezierSpline : public Curve2D * @param step sampling [0, 1] * @return list of params [0, nbz] */ - std::vector getArcLengthParameterization( float resolution, float epsilon ) const { - - std::vector params; - int nbz = getNbBezier(); - - if ( nbz <= 0 ) return params; - - params = spline[0].getArcLengthParameterization( resolution, epsilon ); - - for ( int i = 1; i < nbz; ++i ) { - std::vector tmpParams = - spline[i].getArcLengthParameterization( resolution, epsilon ); - std::transform( tmpParams.begin(), - tmpParams.end(), - tmpParams.begin(), - [&]( auto const& elem ) { return elem + i; } ); - params.insert( params.end(), tmpParams.begin() + 1, tmpParams.end() ); - } - - return params; - } + std::vector getArcLengthParameterization( float resolution, float epsilon ) const; private: std::vector spline; // Vector of Bézier segments in the spline - std::pair getLocalParameter( float u ) const { - return getLocalParameter( u, getNbBezier() ); - } + std::pair getLocalParameter( float u ) const; }; /*--------------------------------------------------*/ @@ -486,6 +369,65 @@ Curve2D::Vector QuadraSpline::fdf( Scalar u, Vector& grad ) const { return spline.f( u ); } +/*--------------------------------------------------*/ + +inline Curve2D::Vector CubicBezierSpline::f( float u ) const { + using namespace Ra::Core::Utils; + std::pair locpar { getLocalParameter( u ) }; + + if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { + LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; + Vector p; + p.fill( 0 ); + return p; + } + + return spline[locpar.first].f( locpar.second ); +} + +VectorArray CubicBezierSpline::f( std::vector params ) const { + VectorArray controlPoints; + + for ( int i = 0; i < (int)params.size(); ++i ) { + controlPoints.push_back( f( params[i] ) ); + } + + return controlPoints; +} + +Curve2D::Vector CubicBezierSpline::df( float u ) const { + using namespace Ra::Core::Utils; + std::pair locpar { getLocalParameter( u ) }; + + if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { + LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; + Vector p; + p.fill( 0 ); + return p; + } + + return spline[locpar.first].df( locpar.second ); +} + +Curve2D::Vector CubicBezierSpline::fdf( Scalar t, Vector& grad ) const { + using namespace Ra::Core::Utils; + std::pair locpar { getLocalParameter( t ) }; + + if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { + LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; + Vector p; + p.fill( 0 ); + return p; + } + + return spline[locpar.first].fdf( locpar.second, grad ); +} + +void CubicBezierSpline::addPoint( const Curve2D::Vector p ) { + if ( spline[spline.size() - 1].getCtrlPoints().size() < 4 ) + spline[spline.size() - 1].addPoint( p ); +} + } // namespace Geometry } // namespace Core } // namespace Ra diff --git a/src/Core/filelist.cmake b/src/Core/filelist.cmake index 7aabbe1b640..9373b9977a6 100644 --- a/src/Core/filelist.cmake +++ b/src/Core/filelist.cmake @@ -26,6 +26,7 @@ set(core_sources Containers/AdjacencyList.cpp Containers/VariableSet.cpp Geometry/CatmullClarkSubdivider.cpp + Geometry/Curve2D.cpp Geometry/IndexedGeometry.cpp Geometry/LoopSubdivider.cpp Geometry/MeshPrimitives.cpp From d4a709bcfedb6c62aefaa1b1c903025c371e0f91 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Wed, 6 Jul 2022 14:26:50 +0200 Subject: [PATCH 06/19] [tests] Cleanup and add comments to curve editor --- examples/CurveEditor/CurveEditor.cpp | 2 - examples/CurveEditor/CurveEditor.hpp | 105 +++++++----------- .../Painty/CubicBezierApproximation.hpp | 22 +--- src/Core/Geometry/Curve2D.cpp | 89 ++++++++++++--- src/Core/Geometry/Curve2D.hpp | 92 ++++----------- 5 files changed, 136 insertions(+), 174 deletions(-) diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index a989f6dbadf..fd2c5bafa14 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -18,9 +18,7 @@ CurveEditor::CurveEditor( // Approximate polyline with bezier CubicBezierApproximation approximator; approximator.init( polyline ); - std::cout << "INITED" << std::endl; approximator.compute(); - std::cout << "COMPUTED" << std::endl; auto solution = approximator.getSolution(); auto solutionPts = solution.getCtrlPoints(); diff --git a/examples/CurveEditor/CurveEditor.hpp b/examples/CurveEditor/CurveEditor.hpp index 38ccf25d25b..e78f807a6c6 100644 --- a/examples/CurveEditor/CurveEditor.hpp +++ b/examples/CurveEditor/CurveEditor.hpp @@ -1,5 +1,6 @@ #include "CurveFactory.hpp" #include "Gui/MyViewer.hpp" +#include "PointFactory.hpp" #include #include #include @@ -9,16 +10,45 @@ class CurveEditor : public Ra::Engine::Scene::Entity { public: - CurveEditor( std::vector polyline, MyViewer* viewer ); + CurveEditor( const Ra::Core::VectorArray& polyline, + MyViewer* viewer ); ~CurveEditor(); + /** + * @brief add a point at the end of the polyline + * @param the world position where to add the point + */ void addPointAtEnd( const Ra::Core::Vector3& worldPos ); + + /** + * @brief add a point in the existing polyline + * @param the world position where to add the point + * @param the corresponding window x position + * @param the corresponding window y position + */ void addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX, int mouseY ); + /** + * @brief update all curves, if onRelease then it will clean the entities transform + * @param the world position where to add the point + */ void updateCurves( bool onRelease = false ); + /** + * @brief change the color of the corresponding ro and save it for future manipulation + * @param the renderObject that is currently being hovered + */ bool processHover( std::shared_ptr ro ); + + /** + * @brief change saved ro color to their default and unsave the ro + */ void processUnhovering(); + + /** + * @brief process the saved ro and move it to the given world position + * @param the world position of where to move the saved ro + */ void processPicking( const Ra::Core::Vector3& worldPos ); std::shared_ptr getSelected() { return m_selectedRo; } @@ -28,7 +58,16 @@ class CurveEditor : public Ra::Engine::Scene::Entity m_currentPoint = -1; } + /** + * @brief smooth to keep a G1 continuity + * @param smooth state + */ void setSmooth( bool smooth ) { m_smooth = smooth; } + + /** + * @brief symetry to keep a C1 continuity + * @param symetry state + */ void setSymetry( bool symetry ) { m_symetry = symetry; } private: @@ -64,65 +103,5 @@ class CurveEditor : public Ra::Engine::Scene::Entity bool m_smooth { false }; bool m_symetry { false }; - class CurveEditor : public Ra::Engine::Scene::Entity - { - - public: - CurveEditor( const Ra::Core::VectorArray& polyline, - MyViewer* viewer ); - ~CurveEditor(); - - void addPointAtEnd( const Ra::Core::Vector3& worldPos ); - void addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX, int mouseY ); - - void updateCurves( bool onRelease = false ); - - bool processHover( std::shared_ptr ro ); - void processUnhovering(); - void processPicking( const Ra::Core::Vector3& worldPos ); - - std::shared_ptr getSelected() { return m_selectedRo; } - - void resetSelected() { - m_selectedRo = nullptr; - m_currentPoint = -1; - } - - void setSmooth( bool smooth ) { m_smooth = smooth; } - void setSymetry( bool symetry ) { m_symetry = symetry; } - - private: - inline float distanceSquared( const Ra::Core::Vector3f& pointA, - const Ra::Core::Vector3f& pointB ); - unsigned int getPointIndex( unsigned int curveIdSize, - unsigned int currentPtIndex, - const Ra::Core::Vector3& firstCtrlPt, - const Ra::Core::Vector3& currentPt ); - void updateCurve( unsigned int curveId, - unsigned int curveIdSize, - PointComponent* pointComponent, - const Ra::Core::Vector3& currentPt, - unsigned int currentPoint ); - Ra::Core::Transform computePointTransform( PointComponent* pointCmp, - const Ra::Core::Vector3& midPoint, - const Ra::Core::Vector3& worldPos ); - void subdivisionBezier( int vertexIndex, - unsigned int curveIndex, - const Ra::Core::Vector3Array& ctrlPts ); - void refreshPoint( unsigned int pointIndex, - const Ra::Core::Vector3& newPoint = Ra::Core::Vector3::Zero() ); - - private: - Ra::Engine::Scene::EntityManager* m_entityMgr; - int m_currentPoint { -1 }; - std::shared_ptr m_selectedRo { nullptr }; - std::vector m_pointEntities; - std::vector m_curveEntities; - std::vector m_tangentPoints; - Ra::Engine::Rendering::RenderObjectManager* m_roMgr; - - bool m_smooth { false }; - bool m_symetry { false }; - - MyViewer* m_viewer { nullptr }; - }; + MyViewer* m_viewer { nullptr }; +}; diff --git a/examples/CurveEditor/Painty/CubicBezierApproximation.hpp b/examples/CurveEditor/Painty/CubicBezierApproximation.hpp index 9c67de44298..f1d0b31efd7 100644 --- a/examples/CurveEditor/Painty/CubicBezierApproximation.hpp +++ b/examples/CurveEditor/Painty/CubicBezierApproximation.hpp @@ -26,8 +26,7 @@ class CubicBezierApproximation ( *logfile ) << "eps= " << epsilon << ";" << std::endl; } - m_data = data; - std::cout << "DATA SIZE: " << m_data.size() << std::endl; + m_data = data; m_distThreshold = epsilon; m_logfile = logfile; @@ -44,7 +43,6 @@ class CubicBezierApproximation bool compute() { using namespace Ra::Core::Utils; - std::cout << "INIT COMPUTE" << std::endl; if ( !m_isInitialized ) { LOG( logERROR ) << "CubicBezierApproximation is not initialized"; @@ -65,33 +63,22 @@ class CubicBezierApproximation ++m_step; printPolygonMatlab( m_data, "P_" + std::to_string( m_step ) ); - std::cout << "END M_DATA PRING" << std::endl; - std::cout << "COMPUTE LSS" << std::endl; auto okflag = computeLeastSquareSolution(); if ( !okflag ) { return false; } - std::cout << "END COMPUTE LSS" << std::endl; - std::cout << "START M_CUR_SOL PRINT" << std::endl; - std::cout << m_curSol.getCtrlPoints().size() << std::endl; printPolygonMatlab( m_curSol.getCtrlPoints(), "B_" + std::to_string( m_step ) ); - std::cout << "END M_CUR_SOL PRINT" << std::endl; float err = evaluateSolution(); if ( err > m_distThreshold ) { - std::cout << "COMPUTE JUNC" << std::endl; bool stopFlag { recomputeJunctions( m_bzjunctions.size() + 1 ) }; - std::cout << "END JUNC" << std::endl; - if ( !stopFlag ) { return false; } - return compute(); } m_hasComputed = true; - std::cout << "END COMPUTE" << std::endl; return true; } @@ -117,13 +104,10 @@ class CubicBezierApproximation ++m_step; printPolygonMatlab( m_data, "P_" + std::to_string( m_step ) ); - std::cout << "END POLYGON DATA" << std::endl; - std::cout << "COMPUTE LSS" << std::endl; auto okflag = computeLeastSquareSolution(); if ( !okflag ) { return false; } - std::cout << "START M_CUR_SOL PRINT" << std::endl; printPolygonMatlab( m_curSol.getCtrlPoints(), "B_" + std::to_string( m_step ) ); if ( m_curSol.getNbBezier() < nbz ) { @@ -269,7 +253,6 @@ class CubicBezierApproximation bool computeLeastSquareSolution() { int nbz { (int)( m_bzjunctions.size() ) - 1 }; int nvar { 3 * nbz + 1 }; - std::cout << "nvar: " << nvar << std::endl; if ( nbz < 1 ) { using namespace Ra::Core::Utils; @@ -289,7 +272,6 @@ class CubicBezierApproximation } m_curSol.setCtrlPoints( cpts ); - std::cout << "cpts size: " << cpts.size() << std::endl; return true; } @@ -365,8 +347,6 @@ class CubicBezierApproximation if ( m_logfile == nullptr ) { return; } ( *m_logfile ) << varname << "= ["; for ( unsigned int i = 0; i < poly.size(); i++ ) { - std::cout << "POINT:" << std::endl; - std::cout << poly[i].x() << " " << poly[i].y() << std::endl; ( *m_logfile ) << "[" << poly[i].x() << ";" << poly[i].y() << "] "; } ( *m_logfile ) << "];" << std::endl; diff --git a/src/Core/Geometry/Curve2D.cpp b/src/Core/Geometry/Curve2D.cpp index 38ab9c3011f..548bc5bbade 100644 --- a/src/Core/Geometry/Curve2D.cpp +++ b/src/Core/Geometry/Curve2D.cpp @@ -1,8 +1,67 @@ #include #include -using namespace Ra::Core; -using namespace Ra::Core::Geometry; +namespace Ra { +namespace Core { +namespace Geometry { + +/*--------------------------------------------------*/ + +std::vector CubicBezier::bernsteinCoefsAt( float u, int deriv ) { + if ( deriv == 2 ) { return { 6 * ( 1 - u ), 6 * ( -2 + 3 * u ), 6 * ( 1 - 3 * u ), 6 * u }; } + else if ( deriv == 1 ) + return { -3 * powf( 1 - u, 2 ), + 3 * ( 1 - u ) * ( 1 - 3 * u ), + 3 * u * ( 2 - 3 * u ), + 3 * powf( u, 2 ) }; + else + return { powf( 1 - u, 3 ), + 3 * u * powf( 1 - u, 2 ), + 3 * powf( u, 2 ) * ( 1 - u ), + powf( u, 3 ) }; +} + +std::vector CubicBezier::getArcLengthParameterization( float resolution, + float epsilon ) const { + std::vector params; + float start = 0.0f; + float end = 1.0f; + float curParam = start; + float curDist = 0.0f; + + params.push_back( curParam ); + + Vector p0 = f( curParam ); + curParam += epsilon; + + while ( curParam <= end ) { + Vector p1 = f( curParam ); + curDist += sqrt( pow( p0.x() - p1.x(), 2 ) + pow( p0.y() - p1.y(), 2 ) ); + if ( curDist >= resolution ) { + params.push_back( curParam ); + curDist = 0.0f; + } + p0 = p1; + curParam += epsilon; + } + + // push last sample point to the end to ensure bounds [0, 1] + params[params.size() - 1] = end; + + return params; +} + +const VectorArray CubicBezier::getCtrlPoints() const { + VectorArray ctrlPts; + ctrlPts.reserve( 4 ); + ctrlPts.push_back( m_points[0] ); + ctrlPts.push_back( m_points[1] ); + ctrlPts.push_back( m_points[2] ); + ctrlPts.push_back( m_points[3] ); + return ctrlPts; +} + +/*--------------------------------------------------*/ std::pair CubicBezierSpline::getLocalParameter( float u, int nbz ) { int b { (int)( std::floor( u ) ) }; @@ -22,22 +81,22 @@ float CubicBezierSpline::getGlobalParameter( float u, int nbz ) { VectorArray CubicBezierSpline::getCtrlPoints() const { VectorArray cp; cp.reserve( 3 * getNbBezier() + 1 ); - for ( unsigned int i = 0; i < spline.size(); i++ ) { - cp.push_back( spline[i].getCtrlPoints()[0] ); - cp.push_back( spline[i].getCtrlPoints()[1] ); - cp.push_back( spline[i].getCtrlPoints()[2] ); + for ( unsigned int i = 0; i < m_spline.size(); i++ ) { + cp.push_back( m_spline[i].getCtrlPoints()[0] ); + cp.push_back( m_spline[i].getCtrlPoints()[1] ); + cp.push_back( m_spline[i].getCtrlPoints()[2] ); } - if ( !spline.empty() ) { cp.push_back( spline[spline.size() - 1].getCtrlPoints()[3] ); } + if ( !m_spline.empty() ) { cp.push_back( m_spline[m_spline.size() - 1].getCtrlPoints()[3] ); } return cp; } void CubicBezierSpline::setCtrlPoints( const VectorArray& cpoints ) { int nbz { (int)( ( cpoints.size() - 1 ) / 3 ) }; - spline.clear(); - spline.reserve( nbz ); + m_spline.clear(); + m_spline.reserve( nbz ); for ( int b = 0; b < nbz; ++b ) { - spline.emplace_back( CubicBezier( + m_spline.emplace_back( CubicBezier( cpoints[3 * b], cpoints[3 * b + 1], cpoints[3 * b + 2], cpoints[3 * b + 3] ) ); } } @@ -69,11 +128,11 @@ std::vector CubicBezierSpline::getArcLengthParameterization( float resolu if ( nbz <= 0 ) return params; - params = spline[0].getArcLengthParameterization( resolution, epsilon ); + params = m_spline[0].getArcLengthParameterization( resolution, epsilon ); for ( int i = 1; i < nbz; ++i ) { std::vector tmpParams = - spline[i].getArcLengthParameterization( resolution, epsilon ); + m_spline[i].getArcLengthParameterization( resolution, epsilon ); std::transform( tmpParams.begin(), tmpParams.end(), tmpParams.begin(), @@ -83,6 +142,6 @@ std::vector CubicBezierSpline::getArcLengthParameterization( float resolu return params; } -std::pair CubicBezierSpline::getLocalParameter( float u ) const { - return getLocalParameter( u, getNbBezier() ); -} +} // namespace Geometry +} // namespace Core +} // namespace Ra diff --git a/src/Core/Geometry/Curve2D.hpp b/src/Core/Geometry/Curve2D.hpp index f56975a2314..b41c310d27f 100644 --- a/src/Core/Geometry/Curve2D.hpp +++ b/src/Core/Geometry/Curve2D.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include namespace Ra { @@ -74,21 +73,7 @@ class CubicBezier : public Curve2D * @param deriv derivative order * @return a vector of 4 scalar coefficients */ - static std::vector bernsteinCoefsAt( float u, int deriv = 0 ) { - if ( deriv == 2 ) { - return { 6 * ( 1 - u ), 6 * ( -2 + 3 * u ), 6 * ( 1 - 3 * u ), 6 * u }; - } - else if ( deriv == 1 ) - return { -3 * powf( 1 - u, 2 ), - 3 * ( 1 - u ) * ( 1 - 3 * u ), - 3 * u * ( 2 - 3 * u ), - 3 * powf( u, 2 ) }; - else - return { powf( 1 - u, 3 ), - 3 * u * powf( 1 - u, 2 ), - 3 * powf( u, 2 ) * ( 1 - u ), - powf( u, 3 ) }; - } + static std::vector bernsteinCoefsAt( float u, int deriv = 0 ); /** * @brief get a list of curviline abscisses @@ -96,44 +81,9 @@ class CubicBezier : public Curve2D * @param step sampling [0, 1] * @return list of params [0, 1] */ - std::vector getArcLengthParameterization( float resolution, float epsilon ) const { - std::vector params; - float start = 0.0f; - float end = 1.0f; - float curParam = start; - float curDist = 0.0f; - - params.push_back( curParam ); - - Vector p0 = f( curParam ); - curParam += epsilon; - - while ( curParam <= end ) { - Vector p1 = f( curParam ); - curDist += sqrt( pow( p0.x() - p1.x(), 2 ) + pow( p0.y() - p1.y(), 2 ) ); - if ( curDist >= resolution ) { - params.push_back( curParam ); - curDist = 0.0f; - } - p0 = p1; - curParam += epsilon; - } - - // push last sample point to the end to ensure bounds [0, 1] - params[params.size() - 1] = end; - - return params; - } + std::vector getArcLengthParameterization( float resolution, float epsilon ) const; - const VectorArray getCtrlPoints() const { - VectorArray ctrlPts; - ctrlPts.reserve( 4 ); - ctrlPts.push_back( m_points[0] ); - ctrlPts.push_back( m_points[1] ); - ctrlPts.push_back( m_points[2] ); - ctrlPts.push_back( m_points[3] ); - return ctrlPts; - } + const VectorArray getCtrlPoints() const; private: Vector m_points[4]; @@ -189,9 +139,9 @@ class CubicBezierSpline : public Curve2D CubicBezierSpline( const CubicBezierSpline& other ) { setCtrlPoints( other.getCtrlPoints() ); } - int getNbBezier() const { return spline.size(); } + int getNbBezier() const { return m_spline.size(); } - const std::vector getSplines() const { return spline; } + const std::vector getSplines() const { return m_spline; } /** * @brief Computes a sample point in the bezier spline @@ -203,10 +153,6 @@ class CubicBezierSpline : public Curve2D */ inline Vector f( float u ) const override; - inline Vector df( float u ) const override; - - inline Vector fdf( Scalar t, Vector& grad ) const override; - /** * @brief Computes a list of samples points in the bezier spline * @param list of u global parameter of the sample, should be in [0,nbz] @@ -217,6 +163,10 @@ class CubicBezierSpline : public Curve2D */ inline VectorArray f( std::vector params ) const; + inline Vector df( float u ) const override; + + inline Vector fdf( Scalar t, Vector& grad ) const override; + inline void addPoint( const Vector p ) override; VectorArray getCtrlPoints() const; @@ -256,9 +206,11 @@ class CubicBezierSpline : public Curve2D std::vector getArcLengthParameterization( float resolution, float epsilon ) const; private: - std::vector spline; // Vector of Bézier segments in the spline + std::vector m_spline; // Vector of Bézier segments in the spline - std::pair getLocalParameter( float u ) const; + std::pair getLocalParameter( float u ) const { + return getLocalParameter( u, getNbBezier() ); + } }; /*--------------------------------------------------*/ @@ -371,18 +323,16 @@ Curve2D::Vector QuadraSpline::fdf( Scalar u, Vector& grad ) const { /*--------------------------------------------------*/ -inline Curve2D::Vector CubicBezierSpline::f( float u ) const { - using namespace Ra::Core::Utils; +Curve2D::Vector CubicBezierSpline::f( float u ) const { std::pair locpar { getLocalParameter( u ) }; if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { - LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; Vector p; p.fill( 0 ); return p; } - return spline[locpar.first].f( locpar.second ); + return m_spline[locpar.first].f( locpar.second ); } VectorArray CubicBezierSpline::f( std::vector params ) const { @@ -396,36 +346,32 @@ VectorArray CubicBezierSpline::f( std::vector params ) c } Curve2D::Vector CubicBezierSpline::df( float u ) const { - using namespace Ra::Core::Utils; std::pair locpar { getLocalParameter( u ) }; if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { - LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; Vector p; p.fill( 0 ); return p; } - return spline[locpar.first].df( locpar.second ); + return m_spline[locpar.first].df( locpar.second ); } Curve2D::Vector CubicBezierSpline::fdf( Scalar t, Vector& grad ) const { - using namespace Ra::Core::Utils; std::pair locpar { getLocalParameter( t ) }; if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { - LOG( logERROR ) << "Cubic Bezier Spline : invalid parameter"; Vector p; p.fill( 0 ); return p; } - return spline[locpar.first].fdf( locpar.second, grad ); + return m_spline[locpar.first].fdf( locpar.second, grad ); } void CubicBezierSpline::addPoint( const Curve2D::Vector p ) { - if ( spline[spline.size() - 1].getCtrlPoints().size() < 4 ) - spline[spline.size() - 1].addPoint( p ); + if ( m_spline[m_spline.size() - 1].getCtrlPoints().size() < 4 ) + m_spline[m_spline.size() - 1].addPoint( p ); } } // namespace Geometry From 50425bbc7d26c171970dce049094f9c9740cdfca Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Wed, 6 Jul 2022 12:14:28 +0200 Subject: [PATCH 07/19] [tests] Refactor CubicBezierSpline --- .../CubicBezierApproximation.hpp | 0 .../{Painty => BezierUtils}/LeastSquareSystem.hpp | 0 examples/CurveEditor/CMakeLists.txt | 4 ++-- examples/CurveEditor/CurveEditor.cpp | 2 +- examples/CurveEditor/Gui/MainWindow.cpp | 2 +- examples/CurveEditor/README.md | 12 ------------ 6 files changed, 4 insertions(+), 16 deletions(-) rename examples/CurveEditor/{Painty => BezierUtils}/CubicBezierApproximation.hpp (100%) rename examples/CurveEditor/{Painty => BezierUtils}/LeastSquareSystem.hpp (100%) delete mode 100644 examples/CurveEditor/README.md diff --git a/examples/CurveEditor/Painty/CubicBezierApproximation.hpp b/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp similarity index 100% rename from examples/CurveEditor/Painty/CubicBezierApproximation.hpp rename to examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp diff --git a/examples/CurveEditor/Painty/LeastSquareSystem.hpp b/examples/CurveEditor/BezierUtils/LeastSquareSystem.hpp similarity index 100% rename from examples/CurveEditor/Painty/LeastSquareSystem.hpp rename to examples/CurveEditor/BezierUtils/LeastSquareSystem.hpp diff --git a/examples/CurveEditor/CMakeLists.txt b/examples/CurveEditor/CMakeLists.txt index 6188dd357cc..d1721eaf604 100644 --- a/examples/CurveEditor/CMakeLists.txt +++ b/examples/CurveEditor/CMakeLists.txt @@ -44,8 +44,8 @@ set(app_headers CurveFactory.hpp PointComponent.hpp PointFactory.hpp - Painty/CubicBezierApproximation.hpp - Painty/LeastSquareSystem.hpp + BezierUtils/CubicBezierApproximation.hpp + BezierUtils/LeastSquareSystem.hpp CurveEditor.hpp ) diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index fd2c5bafa14..79a01687f21 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -5,8 +5,8 @@ #include #include +#include "BezierUtils/CubicBezierApproximation.hpp" #include "CurveEditor.hpp" -#include "Painty/CubicBezierApproximation.hpp" CurveEditor::CurveEditor( const Ra::Core::VectorArray& polyline, diff --git a/examples/CurveEditor/Gui/MainWindow.cpp b/examples/CurveEditor/Gui/MainWindow.cpp index 5d5f0c50681..80faf844ace 100644 --- a/examples/CurveEditor/Gui/MainWindow.cpp +++ b/examples/CurveEditor/Gui/MainWindow.cpp @@ -1,5 +1,5 @@ #include "MainWindow.hpp" -#include "Painty/CubicBezierApproximation.hpp" +#include "BezierUtils/CubicBezierApproximation.hpp" #include "PointFactory.hpp" #include #include diff --git a/examples/CurveEditor/README.md b/examples/CurveEditor/README.md deleted file mode 100644 index 374fcef23aa..00000000000 --- a/examples/CurveEditor/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# PaintyStrokes - -```{.bash} -mkdir build -cd build -cmake -DCMAKE_BUILD_TYPE=Release -DRadium_DIR={RADIUM_DIR}/Bundle-GNU/lib/cmake/Radium/ ../ -./src/DrawStrokes -``` - -Disclaimer: -You have to run DrawStrokes from the ./build directory since the Assets directory -to read/load XML files is relative to the execution directory From 4b3ada1c36e91c8ca287d5e9e0964b1be2e84be3 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Wed, 6 Jul 2022 14:26:50 +0200 Subject: [PATCH 08/19] [tests] Cleanup and add comments to curve editor --- examples/CurveEditor/CMakeLists.txt | 4 +-- examples/CurveEditor/Gui/MainWindow.cpp | 25 +++++++++-------- examples/CurveEditor/Gui/MainWindow.hpp | 28 ++++--------------- ...okeComponent.cpp => PolylineComponent.cpp} | 13 +++++---- ...okeComponent.hpp => PolylineComponent.hpp} | 6 ++-- ...{StrokeFactory.hpp => PolylineFactory.hpp} | 15 +++++----- examples/CurveEditor/main.cpp | 8 +++--- 7 files changed, 43 insertions(+), 56 deletions(-) rename examples/CurveEditor/{StrokeComponent.cpp => PolylineComponent.cpp} (76%) rename examples/CurveEditor/{StrokeComponent.hpp => PolylineComponent.hpp} (60%) rename examples/CurveEditor/{StrokeFactory.hpp => PolylineFactory.hpp} (58%) diff --git a/examples/CurveEditor/CMakeLists.txt b/examples/CurveEditor/CMakeLists.txt index d1721eaf604..fb3ce82d5fe 100644 --- a/examples/CurveEditor/CMakeLists.txt +++ b/examples/CurveEditor/CMakeLists.txt @@ -32,7 +32,7 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) # ------------------------------------------------------------------------------ -set(app_sources main.cpp CurveComponent.cpp StrokeComponent.cpp PointComponent.cpp +set(app_sources main.cpp CurveComponent.cpp PolylineComponent.cpp PointComponent.cpp Gui/MainWindow.cpp Gui/MyViewer.cpp CurveEditor.cpp ) @@ -40,7 +40,7 @@ set(app_headers Gui/MainWindow.hpp Gui/MyViewer.hpp CurveComponent.hpp - StrokeComponent.hpp + PolylineComponent.hpp CurveFactory.hpp PointComponent.hpp PointFactory.hpp diff --git a/examples/CurveEditor/Gui/MainWindow.cpp b/examples/CurveEditor/Gui/MainWindow.cpp index 80faf844ace..ba526183389 100644 --- a/examples/CurveEditor/Gui/MainWindow.cpp +++ b/examples/CurveEditor/Gui/MainWindow.cpp @@ -48,15 +48,15 @@ MainWindow::MainWindow( QWidget* parent ) : MainWindowInterface( parent ) { QDockWidget* dockWidget = new QDockWidget( "Dock", this ); QWidget* myWidget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(); - m_editCurveButton = new QPushButton( "Edit stroke" ); + m_editCurveButton = new QPushButton( "Edit polyline" ); m_button = new QPushButton( "smooth" ); m_button->setCheckable( true ); - m_hideStrokeButton = new QPushButton( "Hide initial stroke" ); - m_hideStrokeButton->setCheckable( true ); + m_hidePolylineButton = new QPushButton( "Hide initial polyline" ); + m_hidePolylineButton->setCheckable( true ); m_symetryButton = new QPushButton( "Symetry" ); m_symetryButton->setCheckable( true ); m_symetryButton->setEnabled( false ); - layout->addWidget( m_hideStrokeButton ); + layout->addWidget( m_hidePolylineButton ); layout->addWidget( m_editCurveButton ); layout->addWidget( m_button ); layout->addWidget( m_symetryButton ); @@ -110,9 +110,11 @@ void MainWindow::createConnections() { connect( getViewer(), &MyViewer::onMouseRelease, this, &MainWindow::handleMouseReleaseEvent ); connect( getViewer(), &MyViewer::onMousePress, this, &MainWindow::handleMousePressEvent ); connect( - m_editCurveButton, &QPushButton::pressed, this, &MainWindow::onEditStrokeButtonPressed ); - connect( - m_hideStrokeButton, &QPushButton::clicked, this, &MainWindow::onHideStrokeButtonClicked ); + m_editCurveButton, &QPushButton::pressed, this, &MainWindow::onEditPolylineButtonPressed ); + connect( m_hidePolylineButton, + &QPushButton::clicked, + this, + &MainWindow::onHidePolylineButtonClicked ); connect( m_button, &QPushButton::clicked, this, &MainWindow::onSmoothButtonClicked ); connect( m_symetryButton, &QPushButton::clicked, this, &MainWindow::onSymetryButtonClicked ); connect( getViewer(), @@ -130,16 +132,17 @@ void MainWindow::onSymetryButtonClicked() { m_curveEditor->setSymetry( m_symetryButton->isChecked() ); } -void MainWindow::onHideStrokeButtonClicked() { +void MainWindow::onHidePolylineButtonClicked() { auto roMgr = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager(); - auto ro = roMgr->getRenderObject( m_initialStroke->getComponents()[0]->getRenderObjects()[0] ); - if ( m_hideStrokeButton->isChecked() ) { ro->setVisible( false ); } + auto ro = + roMgr->getRenderObject( m_initialPolyline->getComponents()[0]->getRenderObjects()[0] ); + if ( m_hidePolylineButton->isChecked() ) { ro->setVisible( false ); } else ro->setVisible( true ); m_viewer->needUpdate(); } -void MainWindow::onEditStrokeButtonPressed() { +void MainWindow::onEditPolylineButtonPressed() { if ( m_polyline.empty() || m_edited ) return; m_edited = true; m_curveEditor = new CurveEditor( m_polyline, m_viewer ); diff --git a/examples/CurveEditor/Gui/MainWindow.hpp b/examples/CurveEditor/Gui/MainWindow.hpp index c2394743509..f132475207d 100644 --- a/examples/CurveEditor/Gui/MainWindow.hpp +++ b/examples/CurveEditor/Gui/MainWindow.hpp @@ -41,7 +41,7 @@ class MainWindow : public Ra::Gui::MainWindowInterface void setPolyline( Ra::Core::VectorArray polyline ) { m_polyline = polyline; } - void setInitialStroke( Ra::Engine::Scene::Entity* e ) { m_initialStroke = e; } + void setInitialPolyline( Ra::Engine::Scene::Entity* e ) { m_initialPolyline = e; } public slots: void prepareDisplay() override; @@ -52,8 +52,8 @@ class MainWindow : public Ra::Gui::MainWindowInterface void handleMouseReleaseEvent( QMouseEvent* event ); void handleMousePressEvent( QMouseEvent* event ); void handleMouseDoubleClickEvent( QMouseEvent* event ); - void onEditStrokeButtonPressed(); - void onHideStrokeButtonClicked(); + void onEditPolylineButtonPressed(); + void onHidePolylineButtonClicked(); void onSmoothButtonClicked(); void onSymetryButtonClicked(); signals: @@ -68,32 +68,14 @@ class MainWindow : public Ra::Gui::MainWindowInterface QDockWidget* m_dockWidget; QPushButton* m_button; QPushButton* m_editCurveButton; - QPushButton* m_hideStrokeButton; + QPushButton* m_hidePolylineButton; QPushButton* m_symetryButton; bool m_clicked = false; bool m_isTracking { false }; bool m_hovering { false }; bool m_edited { false }; Ra::Core::VectorArray m_polyline; - Ra::Engine::Scene::Entity* m_initialStroke; - - private: - void createConnections(); - MyViewer* m_viewer; - Ra::Gui::SelectionManager* m_selectionManager; - Ra::Gui::ItemModel* m_sceneModel; - Ra::Engine::RadiumEngine* m_engine; - QDockWidget* m_dockWidget; - QPushButton* m_button; - QPushButton* m_editCurveButton; - QPushButton* m_hideStrokeButton; - QPushButton* m_symetryButton; - bool m_clicked = false; - bool m_isTracking { false }; - bool m_hovering { false }; - bool m_edited { false }; - std::vector m_polyline; - Ra::Engine::Scene::Entity* m_initialStroke; + Ra::Engine::Scene::Entity* m_initialPolyline; CurveEditor* m_curveEditor { nullptr }; }; diff --git a/examples/CurveEditor/StrokeComponent.cpp b/examples/CurveEditor/PolylineComponent.cpp similarity index 76% rename from examples/CurveEditor/StrokeComponent.cpp rename to examples/CurveEditor/PolylineComponent.cpp index b1a00ee85a9..91e100dfbf0 100644 --- a/examples/CurveEditor/StrokeComponent.cpp +++ b/examples/CurveEditor/PolylineComponent.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -36,12 +36,13 @@ using namespace Ra::Engine::Scene; * supported by Radium */ -StrokeComponent::StrokeComponent( Ra::Engine::Scene::Entity* entity, Vector3Array strokePoints ) : - Ra::Engine::Scene::Component( "Stroke Component", entity ), m_strokePts( strokePoints ) {} +PolylineComponent::PolylineComponent( Ra::Engine::Scene::Entity* entity, + Vector3Array polylinePoints ) : + Ra::Engine::Scene::Component( "Polyline Component", entity ), m_polylinePts( polylinePoints ) {} /// This function is called when the component is properly /// setup, i.e. it has an entity. -void StrokeComponent::initialize() { +void PolylineComponent::initialize() { auto plainMaterial = make_shared( "Plain Material" ); plainMaterial->m_perVertexColor = true; @@ -50,8 +51,8 @@ void StrokeComponent::initialize() { "PolyMesh", this, RenderObjectType::Geometry, - DrawPrimitives::LineStrip( m_strokePts, - Vector4Array { m_strokePts.size(), { 0, 0, 0.7, 1 } } ), + DrawPrimitives::LineStrip( m_polylinePts, + Vector4Array { m_polylinePts.size(), { 0, 0, 0.7, 1 } } ), Ra::Engine::Rendering::RenderTechnique {} ); renderObject1->setMaterial( plainMaterial ); addRenderObject( renderObject1 ); diff --git a/examples/CurveEditor/StrokeComponent.hpp b/examples/CurveEditor/PolylineComponent.hpp similarity index 60% rename from examples/CurveEditor/StrokeComponent.hpp rename to examples/CurveEditor/PolylineComponent.hpp index 83a7726add4..8662864da69 100644 --- a/examples/CurveEditor/StrokeComponent.hpp +++ b/examples/CurveEditor/PolylineComponent.hpp @@ -5,15 +5,15 @@ #include #include -class StrokeComponent : public Ra::Engine::Scene::Component +class PolylineComponent : public Ra::Engine::Scene::Component { public: - StrokeComponent( Ra::Engine::Scene::Entity* entity, Ra::Core::Vector3Array strokePoints ); + PolylineComponent( Ra::Engine::Scene::Entity* entity, Ra::Core::Vector3Array polylinePoints ); /// This function is called when the component is properly /// setup, i.e. it has an entity. void initialize() override; - Ra::Core::Vector3Array m_strokePts; + Ra::Core::Vector3Array m_polylinePts; }; diff --git a/examples/CurveEditor/StrokeFactory.hpp b/examples/CurveEditor/PolylineFactory.hpp similarity index 58% rename from examples/CurveEditor/StrokeFactory.hpp rename to examples/CurveEditor/PolylineFactory.hpp index d3085a8aed0..b63f872383f 100644 --- a/examples/CurveEditor/StrokeFactory.hpp +++ b/examples/CurveEditor/PolylineFactory.hpp @@ -1,16 +1,17 @@ #pragma once +#include "PolylineComponent.hpp" #include #include #include #include -class StrokeFactory +class PolylineFactory { public: - static StrokeComponent* createStrokeComponent( Ra::Engine::Scene::Entity* e, - Ra::Core::Vector3Array strokePts ) { - auto c = new StrokeComponent( e, strokePts ); + static PolylineComponent* createPolylineComponent( Ra::Engine::Scene::Entity* e, + Ra::Core::Vector3Array polylinePts ) { + auto c = new PolylineComponent( e, polylinePts ); c->initialize(); return c; } @@ -19,11 +20,11 @@ class StrokeFactory const std::string& name ) { auto engine = Ra::Engine::RadiumEngine::getInstance(); Ra::Engine::Scene::Entity* e = engine->getEntityManager()->createEntity( name ); - Ra::Core::Vector3Array strokePts; + Ra::Core::Vector3Array polylinePts; for ( auto& pt : polyline ) { - strokePts.push_back( Ra::Core::Vector3( pt.x(), 0, pt.y() ) ); + polylinePts.push_back( Ra::Core::Vector3( pt.x(), 0, pt.y() ) ); } - createStrokeComponent( e, strokePts ); + createPolylineComponent( e, polylinePts ); return e; } diff --git a/examples/CurveEditor/main.cpp b/examples/CurveEditor/main.cpp index 8267c031d06..11daa8b68d6 100644 --- a/examples/CurveEditor/main.cpp +++ b/examples/CurveEditor/main.cpp @@ -15,7 +15,7 @@ #include #include "Gui/MainWindow.hpp" -#include "StrokeFactory.hpp" +#include "PolylineFactory.hpp" class MainWindowFactory : public Ra::Gui::BaseApplication::WindowFactory { @@ -31,7 +31,7 @@ int main( int argc, char* argv[] ) { app.initialize( MainWindowFactory() ); app.setContinuousUpdate( false ); - // Create polyline from stroke points + // Create polyline from polyline points Ra::Core::VectorArray polyline = { { -7.07583, -7.77924 }, { -6.4575, -7.69091 }, { -5.35333, -7.33757 }, { -3.27749, -6.54257 }, { -1.68749, -5.39423 }, { -0.671654, -4.20173 }, @@ -49,8 +49,8 @@ int main( int argc, char* argv[] ) { auto window = static_cast( app.m_mainWindow.get() ); window->setPolyline( polyline ); - auto initialStroke = StrokeFactory::createCurveEntity( polyline, "Initial stroke" ); - window->setInitialStroke( initialStroke ); + auto initialPolyline = PolylineFactory::createCurveEntity( polyline, "Initial polyline" ); + window->setInitialPolyline( initialPolyline ); app.m_mainWindow->prepareDisplay(); From 4d413d849ffb8af36573ce66b1d193587cea0fdf Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Wed, 6 Jul 2022 15:24:47 +0200 Subject: [PATCH 09/19] [tests] Rename CubicBezierSpline to PiecewiseCubicBezier --- .../BezierUtils/CubicBezierApproximation.hpp | 6 +++--- src/Core/Geometry/Curve2D.cpp | 14 ++++++------- src/Core/Geometry/Curve2D.hpp | 20 ++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp b/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp index f1d0b31efd7..cbc861a5a3a 100644 --- a/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp +++ b/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp @@ -123,7 +123,7 @@ class CubicBezierApproximation return true; } - CubicBezierSpline getSolution() const { return m_curSol; } + PiecewiseCubicBezier getSolution() const { return m_curSol; } int getNstep() const { return m_step; } @@ -136,7 +136,7 @@ class CubicBezierApproximation VectorArray m_data; std::vector m_params; std::set m_bzjunctions; - CubicBezierSpline m_curSol; + PiecewiseCubicBezier m_curSol; std::ofstream* m_logfile { nullptr }; @@ -206,7 +206,7 @@ class CubicBezierApproximation auto computePointDistanceConstraint = [nbz]( float u ) { std::map A_cstr; - auto locpar = CubicBezierSpline::getLocalParameter( u, nbz ); + auto locpar = PiecewiseCubicBezier::getLocalParameter( u, nbz ); auto bcoefs = CubicBezier::bernsteinCoefsAt( locpar.second ); int bi = locpar.first; diff --git a/src/Core/Geometry/Curve2D.cpp b/src/Core/Geometry/Curve2D.cpp index 548bc5bbade..f6fe1c46c68 100644 --- a/src/Core/Geometry/Curve2D.cpp +++ b/src/Core/Geometry/Curve2D.cpp @@ -63,7 +63,7 @@ const VectorArray CubicBezier::getCtrlPoints() const { /*--------------------------------------------------*/ -std::pair CubicBezierSpline::getLocalParameter( float u, int nbz ) { +std::pair PiecewiseCubicBezier::getLocalParameter( float u, int nbz ) { int b { (int)( std::floor( u ) ) }; float t { u - b }; @@ -74,11 +74,11 @@ std::pair CubicBezierSpline::getLocalParameter( float u, int nbz ) { return { b, t }; } -float CubicBezierSpline::getGlobalParameter( float u, int nbz ) { +float PiecewiseCubicBezier::getGlobalParameter( float u, int nbz ) { return u * nbz; } -VectorArray CubicBezierSpline::getCtrlPoints() const { +VectorArray PiecewiseCubicBezier::getCtrlPoints() const { VectorArray cp; cp.reserve( 3 * getNbBezier() + 1 ); for ( unsigned int i = 0; i < m_spline.size(); i++ ) { @@ -91,7 +91,7 @@ VectorArray CubicBezierSpline::getCtrlPoints() const { return cp; } -void CubicBezierSpline::setCtrlPoints( const VectorArray& cpoints ) { +void PiecewiseCubicBezier::setCtrlPoints( const VectorArray& cpoints ) { int nbz { (int)( ( cpoints.size() - 1 ) / 3 ) }; m_spline.clear(); m_spline.reserve( nbz ); @@ -101,7 +101,7 @@ void CubicBezierSpline::setCtrlPoints( const VectorArray& cpoin } } -std::vector CubicBezierSpline::getUniformParameterization( int nbSamples ) const { +std::vector PiecewiseCubicBezier::getUniformParameterization( int nbSamples ) const { std::vector params; params.resize( nbSamples ); @@ -120,8 +120,8 @@ std::vector CubicBezierSpline::getUniformParameterization( int nbSamples return params; } -std::vector CubicBezierSpline::getArcLengthParameterization( float resolution, - float epsilon ) const { +std::vector PiecewiseCubicBezier::getArcLengthParameterization( float resolution, + float epsilon ) const { std::vector params; int nbz = getNbBezier(); diff --git a/src/Core/Geometry/Curve2D.hpp b/src/Core/Geometry/Curve2D.hpp index b41c310d27f..01523bc13d8 100644 --- a/src/Core/Geometry/Curve2D.hpp +++ b/src/Core/Geometry/Curve2D.hpp @@ -125,19 +125,21 @@ class SplineCurve : public Curve2D Core::VectorArray m_points; }; -class CubicBezierSpline : public Curve2D +class PiecewiseCubicBezier : public Curve2D { public: - CubicBezierSpline() {} + PiecewiseCubicBezier() {} /** * @brief Spline of cubic Bézier segments. Construction guarantees C0 continuity. * ie extremities of successive segments share the same coordinates * @param vector of control points, should be 3*n+1 points where n is the number of segments */ - CubicBezierSpline( const Core::VectorArray& cpoints ) { setCtrlPoints( cpoints ); } + PiecewiseCubicBezier( const Core::VectorArray& cpoints ) { setCtrlPoints( cpoints ); } - CubicBezierSpline( const CubicBezierSpline& other ) { setCtrlPoints( other.getCtrlPoints() ); } + PiecewiseCubicBezier( const PiecewiseCubicBezier& other ) { + setCtrlPoints( other.getCtrlPoints() ); + } int getNbBezier() const { return m_spline.size(); } @@ -323,7 +325,7 @@ Curve2D::Vector QuadraSpline::fdf( Scalar u, Vector& grad ) const { /*--------------------------------------------------*/ -Curve2D::Vector CubicBezierSpline::f( float u ) const { +Curve2D::Vector PiecewiseCubicBezier::f( float u ) const { std::pair locpar { getLocalParameter( u ) }; if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { @@ -335,7 +337,7 @@ Curve2D::Vector CubicBezierSpline::f( float u ) const { return m_spline[locpar.first].f( locpar.second ); } -VectorArray CubicBezierSpline::f( std::vector params ) const { +VectorArray PiecewiseCubicBezier::f( std::vector params ) const { VectorArray controlPoints; for ( int i = 0; i < (int)params.size(); ++i ) { @@ -345,7 +347,7 @@ VectorArray CubicBezierSpline::f( std::vector params ) c return controlPoints; } -Curve2D::Vector CubicBezierSpline::df( float u ) const { +Curve2D::Vector PiecewiseCubicBezier::df( float u ) const { std::pair locpar { getLocalParameter( u ) }; if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { @@ -357,7 +359,7 @@ Curve2D::Vector CubicBezierSpline::df( float u ) const { return m_spline[locpar.first].df( locpar.second ); } -Curve2D::Vector CubicBezierSpline::fdf( Scalar t, Vector& grad ) const { +Curve2D::Vector PiecewiseCubicBezier::fdf( Scalar t, Vector& grad ) const { std::pair locpar { getLocalParameter( t ) }; if ( locpar.first < 0 || locpar.first > getNbBezier() - 1 ) { @@ -369,7 +371,7 @@ Curve2D::Vector CubicBezierSpline::fdf( Scalar t, Vector& grad ) const { return m_spline[locpar.first].fdf( locpar.second, grad ); } -void CubicBezierSpline::addPoint( const Curve2D::Vector p ) { +void PiecewiseCubicBezier::addPoint( const Curve2D::Vector p ) { if ( m_spline[m_spline.size() - 1].getCtrlPoints().size() < 4 ) m_spline[m_spline.size() - 1].addPoint( p ); } From ae4517f8563ff12a6e31c81fbdc739637411a9bf Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Thu, 7 Jul 2022 11:25:35 +0200 Subject: [PATCH 10/19] [tests] Remove useless header --- examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp b/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp index cbc861a5a3a..f214245147f 100644 --- a/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp +++ b/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp @@ -1,9 +1,7 @@ #pragma once -#include -#include -// #include "CubicBezier.hpp" #include "LeastSquareSystem.hpp" +#include #include #include From 9d76726c341eb3edc4573e1ebf11010f2eb19ebe Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Thu, 7 Jul 2022 11:49:26 +0200 Subject: [PATCH 11/19] [tests] Refactor with namespaces for curve editor --- examples/CurveEditor/CurveEditor.cpp | 152 +++++++++++++-------------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index 79a01687f21..94382e2d962 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -8,12 +8,17 @@ #include "BezierUtils/CubicBezierApproximation.hpp" #include "CurveEditor.hpp" -CurveEditor::CurveEditor( - const Ra::Core::VectorArray& polyline, - MyViewer* viewer ) : - Ra::Engine::Scene::Entity( "Curve Editor" ), m_viewer( viewer ) { - m_entityMgr = Ra::Engine::RadiumEngine::getInstance()->getEntityManager(); - m_roMgr = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager(); +using namespace Ra::Core; +using namespace Ra::Core::Geometry; +using namespace Ra::Core::Utils; +using namespace Ra::Engine; +using namespace Ra::Engine::Scene; + +CurveEditor::CurveEditor( const VectorArray& polyline, MyViewer* viewer ) : + Entity( "Curve Editor" ), m_viewer( viewer ) { + + m_entityMgr = RadiumEngine::getInstance()->getEntityManager(); + m_roMgr = RadiumEngine::getInstance()->getRenderObjectManager(); // Approximate polyline with bezier CubicBezierApproximation approximator; @@ -24,14 +29,13 @@ CurveEditor::CurveEditor( // Create and render solution entities CurveFactory factory; - std::vector allCtrlPts; + std::vector allCtrlPts; m_viewer->makeCurrent(); for ( unsigned int i = 0; i < solutionPts.size() - 3; i += 3 ) { - Ra::Core::Vector3Array ctrlPts; - ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i].x(), 0, solutionPts[i].y() ) ); - ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i + 1].x(), 0, solutionPts[i + 1].y() ) ); - ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i + 2].x(), 0, solutionPts[i + 2].y() ) ); - ctrlPts.push_back( Ra::Core::Vector3( solutionPts[i + 3].x(), 0, solutionPts[i + 3].y() ) ); + Vector3Array ctrlPts { Vector3( solutionPts[i].x(), 0, solutionPts[i].y() ), + Vector3( solutionPts[i + 1].x(), 0, solutionPts[i + 1].y() ), + Vector3( solutionPts[i + 2].x(), 0, solutionPts[i + 2].y() ), + Vector3( solutionPts[i + 3].x(), 0, solutionPts[i + 3].y() ) }; std::string name = "Curve_" + std::to_string( i / 3 ); auto e = factory.createCurveComponent( this, ctrlPts, name ); m_curveEntities.push_back( e ); @@ -39,9 +43,10 @@ CurveEditor::CurveEditor( } std::string namePt = "CtrlPt_" + std::to_string( 0 ); - auto e = PointFactory::createPointComponent( - this, allCtrlPts[0][0], { 0 }, namePt, Ra::Core::Utils::Color::Blue() ); + auto e = + PointFactory::createPointComponent( this, allCtrlPts[0][0], { 0 }, namePt, Color::Blue() ); m_pointEntities.push_back( e ); + int nameIndex = 1; for ( unsigned int i = 0; i < allCtrlPts.size(); i++ ) { for ( unsigned int j = 1; j < allCtrlPts[i].size() - 1; j++ ) { @@ -53,10 +58,10 @@ CurveEditor::CurveEditor( namePt = "CtrlPt_" + std::to_string( nameIndex ); if ( i == allCtrlPts.size() - 1 ) e = PointFactory::createPointComponent( - this, allCtrlPts[i][3], { i }, namePt, Ra::Core::Utils::Color::Blue() ); + this, allCtrlPts[i][3], { i }, namePt, Color::Blue() ); else e = PointFactory::createPointComponent( - this, allCtrlPts[i][3], { i, i + 1 }, namePt, Ra::Core::Utils::Color::Blue() ); + this, allCtrlPts[i][3], { i, i + 1 }, namePt, Color::Blue() ); m_pointEntities.push_back( e ); nameIndex++; } @@ -69,8 +74,8 @@ CurveEditor::~CurveEditor() {} unsigned int CurveEditor::getPointIndex( unsigned int curveIdSize, unsigned int currentPtIndex, - const Ra::Core::Vector3& firstCtrlPt, - const Ra::Core::Vector3& currentPt ) { + const Vector3& firstCtrlPt, + const Vector3& currentPt ) { unsigned int pointIndex; if ( curveIdSize > 1 || currentPtIndex == m_pointEntities.size() - 1 ) { pointIndex = ( firstCtrlPt == currentPt ) ? 0 : 3; @@ -82,7 +87,7 @@ unsigned int CurveEditor::getPointIndex( unsigned int curveIdSize, void CurveEditor::updateCurve( unsigned int curveId, unsigned int curveIdSize, PointComponent* pointComponent, - const Ra::Core::Vector3& oldPoint, + const Vector3& oldPoint, unsigned int currentPoint ) { auto component = m_curveEntities[curveId]; auto ctrlPts = component->m_ctrlPts; @@ -99,13 +104,13 @@ void CurveEditor::updateCurve( unsigned int curveId, m_curveEntities[curveId] = e; } -void CurveEditor::refreshPoint( unsigned int pointIndex, const Ra::Core::Vector3& newPoint ) { +void CurveEditor::refreshPoint( unsigned int pointIndex, const Vector3& newPoint ) { auto pointName = m_pointEntities[pointIndex]->getName(); auto pointComponent = m_pointEntities[pointIndex]; auto pointColor = pointComponent->m_color; auto pointCurve = pointComponent->m_curveId; - Ra::Core::Vector3 point; - if ( newPoint == Ra::Core::Vector3::Zero() ) + Vector3 point; + if ( newPoint == Vector3::Zero() ) point = pointComponent->m_point; else point = newPoint; @@ -132,9 +137,9 @@ void CurveEditor::updateCurves( bool onRelease ) { } for ( unsigned int i = 0; i < m_tangentPoints.size(); i++ ) { - auto tangentPtComponent = m_pointEntities[m_tangentPoints[i]]; - Ra::Core::Vector3 tangentPt = tangentPtComponent->m_point; - auto symCurveId = tangentPtComponent->m_curveId[0]; + auto tangentPtComponent = m_pointEntities[m_tangentPoints[i]]; + Vector3 tangentPt = tangentPtComponent->m_point; + auto symCurveId = tangentPtComponent->m_curveId[0]; updateCurve( symCurveId, 1, tangentPtComponent, tangentPt, m_tangentPoints[i] ); } @@ -152,11 +157,11 @@ void CurveEditor::updateCurves( bool onRelease ) { m_viewer->needUpdate(); } -Ra::Core::Transform CurveEditor::computePointTransform( PointComponent* pointCmp, - const Ra::Core::Vector3& midPoint, - const Ra::Core::Vector3& worldPos ) { - auto transform = Ra::Core::Transform::Identity(); - Ra::Core::Vector3 diff = ( midPoint - worldPos ); +Transform CurveEditor::computePointTransform( PointComponent* pointCmp, + const Vector3& midPoint, + const Vector3& worldPos ) { + auto transform = Transform::Identity(); + Vector3 diff = ( midPoint - worldPos ); if ( m_symetry ) { transform.translate( ( midPoint + diff ) - pointCmp->m_defaultPoint ); } else { diff.normalize(); @@ -166,8 +171,7 @@ Ra::Core::Transform CurveEditor::computePointTransform( PointComponent* pointCmp return transform; } -inline float CurveEditor::distanceSquared( const Ra::Core::Vector3f& pointA, - const Ra::Core::Vector3f& pointB ) { +inline float CurveEditor::distanceSquared( const Vector3f& pointA, const Vector3f& pointB ) { float dx = pointA.x() - pointB.x(); float dy = pointA.y() - pointB.y(); float dz = pointA.z() - pointB.z(); @@ -177,21 +181,20 @@ inline float CurveEditor::distanceSquared( const Ra::Core::Vector3f& pointA, // De Casteljau algorithm void CurveEditor::subdivisionBezier( int vertexIndex, unsigned int curveIndex, - const Ra::Core::Vector3Array& ctrlPts ) { - auto bezier = - Ra::Core::Geometry::CubicBezier( Ra::Core::Vector2( ctrlPts[0].x(), ctrlPts[0].z() ), - Ra::Core::Vector2( ctrlPts[1].x(), ctrlPts[1].z() ), - Ra::Core::Vector2( ctrlPts[2].x(), ctrlPts[2].z() ), - Ra::Core::Vector2( ctrlPts[3].x(), ctrlPts[3].z() ) ); - float u = float( vertexIndex ) / 100.f; - - Ra::Core::Vector2 fu = bezier.f( u ); - auto clickedPoint = Ra::Core::Vector3( fu.x(), 0, fu.y() ); - Ra::Core::Vector3 firstPoint = Ra::Core::Math::linearInterpolate( ctrlPts[0], ctrlPts[1], u ); - Ra::Core::Vector3 sndPoint = Ra::Core::Math::linearInterpolate( ctrlPts[1], ctrlPts[2], u ); - Ra::Core::Vector3 thirdPoint = Ra::Core::Math::linearInterpolate( ctrlPts[2], ctrlPts[3], u ); - Ra::Core::Vector3 fourthPoint = Ra::Core::Math::linearInterpolate( firstPoint, sndPoint, u ); - Ra::Core::Vector3 fifthPoint = Ra::Core::Math::linearInterpolate( sndPoint, thirdPoint, u ); + const Vector3Array& ctrlPts ) { + auto bezier = Geometry::CubicBezier( Vector2( ctrlPts[0].x(), ctrlPts[0].z() ), + Vector2( ctrlPts[1].x(), ctrlPts[1].z() ), + Vector2( ctrlPts[2].x(), ctrlPts[2].z() ), + Vector2( ctrlPts[3].x(), ctrlPts[3].z() ) ); + float u = float( vertexIndex ) / 100.f; + + Vector2 fu = bezier.f( u ); + auto clickedPoint = Vector3( fu.x(), 0, fu.y() ); + Vector3 firstPoint = Math::linearInterpolate( ctrlPts[0], ctrlPts[1], u ); + Vector3 sndPoint = Math::linearInterpolate( ctrlPts[1], ctrlPts[2], u ); + Vector3 thirdPoint = Math::linearInterpolate( ctrlPts[2], ctrlPts[3], u ); + Vector3 fourthPoint = Math::linearInterpolate( firstPoint, sndPoint, u ); + Vector3 fifthPoint = Math::linearInterpolate( sndPoint, thirdPoint, u ); auto newCtrlPts = ctrlPts; newCtrlPts[1] = firstPoint; @@ -206,11 +209,8 @@ void CurveEditor::subdivisionBezier( int vertexIndex, auto firstInsertionIdx = curveIndex * 3 + 2; std::string namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); - auto ptE = PointFactory::createPointComponent( this, - clickedPoint, - { curveIndex, curveIndex + 1 }, - namePt, - Ra::Core::Utils::Color::Blue() ); + auto ptE = PointFactory::createPointComponent( + this, clickedPoint, { curveIndex, curveIndex + 1 }, namePt, Color::Blue() ); m_pointEntities.insert( m_pointEntities.begin() + firstInsertionIdx, ptE ); namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); ptE = PointFactory::createPointComponent( this, fourthPoint, { curveIndex }, namePt ); @@ -220,7 +220,7 @@ void CurveEditor::subdivisionBezier( int vertexIndex, ptE = PointFactory::createPointComponent( this, fifthPoint, { curveIndex + 1 }, namePt ); m_pointEntities.insert( m_pointEntities.begin() + ( ( curveIndex + 1 ) * 3 + 1 ), ptE ); - Ra::Core::Vector3Array newCtrlPts1; + Vector3Array newCtrlPts1; newCtrlPts1.push_back( clickedPoint ); newCtrlPts1.push_back( fifthPoint ); newCtrlPts1.push_back( thirdPoint ); @@ -239,17 +239,17 @@ void CurveEditor::subdivisionBezier( int vertexIndex, } } -void CurveEditor::addPointAtEnd( const Ra::Core::Vector3& worldPos ) { - Ra::Core::Vector3Array ctrlPts; - auto lastIndex = m_pointEntities.size() - 1; - auto beforeLast = m_pointEntities[lastIndex - 1]; - auto last = m_pointEntities[lastIndex]; - Ra::Core::Vector3 diff = ( last->m_point - beforeLast->m_point ); +void CurveEditor::addPointAtEnd( const Vector3& worldPos ) { + Vector3Array ctrlPts; + auto lastIndex = m_pointEntities.size() - 1; + auto beforeLast = m_pointEntities[lastIndex - 1]; + auto last = m_pointEntities[lastIndex]; + Vector3 diff = ( last->m_point - beforeLast->m_point ); diff.normalize(); - Ra::Core::Vector3 secondPt = ( last->m_point + diff ); - Ra::Core::Vector3 thirdDiff = ( last->m_point - worldPos ); + Vector3 secondPt = ( last->m_point + diff ); + Vector3 thirdDiff = ( last->m_point - worldPos ); thirdDiff.normalize(); - Ra::Core::Vector3 thirdPt = ( worldPos - diff ); + Vector3 thirdPt = ( worldPos - diff ); ctrlPts.push_back( last->m_point ); ctrlPts.push_back( secondPt ); ctrlPts.push_back( thirdPt ); @@ -270,23 +270,22 @@ void CurveEditor::addPointAtEnd( const Ra::Core::Vector3& worldPos ) { m_pointEntities.push_back( eb ); namePt = "CtrlPt_" + std::to_string( pointIndex + 2 ); ptC = PointFactory::createPointComponent( - this, ctrlPts[3], { ( pointIndex / 3 ) }, namePt, Ra::Core::Utils::Color::Blue() ); + this, ctrlPts[3], { ( pointIndex / 3 ) }, namePt, Color::Blue() ); m_pointEntities.push_back( ptC ); } -void CurveEditor::addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX, int mouseY ) { +void CurveEditor::addPointInCurve( const Vector3& worldPos, int mouseX, int mouseY ) { auto camera = m_viewer->getCameraManipulator()->getCamera(); auto radius = int( m_viewer->getRenderer()->getBrushRadius() ); bool found = false; - Ra::Engine::Rendering::Renderer::PickingResult pres; + Rendering::Renderer::PickingResult pres; for ( int i = mouseX - radius; i < mouseX + radius && !found; i++ ) { for ( int j = mouseY - radius; j < mouseY + radius; j++ ) { // m_viewer->cursor().setPos(m_viewer->mapToGlobal(QPoint(i, j))); - Ra::Engine::Rendering::Renderer::PickingQuery query { - Ra::Core::Vector2( i, m_viewer->height() - j ), - Ra::Engine::Rendering::Renderer::SELECTION, - Ra::Engine::Rendering::Renderer::RO }; - Ra::Engine::Data::ViewingParameters renderData { + Rendering::Renderer::PickingQuery query { Vector2( i, m_viewer->height() - j ), + Rendering::Renderer::SELECTION, + Rendering::Renderer::RO }; + Data::ViewingParameters renderData { camera->getViewMatrix(), camera->getProjMatrix(), 0 }; pres = m_viewer->getRenderer()->doPickingNow( query, renderData ); @@ -306,7 +305,7 @@ void CurveEditor::addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX if ( curveIndex < 0 ) continue; auto meshPtr = ro->getMesh().get(); - auto mesh = dynamic_cast( meshPtr ); + auto mesh = dynamic_cast( meshPtr ); auto curveCmp = static_cast( ro->getComponent() ); auto ctrlPts = curveCmp->m_ctrlPts; @@ -331,7 +330,7 @@ void CurveEditor::addPointInCurve( const Ra::Core::Vector3& worldPos, int mouseX } } -bool CurveEditor::processHover( std::shared_ptr ro ) { +bool CurveEditor::processHover( std::shared_ptr ro ) { auto e = ro->getComponent(); // if not a control point -> do nothing @@ -343,8 +342,7 @@ bool CurveEditor::processHover( std::shared_ptrgetMaterial()->getParameters().addParameter( "material.color", - Ra::Core::Utils::Color::Red() ); + ro->getMaterial()->getParameters().addParameter( "material.color", Color::Red() ); m_selectedRo = ro; return true; } @@ -357,15 +355,15 @@ void CurveEditor::processUnhovering() { m_currentPoint = -1; } -void CurveEditor::processPicking( const Ra::Core::Vector3& worldPos ) { +void CurveEditor::processPicking( const Vector3& worldPos ) { if ( m_currentPoint < 0 ) return; auto pointComponent = static_cast( m_selectedRo->getComponent() ); auto point = pointComponent->m_point; - auto transformTranslate = Ra::Core::Transform::Identity(); + auto transformTranslate = Transform::Identity(); transformTranslate.translate( worldPos - point ); - auto transform = Ra::Core::Transform::Identity(); + auto transform = Transform::Identity(); transform = m_selectedRo->getLocalTransform() * transformTranslate; m_selectedRo->setLocalTransform( transform ); From 7c975674b3e8d73e1b888f345f6d5377d5cbe6e4 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Thu, 7 Jul 2022 12:31:38 +0200 Subject: [PATCH 12/19] [tests] Refactor point/curve component creation --- examples/CurveEditor/CurveEditor.cpp | 81 +++++++++++++-------------- examples/CurveEditor/CurveFactory.hpp | 7 +++ examples/CurveEditor/PointFactory.hpp | 13 +++++ 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index 94382e2d962..bfb7b1d16ca 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -36,32 +36,27 @@ CurveEditor::CurveEditor( const VectorArray& polyline, MyViewer Vector3( solutionPts[i + 1].x(), 0, solutionPts[i + 1].y() ), Vector3( solutionPts[i + 2].x(), 0, solutionPts[i + 2].y() ), Vector3( solutionPts[i + 3].x(), 0, solutionPts[i + 3].y() ) }; - std::string name = "Curve_" + std::to_string( i / 3 ); - auto e = factory.createCurveComponent( this, ctrlPts, name ); + auto e = factory.createCurveComponent( this, ctrlPts, i / 3 ); m_curveEntities.push_back( e ); allCtrlPts.push_back( ctrlPts ); } - std::string namePt = "CtrlPt_" + std::to_string( 0 ); - auto e = - PointFactory::createPointComponent( this, allCtrlPts[0][0], { 0 }, namePt, Color::Blue() ); + auto e = PointFactory::createPointComponent( this, allCtrlPts[0][0], { 0 }, 0, Color::Blue() ); m_pointEntities.push_back( e ); int nameIndex = 1; for ( unsigned int i = 0; i < allCtrlPts.size(); i++ ) { for ( unsigned int j = 1; j < allCtrlPts[i].size() - 1; j++ ) { - namePt = "CtrlPt_" + std::to_string( nameIndex ); - e = PointFactory::createPointComponent( this, allCtrlPts[i][j], { i }, namePt ); + e = PointFactory::createPointComponent( this, allCtrlPts[i][j], { i }, nameIndex ); m_pointEntities.push_back( e ); nameIndex++; } - namePt = "CtrlPt_" + std::to_string( nameIndex ); if ( i == allCtrlPts.size() - 1 ) e = PointFactory::createPointComponent( - this, allCtrlPts[i][3], { i }, namePt, Color::Blue() ); + this, allCtrlPts[i][3], { i }, nameIndex, Color::Blue() ); else e = PointFactory::createPointComponent( - this, allCtrlPts[i][3], { i, i + 1 }, namePt, Color::Blue() ); + this, allCtrlPts[i][3], { i, i + 1 }, nameIndex, Color::Blue() ); m_pointEntities.push_back( e ); nameIndex++; } @@ -178,18 +173,19 @@ inline float CurveEditor::distanceSquared( const Vector3f& pointA, const Vector3 return dx * dx + dy * dy + dz * dz; } -// De Casteljau algorithm void CurveEditor::subdivisionBezier( int vertexIndex, unsigned int curveIndex, const Vector3Array& ctrlPts ) { - auto bezier = Geometry::CubicBezier( Vector2( ctrlPts[0].x(), ctrlPts[0].z() ), + + auto bezier = Geometry::CubicBezier( Vector2( ctrlPts[0].x(), ctrlPts[0].z() ), Vector2( ctrlPts[1].x(), ctrlPts[1].z() ), Vector2( ctrlPts[2].x(), ctrlPts[2].z() ), Vector2( ctrlPts[3].x(), ctrlPts[3].z() ) ); - float u = float( vertexIndex ) / 100.f; + float u = float( vertexIndex ) / 100.f; + Vector2 fu = bezier.f( u ); + auto clickedPoint = Vector3( fu.x(), 0, fu.y() ); - Vector2 fu = bezier.f( u ); - auto clickedPoint = Vector3( fu.x(), 0, fu.y() ); + // De Casteljau Vector3 firstPoint = Math::linearInterpolate( ctrlPts[0], ctrlPts[1], u ); Vector3 sndPoint = Math::linearInterpolate( ctrlPts[1], ctrlPts[2], u ); Vector3 thirdPoint = Math::linearInterpolate( ctrlPts[2], ctrlPts[3], u ); @@ -200,7 +196,8 @@ void CurveEditor::subdivisionBezier( int vertexIndex, newCtrlPts[1] = firstPoint; newCtrlPts[2] = fourthPoint; newCtrlPts[3] = clickedPoint; - auto nameCurve = m_curveEntities[curveIndex]->getName(); + + auto nameCurve = m_curveEntities[curveIndex]->getName(); removeComponent( nameCurve ); m_curveEntities[curveIndex] = CurveFactory::createCurveComponent( this, newCtrlPts, nameCurve ); @@ -208,16 +205,15 @@ void CurveEditor::subdivisionBezier( int vertexIndex, refreshPoint( curveIndex * 3 + 2, thirdPoint ); auto firstInsertionIdx = curveIndex * 3 + 2; - std::string namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); auto ptE = PointFactory::createPointComponent( - this, clickedPoint, { curveIndex, curveIndex + 1 }, namePt, Color::Blue() ); + this, clickedPoint, { curveIndex, curveIndex + 1 }, m_pointEntities.size(), Color::Blue() ); m_pointEntities.insert( m_pointEntities.begin() + firstInsertionIdx, ptE ); - namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); - ptE = PointFactory::createPointComponent( this, fourthPoint, { curveIndex }, namePt ); + ptE = PointFactory::createPointComponent( + this, fourthPoint, { curveIndex }, m_pointEntities.size() ); m_pointEntities.insert( m_pointEntities.begin() + firstInsertionIdx, ptE ); - namePt = "CtrlPt_" + std::to_string( m_pointEntities.size() ); - ptE = PointFactory::createPointComponent( this, fifthPoint, { curveIndex + 1 }, namePt ); + ptE = PointFactory::createPointComponent( + this, fifthPoint, { curveIndex + 1 }, m_pointEntities.size() ); m_pointEntities.insert( m_pointEntities.begin() + ( ( curveIndex + 1 ) * 3 + 1 ), ptE ); Vector3Array newCtrlPts1; @@ -226,8 +222,7 @@ void CurveEditor::subdivisionBezier( int vertexIndex, newCtrlPts1.push_back( thirdPoint ); newCtrlPts1.push_back( ctrlPts[3] ); - nameCurve = "Curve_" + std::to_string( m_curveEntities.size() ); - auto curveE = CurveFactory::createCurveComponent( this, newCtrlPts1, nameCurve ); + auto curveE = CurveFactory::createCurveComponent( this, newCtrlPts1, m_curveEntities.size() ); m_curveEntities.insert( m_curveEntities.begin() + curveIndex + 1, curveE ); @@ -240,37 +235,32 @@ void CurveEditor::subdivisionBezier( int vertexIndex, } void CurveEditor::addPointAtEnd( const Vector3& worldPos ) { - Vector3Array ctrlPts; auto lastIndex = m_pointEntities.size() - 1; auto beforeLast = m_pointEntities[lastIndex - 1]; auto last = m_pointEntities[lastIndex]; Vector3 diff = ( last->m_point - beforeLast->m_point ); diff.normalize(); + Vector3 secondPt = ( last->m_point + diff ); Vector3 thirdDiff = ( last->m_point - worldPos ); thirdDiff.normalize(); Vector3 thirdPt = ( worldPos - diff ); - ctrlPts.push_back( last->m_point ); - ctrlPts.push_back( secondPt ); - ctrlPts.push_back( thirdPt ); - ctrlPts.push_back( worldPos ); - std::string name = "Curve_" + std::to_string( m_curveEntities.size() ); - auto e = CurveFactory::createCurveComponent( this, ctrlPts, name ); + + Vector3Array ctrlPts { last->m_point, secondPt, thirdPt, worldPos }; + + auto e = CurveFactory::createCurveComponent( this, ctrlPts, m_curveEntities.size() ); m_curveEntities.push_back( e ); unsigned int pointIndex = lastIndex + 1; last->m_curveId.push_back( ( pointIndex / 3 ) ); - std::string namePt = "CtrlPt_" + std::to_string( pointIndex ); auto ptC = - PointFactory::createPointComponent( this, ctrlPts[1], { ( pointIndex / 3 ) }, namePt ); + PointFactory::createPointComponent( this, ctrlPts[1], { ( pointIndex / 3 ) }, pointIndex ); m_pointEntities.push_back( ptC ); - namePt = "CtrlPt_" + std::to_string( pointIndex + 1 ); - auto eb = - PointFactory::createPointComponent( this, ctrlPts[2], { ( pointIndex / 3 ) }, namePt ); + auto eb = PointFactory::createPointComponent( + this, ctrlPts[2], { ( pointIndex / 3 ) }, pointIndex + 1 ); m_pointEntities.push_back( eb ); - namePt = "CtrlPt_" + std::to_string( pointIndex + 2 ); - ptC = PointFactory::createPointComponent( - this, ctrlPts[3], { ( pointIndex / 3 ) }, namePt, Color::Blue() ); + ptC = PointFactory::createPointComponent( + this, ctrlPts[3], { ( pointIndex / 3 ) }, pointIndex + 2, Color::Blue() ); m_pointEntities.push_back( ptC ); } @@ -279,16 +269,17 @@ void CurveEditor::addPointInCurve( const Vector3& worldPos, int mouseX, int mous auto radius = int( m_viewer->getRenderer()->getBrushRadius() ); bool found = false; Rendering::Renderer::PickingResult pres; + for ( int i = mouseX - radius; i < mouseX + radius && !found; i++ ) { for ( int j = mouseY - radius; j < mouseY + radius; j++ ) { - // m_viewer->cursor().setPos(m_viewer->mapToGlobal(QPoint(i, j))); + Rendering::Renderer::PickingQuery query { Vector2( i, m_viewer->height() - j ), Rendering::Renderer::SELECTION, Rendering::Renderer::RO }; Data::ViewingParameters renderData { camera->getViewMatrix(), camera->getProjMatrix(), 0 }; - pres = m_viewer->getRenderer()->doPickingNow( query, renderData ); + if ( pres.getRoIdx().isValid() && m_roMgr->exists( pres.getRoIdx() ) ) { auto ro = m_roMgr->getRenderObject( pres.getRoIdx() ); if ( ro->getMesh()->getNumVertices() == 2 ) continue; @@ -322,9 +313,9 @@ void CurveEditor::addPointInCurve( const Vector3& worldPos, int mouseX, int mous } subdivisionBezier( vIndex, curveIndex, ctrlPts ); + found = true; + break; } - found = true; - break; } } } @@ -378,6 +369,7 @@ void CurveEditor::processPicking( const Vector3& worldPos ) { computePointTransform( pointComponent, pointMid->m_point, worldPos ); auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); ro->setLocalTransform( symTransform ); + if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint + 2 ); } } else if ( m_currentPoint % 3 == 1 ) { @@ -387,6 +379,7 @@ void CurveEditor::processPicking( const Vector3& worldPos ) { computePointTransform( pointComponent, pointMid->m_point, worldPos ); auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); ro->setLocalTransform( symTransform ); + if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint - 2 ); } } else if ( m_currentPoint % 3 == 0 ) { @@ -394,10 +387,12 @@ void CurveEditor::processPicking( const Vector3& worldPos ) { m_pointEntities[m_currentPoint - 1]->getRenderObjects()[0] ); auto leftTransform = leftRo->getTransform() * transformTranslate; leftRo->setLocalTransform( leftTransform ); + auto rightRo = m_roMgr->getRenderObject( m_pointEntities[m_currentPoint + 1]->getRenderObjects()[0] ); auto rightTransform = rightRo->getTransform() * transformTranslate; rightRo->setLocalTransform( rightTransform ); + if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint - 1 ); m_tangentPoints.push_back( m_currentPoint + 1 ); diff --git a/examples/CurveEditor/CurveFactory.hpp b/examples/CurveEditor/CurveFactory.hpp index 7aa26e2edd0..8f2ec3ccff7 100644 --- a/examples/CurveEditor/CurveFactory.hpp +++ b/examples/CurveEditor/CurveFactory.hpp @@ -7,6 +7,13 @@ class CurveFactory { public: + static CurveComponent* + createCurveComponent( Ra::Engine::Scene::Entity* e, Ra::Core::Vector3Array ctrlPts, int id ) { + std::string name = "Curve_" + std::to_string( id ); + auto c = new CurveComponent( e, ctrlPts, name ); + c->initialize(); + return c; + } static CurveComponent* createCurveComponent( Ra::Engine::Scene::Entity* e, Ra::Core::Vector3Array ctrlPts, const std::string& name ) { diff --git a/examples/CurveEditor/PointFactory.hpp b/examples/CurveEditor/PointFactory.hpp index 27c3d436f12..8ee67ec2db7 100644 --- a/examples/CurveEditor/PointFactory.hpp +++ b/examples/CurveEditor/PointFactory.hpp @@ -8,6 +8,18 @@ class PointFactory { public: + static PointComponent* + createPointComponent( Ra::Engine::Scene::Entity* e, + Ra::Core::Vector3 point, + const std::vector& curveId, + int id, + Ra::Core::Utils::Color color = Ra::Core::Utils::Color::Black() ) { + std::string name = "CtrlPt_" + std::to_string( id ); + auto c = new PointComponent( e, point, curveId, name, color ); + c->initialize(); + return c; + } + static PointComponent* createPointComponent( Ra::Engine::Scene::Entity* e, Ra::Core::Vector3 point, @@ -18,6 +30,7 @@ class PointFactory c->initialize(); return c; } + static Ra::Engine::Scene::Entity* createPointEntity( Ra::Core::Vector3 point, const std::string& name, From 70d0a20444e23b552ce4bd8272bbb2784dd445f5 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Thu, 7 Jul 2022 14:57:35 +0200 Subject: [PATCH 13/19] [tests] small refactor --- examples/CurveEditor/CurveEditor.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index bfb7b1d16ca..4e09719a392 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -208,6 +208,7 @@ void CurveEditor::subdivisionBezier( int vertexIndex, auto ptE = PointFactory::createPointComponent( this, clickedPoint, { curveIndex, curveIndex + 1 }, m_pointEntities.size(), Color::Blue() ); m_pointEntities.insert( m_pointEntities.begin() + firstInsertionIdx, ptE ); + ptE = PointFactory::createPointComponent( this, fourthPoint, { curveIndex }, m_pointEntities.size() ); m_pointEntities.insert( m_pointEntities.begin() + firstInsertionIdx, ptE ); @@ -223,7 +224,6 @@ void CurveEditor::subdivisionBezier( int vertexIndex, newCtrlPts1.push_back( ctrlPts[3] ); auto curveE = CurveFactory::createCurveComponent( this, newCtrlPts1, m_curveEntities.size() ); - m_curveEntities.insert( m_curveEntities.begin() + curveIndex + 1, curveE ); for ( unsigned int i = ( ( curveIndex + 1 ) * 3 + 2 ); i < m_pointEntities.size(); i++ ) { @@ -253,12 +253,15 @@ void CurveEditor::addPointAtEnd( const Vector3& worldPos ) { unsigned int pointIndex = lastIndex + 1; last->m_curveId.push_back( ( pointIndex / 3 ) ); + auto ptC = PointFactory::createPointComponent( this, ctrlPts[1], { ( pointIndex / 3 ) }, pointIndex ); m_pointEntities.push_back( ptC ); - auto eb = PointFactory::createPointComponent( + + ptC = PointFactory::createPointComponent( this, ctrlPts[2], { ( pointIndex / 3 ) }, pointIndex + 1 ); - m_pointEntities.push_back( eb ); + m_pointEntities.push_back( ptC ); + ptC = PointFactory::createPointComponent( this, ctrlPts[3], { ( pointIndex / 3 ) }, pointIndex + 2, Color::Blue() ); m_pointEntities.push_back( ptC ); From c1b6d3a031118989aa7038934165182c351d0fff Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Tue, 12 Jul 2022 14:05:56 +0200 Subject: [PATCH 14/19] [tests] Change transformation logic --- .../BezierUtils/CubicBezierApproximation.hpp | 8 +-- .../BezierUtils/LeastSquareSystem.hpp | 10 +--- examples/CurveEditor/CurveEditor.cpp | 60 +++++++++---------- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp b/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp index f214245147f..47c1e5c92aa 100644 --- a/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp +++ b/examples/CurveEditor/BezierUtils/CubicBezierApproximation.hpp @@ -130,7 +130,7 @@ class CubicBezierApproximation bool m_hasComputed { false }; int m_dim { 0 }; int m_step { 0 }; - float m_distThreshold; + float m_distThreshold { 0.f }; VectorArray m_data; std::vector m_params; std::set m_bzjunctions; @@ -174,10 +174,10 @@ class CubicBezierApproximation int b { (int)( m_data.size() - 1 ) }; float h = ( b - a ) / (float)( nb_junctions - 1 ); std::vector xs( nb_junctions ); - typename std::vector::iterator x; + typename std::vector::iterator it; float val; - for ( x = xs.begin(), val = a; x != xs.end(); ++x, val += h ) - *x = (int)val; + for ( it = xs.begin(), val = a; it != xs.end(); ++it, val += h ) + *it = (int)val; for ( int x : xs ) { m_bzjunctions.insert( x ); diff --git a/examples/CurveEditor/BezierUtils/LeastSquareSystem.hpp b/examples/CurveEditor/BezierUtils/LeastSquareSystem.hpp index 8bef5ea4a98..c455c31e3ef 100644 --- a/examples/CurveEditor/BezierUtils/LeastSquareSystem.hpp +++ b/examples/CurveEditor/BezierUtils/LeastSquareSystem.hpp @@ -29,7 +29,8 @@ class LeastSquareSystem * @param dimension of the variables (optional) * @param expected number of constraints (optional) */ - LeastSquareSystem( int nvar, int dim = 1, int exp_ncstr = 0 ) : m_nvar( nvar ), m_dim( dim ) { + explicit LeastSquareSystem( int nvar, int dim = 1, int exp_ncstr = 0 ) : + m_nvar( nvar ), m_dim( dim ) { m_constrainedVar.resize( m_nvar, false ); if ( exp_ncstr > 0 ) { m_bval.reserve( exp_ncstr ); } } @@ -188,13 +189,6 @@ class LeastSquareSystem return true; } - bool simpleCholesky( const SparseMat& A, const VectorXf& b, VectorXf& x ) const { - Eigen::SimplicialCholesky chol( A.transpose() * A ); - if ( chol.info() != Eigen::Success ) { return false; } - x = chol.solve( A.transpose() * b ); - return true; - } - bool jacobiSVD( const SparseMat& A, const VectorXf& b, VectorXf& x ) const { MatrixXf denseA( A ); Eigen::JacobiSVD svd( denseA, Eigen::ComputeThinU | Eigen::ComputeThinV ); diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index 4e09719a392..f63f72e58f8 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -123,7 +123,6 @@ void CurveEditor::updateCurves( bool onRelease ) { auto oldPoint = pointComponent->m_point; unsigned int curveIdSize = pointComponent->m_curveId.size(); - CurveFactory factory; m_viewer->makeCurrent(); for ( unsigned int i = 0; i < curveIdSize; i++ ) { @@ -290,9 +289,9 @@ void CurveEditor::addPointInCurve( const Vector3& worldPos, int mouseX, int mous auto e = ro->getComponent(); int curveIndex = -1; - for ( int i = 0; i < m_curveEntities.size(); i++ ) { - if ( m_curveEntities[i] == e ) { - curveIndex = i; + for ( int z = 0; z < m_curveEntities.size(); z++ ) { + if ( m_curveEntities[z] == e ) { + curveIndex = z; break; } } @@ -357,35 +356,14 @@ void CurveEditor::processPicking( const Vector3& worldPos ) { auto transformTranslate = Transform::Identity(); transformTranslate.translate( worldPos - point ); - auto transform = Transform::Identity(); - transform = m_selectedRo->getLocalTransform() * transformTranslate; + auto transform = m_selectedRo->getLocalTransform() * transformTranslate; m_selectedRo->setLocalTransform( transform ); - if ( m_smooth && !( m_currentPoint == 0 || m_currentPoint == 1 || - m_currentPoint == m_pointEntities.size() - 2 || - m_currentPoint == m_pointEntities.size() - 1 ) ) { + if ( !( m_currentPoint == 0 || m_currentPoint == 1 || + m_currentPoint == m_pointEntities.size() - 2 || + m_currentPoint == m_pointEntities.size() - 1 ) ) { - if ( m_currentPoint % 3 == 2 ) { - auto pointMid = m_pointEntities[m_currentPoint + 1]; - pointComponent = m_pointEntities[m_currentPoint + 2]; - auto symTransform = - computePointTransform( pointComponent, pointMid->m_point, worldPos ); - auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); - ro->setLocalTransform( symTransform ); - - if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint + 2 ); } - } - else if ( m_currentPoint % 3 == 1 ) { - auto pointMid = m_pointEntities[m_currentPoint - 1]; - pointComponent = m_pointEntities[m_currentPoint - 2]; - auto symTransform = - computePointTransform( pointComponent, pointMid->m_point, worldPos ); - auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); - ro->setLocalTransform( symTransform ); - - if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint - 2 ); } - } - else if ( m_currentPoint % 3 == 0 ) { + if ( m_currentPoint % 3 == 0 ) { auto leftRo = m_roMgr->getRenderObject( m_pointEntities[m_currentPoint - 1]->getRenderObjects()[0] ); auto leftTransform = leftRo->getTransform() * transformTranslate; @@ -401,6 +379,28 @@ void CurveEditor::processPicking( const Vector3& worldPos ) { m_tangentPoints.push_back( m_currentPoint + 1 ); } } + else if ( m_smooth ) { + if ( m_currentPoint % 3 == 2 ) { + auto pointMid = m_pointEntities[m_currentPoint + 1]; + pointComponent = m_pointEntities[m_currentPoint + 2]; + auto symTransform = + computePointTransform( pointComponent, pointMid->m_point, worldPos ); + auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); + ro->setLocalTransform( symTransform ); + + if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint + 2 ); } + } + else if ( m_currentPoint % 3 == 1 ) { + auto pointMid = m_pointEntities[m_currentPoint - 1]; + pointComponent = m_pointEntities[m_currentPoint - 2]; + auto symTransform = + computePointTransform( pointComponent, pointMid->m_point, worldPos ); + auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); + ro->setLocalTransform( symTransform ); + + if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint - 2 ); } + } + } } updateCurves(); } From c2dfa4d9eb795c97cfdbf68a798ed14be9ac165b Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Wed, 13 Jul 2022 15:10:30 +0200 Subject: [PATCH 15/19] [tests] Change smoothness and symetry to be a point attribute --- examples/CurveEditor/CurveEditor.cpp | 146 +++++++++++++++++++++--- examples/CurveEditor/CurveEditor.hpp | 22 +++- examples/CurveEditor/Gui/MainWindow.cpp | 52 +++++++-- examples/CurveEditor/Gui/MainWindow.hpp | 3 + examples/CurveEditor/PointComponent.hpp | 3 + 5 files changed, 200 insertions(+), 26 deletions(-) diff --git a/examples/CurveEditor/CurveEditor.cpp b/examples/CurveEditor/CurveEditor.cpp index f63f72e58f8..05ef8a153a0 100644 --- a/examples/CurveEditor/CurveEditor.cpp +++ b/examples/CurveEditor/CurveEditor.cpp @@ -104,6 +104,7 @@ void CurveEditor::refreshPoint( unsigned int pointIndex, const Vector3& newPoint auto pointComponent = m_pointEntities[pointIndex]; auto pointColor = pointComponent->m_color; auto pointCurve = pointComponent->m_curveId; + auto pointState = pointComponent->m_state; Vector3 point; if ( newPoint == Vector3::Zero() ) point = pointComponent->m_point; @@ -112,6 +113,7 @@ void CurveEditor::refreshPoint( unsigned int pointIndex, const Vector3& newPoint removeComponent( m_pointEntities[pointIndex]->getName() ); auto pointEntity = PointFactory::createPointComponent( this, point, pointCurve, pointName, pointColor ); + pointEntity->m_state = pointState; m_pointEntities[pointIndex] = pointEntity; } @@ -152,11 +154,14 @@ void CurveEditor::updateCurves( bool onRelease ) { } Transform CurveEditor::computePointTransform( PointComponent* pointCmp, - const Vector3& midPoint, + PointComponent* midPointCmp, const Vector3& worldPos ) { auto transform = Transform::Identity(); + auto midPoint = midPointCmp->m_point; Vector3 diff = ( midPoint - worldPos ); - if ( m_symetry ) { transform.translate( ( midPoint + diff ) - pointCmp->m_defaultPoint ); } + if ( midPointCmp->m_state == PointComponent::SYMETRIC ) { + transform.translate( ( midPoint + diff ) - pointCmp->m_defaultPoint ); + } else { diff.normalize(); auto actualdiff = diff * ( midPoint - pointCmp->m_point ).norm(); @@ -335,6 +340,12 @@ bool CurveEditor::processHover( std::shared_ptr ro ) { } if ( m_currentPoint < 0 ) return false; + if ( m_currentPoint % 3 == 0 ) + m_savedPoint = m_currentPoint; + else if ( m_savedPoint >= 0 && m_savedPoint - 1 != m_currentPoint && + m_savedPoint + 1 != m_currentPoint ) + m_savedPoint = -1; + ro->getMaterial()->getParameters().addParameter( "material.color", Color::Red() ); m_selectedRo = ro; return true; @@ -379,22 +390,22 @@ void CurveEditor::processPicking( const Vector3& worldPos ) { m_tangentPoints.push_back( m_currentPoint + 1 ); } } - else if ( m_smooth ) { - if ( m_currentPoint % 3 == 2 ) { - auto pointMid = m_pointEntities[m_currentPoint + 1]; - pointComponent = m_pointEntities[m_currentPoint + 2]; - auto symTransform = - computePointTransform( pointComponent, pointMid->m_point, worldPos ); + else if ( m_currentPoint % 3 == 2 ) { + auto pointMid = m_pointEntities[m_currentPoint + 1]; + if ( !( pointMid->m_state == PointComponent::DEFAULT ) ) { + pointComponent = m_pointEntities[m_currentPoint + 2]; + auto symTransform = computePointTransform( pointComponent, pointMid, worldPos ); auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); ro->setLocalTransform( symTransform ); if ( m_tangentPoints.empty() ) { m_tangentPoints.push_back( m_currentPoint + 2 ); } } - else if ( m_currentPoint % 3 == 1 ) { - auto pointMid = m_pointEntities[m_currentPoint - 1]; - pointComponent = m_pointEntities[m_currentPoint - 2]; - auto symTransform = - computePointTransform( pointComponent, pointMid->m_point, worldPos ); + } + else if ( m_currentPoint % 3 == 1 ) { + auto pointMid = m_pointEntities[m_currentPoint - 1]; + if ( !( pointMid->m_state == PointComponent::DEFAULT ) ) { + pointComponent = m_pointEntities[m_currentPoint - 2]; + auto symTransform = computePointTransform( pointComponent, pointMid, worldPos ); auto ro = m_roMgr->getRenderObject( pointComponent->getRenderObjects()[0] ); ro->setLocalTransform( symTransform ); @@ -404,3 +415,112 @@ void CurveEditor::processPicking( const Vector3& worldPos ) { } updateCurves(); } + +void CurveEditor::setSmooth( bool smooth ) { + if ( m_savedPoint >= 0 ) { + if ( smooth ) { + m_pointEntities[m_savedPoint]->m_state = PointComponent::SMOOTH; + smoothify( m_savedPoint ); + } + else + m_pointEntities[m_savedPoint]->m_state = PointComponent::DEFAULT; + } +} + +void CurveEditor::setSymetry( bool symetry ) { + if ( m_savedPoint >= 0 ) { + if ( symetry ) { + m_pointEntities[m_savedPoint]->m_state = PointComponent::SYMETRIC; + symmetrize( m_savedPoint ); + } + else + m_pointEntities[m_savedPoint]->m_state = PointComponent::SMOOTH; + } +} + +void CurveEditor::smoothify( int pointId ) { + if ( ( pointId == 0 || pointId == 1 || pointId == m_pointEntities.size() - 2 || + pointId == m_pointEntities.size() - 1 ) ) + return; + + m_viewer->makeCurrent(); + Vector3 backPoint = m_pointEntities[pointId - 1]->m_point; + Vector3 back = backPoint - m_pointEntities[pointId]->m_point; + Vector3 frontPoint = m_pointEntities[pointId + 1]->m_point; + Vector3 front = frontPoint - m_pointEntities[pointId]->m_point; + + Vector3 newPointDir = back - front; + newPointDir.normalize(); + newPointDir = newPointDir * back.norm(); + Vector3 newBackPoint = m_pointEntities[pointId]->m_point + newPointDir; + + auto transform = Transform::Identity(); + transform.translate( newBackPoint - m_pointEntities[pointId - 1]->m_point ); + auto roIdx = m_pointEntities[pointId - 1]->getRenderObjects()[0]; + auto ro = m_roMgr->getRenderObject( roIdx ); + ro->setLocalTransform( transform ); + m_pointEntities[pointId - 1]->m_point = newBackPoint; + + newPointDir.normalize(); + newPointDir = newPointDir * front.norm(); + Vector3 newFrontPoint = m_pointEntities[pointId]->m_point - newPointDir; + + transform = Transform::Identity(); + transform.translate( newFrontPoint - m_pointEntities[pointId + 1]->m_point ); + roIdx = m_pointEntities[pointId + 1]->getRenderObjects()[0]; + ro = m_roMgr->getRenderObject( roIdx ); + ro->setLocalTransform( transform ); + m_pointEntities[pointId + 1]->m_point = newFrontPoint; + + m_currentPoint = pointId - 1; + updateCurves( true ); + m_currentPoint = pointId + 1; + updateCurves( true ); + m_currentPoint = -1; + + m_viewer->doneCurrent(); + m_viewer->getRenderer()->buildAllRenderTechniques(); + m_viewer->needUpdate(); +} + +void CurveEditor::symmetrize( int pointId ) { + if ( ( pointId == 0 || pointId == 1 || pointId == m_pointEntities.size() - 2 || + pointId == m_pointEntities.size() - 1 ) ) + return; + + m_viewer->makeCurrent(); + + Vector3 backPoint = m_pointEntities[pointId - 1]->m_point; + Vector3 back = backPoint - m_pointEntities[pointId]->m_point; + Vector3 frontPoint = m_pointEntities[pointId + 1]->m_point; + Vector3 front = frontPoint - m_pointEntities[pointId]->m_point; + + auto transform = Transform::Identity(); + Index roIdx = -1; + if ( std::min( back.norm(), front.norm() ) == back.norm() ) { + front.normalize(); + front = front * back.norm(); + Vector3 newPoint = m_pointEntities[pointId]->m_point + front; + transform.translate( newPoint - m_pointEntities[pointId + 1]->m_point ); + roIdx = m_pointEntities[pointId + 1]->getRenderObjects()[0]; + m_pointEntities[pointId + 1]->m_point = newPoint; + m_currentPoint = pointId + 1; + } + else { + back.normalize(); + back = back * front.norm(); + Vector3 newPoint = m_pointEntities[pointId]->m_point + back; + transform.translate( newPoint - m_pointEntities[pointId - 1]->m_point ); + roIdx = m_pointEntities[pointId - 1]->getRenderObjects()[0]; + m_pointEntities[pointId - 1]->m_point = newPoint; + m_currentPoint = pointId - 1; + } + auto ro = m_roMgr->getRenderObject( roIdx ); + ro->setLocalTransform( transform ); + updateCurves( true ); + m_currentPoint = -1; + + m_viewer->doneCurrent(); + m_viewer->getRenderer()->buildAllRenderTechniques(); + m_viewer->needUpdate(); +} diff --git a/examples/CurveEditor/CurveEditor.hpp b/examples/CurveEditor/CurveEditor.hpp index e78f807a6c6..ca9ba9134a8 100644 --- a/examples/CurveEditor/CurveEditor.hpp +++ b/examples/CurveEditor/CurveEditor.hpp @@ -58,32 +58,44 @@ class CurveEditor : public Ra::Engine::Scene::Entity m_currentPoint = -1; } + void resetSavedPoint() { m_savedPoint = -1; } + + PointComponent* getSavedPoint() { + if ( m_savedPoint >= 0 ) + return m_pointEntities[m_savedPoint]; + else + return nullptr; + } + /** * @brief smooth to keep a G1 continuity * @param smooth state */ - void setSmooth( bool smooth ) { m_smooth = smooth; } + void setSmooth( bool smooth ); /** * @brief symetry to keep a C1 continuity * @param symetry state */ - void setSymetry( bool symetry ) { m_symetry = symetry; } + void setSymetry( bool symetry ); private: inline float distanceSquared( const Ra::Core::Vector3f& pointA, const Ra::Core::Vector3f& pointB ); + unsigned int getPointIndex( unsigned int curveIdSize, unsigned int currentPtIndex, const Ra::Core::Vector3& firstCtrlPt, const Ra::Core::Vector3& currentPt ); + void updateCurve( unsigned int curveId, unsigned int curveIdSize, PointComponent* pointComponent, const Ra::Core::Vector3& currentPt, unsigned int currentPoint ); + Ra::Core::Transform computePointTransform( PointComponent* pointCmp, - const Ra::Core::Vector3& midPoint, + PointComponent* midPoint, const Ra::Core::Vector3& worldPos ); void subdivisionBezier( int vertexIndex, unsigned int curveIndex, @@ -91,6 +103,9 @@ class CurveEditor : public Ra::Engine::Scene::Entity void refreshPoint( unsigned int pointIndex, const Ra::Core::Vector3& newPoint = Ra::Core::Vector3::Zero() ); + void smoothify( int pointId ); + void symmetrize( int pointId ); + private: Ra::Engine::Scene::EntityManager* m_entityMgr; int m_currentPoint { -1 }; @@ -99,6 +114,7 @@ class CurveEditor : public Ra::Engine::Scene::Entity std::vector m_curveEntities; std::vector m_tangentPoints; Ra::Engine::Rendering::RenderObjectManager* m_roMgr; + int m_savedPoint { -1 }; bool m_smooth { false }; bool m_symetry { false }; diff --git a/examples/CurveEditor/Gui/MainWindow.cpp b/examples/CurveEditor/Gui/MainWindow.cpp index ba526183389..9af2417518d 100644 --- a/examples/CurveEditor/Gui/MainWindow.cpp +++ b/examples/CurveEditor/Gui/MainWindow.cpp @@ -56,6 +56,7 @@ MainWindow::MainWindow( QWidget* parent ) : MainWindowInterface( parent ) { m_symetryButton = new QPushButton( "Symetry" ); m_symetryButton->setCheckable( true ); m_symetryButton->setEnabled( false ); + m_button->setEnabled( false ); layout->addWidget( m_hidePolylineButton ); layout->addWidget( m_editCurveButton ); layout->addWidget( m_button ); @@ -105,10 +106,10 @@ void MainWindow::cleanup() { } void MainWindow::createConnections() { - connect( getViewer(), &MyViewer::toggleBrushPicking, this, &MainWindow::toggleCirclePicking ); - connect( getViewer(), &MyViewer::onMouseMove, this, &MainWindow::handleMouseMoveEvent ); - connect( getViewer(), &MyViewer::onMouseRelease, this, &MainWindow::handleMouseReleaseEvent ); - connect( getViewer(), &MyViewer::onMousePress, this, &MainWindow::handleMousePressEvent ); + connect( m_viewer, &MyViewer::toggleBrushPicking, this, &MainWindow::toggleCirclePicking ); + connect( m_viewer, &MyViewer::onMouseMove, this, &MainWindow::handleMouseMoveEvent ); + connect( m_viewer, &MyViewer::onMouseRelease, this, &MainWindow::handleMouseReleaseEvent ); + connect( m_viewer, &MyViewer::onMousePress, this, &MainWindow::handleMousePressEvent ); connect( m_editCurveButton, &QPushButton::pressed, this, &MainWindow::onEditPolylineButtonPressed ); connect( m_hidePolylineButton, @@ -117,15 +118,13 @@ void MainWindow::createConnections() { &MainWindow::onHidePolylineButtonClicked ); connect( m_button, &QPushButton::clicked, this, &MainWindow::onSmoothButtonClicked ); connect( m_symetryButton, &QPushButton::clicked, this, &MainWindow::onSymetryButtonClicked ); - connect( getViewer(), - &MyViewer::onMouseDoubleClick, - this, - &MainWindow::handleMouseDoubleClickEvent ); + connect( + m_viewer, &MyViewer::onMouseDoubleClick, this, &MainWindow::handleMouseDoubleClickEvent ); } void MainWindow::onSmoothButtonClicked() { - m_symetryButton->setEnabled( m_button->isChecked() ); m_curveEditor->setSmooth( m_button->isChecked() ); + m_symetryButton->setEnabled( true ); } void MainWindow::onSymetryButtonClicked() { @@ -148,8 +147,41 @@ void MainWindow::onEditPolylineButtonPressed() { m_curveEditor = new CurveEditor( m_polyline, m_viewer ); } +void MainWindow::processSavedPoint() { + auto savedPoint = m_curveEditor->getSavedPoint(); + if ( savedPoint ) { + if ( savedPoint->m_state == PointComponent::DEFAULT ) { + m_button->setChecked( false ); + m_button->setEnabled( true ); + m_symetryButton->setEnabled( false ); + m_symetryButton->setChecked( false ); + } + else if ( savedPoint->m_state == PointComponent::SMOOTH ) { + m_button->setEnabled( true ); + m_button->setChecked( true ); + m_symetryButton->setEnabled( true ); + m_symetryButton->setChecked( false ); + } + else { + m_button->setEnabled( true ); + m_button->setChecked( true ); + m_symetryButton->setEnabled( true ); + m_symetryButton->setChecked( true ); + } + } + else { + m_button->setEnabled( false ); + m_button->setChecked( false ); + m_symetryButton->setEnabled( false ); + m_symetryButton->setChecked( false ); + } +} + void MainWindow::handleMousePressEvent( QMouseEvent* event ) { - if ( event->button() == Qt::RightButton ) m_clicked = true; + if ( m_edited ) { + if ( event->button() == Qt::RightButton ) m_clicked = true; + processSavedPoint(); + } } void MainWindow::displayHelpDialog() { diff --git a/examples/CurveEditor/Gui/MainWindow.hpp b/examples/CurveEditor/Gui/MainWindow.hpp index f132475207d..480b1a1f80c 100644 --- a/examples/CurveEditor/Gui/MainWindow.hpp +++ b/examples/CurveEditor/Gui/MainWindow.hpp @@ -61,6 +61,9 @@ class MainWindow : public Ra::Gui::MainWindowInterface private: void createConnections(); + + void processSavedPoint(); + MyViewer* m_viewer; Ra::Gui::SelectionManager* m_selectionManager; Ra::Gui::ItemModel* m_sceneModel; diff --git a/examples/CurveEditor/PointComponent.hpp b/examples/CurveEditor/PointComponent.hpp index 31fe302b5d0..be7d2e87e60 100644 --- a/examples/CurveEditor/PointComponent.hpp +++ b/examples/CurveEditor/PointComponent.hpp @@ -19,6 +19,9 @@ class PointComponent : public Ra::Engine::Scene::Component /// setup, i.e. it has an entity. void initialize() override; + enum State { DEFAULT = 0, SMOOTH, SYMETRIC }; + + State m_state { DEFAULT }; Ra::Core::Vector3 m_point; Ra::Core::Vector3 m_defaultPoint; Ra::Core::Utils::Color m_color; From fec0fbc5caae27535a5e14aab68c11fb909d9981 Mon Sep 17 00:00:00 2001 From: Mafo369 Date: Wed, 13 Jul 2022 16:22:04 +0200 Subject: [PATCH 16/19] [tests] Small cleanup --- examples/CurveEditor/CurveEditor.hpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/CurveEditor/CurveEditor.hpp b/examples/CurveEditor/CurveEditor.hpp index ca9ba9134a8..40142e09fb1 100644 --- a/examples/CurveEditor/CurveEditor.hpp +++ b/examples/CurveEditor/CurveEditor.hpp @@ -58,8 +58,6 @@ class CurveEditor : public Ra::Engine::Scene::Entity m_currentPoint = -1; } - void resetSavedPoint() { m_savedPoint = -1; } - PointComponent* getSavedPoint() { if ( m_savedPoint >= 0 ) return m_pointEntities[m_savedPoint]; @@ -104,6 +102,7 @@ class CurveEditor : public Ra::Engine::Scene::Entity const Ra::Core::Vector3& newPoint = Ra::Core::Vector3::Zero() ); void smoothify( int pointId ); + void symmetrize( int pointId ); private: @@ -116,8 +115,5 @@ class CurveEditor : public Ra::Engine::Scene::Entity Ra::Engine::Rendering::RenderObjectManager* m_roMgr; int m_savedPoint { -1 }; - bool m_smooth { false }; - bool m_symetry { false }; - MyViewer* m_viewer { nullptr }; }; From 27398c7ccb7ce091af4cf33d0aae033f211bd75c Mon Sep 17 00:00:00 2001 From: dlyr Date: Fri, 7 Apr 2023 10:07:54 +0200 Subject: [PATCH 17/19] [core] Add RA_CORE_API to Curve2D classes. --- src/Core/Geometry/Curve2D.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/Geometry/Curve2D.hpp b/src/Core/Geometry/Curve2D.hpp index 01523bc13d8..1062d340d31 100644 --- a/src/Core/Geometry/Curve2D.hpp +++ b/src/Core/Geometry/Curve2D.hpp @@ -8,7 +8,7 @@ namespace Ra { namespace Core { namespace Geometry { -class Curve2D +class RA_CORE_API Curve2D { public: enum CurveType { LINE, CUBICBEZIER, SPLINE, SIZE }; @@ -26,7 +26,7 @@ class Curve2D int size; }; -class QuadraSpline : public Curve2D +class RA_CORE_API QuadraSpline : public Curve2D { public: QuadraSpline() { this->size = 0; } @@ -48,7 +48,7 @@ class QuadraSpline : public Curve2D Core::VectorArray m_points; }; -class CubicBezier : public Curve2D +class RA_CORE_API CubicBezier : public Curve2D { public: CubicBezier() { this->size = 0; } @@ -89,7 +89,7 @@ class CubicBezier : public Curve2D Vector m_points[4]; }; -class Line : public Curve2D +class RA_CORE_API Line : public Curve2D { public: Line() { this->size = 0; } @@ -106,7 +106,7 @@ class Line : public Curve2D Vector m_points[2]; }; -class SplineCurve : public Curve2D +class RA_CORE_API SplineCurve : public Curve2D { public: SplineCurve() { this->size = 0; } @@ -125,7 +125,7 @@ class SplineCurve : public Curve2D Core::VectorArray m_points; }; -class PiecewiseCubicBezier : public Curve2D +class RA_CORE_API PiecewiseCubicBezier : public Curve2D { public: PiecewiseCubicBezier() {} From 1d0bbc98873a243effbc7049ac7f22d10ec14470 Mon Sep 17 00:00:00 2001 From: vanderha Date: Wed, 14 Jun 2023 18:02:26 +0200 Subject: [PATCH 18/19] [examples] remove copy of unexisting assets (curve Editor). --- examples/CurveEditor/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CurveEditor/CMakeLists.txt b/examples/CurveEditor/CMakeLists.txt index fb3ce82d5fe..4acb715ffc2 100644 --- a/examples/CurveEditor/CMakeLists.txt +++ b/examples/CurveEditor/CMakeLists.txt @@ -96,5 +96,5 @@ target_link_libraries( # configure the application configure_radium_app( - NAME ${PROJECT_NAME} PREFIX Examples RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/Assets" + NAME ${PROJECT_NAME} PREFIX Examples ) From e11bb2edb3cb6cccf89e1abc832398f19c5cf374 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 11:59:10 +0000 Subject: [PATCH 19/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/CurveEditor/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/CurveEditor/CMakeLists.txt b/examples/CurveEditor/CMakeLists.txt index 4acb715ffc2..6b4bcf433c9 100644 --- a/examples/CurveEditor/CMakeLists.txt +++ b/examples/CurveEditor/CMakeLists.txt @@ -95,6 +95,4 @@ target_link_libraries( ) # configure the application -configure_radium_app( - NAME ${PROJECT_NAME} PREFIX Examples -) +configure_radium_app(NAME ${PROJECT_NAME} PREFIX Examples)