/* * This file is part of ArmarX. * * ArmarX is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * ArmarX is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @package robdekon * @author Christoph Pohl ( christoph dot pohl at kit dot edu ) * @date 07.09.22 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ #include "RemoteGuiVisitors.h" #include <SimoxUtility/math/convert/quat_to_rpy.h> #include <SimoxUtility/math/convert/rpy_to_quat.h> #include <ArmarXCore/util/CPPUtility/Iterator.h> #include <ArmarXGui/libraries/RemoteGui/RemoteGui.h> #include <RobotAPI/libraries/aron/converter/eigen/EigenConverter.h> #include <RobotAPI/libraries/aron/core/data/variant/All.h> #include <RobotAPI/libraries/aron_component_config/VariantHelperFactory.h> #include "Util.h" #define INPUT_GUARD(i) \ ARMARX_TRACE; \ ARMARX_CHECK_NOT_NULL(i); \ if (in_list_) \ return; namespace armarx::aron::component_config { void MakeConfigGuiVisitor::visitObjectOnEnter(DataInput& dict, TypeInput& /*unused*/) { INPUT_GUARD(dict); if (dict->getPath().hasElement()) { std::string name = pathToName(dict); group_hierarchy_.emplace_back(std::make_shared<RemoteGui::detail::GroupBoxBuilder>( RemoteGui::makeGroupBox(name))); } else { group_hierarchy_.push_back(builder_); } } void MakeConfigGuiVisitor::visitObjectOnExit(DataInput& dict, TypeInput& /*j*/) { INPUT_GUARD(dict); auto builder = *group_hierarchy_.back(); group_hierarchy_.pop_back(); if (!group_hierarchy_.empty()) { group_hierarchy_.back()->addChild(builder); } } void MakeConfigGuiVisitor::visitInt(DataInput& i, TypeInput& /*unused*/) { INPUT_GUARD(i); auto value = data::Int::DynamicCastAndCheck(i); const auto& name = pathToName(i); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild(RemoteGui::makeLabel(name + "_label").value(i->getPath().getLastElement())); group.addHSpacer(); group.addChild(RemoteGui::makeIntSpinBox(name) .value(value->getValue()) .min(-1000) .max(1000) .toolTip(name)); group_hierarchy_.back()->addChild(group); } void MakeConfigGuiVisitor::visitFloat(DataInput& f, TypeInput& /*unused*/) { INPUT_GUARD(f); auto value = data::Float::DynamicCastAndCheck(f); const auto& name = pathToName(f); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild(RemoteGui::makeLabel(name + "_label").value(f->getPath().getLastElement())); group.addHSpacer(); group.addChild(RemoteGui::makeFloatSpinBox(name) .value(value->getValue()) .min(-1000) .max(1000) .toolTip(name)); group_hierarchy_.back()->addChild(group); } void MakeConfigGuiVisitor::visitDouble(DataInput& d, TypeInput& /*unused*/) { INPUT_GUARD(d); auto value = data::Double::DynamicCastAndCheck(d); const auto& name = pathToName(d); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild(RemoteGui::makeLabel(name + "_label").value(d->getPath().getLastElement())); group.addHSpacer(); group.addChild(RemoteGui::makeFloatSpinBox(name) .value(value->getValue()) .min(-1000) .max(1000) .toolTip(name)); group_hierarchy_.back()->addChild(group); } void MakeConfigGuiVisitor::visitBool(DataInput& b, TypeInput& /*unused*/) { INPUT_GUARD(b); auto value = data::Bool::DynamicCastAndCheck(b); const auto& name = pathToName(b); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild(RemoteGui::makeLabel(name + "_label").value(b->getPath().getLastElement())); group.addHSpacer(); group.addChild( RemoteGui::makeCheckBox(name).label("").value(value->getValue()).toolTip(name)); group_hierarchy_.back()->addChild(group); } void MakeConfigGuiVisitor::visitString(DataInput& string, TypeInput& /*unused*/) { INPUT_GUARD(string); auto value = data::String::DynamicCastAndCheck(string); const auto& name = pathToName(string); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild( RemoteGui::makeLabel(name + "_label").value(string->getPath().getLastElement())); group.addHSpacer(); group.addChild(RemoteGui::makeLineEdit(name).value(value->getValue()).toolTip(name)); group_hierarchy_.back()->addChild(group); } MakeConfigGuiVisitor::MakeConfigGuiVisitor(const std::string& name) : builder_(std::make_unique<RemoteGui::detail::GroupBoxBuilder>(name)) { } RemoteGui::detail::GroupBoxBuilder MakeConfigGuiVisitor::getGroupBoxBuilder() const { return *builder_; } void MakeConfigGuiVisitor::visitListOnEnter(DataInput& o, TypeInput& t) { in_list_ = true; auto group = RemoteGui::makeSimpleGridLayout(pathToName(o) + "_grid").cols(20); auto data = data::List::DynamicCastAndCheck(o); auto type = type::List::DynamicCast(t)->getAcceptedType()->getDescriptor(); if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) == implementedListDescriptors.end()) { return; } for (const auto& el : data->getElements()) { group.addChild(RemoteGui::makeLineEdit(pathToName(el)) .value(factories::VariantHelper::make(type)->to_string(el)), 10); group.addHSpacer(8); group.addChild(RemoteGui::makeButton(pathToName(el) + "_button") .label("-") .toolTip("Remove List Element"), 2); } group_hierarchy_.back()->addChild( RemoteGui::makeGroupBox(pathToName(o) + "_grp") .label(o->getPath().getLastElement()) .collapsed(true) .addChild(group) .addChild(RemoteGui::makeButton(pathToName(o) + "_add") .label("+") .toolTip("Add new list entry."))); } void MakeConfigGuiVisitor::visitListOnExit(DataInput& /*unused*/, TypeInput& /*unused*/) { in_list_ = false; } void MakeConfigGuiVisitor::visitIntEnum(DataInput& o, TypeInput& t) { INPUT_GUARD(o); auto data = data::Int::DynamicCastAndCheck(o); auto type = type::IntEnum::DynamicCast(t); const auto& name = pathToName(o); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild(RemoteGui::makeLabel(name + "_label").value(o->getPath().getLastElement())); group.addHSpacer(); group.addChild(RemoteGui::makeComboBox(name) .options(type->getAcceptedValueNames()) .value(type->getValueName(data->getValue())) .toolTip(name)); group_hierarchy_.back()->addChild(group); } void MakeConfigGuiVisitor::visitMatrix(const std::shared_ptr<data::Variant>& q, const std::shared_ptr<type::Variant>& t) { auto data = data::NDArray::DynamicCastAndCheck(q); auto type = type::Matrix::DynamicCast(t); const auto& cols = type->getCols(); const auto& rows = type->getRows(); const auto& name = pathToName(q); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild(RemoteGui::makeLabel(name + "_label").value(q->getPath().getLastElement())); group.addHSpacer(); if (cols == 4 && rows == 4) { // Poses const auto& matrix = aron::converter::AronEigenConverter::ConvertToMatrix4f(data); group.addChild(RemoteGui::makePosRPYSpinBoxes(name).value(matrix).toolTip(name)); } else if ((cols == 3 and rows == 1) or (rows == 3 and cols == 1)) { // Positions const auto& vector = aron::converter::AronEigenConverter::ConvertToVector3f(data); group.addChild(RemoteGui::makeVector3fSpinBoxes(name).value(vector).toolTip(name)); } group_hierarchy_.back()->addChild(group); } void MakeConfigGuiVisitor::visitQuaternion(const std::shared_ptr<data::Variant>& q, const std::shared_ptr<type::Variant>& t) { INPUT_GUARD(q); auto data = data::NDArray::DynamicCastAndCheck(q); auto type = type::Quaternion::DynamicCast(t); const auto& quat = simox::math::quat_to_rpy( aron::converter::AronEigenConverter::ConvertToQuaternionf(data)); const auto& name = pathToName(q); auto group = RemoteGui::makeHBoxLayout(name + "_layout"); group.addChild(RemoteGui::makeLabel(name + "_label").value(q->getPath().getLastElement())); group.addHSpacer(); group.addChild(RemoteGui::makeVector3fSpinBoxes(name).value(quat).min(-M_PI).max(M_PI).steps(601).decimals(2).toolTip(name)); group_hierarchy_.back()->addChild(group); } void MakeConfigGuiVisitor::visitDictOnEnter(const std::shared_ptr<data::Variant>& o, const std::shared_ptr<type::Variant>& t) { in_list_ = true; auto group = RemoteGui::makeSimpleGridLayout(pathToName(o) + "_grid").cols(20); auto data = data::Dict::DynamicCastAndCheck(o); auto type = type::Dict::DynamicCast(t)->getAcceptedType()->getDescriptor(); if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) == implementedListDescriptors.end()) { return; } for (const auto& el : data->getElements()) { group.addChild(RemoteGui::makeLineEdit(pathToName(el.second) + "_lbl").value(el.first), 5); group.addHSpacer(2); group.addChild(RemoteGui::makeLineEdit(pathToName(el.second)) .value(factories::VariantHelper::make(type)->to_string(el.second)), 5); group.addHSpacer(6); group.addChild(RemoteGui::makeButton(pathToName(el.second) + "_button") .label("-") .toolTip("Remove List Element"), 2); } group_hierarchy_.back()->addChild( RemoteGui::makeGroupBox(pathToName(o) + "_grp") .label(o->getPath().getLastElement()) .collapsed(true) .addChild(group) .addChild(RemoteGui::makeButton(pathToName(o) + "_add") .label("+") .toolTip("Add new dict entry."))); } void MakeConfigGuiVisitor::visitDictOnExit(const std::shared_ptr<data::Variant>&, const std::shared_ptr<type::Variant>&) { in_list_ = false; } GetValueFromMapVisitor::GetValueFromMapVisitor(armarx::RemoteGui::TabProxy* proxy) : proxy_(std::experimental::make_observer(proxy)) { } void GetValueFromMapVisitor::visitInt(DataInput& i, TypeInput&) { INPUT_GUARD(i); auto value = data::Int::DynamicCastAndCheck(i); const std::string name = pathToName(i); auto gui_value = proxy_->getValue<int>(name); if (value->getValue() != gui_value.get()) { if (proxy_->hasValueChanged(name)) { value->setValue(gui_value.get()); i = value; } else { proxy_->setValue(value->getValue(), name); } } } void GetValueFromMapVisitor::visitFloat(DataInput& f, TypeInput&) { INPUT_GUARD(f); auto value = data::Float::DynamicCastAndCheck(f); const std::string name = pathToName(f); auto gui_value = proxy_->getValue<float>(name); if (value->getValue() != gui_value.get()) { if (proxy_->hasValueChanged(name)) { value->setValue(gui_value.get()); f = value; } else { proxy_->setValue(value->getValue(), name); } } } void GetValueFromMapVisitor::visitDouble(DataInput& d, TypeInput&) { INPUT_GUARD(d); auto value = data::Double::DynamicCastAndCheck(d); const std::string name = pathToName(d); // TODO: does double work here? auto gui_value = proxy_->getValue<float>(name); if (value->getValue() != gui_value.get()) { if (proxy_->hasValueChanged(name)) { value->setValue(gui_value.get()); d = value; } else { proxy_->setValue(static_cast<float>(value->getValue()), name); } } } void GetValueFromMapVisitor::visitBool(DataInput& b, TypeInput&) { INPUT_GUARD(b); auto value = data::Bool::DynamicCastAndCheck(b); const std::string name = pathToName(b); auto gui_value = proxy_->getValue<bool>(name); if (value->getValue() != gui_value.get()) { if (proxy_->hasValueChanged(name)) { value->setValue(gui_value.get()); b = value; } else { proxy_->setValue(value->getValue(), name); } } } void GetValueFromMapVisitor::visitString(DataInput& string, TypeInput&) { INPUT_GUARD(string); auto value = data::String::DynamicCastAndCheck(string); const std::string name = pathToName(string); auto gui_value = proxy_->getValue<std::string>(name); if (value->getValue() != gui_value.get()) { if (proxy_->hasValueChanged(name)) { value->setValue(gui_value.get()); string = value; } else { proxy_->setValue(value->getValue(), name); } } } type::Descriptor GetValueFromMapVisitor::getDescriptor(DataInput& o, TypeInput& t) { return data::ConstTypedVariantVisitor::GetDescriptor(o, t); } GetValueFromMapVisitor::MapElements GetValueFromMapVisitor::getObjectElements(DataInput& o, TypeInput& t) { return component_config::getObjectElements(o, t); } GetValueFromMapVisitor::MapElements GetValueFromMapVisitor::getDictElements(DataInput& o, TypeInput& t) { return component_config::getDictElements(o, t); } GetValueFromMapVisitor::ListElements GetValueFromMapVisitor::getListElements(DataInput& o, TypeInput& t) { return component_config::getListElements(o, t); } GetValueFromMapVisitor::PairElements GetValueFromMapVisitor::getPairElements(DataInput& o, TypeInput& t) { return component_config::getPairElements(o, t); } GetValueFromMapVisitor::TupleElements GetValueFromMapVisitor::getTupleElements(DataInput& o, TypeInput& t) { return component_config::getTupleElements(o, t); } void GetValueFromMapVisitor::visitIntEnum(DataInput& o, TypeInput& t) { INPUT_GUARD(o); auto data = data::Int::DynamicCastAndCheck(o); auto type = type::IntEnum::DynamicCastAndCheck(t); std::string str; const std::string name = pathToName(o); proxy_->getValue(str, pathToName(o)); if (data->getValue() != type->getValue(str)) { if (proxy_->hasValueChanged(name)) { data->getValue() = type->getValue(str); o = data; } else { proxy_->setValue(type->getValueName(data->getValue()), name); } } } void GetValueFromMapVisitor::visitListOnEnter(DataInput& o, TypeInput& t) { in_list_ = true; auto data = data::List::DynamicCastAndCheck(o); auto type = type::List::DynamicCast(t)->getAcceptedType()->getDescriptor(); if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) == implementedListDescriptors.end()) { return; } const auto& elements = data->getElements(); for (const auto& [idx, el] : armarx::MakeIndexedContainer(elements)) { if (proxy_->getButtonClicked(pathToName(el) + "_button")) { data->removeElement(idx); tab_rebuild_required_ = true; } auto gui_value = proxy_->getValue<std::string>(pathToName(el)).get(); factories::VariantHelper::make(type)->set_value_from_string(el, gui_value); } if (proxy_->getButtonClicked(pathToName(o) + "_add")) { data->addElement(factories::VariantHelper::make(type)->from_string( "", o->getPath().withIndex(data->childrenSize()))); tab_rebuild_required_ = true; } } void GetValueFromMapVisitor::visitListOnExit(DataInput&, TypeInput&) { in_list_ = false; } bool GetValueFromMapVisitor::tabRebuildRequired() const { return tab_rebuild_required_; } void GetValueFromMapVisitor::visitMatrix(std::shared_ptr<data::Variant>& o, const std::shared_ptr<type::Variant>& t) { INPUT_GUARD(o); auto value = data::NDArray::DynamicCastAndCheck(o); auto type = type::Matrix::DynamicCastAndCheck(t); const std::string name = pathToName(o); const auto& cols = type->getCols(); const auto& rows = type->getRows(); // TODO: does double work here? if (cols == 4 && rows == 4) { auto gui_value = proxy_->getValue<Eigen::Matrix4f>(name); auto config_value = converter::AronEigenConverter::ConvertToMatrix4f(value); if (config_value != gui_value.get()) { if (proxy_->hasValueChanged(name)) { auto variant = converter::AronEigenConverter::ConvertFromMatrix(gui_value.get()); value->setData(gui_value.get().size() * sizeof(float), variant->getData()); } else { proxy_->setValue(config_value, name); } } } else if ((cols == 3 and rows == 1) or (cols == 1 and rows == 3)) { auto gui_value = proxy_->getValue<Eigen::Vector3f>(name); auto config_value = converter::AronEigenConverter::ConvertToVector3f(value); if (config_value != gui_value.get()) { if (proxy_->hasValueChanged(name)) { auto variant = converter::AronEigenConverter::ConvertFromMatrix(gui_value.get()); value->setData(gui_value.get().size() * sizeof(float), variant->getData()); } else { proxy_->setValue(config_value, name); } } } } void GetValueFromMapVisitor::visitQuaternion(std::shared_ptr<data::Variant>& o, const std::shared_ptr<type::Variant>& t) { INPUT_GUARD(o); auto value = data::NDArray::DynamicCastAndCheck(o); const std::string name = pathToName(o); // TODO: does double work here? auto gui_value = proxy_->getValue<Eigen::Vector3f>(name); const auto& quat = simox::math::quat_to_rpy(converter::AronEigenConverter::ConvertToQuaternionf(value)); if (quat != gui_value.get()) { if (proxy_->hasValueChanged(name)) { auto variant = converter::AronEigenConverter::ConvertFromQuaternion(simox::math::rpy_to_quat(gui_value.get())); value->setData(4 * sizeof(float), variant->getData()); } else { proxy_->setValue(quat, name); } } } void GetValueFromMapVisitor::visitDictOnEnter(std::shared_ptr<data::Variant>& o, const std::shared_ptr<type::Variant>& t) { in_list_ = true; auto data = data::Dict::DynamicCastAndCheck(o); auto type = type::Dict::DynamicCast(t)->getAcceptedType()->getDescriptor(); if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) == implementedListDescriptors.end()) { return; } const auto& elements = data->getElements(); std::map<std::string, std::string> changed_labels; for (const auto& [idx, el] : elements) { const std::string name = pathToName(el); if (proxy_->getButtonClicked(name + "_button")) { data->removeElement(idx); tab_rebuild_required_ = true; } auto gui_value = proxy_->getValue<std::string>(name).get(); auto gui_key = proxy_->getValue<std::string>(name + "_lbl").get(); auto config_value = factories::VariantHelper::make(type)->to_string(el); if (gui_value != config_value) { if (proxy_->hasValueChanged(name)) { factories::VariantHelper::make(type)->set_value_from_string(el, gui_value); } else { proxy_->setValue(config_value, name); } } if (gui_key != idx) { if (proxy_->hasValueChanged(name + "_lbl")) { changed_labels.emplace(idx, gui_key); } else { proxy_->setValue(idx, name + "_lbl"); } } } // replace changed keys in map for (const auto& [old_label, new_label] : changed_labels) { auto element = data->getElement(old_label); data->removeElement(old_label); auto variantHelper = factories::VariantHelper::make(type); data->addElement(new_label, variantHelper->from_string( variantHelper->to_string(element), o->getPath().withDetachedLastElement().withElement(new_label))); tab_rebuild_required_ = true; } if (proxy_->getButtonClicked(pathToName(o) + "_add")) { data->addElement("defaultKey", factories::VariantHelper::make(type)->from_string( "", o->getPath().withElement("defaultKey"))); tab_rebuild_required_ = true; } } void GetValueFromMapVisitor::visitDictOnExit(std::shared_ptr<data::Variant>&, const std::shared_ptr<type::Variant>&) { in_list_ = false; } } // namespace armarx::aron::component_config #undef INPUT_GUARD