From 00b1e3a52cb414f2ca678bbd88d9967b36e8ebef Mon Sep 17 00:00:00 2001 From: Fabian Paus <fabian.paus@kit.edu> Date: Tue, 26 Apr 2022 10:45:50 +0200 Subject: [PATCH] ArViz: Add Transformable<T> abstraction --- .../ArViz/Example/ArVizInteractExample.cpp | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/source/RobotAPI/components/ArViz/Example/ArVizInteractExample.cpp b/source/RobotAPI/components/ArViz/Example/ArVizInteractExample.cpp index 9f5144595..629941ecc 100644 --- a/source/RobotAPI/components/ArViz/Example/ArVizInteractExample.cpp +++ b/source/RobotAPI/components/ArViz/Example/ArVizInteractExample.cpp @@ -31,6 +31,7 @@ namespace armarx { + struct SingleSlider { SingleSlider(std::string const& name, viz::Color color) @@ -202,6 +203,216 @@ namespace armarx }; + // --------------- + + // What abstractions are needed? + // MovableElement + // SpawnerElement + + //template <typename ElementT> + template <typename ElementT> + struct Transformable + { + Transformable(std::string const& name) + : element(name) + { + } + + Transformable& pose(Eigen::Matrix4f const& pose) + { + element.pose(pose); + initialPose = pose; + return *this; + } + + Transformable& position(Eigen::Vector3f const& position) + { + element.position(position); + initialPose.block<3, 1>(0, 3) = position; + return *this; + } + + Transformable& orientation(Eigen::Matrix3f const& rotationMatrix) + { + element.orientation(rotationMatrix); + initialPose.block<3, 3>(0, 0) = rotationMatrix; + return *this; + } + + Transformable& orientation(Eigen::Quaternionf const& quaternion) + { + element.orientation(quaternion); + initialPose.block<3, 3>(0, 0) = quaternion.toRotationMatrix(); + return *this; + } + + Transformable& enable(viz::InteractionDescription const& interaction) + { + element.enable(interaction); + // A movable element is always hidden during the interaction + element.data_->interaction.enableFlags |= viz::data::InteractionEnableFlags::TRANSFORM_HIDE; + return *this; + } + + // The pose after the current transformation has been applied + Eigen::Matrix4f getCurrentPose() const + { + return transformation * initialPose; + } + + // Returns true, if the element has been changed and the layer needs to be comitted again + bool handle(viz::InteractionFeedback const& interaction) + { + if (interaction.element() == element.data_->id) + { + switch (interaction.type()) + { + case viz::InteractionFeedbackType::Transform: + { + // Keep track of the transformation + transformation = interaction.transformation(); + } break; + case viz::InteractionFeedbackType::Deselect: + { + // If an object is deselected, we apply the transformation + initialPose = transformation * initialPose; + transformation = Eigen::Matrix4f::Identity(); + element.pose(initialPose); + return true; + } break; + default: + { + // Ignore the other events + } + } + } + return false; + } + + + Eigen::Matrix4f initialPose = Eigen::Matrix4f::Identity(); + Eigen::Matrix4f transformation = Eigen::Matrix4f::Identity(); + + ElementT element; + }; + + + struct SlidersState2 + { + SlidersState2(Eigen::Vector3f origin) + : origin(origin) + , x("BoxX") + , y("BoxY") + , z("BoxZ") + , sphere("Sphere") + { + float boxSize = 50.0f; + + // We use the Transformable<T>::position to set the position + // This keeps track of the internal state + x.position(origin + Eigen::Vector3f(0.5f * ARROW_LENGTH, 0.0f, 0.0f)); + // A movable object is always hidden during the transformation + // The hideDuringTransform() flag is automatically set here + x.enable(viz::interaction().translation(viz::AXES_X)); + + // Other attributes of the element can be set directly on the member + x.element.color(viz::Color::red()) + .size(boxSize); + + y.position(origin + Eigen::Vector3f(0.0f, 0.5f * ARROW_LENGTH, 0.0f)); + y.enable(viz::interaction().translation(viz::AXES_Y)); + + y.element.color(viz::Color::green()) + .size(boxSize); + + z.position(origin + Eigen::Vector3f(0.0f, 0.0f, 0.5f * ARROW_LENGTH)); + z.enable(viz::interaction().translation(viz::AXES_Z)); + z.element.color(viz::Color::blue()) + .size(boxSize); + + sphere.position(origin + 0.5f * ARROW_LENGTH * Eigen::Vector3f(1.0f, 1.0f, 1.0f)) + .color(viz::Color::orange()) + .radius(30.0f); + } + + static constexpr const float ARROW_LENGTH = 1000.0f; + + void visualize(viz::Client& arviz) + { + layerInteract = arviz.layer("Sliders2"); + + float arrowWidth = 10.0f; + + viz::Arrow arrowX = viz::Arrow("ArrowX") + .color(viz::Color::red()) + .fromTo(origin, origin + Eigen::Vector3f(ARROW_LENGTH, 0.0f, 0.0f)) + .width(arrowWidth); + layerInteract.add(arrowX); + + viz::Arrow arrowY = viz::Arrow("ArrowY") + .color(viz::Color::green()) + .fromTo(origin, origin + Eigen::Vector3f(0.0f, ARROW_LENGTH, 0.0f)) + .width(arrowWidth); + layerInteract.add(arrowY); + + viz::Arrow arrowZ = viz::Arrow("ArrowZ") + .color(viz::Color::blue()) + .fromTo(origin, origin + Eigen::Vector3f(0.0f, 0.0f, ARROW_LENGTH)) + .width(arrowWidth); + layerInteract.add(arrowZ); + + + layerInteract.add(x.element); + layerInteract.add(y.element); + layerInteract.add(z.element); + + layerResult = arviz.layer("SlidersResult2"); + layerResult.add(sphere); + } + + void handle(viz::InteractionFeedback const& interaction, + viz::StagedCommit* stage) + { + // Let the Transformable<T> handle all internal events + bool needsLayerUpdate = false; + needsLayerUpdate |= x.handle(interaction); + needsLayerUpdate |= y.handle(interaction); + needsLayerUpdate |= z.handle(interaction); + if (needsLayerUpdate) + { + // The handle() functions indicated that the layer needs to be updated + stage->add(layerInteract); + } + + // We handle the transform event ourselves to add custom behavior + // Here, we move the sphere based on the position of the sliders + if (interaction.type() == viz::InteractionFeedbackType::Transform) + { + // We can query getCurrentPose() to determine the poses of the sliders + // with the transformation applied to them + Eigen::Vector3f spherePosition( + x.getCurrentPose().col(3).x(), + y.getCurrentPose().col(3).y(), + z.getCurrentPose().col(3).z()); + sphere.position(spherePosition); + + stage->add(layerResult); + } + } + + Eigen::Vector3f origin; + Transformable<viz::Box> x; + Transformable<viz::Box> y; + Transformable<viz::Box> z; + + viz::Sphere sphere; + + viz::Layer layerInteract; + viz::Layer layerResult; + }; + // --------------- + + enum class SpawnerType { Box, @@ -587,12 +798,17 @@ namespace armarx SlidersState sliders(origin1 + Eigen::Vector3f(500.0f, 500.0f, 0.0f)); + SlidersState2 sliders2(origin3 + Eigen::Vector3f(500.0f, 500.0f, 0.0f)); SpawnersState spawners(origin2); sliders.visualize(arviz); stage.add(sliders.layerInteract); stage.add(sliders.layerResult); + sliders2.visualize(arviz); + stage.add(sliders2.layerInteract); + stage.add(sliders2.layerResult); + spawners.visualize(arviz); stage.add(spawners.layerSpawners); stage.add(spawners.layerObjects); @@ -609,6 +825,7 @@ namespace armarx stage.reset(); stage.requestInteraction(sliders.layerInteract); + stage.requestInteraction(sliders2.layerInteract); stage.requestInteraction(spawners.layerSpawners); stage.requestInteraction(spawners.layerObjects); @@ -619,6 +836,10 @@ namespace armarx { sliders.handle(interaction, &stage); } + if (interaction.layer() == "Sliders2") + { + sliders2.handle(interaction, &stage); + } if (interaction.layer() == "Spawners" || interaction.layer() == "SpawnedObjects") { spawners.handle(interaction, &stage); -- GitLab