From c9094d98d3f40617dd5734ccdf57f8fa6ea91e05 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 14 Sep 2026 23:34:11 +0200 Subject: [PATCH 01/11] add Global::get_per_axis_per_criteria<>() --- src/CMakeLists.txt | 1 + src/domain/global.cpp | 135 ++++++++++++++++++++++++++++++++++++++++++ src/domain/global.hpp | 19 +++++- 3 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/domain/global.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6816a9b..66d4060 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,7 @@ target_sources( openemsh "${CMAKE_CURRENT_SOURCE_DIR}/domain/mesh/interval.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/mesh/meshline.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/mesh/meshline_policy.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/domain/global.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/material.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/meshline_policy_manager.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/conflict_manager.cpp" diff --git a/src/domain/global.cpp b/src/domain/global.cpp new file mode 100644 index 0000000..b2f1456 --- /dev/null +++ b/src/domain/global.cpp @@ -0,0 +1,135 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include +#include +#include +#include + +#include "infra/utils/to_string.hpp" +#include "utils/logger.hpp" + +#include "global.hpp" + +using namespace std; + +//****************************************************************************** +template +string to_string(T const& t) { + if constexpr(is_enum_v) + return to_string(t); + else + return format("{}", t); +} + +//****************************************************************************** +template +string to_string(array const& a) { + return a | views::join_with(", ") | ranges::to(); +} + +//****************************************************************************** +template +string to_string(variant const& v) { + return visit([](auto&& arg) { + return to_string(arg); + }, v); +} + +//****************************************************************************** +template +string to_string(optional const& t) { + if(t.has_value()) + return to_string(t.value()); + else + return "*"; +} + +//****************************************************************************** +template +string to_string(domain::Params::PerAxisPer const& m) { + string res; + for(auto const& [k, v] : m) { + auto const& [axis, criteria] = k; + res += format(" {{ {}, {}, {} }}\n", + to_string(axis), + to_string(criteria), + to_string(v)); + } + res.pop_back(); + return res; +} + +namespace domain { + +//****************************************************************************** +GlobalParams::GlobalParams(Timepoint* t) +: Originator(t) +{} + +//****************************************************************************** +GlobalParams::GlobalParams(Params params, Timepoint* t) +: Originator(t, std::move(params)) +{} + + +//****************************************************************************** +template +auto const& GlobalParams::get_per_axis_per_criteria(Axis axis, Material const* material) const { + return get_per_axis_per_criteria(axis, material, get_current_state()); +} + +//****************************************************************************** +template +auto const& GlobalParams::get_per_axis_per_criteria(Axis axis, Material const* material, Params const& state) const { + using Key = MemberType::key_type; + + vector to_try; + if(material) { + if(!material->name.empty()) { + to_try.emplace_back(axis, material->name); + to_try.emplace_back(Params::ALL, material->name); + } + to_try.emplace_back(axis, material->type); + to_try.emplace_back(Params::ALL, material->type); + } + to_try.emplace_back(axis, Params::ALL); + to_try.emplace_back(Params::ALL, Params::ALL); + + for(auto const& k : to_try) + if((state.*Member).contains(k)) + return (state.*Member).at(k); + + { + [[unlikely]] + log({ + .level = Logger::Level::ERROR, + .user_actions = { Logger::UserAction::OK }, + .message = "This should never happen, please report a bug:", + .informative = "Did not match any value to answer the request", + .details = format( + "Requested value:\n" + "- axis: {}\n" + "- material: {}\n" + "Available values:\n{}\n" + "Location (Member is what matters):\n{}", + to_string(axis), + (material + ? format("\n - type: {}\n - name: {}", + to_string(material->type), + material->name) + : string("nullptr")), + to_string(state.*Member), + source_location::current().function_name() + ) + }); + abort(); +// ::unreachable(); // { ALL, ALL } rule MUST be always present if not better + } +} + +} // namespace domain diff --git a/src/domain/global.hpp b/src/domain/global.hpp index 43df9b4..4ddcb23 100644 --- a/src/domain/global.hpp +++ b/src/domain/global.hpp @@ -7,7 +7,10 @@ #pragma once #include +#include +#include #include +#include #include #include "geometrics/space.hpp" @@ -28,13 +31,25 @@ struct Params { double consecutive_diagonal_minimal_angle = 20; // Limite between acute / obtuse angles. std::vector> input_fixed_meshlines; + + template + using PerAxisPer = std::map, std::optional>, Value>; + static auto constexpr ALL = std::nullopt; + + using Mat = std::variant; }; //****************************************************************************** class GlobalParams : public Originator { public: - explicit GlobalParams(Timepoint* t) : Originator(t) {} - GlobalParams(Params params, Timepoint* t) : Originator(t, std::move(params)) {} + explicit GlobalParams(Timepoint* t); + GlobalParams(Params params, Timepoint* t); + +private: + template + auto const& get_per_axis_per_criteria(Axis axis, Material const* material) const; + template + auto const& get_per_axis_per_criteria(Axis axis, Material const* material, Params const& state) const; }; inline double equality_tolerance = 1e-8; From 4d0feeb63bb01a746633454bb9efa214fddb9ef5 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 16 Sep 2026 03:06:15 +0200 Subject: [PATCH 02/11] Cli : add per material option handling --- src/ui/cli/cli.cpp | 175 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 163 insertions(+), 12 deletions(-) diff --git a/src/ui/cli/cli.cpp b/src/ui/cli/cli.cpp index ca0afeb..7f00c79 100644 --- a/src/ui/cli/cli.cpp +++ b/src/ui/cli/cli.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "utils/concepts.hpp" #include "utils/map_utils.hpp" @@ -95,6 +96,87 @@ struct JustDo : CLI::Validator { } }; +//****************************************************************************** +enum class CliAxis { + X, + Y, + Z, + ALL +}; + +//****************************************************************************** +enum class CliMatType { + CONDUCTOR, + DIELECTRIC, + AIR, + ALL +}; + +//****************************************************************************** +static std::map> const axes_without_all { + { "x", CliAxis::X }, + { "y", CliAxis::Y }, + { "z", CliAxis::Z } +}; + +//****************************************************************************** +static std::map> const axes_with_all { + { "x", CliAxis::X }, + { "y", CliAxis::Y }, + { "z", CliAxis::Z }, + { "*", CliAxis::ALL } +}; + +//****************************************************************************** +static std::map> const material_types_with_all { + { "conductor", CliMatType::CONDUCTOR }, + { "dielectric", CliMatType::DIELECTRIC }, + { "air", CliMatType::AIR }, + { "*", CliMatType::ALL } +}; + +//****************************************************************************** +static std::map> const meshline_policies { + { "oneline", domain::MeshlinePolicy::Policy::ONELINE }, + { "halfs", domain::MeshlinePolicy::Policy::HALFS }, + { "thirds", domain::MeshlinePolicy::Policy::THIRDS } +}; + +//****************************************************************************** +static std::map> const output_formats { + { "csx", app::OpenEMSH::Params::OutputFormat::CSX }, + { "plantuml", app::OpenEMSH::Params::OutputFormat::PLANTUML }, + { "prettyprint", app::OpenEMSH::Params::OutputFormat::PRETTYPRINT } +}; + +//****************************************************************************** +std::optional cast(CliAxis axis) noexcept { + switch(axis) { + case CliAxis::X: return domain::X; + case CliAxis::Y: return domain::Y; + case CliAxis::Z: return domain::Z; + case CliAxis::ALL: return std::nullopt; + default: ::unreachable(); + } +} + +//****************************************************************************** +std::optional cast(CliMatType type) noexcept { + using Type = domain::Material::Type; + switch(type) { + case CliMatType::CONDUCTOR: return Type::CONDUCTOR; + case CliMatType::DIELECTRIC: return Type::DIELECTRIC; + case CliMatType::AIR: return Type::AIR; + case CliMatType::ALL: return std::nullopt; + default: ::unreachable(); + } +} + +//****************************************************************************** +std::string cast(std::string const& s) noexcept { + return s; +} + //****************************************************************************** template auto make_overrider(auto& overrides_collector) { @@ -106,6 +188,85 @@ auto make_overrider(auto& overrides_collector) { }; } +//****************************************************************************** +template +auto make_appender_map_kkv(auto& overrides_collector) { + return [&overrides_collector](auto const& value) { // This is CLI::add_option_function callback. + overrides_collector.emplace_back( + [value](auto& to_override) { // This is to be executed to actually apply override. + auto& [k1, k2, v] = value; + (to_override.*Member)[{ cast(k1), cast(k2) }] = v; + }); + }; +} + +// TODO find more elegant +// https://stackoverflow.com/questions/72418821/getting-the-decltype-of-a-member-function +//****************************************************************************** +template +auto* add_per_material_name_option(auto const& name, auto const& description, CLI::App& app, auto& overrides_collector, std::optional>> const& enum_values_map = std::nullopt) { + using Value = MemberType::mapped_type; + + auto* option = app.add_option_function + >("--per-mat-"s + name, + make_appender_map_kkv(overrides_collector), + description + )->group("Mesher options (per Material)") + ->take_all() + ->delimiter(',') + ->transform(CLI::CheckedTransformer(axes_with_all, CLI::ignore_case).application_index(0).description("")) + ->type_name("["s + + CLI::detail::type_name() + ":" + + CLI::detail::generate_map(CLI::detail::smart_deref(axes_with_all), true) + "," + + CLI::detail::type_name() + "," + + CLI::detail::type_name() + + (enum_values_map.has_value() + ? ":" + CLI::detail::generate_map(CLI::detail::smart_deref(enum_values_map.value()), true) + : "") + + "]"); + + if(enum_values_map.has_value()) + option->transform(CLI::CheckedTransformer(enum_values_map.value(), CLI::ignore_case).application_index(2).description("")); + + return option; +} + +//****************************************************************************** +template +auto* add_per_material_type_option(auto const& name, auto const& description, CLI::App& app, auto& overrides_collector, std::optional>> const& enum_values_map = std::nullopt) { + using Value = MemberType::mapped_type; + auto* option = app.add_option_function + >("--per-mat-type-"s + name, + make_appender_map_kkv(overrides_collector), + description + )->group("Mesher options (per Material type)") + ->take_all() + ->delimiter(',') + ->transform(CLI::CheckedTransformer(axes_with_all, CLI::ignore_case).application_index(0).description("")) + ->transform(CLI::CheckedTransformer(material_types_with_all, CLI::ignore_case).application_index(1).description("")) + ->type_name("["s + + CLI::detail::type_name() + ":" + + CLI::detail::generate_map(CLI::detail::smart_deref(axes_with_all), true) + "," + + CLI::detail::type_name() + ":" + + CLI::detail::generate_map(CLI::detail::smart_deref(material_types_with_all), true) + "," + + CLI::detail::type_name() + + (enum_values_map.has_value() + ? ":" + CLI::detail::generate_map(CLI::detail::smart_deref(enum_values_map.value()), true) + : "") + + "]"); + + if(enum_values_map.has_value()) + option->transform(CLI::CheckedTransformer(enum_values_map.value(), CLI::ignore_case).application_index(2).description("")); + + return option; +} + //****************************************************************************** app::OpenEMSH::Params cli(int const argc, char* argv[]) { app::OpenEMSH::Params params; @@ -133,11 +294,6 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { app.add_option("-o,--output", params.output, "Output CSX file. If different from input, will copy and extend it. (Defaults to input, if provided)")->type_name(format("{}:FILE", CLI::detail::type_name())); app.add_flag("-f,--force", params.force, "Allow overwriting a file.")->trigger_on_parse(); - static std::map> const output_formats { - { "csx", app::OpenEMSH::Params::OutputFormat::CSX }, - { "plantuml", app::OpenEMSH::Params::OutputFormat::PLANTUML }, - { "prettyprint", app::OpenEMSH::Params::OutputFormat::PRETTYPRINT } - }; // https://github.com/CLIUtils/CLI11/issues/554#issuecomment-932782337 app.add_option("--output-format", params.output_format, "Output format.")->transform(CLI::CheckedTransformer(output_formats, CLI::ignore_case).description(CLI::detail::generate_map(CLI::detail::smart_deref(output_formats), true)))->default_str(reverse_kv(output_formats).at(params.output_format)); @@ -147,21 +303,16 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { app.add_option("--read-oemsh-params", params.read_oemsh_params, "Read OpenEMSH parameters from file, if any.")->group("Input options")->default_str(to_string(params.read_oemsh_params)); app.add_option("--integrate-old-mesh", params.keep_old_mesh, "Keep current meshlines and integrate those in the final mesh.")->group("Input options")->default_str(to_string(params.keep_old_mesh)); - static std::map> const axes { - { "x", domain::Axis::X }, - { "y", domain::Axis::Y }, - { "z", domain::Axis::Z } - }; app.add_option_function("--add-fixed-meshline", make_overrider<&domain::Params::input_fixed_meshlines>(domain_overrides), "Add MeshlinePolicy at fixed position." )->group("Mesher options") ->take_all() ->delimiter(',') - ->transform(CLI::CheckedTransformer(axes, CLI::ignore_case).application_index(0).description("")) + ->transform(CLI::CheckedTransformer(axes_without_all, CLI::ignore_case).application_index(0).description("")) ->type_name("["s + CLI::detail::type_name() + ":" - + CLI::detail::generate_map(CLI::detail::smart_deref(axes), true) + "," + + CLI::detail::generate_map(CLI::detail::smart_deref(axes_without_all), true) + "," + CLI::detail::type_name() + "]"); From 7bcab5e86e787c2ffde9707ada9665b47e86f68a Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 21 Sep 2026 03:10:29 +0200 Subject: [PATCH 03/11] GUI : add per material option handling --- src/CMakeLists.txt | 2 + src/infra/utils/to_string.cpp | 20 ++ src/infra/utils/to_string.hpp | 4 + src/ui/qt/edit/edit_delegate.cpp | 306 ++++++++++++++++-- src/ui/qt/edit/edit_delegate.hpp | 28 ++ src/ui/qt/edit/edit_dialog.hpp | 4 +- .../edit_dialog_per_axis_per_criteria.cpp | 89 +++++ .../edit_dialog_per_axis_per_criteria.hpp | 34 ++ src/ui/qt/edit/edit_model.hpp | 11 +- .../edit/edit_model_per_axis_per_criteria.cpp | 16 + .../edit/edit_model_per_axis_per_criteria.hpp | 44 +++ .../edit/edit_model_per_axis_per_criteria.ipp | 134 ++++++++ src/utils/enum_utils.hpp | 15 + src/utils/variant_utils.hpp | 15 + 14 files changed, 682 insertions(+), 40 deletions(-) create mode 100644 src/ui/qt/edit/edit_dialog_per_axis_per_criteria.cpp create mode 100644 src/ui/qt/edit/edit_dialog_per_axis_per_criteria.hpp create mode 100644 src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp create mode 100644 src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp create mode 100644 src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp create mode 100644 src/utils/variant_utils.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 66d4060..10229d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,6 +97,7 @@ target_sources( openemsh_bin "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/utils/nodegraph/wire.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_delegate.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_dialog.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_dialog_per_axis_per_criteria.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_global.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_angle.cpp" @@ -105,6 +106,7 @@ target_sources( openemsh_bin "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_edge.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_interval.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_meshline_policy.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_per_axis_per_criteria.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_axis.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_plane.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_style.cpp" diff --git a/src/infra/utils/to_string.cpp b/src/infra/utils/to_string.cpp index 64dbd00..09ad0c8 100644 --- a/src/infra/utils/to_string.cpp +++ b/src/infra/utils/to_string.cpp @@ -4,6 +4,7 @@ /// @author Thomas Lepoix ///***************************************************************************** +#include "utils/variant_utils.hpp" #include "utils/unreachable.hpp" #include "to_string.hpp" @@ -108,3 +109,22 @@ string to_string(domain::Material::Type type) noexcept { default: ::unreachable(); } } + +//****************************************************************************** +string to_string(optional const& axis) noexcept { + if(axis.has_value()) + return to_string(axis.value()); + else + return "*"; +} + +//****************************************************************************** +string to_string(optional const& mat) noexcept { + if(mat.has_value()) + return visit(overloaded { + [&](string const& name) { return name; }, + [&](domain::Material::Type const& type) { return to_string(type); } + }, mat.value()); + else + return "*"; +} diff --git a/src/infra/utils/to_string.hpp b/src/infra/utils/to_string.hpp index 686ad8a..ea2a890 100644 --- a/src/infra/utils/to_string.hpp +++ b/src/infra/utils/to_string.hpp @@ -6,6 +6,8 @@ #pragma once +#include + #include "domain/geometrics/edge.hpp" #include "domain/geometrics/polygon.hpp" #include "domain/geometrics/space.hpp" @@ -24,3 +26,5 @@ std::string to_string(domain::Axis axis) noexcept; std::string to_string(domain::Normal normal) noexcept; std::string to_string(domain::Plane plane) noexcept; std::string to_string(domain::Material::Type type) noexcept; +std::string to_string(std::optional const& axis) noexcept; +std::string to_string(std::optional const& mat) noexcept; diff --git a/src/ui/qt/edit/edit_delegate.cpp b/src/ui/qt/edit/edit_delegate.cpp index 8d0d9a0..1df9742 100644 --- a/src/ui/qt/edit/edit_delegate.cpp +++ b/src/ui/qt/edit/edit_delegate.cpp @@ -4,9 +4,9 @@ /// @author Thomas Lepoix ///***************************************************************************** -#include #include #include +#include #include #include #include @@ -18,19 +18,43 @@ #include "domain/mesh/meshline_policy.hpp" #include "infra/utils/to_string.hpp" #include "utils/concepts.hpp" +#include "utils/enum_utils.hpp" #include "utils/unconst.hpp" -#include "utils/unreachable.hpp" +#include "utils/variant_utils.hpp" +#include "edit_model_per_axis_per_criteria.hpp" +#include "edit_dialog_per_axis_per_criteria.hpp" +#include "edit_dialog.hpp" +#include "ui_edit_dialog.h" #include "edit_delegate.hpp" Q_DECLARE_METATYPE(domain::MeshlinePolicy::Normal) Q_DECLARE_METATYPE(domain::MeshlinePolicy::Policy) +using PerAxisPerMaterialDouble = domain::Params::PerAxisPer; +using PerAxisPerMaterialSizeT = domain::Params::PerAxisPer; + +Q_DECLARE_METATYPE(PerAxisPerMaterialDouble) +Q_DECLARE_METATYPE(PerAxisPerMaterialSizeT) + namespace ui::qt { //****************************************************************************** using Normal = domain::MeshlinePolicy::Normal; using Policy = domain::MeshlinePolicy::Policy; +using MaterialType = domain::Material::Type; +using OptionalAxis = std::optional; + +//****************************************************************************** +OptionalMaterialState state_of(OptionalMaterial const& mat) { + if(mat.has_value()) + return std::visit(overloaded { + [&](std::string const& s) { return s.empty() ? OptionalMaterialState::ALL : OptionalMaterialState::NAME; }, + [&](MaterialType const&) { return OptionalMaterialState::TYPE; } + }, mat.value()); + else + return OptionalMaterialState::ALL; +} //****************************************************************************** static auto constexpr AllNormal = std::array { @@ -47,32 +71,41 @@ static auto constexpr AllPolicy = std::array { }; //****************************************************************************** -template -QString convert(E e) { +static auto constexpr AllMaterialType = std::array { + MaterialType::PORT, + MaterialType::CONDUCTOR, + MaterialType::DIELECTRIC, + MaterialType::AIR +}; + +//****************************************************************************** +static auto constexpr AllOptionalAxis = std::array, 4> { + domain::Axis::X, + domain::Axis::Y, + domain::Axis::Z, + domain::Params::ALL +}; + +//****************************************************************************** +template +QString to_qstring(E e) { return QString::fromStdString(to_string(e)); } //****************************************************************************** -template -QStringList convert(std::array const& in) { +template +QStringList to_qstring(std::array const& in) { QStringList out; for(auto& e : in) - out.push_back(convert(e)); + out.push_back(to_qstring(e)); return out; } //****************************************************************************** -template -constexpr std::size_t key(E e, std::array const& all) { - for(std::size_t i = 0; i < all.size(); ++i) - if(all[i] == e) - return i; - unreachable(); -} - -//****************************************************************************** -static constexpr auto key(Normal normal) { return key(normal, AllNormal); } -static constexpr auto key(Policy policy) { return key(policy, AllPolicy); } +static constexpr auto key(Normal normal) { return ::key(normal, AllNormal); } +static constexpr auto key(Policy policy) { return ::key(policy, AllPolicy); } +static constexpr auto key(MaterialType type) { return ::key(type, AllMaterialType); } +static constexpr auto key(OptionalAxis axis) { return ::key(axis, AllOptionalAxis); } //****************************************************************************** static_assert(AllNormal[key(Normal::NONE)] == Normal::NONE); @@ -81,6 +114,99 @@ static_assert(AllNormal[key(Normal::MAX)] == Normal::MAX); static_assert(AllPolicy[key(Policy::ONELINE)] == Policy::ONELINE); static_assert(AllPolicy[key(Policy::HALFS)] == Policy::HALFS); static_assert(AllPolicy[key(Policy::THIRDS)] == Policy::THIRDS); +static_assert(AllMaterialType[key(MaterialType::PORT)] == MaterialType::PORT); +static_assert(AllMaterialType[key(MaterialType::CONDUCTOR)] == MaterialType::CONDUCTOR); +static_assert(AllMaterialType[key(MaterialType::DIELECTRIC)] == MaterialType::DIELECTRIC); +static_assert(AllMaterialType[key(MaterialType::AIR)] == MaterialType::AIR); +static_assert(AllOptionalAxis[key(domain::Axis::X)] == domain::Axis::X); +static_assert(AllOptionalAxis[key(domain::Axis::Y)] == domain::Axis::Y); +static_assert(AllOptionalAxis[key(domain::Axis::Z)] == domain::Axis::Z); +static_assert(AllOptionalAxis[key((OptionalAxis) domain::Params::ALL)] == domain::Params::ALL); + +//****************************************************************************** +QString const OptionalMaterialEditor::by_name_str = "By name…"; + +//****************************************************************************** +OptionalMaterialEditor::OptionalMaterialEditor(QWidget* parent) +: QComboBox(parent) +{ + addItem("*", -1); + addItems(to_qstring(AllMaterialType)); + addItem(by_name_str, -2); + + setEditable(false); + + connect( + this, &QComboBox::activated, + this, &OptionalMaterialEditor::on_activated); +} + +//****************************************************************************** +void OptionalMaterialEditor::on_activated(int index) { + if(index == index_of_by_name()) { + setEditable(true); + clearEditText(); + lineEdit()->setPlaceholderText(by_name_str); + connect( + lineEdit(), &QLineEdit::editingFinished, + this, &OptionalMaterialEditor::editing_finished, + Qt::UniqueConnection); + } else { + setEditable(false); + emit editing_finished(); + } +} + +//****************************************************************************** +void OptionalMaterialEditor::set_value(OptionalMaterial const& material) { + blockSignals(true); + setEditable(false); + + switch(state_of(material)) { + case OptionalMaterialState::ALL: + setCurrentIndex(index_of_wildcard()); + break; + case OptionalMaterialState::TYPE: + setCurrentIndex(key(std::get(material.value())) + 1); + break; + case OptionalMaterialState::NAME: + setCurrentIndex(index_of_by_name()); + setEditable(true); + setEditText(QString::fromStdString(std::get(material.value()))); + lineEdit()->setCursorPosition(lineEdit()->text().length()); + break; + default: unreachable(); + } + + blockSignals(false); +} + +//****************************************************************************** +OptionalMaterial OptionalMaterialEditor::get_value() const { + int index = currentIndex(); + if(index == index_of_wildcard()) { + return domain::Params::ALL; + } else if(index == index_of_by_name()) { + QString text = currentText().trimmed(); + if(text.isEmpty()) + return domain::Params::ALL; + else + return text.toStdString(); + } else { + return AllMaterialType[index - 1]; + } +} + +//****************************************************************************** +int OptionalMaterialEditor::index_of_wildcard() const { +// return findData(-1); + return 0; +} + +//****************************************************************************** +int OptionalMaterialEditor::index_of_by_name() const { + return findData(-2); +} //****************************************************************************** EditDelegate::EditDelegate(QObject* parent) @@ -93,14 +219,48 @@ EditDelegate::EditDelegate(QObject* parent) QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& option, QModelIndex const& index) const { auto const type = index.data(Qt::UserRole + 1).typeId(); - auto const handle_enum = [&](std::array const& all) { + auto const handle_enum = [&](std::array const& all) { auto* widget = new QComboBox(parent); - widget->addItems(convert(all)); + widget->addItems(to_qstring(all)); widget->setToolTip(index.data(Qt::ToolTipRole).toString()); static_cast(widget->view())->setToolTip(index.data(Qt::ToolTipRole).toString()); return widget; }; + auto const handle_per_axis_per_criteria = [&, parent](QString criteria_name, T::value_type const& default_value) { + QString title = static_cast(index.model())->item(index.row(), 0)->text(); + QString tooltip = static_cast(index.model())->item(index.row(), 0)->toolTip(); + auto* model = new EditModelPerAxisPerCriteria(criteria_name, default_value, parent); + auto* dialog = new EditDialogPerAxisPerCriteria(model, title, tooltip, parent); + model->setParent(dialog); + connect( + dialog, &QDialog::accepted, + [this, dialog]() { + emit unconst(this)->commitData(dialog); + emit unconst(this)->closeEditor(dialog); + } + ); + connect( + dialog, &QDialog::rejected, + [this, dialog]() { + emit unconst(this)->closeEditor(dialog); + } + ); + return dialog; + }; + + auto const handle_optional_material = [&]() { + auto* widget = new OptionalMaterialEditor(parent); + connect( + widget, &OptionalMaterialEditor::editing_finished, + [this, widget]() { + emit unconst(this)->commitData(widget); + emit unconst(this)->closeEditor(widget); + } + ); + return widget; + }; + auto const bound_normal_choice_by_current_policy = [&](auto const* cb) { if(policy_index.isValid()) { auto policy = policy_index.data(Qt::UserRole + 1).value(); @@ -131,6 +291,14 @@ QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& return cb; } else if(type == qMetaTypeId()) { return handle_enum(AllPolicy); + } else if(type == qMetaTypeId()) { + return handle_enum(AllOptionalAxis); + } else if(type == qMetaTypeId()) { + return handle_optional_material(); + } else if(type == qMetaTypeId()) { + return handle_per_axis_per_criteria.operator()("Material", {{ domain::Params::ALL, domain::Params::ALL }, 0.0 }); + } else if(type == qMetaTypeId()) { + return handle_per_axis_per_criteria.operator()("Material", {{ domain::Params::ALL, domain::Params::ALL }, 0 }); } else { return QStyledItemDelegate::createEditor(parent, option, index); } @@ -140,15 +308,35 @@ QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& void EditDelegate::setEditorData(QWidget* editor, QModelIndex const& index) const { auto const type = index.data(Qt::UserRole + 1).typeId(); - auto const handle_enum = [&]() { + auto const handle_enum = [&]() { auto* cb = static_cast(editor); cb->setCurrentIndex((int) key(index.data(Qt::UserRole + 1).value())); }; + auto const handle_optional_material = [&]() { + auto* om = static_cast(editor); + om->set_value(index.data(Qt::UserRole + 1).value()); + }; + + auto const handle_per_axis_per_criteria = [&]() { + auto* d = static_cast(editor); + auto* m = static_cast*>(d->get_model()); + m->set(index.data(Qt::UserRole + 1).value()); + }; + if(type == qMetaTypeId()) { handle_enum.operator()(); } else if(type == qMetaTypeId()) { handle_enum.operator()(); + } else if(type == qMetaTypeId()) { + handle_enum.operator()(); + } else if(type == qMetaTypeId()) { + handle_optional_material(); + return; // Necessary! + } else if(type == qMetaTypeId()) { + handle_per_axis_per_criteria.operator()(); + } else if(type == qMetaTypeId()) { + handle_per_axis_per_criteria.operator()(); } QStyledItemDelegate::setEditorData(editor, index); @@ -158,12 +346,23 @@ void EditDelegate::setEditorData(QWidget* editor, QModelIndex const& index) cons void EditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QModelIndex const& index) const { auto const type = index.data(Qt::UserRole + 1).typeId(); - auto const handle_enum = [&](std::array const& all) { + auto const handle_enum = [&](std::array const& all) { auto const* cb = static_cast(editor); model->setData(index, QVariant::fromValue(all[cb->currentIndex()]), Qt::UserRole + 1); model->setData(index, cb->currentText(), Qt::EditRole); }; + auto const handle_optional_material = [&]() { + auto* om = qobject_cast(editor); + model->setData(index, QVariant::fromValue(om->get_value()), Qt::UserRole + 1); + }; + + auto const handle_per_axis_per_criteria = [&]() { + auto* d = static_cast(editor); + auto* m = static_cast*>(d->get_model()); + model->setData(index, QVariant::fromValue(m->get()), Qt::UserRole + 1); + }; + auto const enforce_coherent_normal_regarding_current_policy = [&]() { if(normal_index.isValid()) { auto policy = model->data(index, Qt::UserRole + 1).value(); @@ -191,6 +390,15 @@ void EditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QMod } else if(type == qMetaTypeId()) { handle_enum(AllPolicy); enforce_coherent_normal_regarding_current_policy(); + } else if(type == qMetaTypeId()) { + handle_enum(AllOptionalAxis); + } else if(type == qMetaTypeId()) { + handle_optional_material(); + return; // Necessary! + } else if(type == qMetaTypeId()) { + handle_per_axis_per_criteria.operator()(); + } else if(type == qMetaTypeId()) { + handle_per_axis_per_criteria.operator()(); } QStyledItemDelegate::setModelData(editor, model, index); @@ -200,11 +408,19 @@ void EditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QMod void EditDelegate::paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const { auto const type = index.data(Qt::UserRole + 1).typeId(); - auto const handle_enum = [&]() { + auto const handle_enum = [&]() { auto* model = unconst(index.model()); model->setData( index, - convert(index.data(Qt::UserRole + 1).value()), + to_qstring(index.data(Qt::UserRole + 1).value()), + Qt::DisplayRole); + }; + + auto const handle_optional_material = [&]() { + auto* model = unconst(static_cast(index.model())); + model->setData( + index, + to_qstring(index.data(Qt::UserRole + 1).value()), Qt::DisplayRole); }; @@ -214,6 +430,14 @@ void EditDelegate::paint(QPainter* painter, QStyleOptionViewItem const& option, } else if(type == qMetaTypeId()) { handle_enum.operator()(); unconst(this)->policy_index = index; + } else if(type == qMetaTypeId()) { + handle_enum.operator()(); + } else if(type == qMetaTypeId()) { + handle_optional_material(); + } else if(type == qMetaTypeId() + || type == qMetaTypeId()) { + auto* model = unconst(index.model()); + model->setData(index, "...", Qt::DisplayRole); } QStyledItemDelegate::paint(painter, option, index); @@ -225,14 +449,32 @@ bool EditDelegate::eventFilter(QObject* object, QEvent* event) { if(!editor) return QStyledItemDelegate::eventFilter(object, event); - if(event->type() == QEvent::KeyPress) { - switch(static_cast(event)->key()) { - case Qt::Key_Enter: [[fallthrough]]; - case Qt::Key_Return: - if(qobject_cast(editor)) { - emit commitData(editor); - emit closeEditor(editor); - return true; + if(auto* d = qobject_cast(editor); d) { + if(event->type() == QEvent::KeyPress) { + switch(static_cast(event)->key()) { + case Qt::Key_Enter: [[fallthrough]]; + case Qt::Key_Return: + d->on_dbb_ok_accepted(); + return true; // Block + default: + // Handled by QStyledItemDelegate::eventFilter + } + } else if(event->type() == QEvent::Show) { + return false; // Propagate further + } else if(event->type() == QEvent::Hide) { + return false; // Propagate further + } else { + // Handled by QStyledItemDelegate::eventFilter + } + } else if(auto* om = qobject_cast(editor); om) { + if(event->type() == QEvent::KeyPress) { + switch(static_cast(event)->key()) { + case Qt::Key_Enter: [[fallthrough]]; + case Qt::Key_Return: + emit om->editing_finished(); + return true; // Block + default: + // Handled by QStyledItemDelegate::eventFilter } } } diff --git a/src/ui/qt/edit/edit_delegate.hpp b/src/ui/qt/edit/edit_delegate.hpp index b0910ab..094e7a6 100644 --- a/src/ui/qt/edit/edit_delegate.hpp +++ b/src/ui/qt/edit/edit_delegate.hpp @@ -6,11 +6,39 @@ #pragma once +#include #include #include +#include "domain/global.hpp" + namespace ui::qt { +//****************************************************************************** +using OptionalMaterial = std::optional; +enum class OptionalMaterialState { ALL, TYPE, NAME }; + +//****************************************************************************** +class OptionalMaterialEditor : public QComboBox { + Q_OBJECT +public: + explicit OptionalMaterialEditor(QWidget* parent = nullptr); + + void set_value(OptionalMaterial const& material); + OptionalMaterial get_value() const; + +signals: + void editing_finished(); + +private slots: + void on_activated(int index); + +private: + static QString const by_name_str; + int index_of_wildcard() const; + int index_of_by_name() const; +}; + //****************************************************************************** class EditDelegate : public QStyledItemDelegate { public: diff --git a/src/ui/qt/edit/edit_dialog.hpp b/src/ui/qt/edit/edit_dialog.hpp index 314b6ef..40a4989 100644 --- a/src/ui/qt/edit/edit_dialog.hpp +++ b/src/ui/qt/edit/edit_dialog.hpp @@ -22,11 +22,11 @@ class EditModel; //****************************************************************************** class EditDialog : public QDialog { Q_OBJECT -private: +protected: std::unique_ptr ui; EditDelegate* delegate; -private slots: +public slots: void on_dbb_ok_accepted(); void on_dbb_ok_rejected(); diff --git a/src/ui/qt/edit/edit_dialog_per_axis_per_criteria.cpp b/src/ui/qt/edit/edit_dialog_per_axis_per_criteria.cpp new file mode 100644 index 0000000..5bc0063 --- /dev/null +++ b/src/ui/qt/edit/edit_dialog_per_axis_per_criteria.cpp @@ -0,0 +1,89 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include +#include +#include +#include + +#include + +#include "edit_model_per_axis_per_criteria.hpp" +#include "ui_edit_dialog.h" + +#include "edit_dialog_per_axis_per_criteria.hpp" + +namespace ui::qt { + +//****************************************************************************** +EditDialogPerAxisPerCriteria::EditDialogPerAxisPerCriteria(IEditModelPerAxisPerCriteria* model, QString const& title, QString const& tooltip, QWidget* parent) +: EditDialog(model, title, parent) +{ + auto* dbb_action = new QDialogButtonBox(this); + pb_delete = new QPushButton(QIcon::fromTheme(QIcon::ThemeIcon::ListRemove), "Delete rules", dbb_action); + pb_add = new QPushButton(QIcon::fromTheme(QIcon::ThemeIcon::ListAdd), "Add rule", dbb_action); + dbb_action->addButton(pb_delete, QDialogButtonBox::ActionRole); + dbb_action->addButton(pb_add, QDialogButtonBox::ActionRole); + ui->vb_main->insertWidget(1, dbb_action); + + ui->tv_properties->setToolTip(tooltip); + + setModal(true); + + connect(pb_delete, &QPushButton::clicked, + [this]() { + std::set rows; + for(auto& index : ui->tv_properties->selectionModel()->selectedIndexes()) { + rows.insert(index.row()); + } + for(auto const& row : rows | std::views::reverse) { + ui->tv_properties->model()->removeRow(row); + } + }); + connect(pb_add, &QPushButton::clicked, + [this]() { + get_model()->add_new_row(); + }); +} + +//****************************************************************************** +IEditModelPerAxisPerCriteria* EditDialogPerAxisPerCriteria::get_model() const { + return static_cast(ui->tv_properties->model()); +} + +//****************************************************************************** +void EditDialogPerAxisPerCriteria::focusInEvent(QFocusEvent* event) { + ui->tv_properties->setFocus(); +} + +//****************************************************************************** +void EditDialogPerAxisPerCriteria::showEvent(QShowEvent* event) { + QDialog::showEvent(event); + + if(parentWidget()) { + QPoint parent_center = parentWidget()->mapToGlobal(parentWidget()->rect().center()); + QPoint dialog_center = rect().center(); + move(parent_center - dialog_center); + } +} + +//****************************************************************************** +void EditDialogPerAxisPerCriteria::keyPressEvent(QKeyEvent* event) { + switch(event->key()) { + case Qt::Key_Plus: + pb_add->click(); + break; + case Qt::Key_Minus: [[fallthrough]]; + case Qt::Key_Delete: + pb_delete->click(); + break; + default: + EditDialog::keyPressEvent(event); + } +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_dialog_per_axis_per_criteria.hpp b/src/ui/qt/edit/edit_dialog_per_axis_per_criteria.hpp new file mode 100644 index 0000000..aecbabf --- /dev/null +++ b/src/ui/qt/edit/edit_dialog_per_axis_per_criteria.hpp @@ -0,0 +1,34 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include "edit_dialog.hpp" + +class QPushButton; + +namespace ui::qt { + +class IEditModelPerAxisPerCriteria; + +//****************************************************************************** +class EditDialogPerAxisPerCriteria : public EditDialog { +public: + explicit EditDialogPerAxisPerCriteria(IEditModelPerAxisPerCriteria* model, QString const& title = QString(), QString const& tooltip = QString(), QWidget* parent = nullptr); + + IEditModelPerAxisPerCriteria* get_model() const; + +protected: + void focusInEvent(QFocusEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void showEvent(QShowEvent* event) override; + +private: + QPushButton* pb_add; + QPushButton* pb_delete; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp index 20b298c..b28bc6e 100644 --- a/src/ui/qt/edit/edit_model.hpp +++ b/src/ui/qt/edit/edit_model.hpp @@ -11,7 +11,6 @@ #include #include "app/steps.hpp" -#include "utils/concepts.hpp" namespace ui::qt { @@ -40,8 +39,8 @@ class EditModel : public QStandardItemModel { static bool try_to_bool(Qt::CheckState const in, bool& out); private: - template - static void set_content(QStandardItem* item, E e); + template + static void set_content(QStandardItem* item, T t); static void set_content(QStandardItem* item, QString const& s); static void set_content(QStandardItem* item, bool b); static void set_uneditable(QStandardItem* item); @@ -51,9 +50,9 @@ class EditModel : public QStandardItemModel { }; //****************************************************************************** -template -void EditModel::set_content(QStandardItem* item, E e) { - item->setData(QVariant::fromValue(e)); +template +void EditModel::set_content(QStandardItem* item, T t) { + item->setData(QVariant::fromValue(t)); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp b/src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp new file mode 100644 index 0000000..41e02c8 --- /dev/null +++ b/src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp @@ -0,0 +1,16 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include "edit_model_per_axis_per_criteria.hpp" + +namespace ui::qt { + +//****************************************************************************** +IEditModelPerAxisPerCriteria::IEditModelPerAxisPerCriteria(QObject* parent) +: EditModel(parent) +{} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp b/src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp new file mode 100644 index 0000000..ee4fae2 --- /dev/null +++ b/src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp @@ -0,0 +1,44 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include "edit_model.hpp" + +namespace ui::qt { + +//****************************************************************************** +class IEditModelPerAxisPerCriteria : public EditModel { + Q_OBJECT +public: + explicit IEditModelPerAxisPerCriteria(QObject* parent = nullptr); + +public slots: + virtual void add_new_row() = 0; +}; + + +//****************************************************************************** +template +class EditModelPerAxisPerCriteria : public IEditModelPerAxisPerCriteria { +public: + explicit EditModelPerAxisPerCriteria(QString const& criteria_name, Var::value_type const& default_value, QObject* parent = nullptr); + void commit() override; + void add_new_row() override; + + void set(Var const& v); + Var const& get(); + +private: + Var::value_type const default_value; + Var var; + + QList make_row(Var::value_type const& value) const; +}; + +} // namespace ui::qt + +#include "edit_model_per_axis_per_criteria.ipp" diff --git a/src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp b/src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp new file mode 100644 index 0000000..3c23d8d --- /dev/null +++ b/src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp @@ -0,0 +1,134 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include + + +#include +#include +#include + +#include "app/steps.hpp" +#include "domain/global.hpp" +#include "utils/logger.hpp" + +Q_DECLARE_METATYPE(domain::Material::Type) +Q_DECLARE_METATYPE(std::optional) +Q_DECLARE_METATYPE(std::optional) + +namespace ui::qt { + +//****************************************************************************** +template +EditModelPerAxisPerCriteria::EditModelPerAxisPerCriteria(QString const& criteria_name, Var::value_type const& default_value, QObject* parent) +: IEditModelPerAxisPerCriteria(parent) +, default_value(default_value) +{ + setColumnCount(3); + setHorizontalHeaderLabels({ "Axis", criteria_name, "Value" }); +} + +//****************************************************************************** +template +void EditModelPerAxisPerCriteria::commit() { + Var v; + + using MaterialType = domain::Material::Type; + using OptionalMaterial = std::optional; + using OptionalAxis = std::optional; + + using Criteria = std::tuple_element_t<1, typename Var::key_type>; + using Value = Var::mapped_type; + + bool is_there_default = false; + + for(int i = 0; i < rowCount(); ++i) { + OptionalAxis k1 = item(i, 0)->data().template value(); + Criteria k2 = item(i, 1)->data().template value(); + + if(k1 == domain::Params::ALL && k2 == domain::Params::ALL) + is_there_default = true; + + Value value; + bool does_succeed = false; + if constexpr(std::is_integral_v) { + does_succeed = try_to_ulong(item(i, 2)->text(), value); + } else if constexpr(std::is_floating_point_v) { + does_succeed = try_to_double(item(i, 2)->text(), value); + } else { + // Rely on Qt meta type system. + v.emplace(std::make_tuple(k1, k2), item(i, 2)->data().template value()); +// static_assert(false, "Usupported case"); + } + + if(does_succeed) { + v.emplace(std::make_tuple(k1, k2), value); + } + } + + if(!is_there_default) { + log({ + .level = Logger::Level::WARNING, + .user_actions = { Logger::UserAction::OK }, + .message = "Default rule must be provided:", + .informative = "{*, *} = ?" + }); + } + + var = v; +} + +//****************************************************************************** +template +QList EditModelPerAxisPerCriteria::make_row(Var::value_type const& value) const { + auto const& [key, val] = value; + auto const& [k1, k2] = key; + + auto* item1 = new QStandardItem(); + item1->setText(QString::fromStdString(to_string(k1))); + item1->setData(QVariant::fromValue(k1)); + + auto* item2 = new QStandardItem(); + item2->setText(QString::fromStdString(to_string(k2))); + item2->setData(QVariant::fromValue(k2)); + + auto* item3 = new QStandardItem(); + if constexpr(std::is_arithmetic_v) { + item3->setText(QString::number(val)); + } else { + // Rely on Qt meta type system. + item3->setData(QVariant::fromValue(val)); +// static_assert(false, "Usupported case"); + } + + return { item1, item2, item3 }; +} + +//****************************************************************************** +template +void EditModelPerAxisPerCriteria::set(Var const& v) { + var = v; + + removeRows(0, rowCount()); + + for(auto const& value : v) { + appendRow(make_row(value)); + } +} + +//****************************************************************************** +template +Var const& EditModelPerAxisPerCriteria::get() { + return var; +} + +//****************************************************************************** +template +void EditModelPerAxisPerCriteria::add_new_row() { + appendRow(make_row(default_value)); +} + +} // namespace ui::qt diff --git a/src/utils/enum_utils.hpp b/src/utils/enum_utils.hpp index ed5b0fb..29d5546 100644 --- a/src/utils/enum_utils.hpp +++ b/src/utils/enum_utils.hpp @@ -8,6 +8,8 @@ #include +#include "unreachable.hpp" + /// @brief Counts variadic arguments ///***************************************************************************** template @@ -31,3 +33,16 @@ constexpr std::size_t va_count(Args&&...) { template \ using ARRAY = typename std::array; \ constexpr ARRAY All ##ENUM { __VA_ARGS__ }; + +/// @brief Returns the index of an enum value among an array that contains all +/// the enumerators. +///***************************************************************************** +// TODO rename key -> index ? +//template +template +constexpr std::size_t key(E e, std::array const& all) { + for(std::size_t i = 0; i < all.size(); ++i) + if(all[i] == e) + return i; + unreachable(); +} diff --git a/src/utils/variant_utils.hpp b/src/utils/variant_utils.hpp new file mode 100644 index 0000000..f25932d --- /dev/null +++ b/src/utils/variant_utils.hpp @@ -0,0 +1,15 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +//****************************************************************************** +template +struct overloaded : Ts... { using Ts::operator()...; }; + +//****************************************************************************** +template +overloaded(Ts...) -> overloaded; From 5029d1ec380a9613a5ff99caa4deb140587b3e81 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 24 Sep 2026 04:47:05 +0200 Subject: [PATCH 04/11] GUI : allow data validation on edition --- src/ui/qt/edit/edit_dialog.cpp | 4 ++-- src/ui/qt/edit/edit_model.cpp | 4 +++- src/ui/qt/edit/edit_model.hpp | 2 +- src/ui/qt/edit/edit_model_angle.cpp | 4 +++- src/ui/qt/edit/edit_model_angle.hpp | 2 +- ...del_conflict_diagonal_or_circular_zone.cpp | 4 +++- ...del_conflict_diagonal_or_circular_zone.hpp | 2 +- ...l_conflict_too_close_meshline_policies.cpp | 4 +++- ...l_conflict_too_close_meshline_policies.hpp | 2 +- src/ui/qt/edit/edit_model_edge.cpp | 4 +++- src/ui/qt/edit/edit_model_edge.hpp | 2 +- src/ui/qt/edit/edit_model_global.cpp | 5 ++++- src/ui/qt/edit/edit_model_global.hpp | 2 +- src/ui/qt/edit/edit_model_interval.cpp | 4 +++- src/ui/qt/edit/edit_model_interval.hpp | 2 +- src/ui/qt/edit/edit_model_meshline_policy.cpp | 4 +++- src/ui/qt/edit/edit_model_meshline_policy.hpp | 2 +- .../edit/edit_model_per_axis_per_criteria.hpp | 4 ++-- .../edit/edit_model_per_axis_per_criteria.ipp | 20 ++++++++++++++++--- 19 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/ui/qt/edit/edit_dialog.cpp b/src/ui/qt/edit/edit_dialog.cpp index 07fc0a8..2c035f6 100644 --- a/src/ui/qt/edit/edit_dialog.cpp +++ b/src/ui/qt/edit/edit_dialog.cpp @@ -36,9 +36,9 @@ EditDialog::~EditDialog() = default; //****************************************************************************** void EditDialog::on_dbb_ok_accepted() { QGuiApplication::setOverrideCursor(Qt::WaitCursor); - static_cast(ui->tv_properties->model())->commit(); + if(static_cast(ui->tv_properties->model())->commit()) + accept(); QGuiApplication::restoreOverrideCursor(); - accept(); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model.cpp b/src/ui/qt/edit/edit_model.cpp index 5363dfd..c3dc524 100644 --- a/src/ui/qt/edit/edit_model.cpp +++ b/src/ui/qt/edit/edit_model.cpp @@ -94,6 +94,8 @@ EditModel::EditModel(QObject* parent) } //****************************************************************************** -void EditModel::commit() {} +bool EditModel::commit() { + return true; +} } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp index b28bc6e..d0ab0c7 100644 --- a/src/ui/qt/edit/edit_model.hpp +++ b/src/ui/qt/edit/edit_model.hpp @@ -25,7 +25,7 @@ class EditModel : public QStandardItemModel { static EditModel* make(nodegraph::Node* node, QObject* parent = nullptr); explicit EditModel(QObject* parent = nullptr); - virtual void commit(); + virtual bool commit(); protected: static auto constexpr V = 2; // Value column index. diff --git a/src/ui/qt/edit/edit_model_angle.cpp b/src/ui/qt/edit/edit_model_angle.cpp index 92feb1a..a36f07f 100644 --- a/src/ui/qt/edit/edit_model_angle.cpp +++ b/src/ui/qt/edit/edit_model_angle.cpp @@ -27,7 +27,7 @@ EditModelAngle::EditModelAngle(domain::Angle* angle, QObject* parent) } //****************************************************************************** -void EditModelAngle::commit() { +bool EditModelAngle::commit() { auto state = angle->get_current_state(); std::array does_succeed = { @@ -39,7 +39,9 @@ void EditModelAngle::commit() { emit edit_from(app::Step::DETECT_CONFLICT_EIP, [&]() { angle->set_next_state(state); }); + return true; } + return false; } } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_angle.hpp b/src/ui/qt/edit/edit_model_angle.hpp index 3e3273d..a1dc97c 100644 --- a/src/ui/qt/edit/edit_model_angle.hpp +++ b/src/ui/qt/edit/edit_model_angle.hpp @@ -18,7 +18,7 @@ namespace ui::qt { class EditModelAngle : public EditModel { public: explicit EditModelAngle(domain::Angle* angle, QObject* parent = nullptr); - void commit() override; + bool commit() override; private: domain::Angle* angle; diff --git a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp index d717f13..7acb856 100644 --- a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp +++ b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp @@ -31,7 +31,7 @@ EditModelConflictDiagonalOrCircularZone::EditModelConflictDiagonalOrCircularZone } //****************************************************************************** -void EditModelConflictDiagonalOrCircularZone::commit() { +bool EditModelConflictDiagonalOrCircularZone::commit() { auto state = conflict->get_current_state(); std::array does_succeed = { @@ -46,7 +46,9 @@ void EditModelConflictDiagonalOrCircularZone::commit() { : app::Step::SOLVE_DIAG_ZONES_INTERVALS), [&]() { conflict->set_next_state(state); }); + return true; } + return false; } } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.hpp b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.hpp index e33fe09..c27ea41 100644 --- a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.hpp +++ b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.hpp @@ -18,7 +18,7 @@ namespace ui::qt { class EditModelConflictDiagonalOrCircularZone : public EditModel { public: explicit EditModelConflictDiagonalOrCircularZone(domain::ConflictDiagonalOrCircularZone* conflict, QObject* parent = nullptr); - void commit() override; + bool commit() override; private: domain::ConflictDiagonalOrCircularZone* conflict; diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp index e3149fb..8a5ffaa 100644 --- a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp @@ -26,7 +26,7 @@ EditModelConflictTooCloseMeshlinePolicies::EditModelConflictTooCloseMeshlinePoli } //****************************************************************************** -void EditModelConflictTooCloseMeshlinePolicies::commit() { +bool EditModelConflictTooCloseMeshlinePolicies::commit() { auto state = conflict->get_current_state(); std::array does_succeed = { @@ -37,7 +37,9 @@ void EditModelConflictTooCloseMeshlinePolicies::commit() { emit edit_from(app::Step::DETECT_AND_SOLVE_TCMLP, [&]() { conflict->set_next_state(state); }); + return true; } + return false; } } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp index 900752a..0c0a487 100644 --- a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp @@ -18,7 +18,7 @@ namespace ui::qt { class EditModelConflictTooCloseMeshlinePolicies : public EditModel { public: explicit EditModelConflictTooCloseMeshlinePolicies(domain::ConflictTooCloseMeshlinePolicies* conflict, QObject* parent = nullptr); - void commit() override; + bool commit() override; private: domain::ConflictTooCloseMeshlinePolicies* conflict; diff --git a/src/ui/qt/edit/edit_model_edge.cpp b/src/ui/qt/edit/edit_model_edge.cpp index bf29a63..a0f3dc7 100644 --- a/src/ui/qt/edit/edit_model_edge.cpp +++ b/src/ui/qt/edit/edit_model_edge.cpp @@ -27,7 +27,7 @@ EditModelEdge::EditModelEdge(domain::Edge* edge, QObject* parent) } //****************************************************************************** -void EditModelEdge::commit() { +bool EditModelEdge::commit() { auto state = edge->get_current_state(); std::array does_succeed = { @@ -39,7 +39,9 @@ void EditModelEdge::commit() { emit edit_from(app::Step::DETECT_CONFLICT_EIP, [&]() { edge->set_next_state(state); }); + return true; } + return false; } } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_edge.hpp b/src/ui/qt/edit/edit_model_edge.hpp index 31b7e35..e7b96b5 100644 --- a/src/ui/qt/edit/edit_model_edge.hpp +++ b/src/ui/qt/edit/edit_model_edge.hpp @@ -18,7 +18,7 @@ namespace ui::qt { class EditModelEdge : public EditModel { public: explicit EditModelEdge(domain::Edge* edge, QObject* parent = nullptr); - void commit() override; + bool commit() override; private: domain::Edge* edge; diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index 52fa7f7..3314f50 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -43,7 +43,7 @@ EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) } //****************************************************************************** -void EditModelGlobal::commit() { +bool EditModelGlobal::commit() { domain::Params params; std::array does_succeed = { @@ -60,6 +60,9 @@ void EditModelGlobal::commit() { emit edit_from(app::Step::DETECT_DIAG_ZONES, [&]() { global->set_next_state(params); }); + return true; + } else { + return false; } } diff --git a/src/ui/qt/edit/edit_model_global.hpp b/src/ui/qt/edit/edit_model_global.hpp index e1c2f84..9d9a147 100644 --- a/src/ui/qt/edit/edit_model_global.hpp +++ b/src/ui/qt/edit/edit_model_global.hpp @@ -18,7 +18,7 @@ namespace ui::qt { class EditModelGlobal : public EditModel { public: explicit EditModelGlobal(domain::GlobalParams* global, QObject* parent = nullptr); - void commit() override; + bool commit() override; private: domain::GlobalParams* global; diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index 3115f59..081e255 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -39,7 +39,7 @@ EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent } //****************************************************************************** -void EditModelInterval::commit() { +bool EditModelInterval::commit() { auto state = interval->get_current_state(); std::array does_succeed = { @@ -54,7 +54,9 @@ void EditModelInterval::commit() { emit edit_from(app::Step::MESH, [&] { interval->set_next_state(state); }); + return true; } + return false; } } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_interval.hpp b/src/ui/qt/edit/edit_model_interval.hpp index 5369322..9c10931 100644 --- a/src/ui/qt/edit/edit_model_interval.hpp +++ b/src/ui/qt/edit/edit_model_interval.hpp @@ -18,7 +18,7 @@ namespace ui::qt { class EditModelInterval : public EditModel { public: explicit EditModelInterval(domain::Interval* interval, QObject* parent = nullptr); - void commit() override; + bool commit() override; private: domain::Interval* interval; diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index 4edc10c..d395301 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -46,7 +46,7 @@ EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshlin } //****************************************************************************** -void EditModelMeshlinePolicy::commit() { +bool EditModelMeshlinePolicy::commit() { auto state = meshline_policy->get_current_state(); auto const are_policy_and_normal_compatible = [&state]() { @@ -83,7 +83,9 @@ void EditModelMeshlinePolicy::commit() { emit edit_from(app::Step::DETECT_INTERVALS, [&]() { meshline_policy->set_next_state(state); }); + return true; } + return false; } } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_meshline_policy.hpp b/src/ui/qt/edit/edit_model_meshline_policy.hpp index 736c5d7..4626269 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.hpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.hpp @@ -18,7 +18,7 @@ namespace ui::qt { class EditModelMeshlinePolicy : public EditModel { public: explicit EditModelMeshlinePolicy(domain::MeshlinePolicy* meshline_policy, QObject* parent = nullptr); - void commit() override; + bool commit() override; private: domain::MeshlinePolicy* meshline_policy; diff --git a/src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp b/src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp index ee4fae2..f7cbbca 100644 --- a/src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp +++ b/src/ui/qt/edit/edit_model_per_axis_per_criteria.hpp @@ -25,8 +25,8 @@ public slots: template class EditModelPerAxisPerCriteria : public IEditModelPerAxisPerCriteria { public: - explicit EditModelPerAxisPerCriteria(QString const& criteria_name, Var::value_type const& default_value, QObject* parent = nullptr); - void commit() override; + EditModelPerAxisPerCriteria(QString const& criteria_name, Var::value_type const& default_value, QObject* parent = nullptr); + bool commit() override; void add_new_row() override; void set(Var const& v); diff --git a/src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp b/src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp index 3c23d8d..09a9791 100644 --- a/src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp +++ b/src/ui/qt/edit/edit_model_per_axis_per_criteria.ipp @@ -9,10 +9,12 @@ #include #include +#include #include #include "app/steps.hpp" #include "domain/global.hpp" +#include "infra/utils/to_string.hpp" #include "utils/logger.hpp" Q_DECLARE_METATYPE(domain::Material::Type) @@ -33,7 +35,7 @@ EditModelPerAxisPerCriteria::EditModelPerAxisPerCriteria(QString const& cri //****************************************************************************** template -void EditModelPerAxisPerCriteria::commit() { +bool EditModelPerAxisPerCriteria::commit() { Var v; using MaterialType = domain::Material::Type; @@ -66,6 +68,17 @@ void EditModelPerAxisPerCriteria::commit() { if(does_succeed) { v.emplace(std::make_tuple(k1, k2), value); + } else { + log({ + .level = Logger::Level::WARNING, + .user_actions = { Logger::UserAction::OK }, + .message = std::format("Invalid data at row {}: {{{}, {}}} = {}", + i+1, + to_string(k1), + to_string(k2), + item(i, 2)->text().toStdString()) + }); + return false; } } @@ -73,12 +86,13 @@ void EditModelPerAxisPerCriteria::commit() { log({ .level = Logger::Level::WARNING, .user_actions = { Logger::UserAction::OK }, - .message = "Default rule must be provided:", - .informative = "{*, *} = ?" + .message = "Default rule must be provided: {*, *} = ?" }); + return false; } var = v; + return true; } //****************************************************************************** From d18e8c226f5f4220db5bd14e02226243f71ea803 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 1 Oct 2026 07:58:00 +0200 Subject: [PATCH 05/11] add parser & serializer per material option handling --- src/infra/parsers/parser_from_csx.cpp | 54 +++++++++++++++++++++ src/infra/serializers/serializer_to_csx.cpp | 54 ++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/infra/parsers/parser_from_csx.cpp b/src/infra/parsers/parser_from_csx.cpp index 8099fb2..6afc94e 100644 --- a/src/infra/parsers/parser_from_csx.cpp +++ b/src/infra/parsers/parser_from_csx.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -130,6 +131,59 @@ expected ParserFromCsx::Pimpl::parse_oemsh(pugi::xml_node const& n if(auto a = global_params.attribute("dmax"); a) domain_params.dmax = a.as_double(); if(auto a = global_params.attribute("lmin"); a) domain_params.lmin = a.as_uint(); + static std::map> const axes { + { "X", domain::Axis::X }, + { "Y", domain::Axis::Y }, + { "Z", domain::Axis::Z }, + { "*", nullopt } + }; + static std::map> const material_types { + { "Conductor", domain::Material::Type::CONDUCTOR }, + { "Dielectric", domain::Material::Type::DIELECTRIC }, + { "Air", domain::Material::Type::AIR }, + { "*", nullopt } + }; + + auto const parse_per_material = [](auto& var, pugi::xml_node const& node) -> expected { + for(auto const& rule : node.children()) { + auto a = rule.attribute("Axis"); + auto v = rule.attribute("Value"); + auto n = rule.attribute("Name"); + auto t = rule.attribute("Type"); + + if(v + && (a && axes.contains(a.as_string())) + && (n || (t && material_types.contains(t.as_string())))) { + var.insert_or_assign( + { + axes.at(a.as_string()), + [&]() -> tuple_element<1, typename remove_reference_t::key_type>::type { + if(n) + return string(n.as_string()); + else + return material_types.at(t.as_string()); + } () + }, + [&]() { + using V = typename remove_reference_t::mapped_type; + if constexpr(is_floating_point_v) { + return v.as_double(); + } else if constexpr(is_integral_v) { + return v.as_uint(); + } else { + static_assert(false, "Usupported case"); + } + } () + ); + } else { + ostringstream rule_text; + rule.print(rule_text, "", pugi::format_raw); + return unexpected(format("Invalid rule \"{}\"", rule_text.str())); + } + } + return {}; + }; + pugi::xml_node fixed_meshlines = node.child("FixedMeshlines"); size_t delta_unit = fixed_meshlines.attribute("DeltaUnit").as_uint(1); AxisSpace lines = { diff --git a/src/infra/serializers/serializer_to_csx.cpp b/src/infra/serializers/serializer_to_csx.cpp index aee005f..c9a974a 100644 --- a/src/infra/serializers/serializer_to_csx.cpp +++ b/src/infra/serializers/serializer_to_csx.cpp @@ -6,9 +6,13 @@ #include +#include + #include "domain/mesh/meshline.hpp" +#include "domain/global.hpp" #include "domain/board.hpp" #include "utils/unreachable.hpp" +#include "utils/variant_utils.hpp" #include "serializer_to_csx.hpp" @@ -16,7 +20,7 @@ using namespace domain; using namespace std; //****************************************************************************** -string to_xml_node(Axis const axis) noexcept { +static string to_xml_node(Axis const axis) noexcept { switch(axis) { case X: return "XLines"; case Y: return "YLines"; @@ -25,6 +29,38 @@ string to_xml_node(Axis const axis) noexcept { } } +//****************************************************************************** +static string to_string(optional const& axis) noexcept { + if(axis.has_value()) + switch(axis.value()) { + case X: return "X"; + case Y: return "Y"; + case Z: return "Z"; + default: ::unreachable(); + } + else + return "*"; +} + +//****************************************************************************** +static string to_string(Material::Type const type) noexcept { + switch(type) { + case Material::Type::PORT: return "Port"; + case Material::Type::CONDUCTOR: return "Conductor"; + case Material::Type::DIELECTRIC: return "Dielectric"; + case Material::Type::AIR: return "Air"; + default: ::unreachable(); + } +} + +//****************************************************************************** +static string to_string(optional const& type) noexcept { + if(type.has_value()) + return to_string(type.value()); + else + return "*"; +} + //****************************************************************************** expected SerializerToCsx::run( Board& board, @@ -115,6 +151,22 @@ void SerializerToCsx::visit(Board& board) { add_meshlines_to_xml_doc(Z); if(params.with_oemsh_params) { + auto const handle_per_material = [](auto const& var, pugi::xml_node&& node) { + for(auto const& [k, v] : var) { + auto const& [axis, material] = k; + pugi::xml_node r = node.append_child("Rule"); + r.append_attribute("Axis").set_value(to_string(axis)); + if(material.has_value()) + std::visit(overloaded { + [&](std::string const& name) { r.append_attribute("Name").set_value(name); }, + [&](Material::Type const& type) { r.append_attribute("Type").set_value(to_string(type)); } + }, material.value()); + else + r.append_attribute("Type").set_value("*"); + r.append_attribute("Value").set_value(v); + } + }; + auto const& p = board.global_params->get_current_state(); pugi::xml_node oemsh = find_or_prepend_child(doc, "OpenEMSH"); pugi::xml_node global_params = find_or_append_child(oemsh, "GlobalParams"); From 0d6eec813f1cf39ebada19d26a7d0dd1c409c017 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 2 Oct 2026 00:38:56 +0200 Subject: [PATCH 06/11] add per material dmax & lmin & smoothness --- src/app/openemsh.cpp | 4 + src/app/steps.hpp | 8 +- src/domain/board.cpp | 72 ++++++++++++ src/domain/board.hpp | 3 + .../conflict_diagonal_or_circular_zone.cpp | 8 +- src/domain/global.cpp | 30 +++++ src/domain/global.hpp | 16 ++- src/domain/mesh/interval.cpp | 13 ++- src/domain/mesh/interval.hpp | 6 +- src/domain/mesh/meshline_policy.cpp | 3 +- src/domain/mesh/meshline_policy.hpp | 2 +- src/infra/parsers/parser_from_csx.cpp | 11 +- src/infra/serializers/serializer_to_csx.cpp | 9 +- src/ui/cli/cli.cpp | 25 ++--- src/ui/qt/edit/edit_model_global.cpp | 17 +-- .../processing_view/processing_interval.cpp | 12 ++ test/unit/app/test_openemsh.cpp | 31 ++++- test/unit/domain/mesh/test_interval.cpp | 106 +++++++++--------- test/unit/domain/test_board.cpp | 83 +++++++++++++- .../domain/test_meshline_policy_manager.cpp | 6 +- .../test_serializer_to_plantuml.cpp | 8 +- 21 files changed, 360 insertions(+), 113 deletions(-) diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index eedcea8..d700221 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -45,6 +45,8 @@ optional next(Step step) { case Step::DETECT_AND_SOLVE_TCMLP: return Step::DETECT_INTERVALS; case Step::DETECT_INTERVALS: + return Step::ADJUST_MESH_TO_MATERIAL; + case Step::ADJUST_MESH_TO_MATERIAL: return Step::DETECT_INTERVALS_PER_DIAG_ZONES; case Step::DETECT_INTERVALS_PER_DIAG_ZONES: return Step::SOLVE_DIAG_ZONES_INTERVALS; @@ -75,6 +77,7 @@ set that_and_after(Step step) { case DETECT_INDIVIDUAL_EDGES: out.emplace(DETECT_INDIVIDUAL_EDGES); [[fallthrough]]; case DETECT_AND_SOLVE_TCMLP: out.emplace(DETECT_AND_SOLVE_TCMLP); [[fallthrough]]; case DETECT_INTERVALS: out.emplace(DETECT_INTERVALS); [[fallthrough]]; + case ADJUST_MESH_TO_MATERIAL: out.emplace(ADJUST_MESH_TO_MATERIAL); [[fallthrough]]; case DETECT_INTERVALS_PER_DIAG_ZONES: out.emplace(DETECT_INTERVALS_PER_DIAG_ZONES); [[fallthrough]]; case SOLVE_DIAG_ZONES_INTERVALS: out.emplace(SOLVE_DIAG_ZONES_INTERVALS); [[fallthrough]]; case MESH: out.emplace(MESH); break; @@ -178,6 +181,7 @@ void OpenEMSH::run(std::set const& steps) const { handle(DETECT_INDIVIDUAL_EDGES, [&] { board->detect_individual_edges(); }); handle(DETECT_AND_SOLVE_TCMLP, [&] { board->detect_and_solve_too_close_meshline_policies(); }); handle(DETECT_INTERVALS, [&] { board->detect_intervals(); }); + handle(ADJUST_MESH_TO_MATERIAL, [&] { board->adjust_mesh_to_materials(); }); handle(DETECT_INTERVALS_PER_DIAG_ZONES, [&] { board->detect_intervals_per_diagonal_zones(); }); handle(SOLVE_DIAG_ZONES_INTERVALS, [&] { board->solve_diagonal_zones_intervals(); }); handle(MESH, [&] { board->mesh(); }); diff --git a/src/app/steps.hpp b/src/app/steps.hpp index a55f31e..dc75ae3 100644 --- a/src/app/steps.hpp +++ b/src/app/steps.hpp @@ -26,6 +26,7 @@ enum class Step { DETECT_INDIVIDUAL_EDGES, DETECT_AND_SOLVE_TCMLP, DETECT_INTERVALS, + ADJUST_MESH_TO_MATERIAL, DETECT_INTERVALS_PER_DIAG_ZONES, SOLVE_DIAG_ZONES_INTERVALS, MESH @@ -49,9 +50,10 @@ inline std::size_t index(std::optional step) { case Step::DETECT_INDIVIDUAL_EDGES: return 10; case Step::DETECT_AND_SOLVE_TCMLP: return 11; case Step::DETECT_INTERVALS: return 12; - case Step::DETECT_INTERVALS_PER_DIAG_ZONES: return 13; - case Step::SOLVE_DIAG_ZONES_INTERVALS: return 14; - case Step::MESH: return 15; + case Step::ADJUST_MESH_TO_MATERIAL: return 13; + case Step::DETECT_INTERVALS_PER_DIAG_ZONES: return 14; + case Step::SOLVE_DIAG_ZONES_INTERVALS: return 15; + case Step::MESH: return 16; default: ::unreachable(); } } diff --git a/src/domain/board.cpp b/src/domain/board.cpp index b018913..6b2210c 100644 --- a/src/domain/board.cpp +++ b/src/domain/board.cpp @@ -173,6 +173,43 @@ Board::Board( get_caretaker().take_care_of(polygon); } +//****************************************************************************** +shared_ptr Board::find_ambient_material(Axis axis, Coord const& coord) const { + // TODO is this order (view_axis) in phase with B1D cast(view_axis, B2D) ? + auto planes = [](Axis axis) -> array { + switch(axis) { + case X: return { ZX, XY }; + case Y: return { YZ, XY }; + case Z: return { YZ, ZX }; + default: ::unreachable(); + } + } (axis); + + vector, remove_const_t>> materials; + + for(ViewAxis view_axis : AllViewAxis) { + for(shared_ptr const& polygon : get_current_state().polygons[planes[view_axis]]) { + if(polygon->material + && does_overlap(cast(view_axis, polygon->bounding), coord)) { + materials.emplace_back(shared_ptr(polygon->material), polygon->priority); + } + } + } + + ranges::sort(materials, [](auto const& a, auto const& b) { + auto const& [material_a, priority_a] = a; + auto const& [material_b, priority_b] = b; + return priority_a != priority_b + ? priority_a < priority_b + : *material_a < *material_b; + }); + + if(!materials.empty()) + return materials.back().first; + else + return material; +} + //****************************************************************************** shared_ptr Board::find_ambient_material(Plane plane, Segment const& segment) const { return find_ambient_material(plane, segment, nullptr).first; @@ -619,6 +656,35 @@ void Board::add_fixed_meshline_policies(Axis axis) { bar.complete(); } +//****************************************************************************** +void Board::adjust_mesh_to_materials(Axis axis) { + auto [t, state_mlpm] = line_policy_manager->make_next_state(); + + auto [bar, i, _] = Progress::Bar::build( + state_mlpm.intervals[axis].size() + 1, + "["s + to_string(axis) + "] Adjusting mesh to Materials "); + + for(auto const& interval : state_mlpm.intervals[axis]) { + auto const& params = global_params->get_current_state(); + auto state_i = interval->get_current_state(); + auto material = find_ambient_material(axis, interval->m); + + auto const& lmin = global_params->get_lmin(axis, material.get(), params); + auto const& smoothness = global_params->get_smoothness(axis, material.get(), params); + state_i.main_material = material.get(); + state_i.dmax = min(state_i.dmax, global_params->get_dmax(axis, material.get(), params)); + state_i.before.lmin = lmin; + state_i.after.lmin = lmin; + state_i.before.smoothness = smoothness; + state_i.after.smoothness = smoothness; + + interval->set_state(t, state_i); + bar.tick(++i); + } + + bar.complete(); +} + //****************************************************************************** void Board::adjust_edges_to_materials() { for(auto const& plane : AllPlane) @@ -673,6 +739,12 @@ void Board::add_fixed_meshline_policies() { add_fixed_meshline_policies(axis); } +//****************************************************************************** +void Board::adjust_mesh_to_materials() { + for(auto const& axis : AllAxis) + adjust_mesh_to_materials(axis); +} + //****************************************************************************** void Board::auto_solve_all_edge_in_polygon() { for(auto const& plane : AllPlane) diff --git a/src/domain/board.hpp b/src/domain/board.hpp index f50f4c7..8463aa3 100644 --- a/src/domain/board.hpp +++ b/src/domain/board.hpp @@ -94,6 +94,7 @@ class Board void detect_diagonal_angles(Plane plane); void detect_diagonal_zones(Plane plane); void add_fixed_meshline_policies(Axis axis); + void adjust_mesh_to_materials(Axis axis); void adjust_edges_to_materials(); void detect_diagonal_angles(); @@ -104,6 +105,7 @@ class Board void detect_colinear_edges(); void detect_individual_edges(); void add_fixed_meshline_policies(); + void adjust_mesh_to_materials(); /// Mesh resolution dependant detection tasks ///************************************************************************* @@ -127,6 +129,7 @@ class Board std::size_t get_mesh_cell_number() const; private: + std::shared_ptr find_ambient_material(Axis axis, Coord const& coord) const; std::shared_ptr find_ambient_material(Plane plane, Segment const& segment) const; std::pair, std::remove_const_t> find_ambient_material(Plane plane, Segment const& segment, std::shared_ptr const& current_polygon) const; }; diff --git a/src/domain/conflicts/conflict_diagonal_or_circular_zone.cpp b/src/domain/conflicts/conflict_diagonal_or_circular_zone.cpp index 07a8902..7c1173b 100644 --- a/src/domain/conflicts/conflict_diagonal_or_circular_zone.cpp +++ b/src/domain/conflicts/conflict_diagonal_or_circular_zone.cpp @@ -20,7 +20,7 @@ using namespace std; ConflictDiagonalOrCircularZone::ConflictDiagonalOrCircularZone(Axis axis, vector const& angles, GlobalParams* global_params, Timepoint* t) : Originator(t, { .dmax = global_params->get_current_state().diagonal_dmax, - .lmin = global_params->get_current_state().lmin, + .lmin = global_params->get_lmin(axis), .minimal_angle = global_params->get_current_state().consecutive_diagonal_minimal_angle, .angles = angles }) , Conflict(Kind::DIAGONAL_OR_CIRCULAR_ZONE) @@ -89,8 +89,10 @@ void ConflictDiagonalOrCircularZone::solve_intervals() { for(Interval* interval : state.intervals) { auto state_i = interval->get_current_state(); - state_i.dmax = state.dmax; - interval->set_state(t, state_i); + if(state.dmax < state_i.dmax) { + state_i.dmax = state.dmax; + interval->set_state(t, state_i); + } } } diff --git a/src/domain/global.cpp b/src/domain/global.cpp index b2f1456..7c8ff80 100644 --- a/src/domain/global.cpp +++ b/src/domain/global.cpp @@ -132,4 +132,34 @@ auto const& GlobalParams::get_per_axis_per_criteria(Axis axis, Material const* m } } +//****************************************************************************** +auto GlobalParams::get_dmax(Axis axis, Material const* material) const -> decltype(Params::dmax)::mapped_type const& { + return get_dmax(axis, material, get_current_state()); +} + +//****************************************************************************** +auto GlobalParams::get_lmin(Axis axis, Material const* material) const -> decltype(Params::lmin)::mapped_type const& { + return get_lmin(axis, material, get_current_state()); +} + +//****************************************************************************** +auto GlobalParams::get_smoothness(Axis axis, Material const* material) const -> decltype(Params::smoothness)::mapped_type const& { + return get_smoothness(axis, material, get_current_state()); +} + +//****************************************************************************** +auto GlobalParams::get_dmax(Axis axis, Material const* material, Params const& state) const -> decltype(Params::dmax)::mapped_type const& { + return get_per_axis_per_criteria<&Params::dmax, decltype(Params::dmax)>(axis, material, state); +} + +//****************************************************************************** +auto GlobalParams::get_lmin(Axis axis, Material const* material, Params const& state) const -> decltype(Params::lmin)::mapped_type const& { + return get_per_axis_per_criteria<&Params::lmin, decltype(Params::lmin)>(axis, material, state); +} + +//****************************************************************************** +auto GlobalParams::get_smoothness(Axis axis, Material const* material, Params const& state) const -> decltype(Params::smoothness)::mapped_type const& { + return get_per_axis_per_criteria<&Params::smoothness, decltype(Params::smoothness)>(axis, material, state); +} + } // namespace domain diff --git a/src/domain/global.hpp b/src/domain/global.hpp index 4ddcb23..80dc067 100644 --- a/src/domain/global.hpp +++ b/src/domain/global.hpp @@ -15,6 +15,7 @@ #include "geometrics/space.hpp" #include "utils/state_management.hpp" +#include "material.hpp" namespace domain { @@ -22,9 +23,6 @@ namespace domain { struct Params { bool has_grid_already = false; // TODO would better fit in infra layer? double proximity_limit = 1; // TODO must be linked to initial d - double smoothness = 2; - std::size_t lmin = 2; - double dmax = 2.5; std::size_t diagonal_lmin = 2; double diagonal_dmax = 0.2; @@ -37,6 +35,10 @@ struct Params { static auto constexpr ALL = std::nullopt; using Mat = std::variant; + + PerAxisPer dmax = {{{ ALL, ALL }, 2.5 }}; + PerAxisPer lmin = {{{ ALL, ALL }, 2 }}; + PerAxisPer smoothness = {{{ ALL, ALL }, 2 }}; }; //****************************************************************************** @@ -45,6 +47,14 @@ class GlobalParams : public Originator { explicit GlobalParams(Timepoint* t); GlobalParams(Params params, Timepoint* t); + auto get_dmax(Axis axis, Material const* material = nullptr) const -> decltype(Params::dmax)::mapped_type const&; + auto get_lmin(Axis axis, Material const* material = nullptr) const -> decltype(Params::lmin)::mapped_type const&; + auto get_smoothness(Axis axis, Material const* material = nullptr) const -> decltype(Params::smoothness)::mapped_type const&; + + auto get_dmax(Axis axis, Material const* material, Params const& state) const -> decltype(Params::dmax)::mapped_type const&; + auto get_lmin(Axis axis, Material const* material, Params const& state) const -> decltype(Params::lmin)::mapped_type const&; + auto get_smoothness(Axis axis, Material const* material, Params const& state) const -> decltype(Params::smoothness)::mapped_type const&; + private: template auto const& get_per_axis_per_criteria(Axis axis, Material const* material) const; diff --git a/src/domain/mesh/interval.cpp b/src/domain/mesh/interval.cpp index 70db3ab..f3a13f0 100644 --- a/src/domain/mesh/interval.cpp +++ b/src/domain/mesh/interval.cpp @@ -44,8 +44,8 @@ Coord calc_h(Coord const& a, Coord const& b) noexcept { //****************************************************************************** Interval::Interval(MeshlinePolicy* before, MeshlinePolicy* after, Axis axis, GlobalParams* global_params, Timepoint* t) : Originator(t, { - .dmax = global_params->get_current_state().dmax, - .before = Side(before, global_params->get_current_state().lmin, global_params->get_current_state().smoothness, calc_h(before->coord, after->coord), [before](double d) noexcept { + .dmax = global_params->get_dmax(axis), + .before = Side(before, global_params->get_lmin(axis), global_params->get_smoothness(axis), calc_h(before->coord, after->coord), [before](double d) noexcept { switch(before->get_current_state().policy) { case MeshlinePolicy::Policy::ONELINE: return 0.0; case MeshlinePolicy::Policy::HALFS: return d / 2.0; @@ -62,7 +62,7 @@ Interval::Interval(MeshlinePolicy* before, MeshlinePolicy* after, Axis axis, Glo default: ::unreachable(); } }), - .after = Side(after, global_params->get_current_state().lmin, global_params->get_current_state().smoothness, calc_h(before->coord, after->coord), [after](double d) noexcept { + .after = Side(after, global_params->get_lmin(axis), global_params->get_smoothness(axis), calc_h(before->coord, after->coord), [after](double d) noexcept { switch(after->get_current_state().policy) { case MeshlinePolicy::Policy::ONELINE: return 0.0; case MeshlinePolicy::Policy::HALFS: return d / 2.0; @@ -78,7 +78,8 @@ Interval::Interval(MeshlinePolicy* before, MeshlinePolicy* after, Axis axis, Glo } (); default: ::unreachable(); } - }) + }), + .main_material = nullptr }) , global_params(global_params) , axis(axis) @@ -369,12 +370,12 @@ void Interval::auto_solve_d() { update_ls(state); auto state_b = state.before.meshline_policy->get_current_state(); + auto state_a = state.after.meshline_policy->get_current_state(); + state_b.d = get<0>(adjust_d_for_dmax_lmin(state.before/*, 10000*/)); state.before.meshline_policy->set_state(t, state_b); update_ls(state.before); - - auto state_a = state.after.meshline_policy->get_current_state(); state_a.d = get<0>(adjust_d_for_dmax_lmin(state.after/*, 10000*/)); state.after.meshline_policy->set_state(t, state_a); update_ls(state.after); diff --git a/src/domain/mesh/interval.hpp b/src/domain/mesh/interval.hpp index 885a8c2..c2877ba 100644 --- a/src/domain/mesh/interval.hpp +++ b/src/domain/mesh/interval.hpp @@ -17,6 +17,7 @@ #include "domain/geometrics/space.hpp" #include "domain/utils/entity_visitor.hpp" #include "domain/global.hpp" +#include "domain/material.hpp" #include "utils/entity.hpp" #include "utils/state_management.hpp" @@ -39,10 +40,8 @@ class Interval : public Originator , public Visitable , public Entity { -private: - GlobalParams* global_params; - public: + GlobalParams* global_params; Axis const axis; struct Side { @@ -88,6 +87,7 @@ struct IntervalState final double dmax; ///< Maximum distance between two adjacent meshlines. Interval::Side before; Interval::Side after; + Material* main_material; }; #ifdef UNITTEST diff --git a/src/domain/mesh/meshline_policy.cpp b/src/domain/mesh/meshline_policy.cpp index 6ebb37e..e9ea65f 100644 --- a/src/domain/mesh/meshline_policy.cpp +++ b/src/domain/mesh/meshline_policy.cpp @@ -4,6 +4,7 @@ /// @author Thomas Lepoix ///***************************************************************************** +#include "domain/global.hpp" #include "domain/geometrics/point.hpp" #include "utils/unreachable.hpp" #include "meshline.hpp" @@ -28,7 +29,7 @@ MeshlinePolicy::MeshlinePolicy( .policy = policy, .normal = normal, .is_enabled = is_enabled, - .d = global_params->get_current_state().dmax / 2, // TODO this seems to help while center of intervals is buggy + .d = global_params->get_dmax(axis) / 2, // TODO this seems to help while center of intervals is buggy .origins = origins }) , axis(axis) diff --git a/src/domain/mesh/meshline_policy.hpp b/src/domain/mesh/meshline_policy.hpp index 12e7012..df3cfe2 100644 --- a/src/domain/mesh/meshline_policy.hpp +++ b/src/domain/mesh/meshline_policy.hpp @@ -17,7 +17,6 @@ #include "domain/geometrics/normal.hpp" #include "domain/geometrics/space.hpp" #include "domain/utils/entity_visitor.hpp" -#include "domain/global.hpp" #include "utils/entity.hpp" #include "utils/state_management.hpp" #include "i_meshline_origin.hpp" @@ -26,6 +25,7 @@ namespace domain { class Conflict; class Meshline; +class GlobalParams; struct MeshlinePolicyState; diff --git a/src/infra/parsers/parser_from_csx.cpp b/src/infra/parsers/parser_from_csx.cpp index 6afc94e..c06459c 100644 --- a/src/infra/parsers/parser_from_csx.cpp +++ b/src/infra/parsers/parser_from_csx.cpp @@ -126,10 +126,8 @@ void ParserFromCsx::Pimpl::warn_unsupported_primitive(string const& primitive_ty //****************************************************************************** expected ParserFromCsx::Pimpl::parse_oemsh(pugi::xml_node const& node) { pugi::xml_node global_params = node.child("GlobalParams"); - if(auto a = global_params.attribute("ProximityLimit"); a) domain_params.proximity_limit = a.as_double(); - if(auto a = global_params.attribute("Smoothness"); a) domain_params.smoothness = a.as_double(); - if(auto a = global_params.attribute("dmax"); a) domain_params.dmax = a.as_double(); - if(auto a = global_params.attribute("lmin"); a) domain_params.lmin = a.as_uint(); + if(auto a = global_params.attribute("ProximityLimit"); a) + domain_params.proximity_limit = a.as_double(); static std::map> const axes { { "X", domain::Axis::X }, @@ -184,6 +182,11 @@ expected ParserFromCsx::Pimpl::parse_oemsh(pugi::xml_node const& n return {}; }; + pugi::xml_node per_material_params = node.child("PerMaterialParams"); + TRY(parse_per_material(domain_params.dmax, per_material_params.child("dmax"))); + TRY(parse_per_material(domain_params.lmin, per_material_params.child("lmin"))); + TRY(parse_per_material(domain_params.smoothness, per_material_params.child("smoothness"))); + pugi::xml_node fixed_meshlines = node.child("FixedMeshlines"); size_t delta_unit = fixed_meshlines.attribute("DeltaUnit").as_uint(1); AxisSpace lines = { diff --git a/src/infra/serializers/serializer_to_csx.cpp b/src/infra/serializers/serializer_to_csx.cpp index c9a974a..3d22d56 100644 --- a/src/infra/serializers/serializer_to_csx.cpp +++ b/src/infra/serializers/serializer_to_csx.cpp @@ -172,9 +172,12 @@ void SerializerToCsx::visit(Board& board) { pugi::xml_node global_params = find_or_append_child(oemsh, "GlobalParams"); global_params.remove_attributes(); global_params.append_attribute("ProximityLimit").set_value(p.proximity_limit); - global_params.append_attribute("Smoothness").set_value(p.smoothness); - global_params.append_attribute("dmax").set_value(p.dmax); - global_params.append_attribute("lmin").set_value(p.lmin); + pugi::xml_node per_material_params = find_or_append_child(oemsh, "PerMaterialParams"); + per_material_params.remove_attributes(); + per_material_params.remove_children(); + handle_per_material(p.smoothness, per_material_params.append_child("smoothness")); + handle_per_material(p.dmax, per_material_params.append_child("dmax")); + handle_per_material(p.lmin, per_material_params.append_child("lmin")); } else { doc.remove_child("OpenEMSH"); } diff --git a/src/ui/cli/cli.cpp b/src/ui/cli/cli.cpp index 7f00c79..2b40400 100644 --- a/src/ui/cli/cli.cpp +++ b/src/ui/cli/cli.cpp @@ -321,21 +321,6 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { "Distance under which two adjacent lines trigger a conflict." )->group("Mesher options"); - app.add_option_function("--dmax", - make_overrider<&domain::Params::dmax>(domain_overrides), - "Maximum distance between two adjacent lines." - )->group("Mesher options"); // TODO is in fact mres / sres / ares - - app.add_option_function("--lmin", - make_overrider<&domain::Params::lmin>(domain_overrides), - "Minimum line number per interval half." - )->group("Mesher options"); - - app.add_option_function("--smoothness", - make_overrider<&domain::Params::smoothness>(domain_overrides), - "Smoothness factor ]1;2]." - )->group("Mesher options")->check(BoundExclusiveInclusive(1.0, 2.0)); - app.add_option_function("--diag-dmax", make_overrider<&domain::Params::diagonal_dmax>(domain_overrides), "Maximum distance between two adjacent lines in diagonal zones." @@ -351,6 +336,16 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { "Angle threshold, above which angles between diagonal edges will generate MeshlinePolicies." )->group("Mesher options"); + // TODO is in fact mres / sres / ares + add_per_material_type_option<&domain::Params::dmax, decltype(domain::Params::dmax)>("dmax", "Maximum distance between two adjacent lines.", app, domain_overrides); + add_per_material_name_option<&domain::Params::dmax, decltype(domain::Params::dmax)>("dmax", "Maximum distance between two adjacent lines.", app, domain_overrides); + + add_per_material_type_option<&domain::Params::lmin, decltype(domain::Params::lmin)>("lmin", "Minimum line number per interval half.", app, domain_overrides); + add_per_material_name_option<&domain::Params::lmin, decltype(domain::Params::lmin)>("lmin", "Minimum line number per interval half.", app, domain_overrides); + + add_per_material_type_option<&domain::Params::smoothness, decltype(domain::Params::smoothness)>("smoothness", "Smoothness factor ]1;2].", app, domain_overrides)->check(BoundExclusiveInclusive(1.0, 2.0).application_index(2)); + add_per_material_name_option<&domain::Params::smoothness, decltype(domain::Params::smoothness)>("smoothness", "Smoothness factor ]1;2].", app, domain_overrides)->check(BoundExclusiveInclusive(1.0, 2.0).application_index(2)); + app.add_flag("--no-x", [¶ms](size_t) { params.with_axis_x = false; }, "Don't include X axis meshlines in output.")->group("Output options"); app.add_flag("--no-y", [¶ms](size_t) { params.with_axis_y = false; }, "Don't include Y axis meshlines in output.")->group("Output options"); app.add_flag("--no-z", [¶ms](size_t) { params.with_axis_z = false; }, "Don't include Z axis meshlines in output.")->group("Output options"); diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index 3314f50..d8526d4 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -24,15 +24,15 @@ EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) , global(global) { auto const& params = global->get_current_state(); - setRowCount(4); + setRowCount(7); make_row(0, "Proximity limit", QString::number(params.proximity_limit), "Distance below which two MeshlinePolicies will be merged."); - make_row(1, "Smoothness", QString::number(params.smoothness), + make_row(1, "Smoothness", params.smoothness, "Smoothness factor ]1;2]. Meshing algorithm will decrease it, better to start high."); - make_row(2, "lmin", QString::number(params.lmin), + make_row(2, "lmin", params.lmin, "Minimum line number per Interval half."); - make_row(3, "dmax", QString::number(params.dmax), + make_row(3, "dmax", params.dmax, "Maximum distance between two adjacent lines."); make_row(4, "Minimal angle", QString::number(params.consecutive_diagonal_minimal_angle), "Angle threshold, above which angles between diagonal edges will generate MeshlinePolicies."); @@ -44,13 +44,14 @@ EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) //****************************************************************************** bool EditModelGlobal::commit() { - domain::Params params; + auto params = global->get_current_state(); + + params.smoothness = item(1, V)->data().value(); + params.lmin = item(2, V)->data().value(); + params.dmax = item(3, V)->data().value(); std::array does_succeed = { try_to_double(item(0, V)->text(), params.proximity_limit), - try_to_double(item(1, V)->text(), params.smoothness), - try_to_ulong(item(2, V)->text(), params.lmin), - try_to_double(item(3, V)->text(), params.dmax), try_to_double(item(4, V)->text(), params.consecutive_diagonal_minimal_angle), try_to_double(item(5, V)->text(), params.diagonal_dmax), try_to_ulong(item(6, V)->text(), params.diagonal_lmin) diff --git a/src/ui/qt/processing_view/processing_interval.cpp b/src/ui/qt/processing_view/processing_interval.cpp index e455b10..8979ff8 100644 --- a/src/ui/qt/processing_view/processing_interval.cpp +++ b/src/ui/qt/processing_view/processing_interval.cpp @@ -11,6 +11,7 @@ #include "domain/conflicts/conflict_diagonal_or_circular_zone.hpp" #include "domain/mesh/interval.hpp" #include "domain/mesh/meshline_policy.hpp" +#include "infra/utils/to_string.hpp" #include "ui/qt/data_keys.hpp" #include "ui/qt/utils/nodegraph/text.hpp" @@ -89,6 +90,7 @@ ProcessingInterval::ProcessingInterval(domain::Interval const* interval, QGraphi return locate_processing_interval_params().port; }; + QString type("Type: "); QString dmax("dmax: "); QString before_lmin("Before.lmin: "); QString before_smoothness("Before.Smoothness: "); @@ -96,6 +98,8 @@ ProcessingInterval::ProcessingInterval(domain::Interval const* interval, QGraphi QString after_smoothness("After.Smoothness: "); if(interval) { auto const& state = interval->get_current_state(); + if(state.main_material) + type += QString::fromStdString(to_string(state.main_material->type)); dmax += QString::number(state.dmax); before_lmin += QString::number(state.before.lmin); before_smoothness += QString::number(state.before.smoothness); @@ -107,6 +111,13 @@ ProcessingInterval::ProcessingInterval(domain::Interval const* interval, QGraphi to_wire.emplace_back(DataKeys::set_to_wire(state.after.meshline_policy, after_port)); } + auto* text_type = new nodegraph::Text(type, this); + text_type->setFlag(QGraphicsItem::ItemIsSelectable); + text_type->setAcceptedMouseButtons(Qt::NoButton); + text_type->locate_text_params = [this]() -> auto& { + return locate_processing_interval_params().main; + }; + auto* text_dmax = new nodegraph::Text(dmax, this); text_dmax->setFlag(QGraphicsItem::ItemIsSelectable); text_dmax->setAcceptedMouseButtons(Qt::NoButton); @@ -142,6 +153,7 @@ ProcessingInterval::ProcessingInterval(domain::Interval const* interval, QGraphi return locate_processing_interval_params().main; }; + v_box2->addItem(text_type); v_box2->addItem(text_dmax); v_box2->addItem(text_before_lmin); v_box2->addItem(text_before_smoothness); diff --git a/test/unit/app/test_openemsh.cpp b/test/unit/app/test_openemsh.cpp index 4df4818..3ae4d42 100644 --- a/test/unit/app/test_openemsh.cpp +++ b/test/unit/app/test_openemsh.cpp @@ -96,9 +96,16 @@ SCENARIO("optional next(Step step)", "[app][openemsh]") { } } WHEN("Running for DETECT_INTERVALS") { - THEN("Should return MESH") { + THEN("Should return ADJUST_MESH_TO_MATERIAL") { std::optional a = next(Step::DETECT_INTERVALS); REQUIRE(a.has_value()); + REQUIRE(a.value() == Step::ADJUST_MESH_TO_MATERIAL); + } + } + WHEN("Running for ADJUST_MESH_TO_MATERIAL") { + THEN("Should return MESH") { + std::optional a = next(Step::ADJUST_MESH_TO_MATERIAL); + REQUIRE(a.has_value()); REQUIRE(a.value() == Step::DETECT_INTERVALS_PER_DIAG_ZONES); } } @@ -141,6 +148,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -161,6 +169,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -180,6 +189,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -198,6 +208,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -215,6 +226,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -231,6 +243,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -246,6 +259,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -260,6 +274,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -273,6 +288,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -285,6 +301,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { Step::DETECT_INDIVIDUAL_EDGES, Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -296,6 +313,7 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { REQUIRE(that_and_after(Step::DETECT_AND_SOLVE_TCMLP) == std::set { Step::DETECT_AND_SOLVE_TCMLP, Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH @@ -306,6 +324,17 @@ SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { THEN("Should return all Steps except those coming before DETECT_INTERVALS") { REQUIRE(that_and_after(Step::DETECT_INTERVALS) == std::set { Step::DETECT_INTERVALS, + Step::ADJUST_MESH_TO_MATERIAL, + Step::DETECT_INTERVALS_PER_DIAG_ZONES, + Step::SOLVE_DIAG_ZONES_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for ADJUST_MESH_TO_MATERIAL") { + THEN("Should return all Steps except those coming before ADJUST_MESH_TO_MATERIAL") { + REQUIRE(that_and_after(Step::ADJUST_MESH_TO_MATERIAL) == std::set { + Step::ADJUST_MESH_TO_MATERIAL, Step::DETECT_INTERVALS_PER_DIAG_ZONES, Step::SOLVE_DIAG_ZONES_INTERVALS, Step::MESH diff --git a/test/unit/domain/mesh/test_interval.cpp b/test/unit/domain/mesh/test_interval.cpp index 07e8dd5..ae0dbe3 100644 --- a/test/unit/domain/mesh/test_interval.cpp +++ b/test/unit/domain/mesh/test_interval.cpp @@ -348,7 +348,7 @@ SCENARIO("double find_dmax(Interval::Side const& side, double dmax)", "[interval t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.smoothness = 2; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 1; p.set_next_state(state_p); a.set_next_state(state_a); @@ -421,7 +421,7 @@ SCENARIO("double find_dmax(Interval::Side const& side, Interval::Side const& b, t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.smoothness = 2; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 1; p.set_next_state(state_p); a.set_next_state(state_a); @@ -461,8 +461,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.dmax = 1.2; - state_p.lmin = 2; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 1.2 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 1.0; p.set_next_state(state_p); a.set_next_state(state_a); @@ -475,8 +475,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } THEN("Side's d should be unchanged") { @@ -503,8 +503,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.dmax = 2.0; - state_p.lmin = 10; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 2.0 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 10 }}; state_a.d = 1.0; p.set_next_state(state_p); a.set_next_state(state_a); @@ -517,8 +517,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } THEN("Side's d should be reduced") { @@ -534,8 +534,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } } } @@ -558,8 +558,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.dmax = 0.8; - state_p.lmin = 2; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 0.8 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 1.0; p.set_next_state(state_p); a.set_next_state(state_a); @@ -572,8 +572,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } THEN("Side's d should be reduced") { @@ -589,8 +589,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } } } @@ -613,8 +613,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.dmax = 0.8; - state_p.lmin = 10; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 0.8 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 10 }}; state_a.d = 1.0; p.set_next_state(state_p); a.set_next_state(state_a); @@ -627,8 +627,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } AND_WHEN("Process iterations are unlimited") { @@ -647,8 +647,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } } } @@ -669,8 +669,8 @@ SCENARIO("std::tuple Interval::adjust_d_for_dmax_lmin(Interval::Si i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); } } } @@ -699,9 +699,9 @@ SCENARIO("std::tuple Interval::adjust_smoothness_for_s(Interval::S t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.dmax = 30.0; - state_p.lmin = 5; - state_p.smoothness = 2; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 30.0 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 5 }}; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 0.1; p.set_next_state(state_p); a.set_next_state(state_a); @@ -718,14 +718,14 @@ SCENARIO("std::tuple Interval::adjust_smoothness_for_s(Interval::S i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); REQUIRE(is_ls_valid_for_dmax_lmin_smoothness( j.get_current_state().before.ls, a.get_current_state().d, j.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); WHEN("Process iterations are unlimited") { THEN("Side's smoothness should be reduced") { { @@ -735,7 +735,7 @@ SCENARIO("std::tuple Interval::adjust_smoothness_for_s(Interval::S i.update_ls(state_i); i.set_next_state(state_i); REQUIRE_FALSE(is_limit_reached); - REQUIRE(i.get_current_state().before.smoothness < p.get_current_state().smoothness); + REQUIRE(i.get_current_state().before.smoothness < p.get_smoothness(Y)); } AND_THEN("The space between the last ls line and the middle of the Interval should be reduced") { @@ -751,7 +751,7 @@ SCENARIO("std::tuple Interval::adjust_smoothness_for_s(Interval::S j.update_ls(state_j); j.set_next_state(state_j); REQUIRE(is_limit_reached); - REQUIRE(j.get_current_state().before.smoothness < p.get_current_state().smoothness); + REQUIRE(j.get_current_state().before.smoothness < p.get_smoothness(Y)); } AND_THEN("The space between the last ls line and the middle of the Interval should be reduced, but less than if unlimited") { @@ -784,9 +784,9 @@ SCENARIO("std::tuple Interval::adjust_smoothness_for_s(Interval::S t); auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); - state_p.dmax = 30.0; - state_p.lmin = 5; - state_p.smoothness = 1; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 30.0 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 5 }}; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 1 }}; state_a.d = 0.1; p.set_next_state(state_p); a.set_next_state(state_a); @@ -799,8 +799,8 @@ SCENARIO("std::tuple Interval::adjust_smoothness_for_s(Interval::S i.get_current_state().before.ls, a.get_current_state().d, i.get_current_state().before.smoothness, - p.get_current_state().dmax, - p.get_current_state().lmin)); + p.get_dmax(Y), + p.get_lmin(Y))); THEN("Side's smoothness should not be reduced") { auto [new_smoothness, is_limit_reached] = i.adjust_smoothness_for_s(i.get_current_state().before); auto state_i = i.get_current_state(); @@ -808,7 +808,7 @@ SCENARIO("std::tuple Interval::adjust_smoothness_for_s(Interval::S i.update_ls(state_i); i.set_next_state(state_i); REQUIRE_FALSE(is_limit_reached); - REQUIRE(new_smoothness == p.get_current_state().smoothness); + REQUIRE(new_smoothness == p.get_smoothness(Y)); AND_THEN("The space between the last ls line and the middle of the Interval should not be reduced") { Coord new_last_space = i.get_current_state().before.ls.back() - i.s(i.get_current_state().before); @@ -866,9 +866,9 @@ SCENARIO("std::vector> Interval::mesh() const", "[inte auto state_b = b.get_current_state(); auto state_c = c.get_current_state(); auto state_d = d.get_current_state(); - state_p.dmax = 2; - state_p.lmin = 5; - state_p.smoothness = 2; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 2 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 5 }}; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 0.1; state_b.d = 0.1; state_c.d = 0.1; @@ -966,9 +966,9 @@ SCENARIO("std::vector> Interval::mesh() const", "[inte auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); auto state_b = b.get_current_state(); - state_p.dmax = 1.5; - state_p.lmin = 5; - state_p.smoothness = 2; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 1.5 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 5 }}; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 0.1; state_b.d = 0.1; p.set_next_state(state_p); @@ -1051,9 +1051,9 @@ SCENARIO("std::vector> Interval::mesh() const", "[inte auto state_b = b.get_current_state(); auto state_c = c.get_current_state(); auto state_d = d.get_current_state(); - state_p.dmax = 2; - state_p.lmin = 5; - state_p.smoothness = 2; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 2 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 5 }}; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 0.1; state_b.d = 0.1; state_c.d = 0.1; @@ -1098,9 +1098,9 @@ SCENARIO("std::vector> Interval::mesh() const", "[inte auto state_p = p.get_current_state(); auto state_a = a.get_current_state(); auto state_b = b.get_current_state(); - state_p.dmax = 1.5; - state_p.lmin = 5; - state_p.smoothness = 2; + state_p.dmax = {{{ Params::ALL, Params::ALL }, 1.5 }}; + state_p.lmin = {{{ Params::ALL, Params::ALL }, 5 }}; + state_p.smoothness = {{{ Params::ALL, Params::ALL }, 2 }}; state_a.d = 0.1; state_b.d = 0.1; p.set_next_state(state_p); diff --git a/test/unit/domain/test_board.cpp b/test/unit/domain/test_board.cpp index 981d3a6..3124054 100644 --- a/test/unit/domain/test_board.cpp +++ b/test/unit/domain/test_board.cpp @@ -21,6 +21,7 @@ /// @test void Board::Builder::add_polygon(Plane plane, std::string const& name, Polygon::RangeZ const& z_placement, std::vector>&& points) /// @test void Board::Builder::add_polygon_from_box(Plane plane, std::string const& name, Polygon::RangeZ const& z_placement, Point const p1, Point const p3) /// @test std::unique_ptr Board::Builder::build() +/// @test shared_ptr Board::find_ambient_material(Axis axis, Coord const& coord) const /// @test std::pair, std::remove_const_t> Board::find_ambient_material(Plane plane, Segment const& segment, std::shared_ptr const& current_polygon) const /// @test void Board::adjust_edges_to_materials() /// @test void Board::detect_edges_in_polygons() @@ -285,11 +286,89 @@ SCENARIO("std::unique_ptr Board::Builder::build()", "[board]") { } } +//****************************************************************************** +SCENARIO("shared_ptr Board::find_ambient_material(Axis axis, Coord const& coord) const", "[board]") { + Timepoint* t = Caretaker::singleton().get_history_root(); + std::unique_ptr b; + GIVEN("A board holding some polygons of each Material type, for some priorities, all overlapping in plane and normal axis") { + Polygon::RangeZ z = { 0, 3 }; + auto a0x = std::make_shared(Material::Type::AIR, ""); + auto c0 = std::make_shared(Material::Type::CONDUCTOR, ""); + auto d0 = std::make_shared(Material::Type::DIELECTRIC, ""); + auto a0 = std::make_shared(Material::Type::AIR, ""); + auto c1 = std::make_shared(Material::Type::CONDUCTOR, ""); + auto d1 = std::make_shared(Material::Type::DIELECTRIC, ""); + auto a1 = std::make_shared(Material::Type::AIR, ""); + auto c2 = std::make_shared(Material::Type::CONDUCTOR, ""); + auto d2 = std::make_shared(Material::Type::DIELECTRIC, ""); + auto a2 = std::make_shared(Material::Type::AIR, ""); + auto px = std::make_shared(XY, a0x, "", 0, z, from_init_list({{ 2, 2 }, { 2, 5 }, { 5, 5 }, { 5, 2 }}), t); + auto pc0 = std::make_shared(XY, c0, "", 0, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pd0 = std::make_shared(XY, d0, "", 0, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pa0 = std::make_shared(XY, a0, "", 0, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pc1 = std::make_shared(XY, c1, "", 1, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pd1 = std::make_shared(XY, d1, "", 1, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pa1 = std::make_shared(XY, a1, "", 1, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pc2 = std::make_shared(XY, c2, "", 2, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pd2 = std::make_shared(XY, d2, "", 2, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + auto pa2 = std::make_shared(XY, a2, "", 2, z, from_init_list({{ 1, 1 }, { 6, 1 }, { 6, 6 }, { 1, 6 }}), t); + { + PlaneSpace>> tmp; + tmp[XY].push_back(px); + tmp[XY].push_back(pc0); + tmp[XY].push_back(pd0); + tmp[XY].push_back(pa0); + tmp[XY].push_back(pc1); + tmp[XY].push_back(pd1); + tmp[XY].push_back(pa1); + tmp[XY].push_back(pc2); + tmp[XY].push_back(pd2); + tmp[XY].push_back(pa2); + b = std::make_unique(std::move(tmp), Params(), t); + } + WHEN("Looking for ambient Material outside of any Polygon") { + AND_WHEN("The board has a background Material") { + auto background = std::make_shared(Material::Type::DIELECTRIC, ""); + b->material = background; + auto material = b->find_ambient_material(X, 10); + THEN("Should return the board background Material") { + REQUIRE(material); + REQUIRE(material == background); + REQUIRE(material->type == Material::Type::DIELECTRIC); + } + } + AND_WHEN("The board has no background Material") { + auto material = b->find_ambient_material(X, 10); + THEN("Should not return any Material") { + REQUIRE_FALSE(material); + } + } + } + WHEN("Looking for ambient Material inside the overlap of all Polygons") { + auto material = b->find_ambient_material(Y, 3.5); + THEN("Should return the CONDUCTOR Material of the Polygon with the highest priority") { + REQUIRE(material); + REQUIRE(material->type == Material::Type::CONDUCTOR); + REQUIRE(material.get() == c2.get()); + } + } + WHEN("Looking for ambient Material in normal axis, inside the overlap of all Polygons") { + AND_WHEN("The board has no background Material") { + auto material = b->find_ambient_material(Z, 2); + THEN("Should not return any Material") { + // TODO is this actually wanted? + REQUIRE_FALSE(material); + } + } + } + } +} + //****************************************************************************** SCENARIO("std::pair, std::remove_const_t> Board::find_ambient_material(Plane plane, Segment const& segment, std::shared_ptr const& current_polygon) const", "[board]") { Timepoint* t = Caretaker::singleton().get_history_root(); std::unique_ptr b; - GIVEN("A board holding some polygons of each Material type, for some prioriries, all overlapping in plane and normal axis") { + GIVEN("A board holding some polygons of each Material type, for some priorities, all overlapping in plane and normal axis") { Polygon::RangeZ z = { 0, 3 }; auto a0x = std::make_shared(Material::Type::AIR, ""); auto c0 = std::make_shared(Material::Type::CONDUCTOR, ""); @@ -347,7 +426,7 @@ SCENARIO("std::pair, std::remove_const_tfind_ambient_material(XY, Range({ 3.5, 3.2 }, { 3.5, 3.8 }), px); - THEN("Should the CONDUCTOR Material of the Polygon with the highest priority") { + THEN("Should return the CONDUCTOR Material of the Polygon with the highest priority") { REQUIRE(material); REQUIRE(material->type == Material::Type::CONDUCTOR); REQUIRE(material.get() == c2.get()); diff --git a/test/unit/domain/test_meshline_policy_manager.cpp b/test/unit/domain/test_meshline_policy_manager.cpp index d9f774a..b507590 100644 --- a/test/unit/domain/test_meshline_policy_manager.cpp +++ b/test/unit/domain/test_meshline_policy_manager.cpp @@ -505,12 +505,12 @@ SCENARIO("void MeshlinePolicyManager::mesh()", "[meshline_policy_manager]") { } }; - GIVEN("A meshline policy manager and some meshline policies") { + GIVEN("A meshline policy manager, some meshline policies and detected intervals") { Wrapper w(t); auto params_state = w.params.get_current_state(); params_state.proximity_limit = 1; - params_state.lmin = 2; - params_state.dmax = 4.0; + params_state.lmin = {{{ Params::ALL, Params::ALL }, 2 }}; + params_state.dmax = {{{ Params::ALL, Params::ALL }, 4.0 }}; w.params.set_next_state(params_state); Point e0(1, 1), e1(1, 3); Edge e(XY, &e0, &e1, t); diff --git a/test/unit/infra/serializers/test_serializer_to_plantuml.cpp b/test/unit/infra/serializers/test_serializer_to_plantuml.cpp index 124017d..00874e3 100644 --- a/test/unit/infra/serializers/test_serializer_to_plantuml.cpp +++ b/test/unit/infra/serializers/test_serializer_to_plantuml.cpp @@ -34,9 +34,9 @@ SCENARIO("string SerializerToPlantuml::run(Board& board)", "[serializer_to_plant GIVEN("The Lpf complex structure") { std::shared_ptr lpf = create_lpf(); auto params_state = lpf->global_params->get_current_state(); - params_state.lmin = 1; + params_state.lmin = {{{ Params::ALL, Params::ALL }, 1 }}; + params_state.dmax = {{{ Params::ALL, Params::ALL }, 2 }}; params_state.proximity_limit = 0; -// params_state.dmax = 2; lpf->global_params->set_next_state(params_state); auto_mesh(lpf); @@ -61,9 +61,9 @@ SCENARIO("string SerializerToPlantuml::run(Board& board)", "[serializer_to_plant GIVEN("The Stub complex structure") { std::shared_ptr stub = create_stub(); auto params_state = stub->global_params->get_current_state(); - params_state.lmin = 0; + params_state.lmin = {{{ Params::ALL, Params::ALL }, 0 }}; + params_state.dmax = {{{ Params::ALL, Params::ALL }, 2 }}; params_state.proximity_limit = 0.1; -// params_state.dmax = 2; stub->global_params->set_next_state(params_state); auto_mesh(stub); From e107e68291a45c4a4a9a47392b33dd4e5f42d42c Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 2 Oct 2026 00:43:20 +0200 Subject: [PATCH 07/11] test Global::get_per_axis_per_criteria<>() --- test/unit/CMakeLists.txt | 1 + test/unit/domain/test_global.cpp | 219 +++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 test/unit/domain/test_global.cpp diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 0f3f1f6..01bf178 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -25,6 +25,7 @@ if( Catch2_FOUND ) "${CMAKE_CURRENT_SOURCE_DIR}/domain/test_conflict_manager.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/test_meshline_policy_manager.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/test_material.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/domain/test_global.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/domain/test_board.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/infra/serializers/test_serializer_to_plantuml.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/infra/utils/test_to_string.cpp" diff --git a/test/unit/domain/test_global.cpp b/test/unit/domain/test_global.cpp new file mode 100644 index 0000000..a6d88f8 --- /dev/null +++ b/test/unit/domain/test_global.cpp @@ -0,0 +1,219 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include + +#include + +#include "domain/global.hpp" + +/// @test template +/// auto const& GlobalParams::get_per_axis_per_criteria( +/// Axis axis, +/// Material const* material, +/// Params const& state) const +///***************************************************************************** + +using namespace domain; +using namespace std::string_literals; + +// Rules precedence order: +// 1- material name, axis +// 2- material name, wildcard axis +// 3- material type, axis +// 4- material type, wildcard axis +// 5- wildcard material, axis +// 6- wildcard material, wildcard axis (default rule) +//****************************************************************************** +SCENARIO("template \ +auto const& GlobalParams::get_per_axis_per_criteria( \ +Axis axis, \ +Material const* material, \ +Params const& state) const", "[domain][global]") { + Timepoint* t = Caretaker::singleton().get_history_root(); + GIVEN("Global parameters containing some matching rules along with unmatching rules") { + decltype(Params::lmin) unmatching_rules = { + {{ Y, "abc" }, 10 }, + {{ Params::ALL, "d" }, 20 }, + {{ Y, Material::Type::DIELECTRIC }, 30 }, + {{ Params::ALL, Material::Type::AIR }, 40 }, + {{ Y, Params::ALL }, 50 }, + }; + GlobalParams p(t); + Material named_material(Material::Type::DIELECTRIC, "abc"); + Material unnamed_material(Material::Type::DIELECTRIC, ""); + + WHEN("The matching rules { 1, 2, 3, 4, 5, 6 } are available") { + Params params; + params.lmin = { + {{ X, "abc" }, 1 }, + {{ Params::ALL, "abc" }, 2 }, + {{ X, Material::Type::DIELECTRIC }, 3 }, + {{ Params::ALL, Material::Type::DIELECTRIC }, 4 }, + {{ X, Params::ALL }, 5 }, + {{ Params::ALL, Params::ALL }, 6 }, + }; + params.lmin.merge(unmatching_rules); + p.set_next_state(params); + WHEN("Requesting property for an axis and a named material") { + THEN("Should return the value associated with matching rule 1: material name, axis") { + REQUIRE(p.get_lmin(X, &named_material) == 1); + } + } + WHEN("Requesting property for an axis and an unnamed material") { + THEN("Should return the value associated with matching rule 3: material type, axis") { + REQUIRE(p.get_lmin(X, &unnamed_material) == 3); + } + } + WHEN("Requesting property for an axis and no material") { + THEN("Should return the value associated with matching rule 5: wildcard material, axis") { + REQUIRE(p.get_lmin(X, nullptr) == 5); + } + } + } + + WHEN("The matching rules { 2, 3, 4, 5, 6 } are available") { + Params params; + params.lmin = { + {{ Params::ALL, "abc" }, 2 }, + {{ X, Material::Type::DIELECTRIC }, 3 }, + {{ Params::ALL, Material::Type::DIELECTRIC }, 4 }, + {{ X, Params::ALL }, 5 }, + {{ Params::ALL, Params::ALL }, 6 }, + }; + params.lmin.merge(unmatching_rules); + p.set_next_state(params); + WHEN("Requesting property for an axis and a named material") { + THEN("Should return the value associated with matching rule 2: material name, wildcard axis") { + REQUIRE(p.get_lmin(X, &named_material) == 2); + } + } + WHEN("Requesting property for an axis and an unnamed material") { + THEN("Should return the value associated with matching rule 3: material type, axis") { + REQUIRE(p.get_lmin(X, &unnamed_material) == 3); + } + } + WHEN("Requesting property for an axis and no material") { + THEN("Should return the value associated with matching rule 5: wildcard material, axis") { + REQUIRE(p.get_lmin(X, nullptr) == 5); + } + } + } + + WHEN("The matching rules { 3, 4, 5, 6 } are available") { + Params params; + params.lmin = { + {{ X, Material::Type::DIELECTRIC }, 3 }, + {{ Params::ALL, Material::Type::DIELECTRIC }, 4 }, + {{ X, Params::ALL }, 5 }, + {{ Params::ALL, Params::ALL }, 6 }, + }; + params.lmin.merge(unmatching_rules); + p.set_next_state(params); + + WHEN("Requesting property for an axis and a named material") { + THEN("Should return the value associated with matching rule 3: material type, axis") { + REQUIRE(p.get_lmin(X, &named_material) == 3); + } + } + WHEN("Requesting property for an axis and an unnamed material") { + THEN("Should return the value associated with matching rule 3: material type, axis") { + REQUIRE(p.get_lmin(X, &unnamed_material) == 3); + } + } + WHEN("Requesting property for an axis and no material") { + THEN("Should return the value associated with matching rule 5: wildcard material, axis") { + REQUIRE(p.get_lmin(X, nullptr) == 5); + } + } + } + + WHEN("The matching rules { 4, 5, 6 } are available") { + Params params; + params.lmin = { + {{ Params::ALL, Material::Type::DIELECTRIC }, 4 }, + {{ X, Params::ALL }, 5 }, + {{ Params::ALL, Params::ALL }, 6 }, + }; + params.lmin.merge(unmatching_rules); + p.set_next_state(params); + + WHEN("Requesting property for an axis and a named material") { + THEN("Should return the value associated with matching rule 4: material type, wildcard axis") { + REQUIRE(p.get_lmin(X, &named_material) == 4); + } + } + WHEN("Requesting property for an axis and an unnamed material") { + THEN("Should return the value associated with matching rule 4: material type, wildcard axis") { + REQUIRE(p.get_lmin(X, &unnamed_material) == 4); + } + } + WHEN("Requesting property for an axis and no material") { + THEN("Should return the value associated with matching rule 5: wildcard material, axis") { + REQUIRE(p.get_lmin(X, nullptr) == 5); + } + } + } + + WHEN("The matching rules { 5, 6 } are available") { + Params params; + params.lmin = { + {{ X, Params::ALL }, 5 }, + {{ Params::ALL, Params::ALL }, 6 }, + }; + params.lmin.merge(unmatching_rules); + p.set_next_state(params); + WHEN("Requesting property for an axis and a named material") { + THEN("Should return the value associated with matching rule 5: wildcard material, axis") { + REQUIRE(p.get_lmin(X, &named_material) == 5); + } + } + WHEN("Requesting property for an axis and an unnamed material") { + THEN("Should return the value associated with matching rule 5: wildcard material, axis") { + REQUIRE(p.get_lmin(X, &unnamed_material) == 5); + } + } + WHEN("Requesting property for an axis and no material") { + THEN("Should return the value associated with matching rule 5: wildcard material, axis") { + REQUIRE(p.get_lmin(X, nullptr) == 5); + } + } + } + + WHEN("The matching rules { 6 } are available") { + Params params; + params.lmin = { + {{ Params::ALL, Params::ALL }, 6 }, + }; + params.lmin.merge(unmatching_rules); + p.set_next_state(params); + WHEN("Requesting property for an axis and a named material") { + THEN("Should return the value associated with matching rule 6: wildcard material, wildcard axis (default rule)") { + REQUIRE(p.get_lmin(X, &named_material) == 6); + } + } + WHEN("Requesting property for an axis and an unnamed material") { + THEN("Should return the value associated with matching rule 6: wildcard material, wildcard axis (default rule)") { + REQUIRE(p.get_lmin(X, &unnamed_material) == 6); + } + } + WHEN("Requesting property for an axis and no material") { + THEN("Should return the value associated with matching rule 6: wildcard material, wildcard axis (default rule)") { + REQUIRE(p.get_lmin(X, nullptr) == 6); + } + } + } + + WHEN("No matching rule is available") { + Params params; + params.lmin = unmatching_rules; + p.set_next_state(params); + THEN("Should abort at runtime") { + // REQUIRE(p.get_lmin(X, nullptr)); + } + } + } +} From 036c14cfd3a2b9d4a9726885ea177cd22ac93953 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 7 Oct 2026 18:39:08 +0200 Subject: [PATCH 08/11] make dmax & diagonal_dmax & proximity_limit absolute -> wavelength-relative --- .../conflict_diagonal_or_circular_zone.hpp | 4 +--- src/domain/global.cpp | 18 ++++++++++++++++++ src/domain/global.hpp | 12 +++++++++--- src/domain/material.cpp | 5 +++++ src/domain/material.hpp | 5 +++++ src/infra/parsers/parser_from_csx.cpp | 16 ++++++++++++++-- src/infra/serializers/serializer_to_csx.cpp | 16 ++++++++++++---- src/infra/serializers/serializer_to_csx.hpp | 1 + src/ui/cli/cli.cpp | 4 ++++ ...odel_conflict_diagonal_or_circular_zone.cpp | 4 +++- src/ui/qt/edit/edit_model_global.cpp | 5 ++++- src/ui/qt/edit/edit_model_interval.cpp | 4 +++- src/ui/qt/edit/edit_model_meshline_policy.cpp | 4 +++- src/ui/qt/main_window.cpp | 17 ++++++++++++++--- src/ui/qt/main_window.hpp | 1 + src/ui/qt/main_window.ui | 11 +++++++++++ ...sing_conflict_diagonal_or_circular_zone.cpp | 2 +- .../qt/processing_view/processing_interval.cpp | 2 +- .../processing_meshline_policy.cpp | 2 +- 19 files changed, 111 insertions(+), 22 deletions(-) diff --git a/src/domain/conflicts/conflict_diagonal_or_circular_zone.hpp b/src/domain/conflicts/conflict_diagonal_or_circular_zone.hpp index 03a96c4..180d274 100644 --- a/src/domain/conflicts/conflict_diagonal_or_circular_zone.hpp +++ b/src/domain/conflicts/conflict_diagonal_or_circular_zone.hpp @@ -33,10 +33,8 @@ class ConflictDiagonalOrCircularZone : public Originator , public Visitable , public Conflict { -private: - GlobalParams* global_params; - public: + GlobalParams* global_params; Axis const axis; ConflictDiagonalOrCircularZone(Axis axis, std::vector const& angles, GlobalParams* global_params, Timepoint* t); diff --git a/src/domain/global.cpp b/src/domain/global.cpp index 7c8ff80..08903c9 100644 --- a/src/domain/global.cpp +++ b/src/domain/global.cpp @@ -162,4 +162,22 @@ auto GlobalParams::get_smoothness(Axis axis, Material const* material, Params co return get_per_axis_per_criteria<&Params::smoothness, decltype(Params::smoothness)>(axis, material, state); } +//****************************************************************************** +double GlobalParams::switch_length_between_absolute_and_wavelength_relative(double length) const noexcept { + return switch_length_between_absolute_and_wavelength_relative(get_current_state(), length); +} + +//****************************************************************************** +double GlobalParams::switch_length_between_absolute_and_wavelength_relative(Params const& state, double length) noexcept { + return state.wavelength_min_vacuum / state.delta_unit / length; +} + +//****************************************************************************** +void GlobalParams::switch_all_lengths_between_absolute_and_wavelength_relative(Params& state) noexcept { + state.diagonal_dmax = switch_length_between_absolute_and_wavelength_relative(state, state.diagonal_dmax); + state.proximity_limit = switch_length_between_absolute_and_wavelength_relative(state, state.proximity_limit); + for(auto& [_, v] : state.dmax) + v = switch_length_between_absolute_and_wavelength_relative(state, v); +} + } // namespace domain diff --git a/src/domain/global.hpp b/src/domain/global.hpp index 80dc067..9a71dad 100644 --- a/src/domain/global.hpp +++ b/src/domain/global.hpp @@ -22,10 +22,12 @@ namespace domain { //****************************************************************************** struct Params { bool has_grid_already = false; // TODO would better fit in infra layer? - double proximity_limit = 1; // TODO must be linked to initial d + double proximity_limit = 200; // Fed wavelength-relative, stored absolute. + double wavelength_min_vacuum = 1; + double delta_unit = 1; std::size_t diagonal_lmin = 2; - double diagonal_dmax = 0.2; + double diagonal_dmax = 30; // Fed wavelength-relative, stored absolute. double consecutive_diagonal_minimal_angle = 20; // Limite between acute / obtuse angles. std::vector> input_fixed_meshlines; @@ -36,7 +38,7 @@ struct Params { using Mat = std::variant; - PerAxisPer dmax = {{{ ALL, ALL }, 2.5 }}; + PerAxisPer dmax = {{{ ALL, ALL }, 10 }}; // Fed wavelength-relative, stored absolute. PerAxisPer lmin = {{{ ALL, ALL }, 2 }}; PerAxisPer smoothness = {{{ ALL, ALL }, 2 }}; }; @@ -55,6 +57,10 @@ class GlobalParams : public Originator { auto get_lmin(Axis axis, Material const* material, Params const& state) const -> decltype(Params::lmin)::mapped_type const&; auto get_smoothness(Axis axis, Material const* material, Params const& state) const -> decltype(Params::smoothness)::mapped_type const&; + double switch_length_between_absolute_and_wavelength_relative(double length) const noexcept; + static double switch_length_between_absolute_and_wavelength_relative(Params const& state, double length) noexcept; + static void switch_all_lengths_between_absolute_and_wavelength_relative(Params& state) noexcept; + private: template auto const& get_per_axis_per_criteria(Axis axis, Material const* material) const; diff --git a/src/domain/material.cpp b/src/domain/material.cpp index 2aa5011..0f0a4d0 100644 --- a/src/domain/material.cpp +++ b/src/domain/material.cpp @@ -12,6 +12,11 @@ using namespace std; namespace domain { +//****************************************************************************** +double Material::calc_wavelength_in_vacuum(double f) { + return c_0 / f; +} + /// Simplified deduction. ///***************************************************************************** Material::Type Material::deduce_type(double epsilon, double mue, double kappa) { diff --git a/src/domain/material.hpp b/src/domain/material.hpp index 9d7f997..cd522aa 100644 --- a/src/domain/material.hpp +++ b/src/domain/material.hpp @@ -12,6 +12,9 @@ namespace domain { +/// https://en.wikipedia.org/wiki/Speed_of_light +constexpr double c_0 = 299792458; + //****************************************************************************** class Material { public: @@ -30,6 +33,8 @@ class Material { unsigned char r, g, b, a; }; + static double calc_wavelength_in_vacuum(double f); + std::strong_ordering operator<=>(Material const& other) const noexcept; Material(Type type, std::string const& name); diff --git a/src/infra/parsers/parser_from_csx.cpp b/src/infra/parsers/parser_from_csx.cpp index c06459c..afe23d7 100644 --- a/src/infra/parsers/parser_from_csx.cpp +++ b/src/infra/parsers/parser_from_csx.cpp @@ -88,6 +88,7 @@ class ParserFromCsx::Pimpl { expected parse_oemsh(pugi::xml_node const& node); expected parse_grid(pugi::xml_node const& node); + expected parse_fdtd(pugi::xml_node const& node); shared_ptr parse_property(pugi::xml_node const& node); @@ -209,11 +210,11 @@ expected ParserFromCsx::Pimpl::parse_oemsh(pugi::xml_node const& n //****************************************************************************** expected ParserFromCsx::Pimpl::parse_grid(pugi::xml_node const& node) { std::size_t coord_system = node.attribute("CoordSystem").as_uint(); - std::size_t delta_unit = node.attribute("DeltaUnit").as_uint(1); if(coord_system == 0) { // First step : into bool has_grid_already pugi::xml_node grid = node.child("RectilinearGrid"); + domain_params.delta_unit = grid.attribute("DeltaUnit").as_double(1); AxisSpace lines = { grid.child_value("XLines"), grid.child_value("YLines"), @@ -226,7 +227,7 @@ expected ParserFromCsx::Pimpl::parse_grid(pugi::xml_node const& no for(auto const part : views::split(lines[axis], ',')) { string_view str(part); if(auto line = str_to_double(str); line.has_value()) - board.add_fixed_meshline_policy(axis, delta_unit * line.value()); + board.add_fixed_meshline_policy(axis, domain_params.delta_unit * line.value()); // TODO *unit might not be required here else return unexpected(format("Invalid meshline value \"{}\": {}", str, line.error())); } @@ -240,6 +241,16 @@ expected ParserFromCsx::Pimpl::parse_grid(pugi::xml_node const& no return {}; } +//****************************************************************************** +expected ParserFromCsx::Pimpl::parse_fdtd(pugi::xml_node const& node) { + if(auto a = node.attribute("f_max"); a) { + double f_max = a.as_double(); + domain_params.wavelength_min_vacuum = Material::calc_wavelength_in_vacuum(f_max); + } else { + return unexpected(format("No maximal frequency specified")); + } +} + //****************************************************************************** shared_ptr ParserFromCsx::Pimpl::parse_property(pugi::xml_node const& node) { string name(node.attribute("Name").as_string()); @@ -746,6 +757,7 @@ expected ParserFromCsx::parse() { }; pugi::xpath_node fdtd = doc.select_node(root("/FDTD").c_str()); + TRY(pimpl->parse_fdtd(fdtd.node())); pugi::xpath_node csx = doc.select_node(root("/ContinuousStructure").c_str()); TRY(pimpl->parse_grid(csx.node())); diff --git a/src/infra/serializers/serializer_to_csx.cpp b/src/infra/serializers/serializer_to_csx.cpp index 3d22d56..a3cff73 100644 --- a/src/infra/serializers/serializer_to_csx.cpp +++ b/src/infra/serializers/serializer_to_csx.cpp @@ -151,7 +151,7 @@ void SerializerToCsx::visit(Board& board) { add_meshlines_to_xml_doc(Z); if(params.with_oemsh_params) { - auto const handle_per_material = [](auto const& var, pugi::xml_node&& node) { + auto const handle_per_material = [this](auto const& var, pugi::xml_node&& node) { for(auto const& [k, v] : var) { auto const& [axis, material] = k; pugi::xml_node r = node.append_child("Rule"); @@ -163,15 +163,23 @@ void SerializerToCsx::visit(Board& board) { }, material.value()); else r.append_attribute("Type").set_value("*"); - r.append_attribute("Value").set_value(v); + + if constexpr(is_floating_point_v) { + r.append_attribute("Value").set_value(v, params.double_precision); + } else if constexpr(is_integral_v) { + r.append_attribute("Value").set_value(v); + } else { + static_assert(false, "Usupported case"); + } } }; - auto const& p = board.global_params->get_current_state(); + auto p = board.global_params->get_current_state(); + GlobalParams::switch_all_lengths_between_absolute_and_wavelength_relative(p); pugi::xml_node oemsh = find_or_prepend_child(doc, "OpenEMSH"); pugi::xml_node global_params = find_or_append_child(oemsh, "GlobalParams"); global_params.remove_attributes(); - global_params.append_attribute("ProximityLimit").set_value(p.proximity_limit); + global_params.append_attribute("ProximityLimit").set_value(p.proximity_limit, params.double_precision); pugi::xml_node per_material_params = find_or_append_child(oemsh, "PerMaterialParams"); per_material_params.remove_attributes(); per_material_params.remove_children(); diff --git a/src/infra/serializers/serializer_to_csx.hpp b/src/infra/serializers/serializer_to_csx.hpp index f483d1c..74c51ae 100644 --- a/src/infra/serializers/serializer_to_csx.hpp +++ b/src/infra/serializers/serializer_to_csx.hpp @@ -25,6 +25,7 @@ class SerializerToCsx final : public domain::EntityVisitor { bool with_axis_y = true; bool with_axis_z = true; bool with_oemsh_params = false; + std::size_t double_precision = 5; }; static std::expected run( diff --git a/src/ui/cli/cli.cpp b/src/ui/cli/cli.cpp index 2b40400..d4794ca 100644 --- a/src/ui/cli/cli.cpp +++ b/src/ui/cli/cli.cpp @@ -373,6 +373,10 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { exit(EXIT_FAILURE); } + domain_overrides.emplace_back([](auto& to_override) { + domain::GlobalParams::switch_all_lengths_between_absolute_and_wavelength_relative(to_override); + }); + params.override_from_cli = [domain_overrides](domain::Params& to_override) { for(auto const& apply : domain_overrides) apply(to_override); diff --git a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp index 7acb856..59fefa0 100644 --- a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp +++ b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp @@ -24,7 +24,7 @@ EditModelConflictDiagonalOrCircularZone::EditModelConflictDiagonalOrCircularZone make_row(0, "lmin", QString::number(state.lmin), "Minimum line number per Interval half, at extremities of the diagonal zone."); - make_row(1, "dmax", QString::number(state.dmax), + make_row(1, "dmax", QString::number(conflict->global_params->switch_length_between_absolute_and_wavelength_relative(state.dmax)), "Maximum distance between two adjacent lines."); make_row(2, "Minimal angle", QString::number(state.minimal_angle), "Angle threshold, above which angles between diagonal edges will generate MeshlinePolicies."); @@ -41,6 +41,8 @@ bool EditModelConflictDiagonalOrCircularZone::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { + state.dmax = conflict->global_params->switch_length_between_absolute_and_wavelength_relative(state.dmax); + emit edit_from((state.minimal_angle != conflict->get_current_state().minimal_angle ? app::Step::SOLVE_DIAG_ZONES_ANGLES : app::Step::SOLVE_DIAG_ZONES_INTERVALS), [&]() { diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index d8526d4..2388618 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -23,7 +23,8 @@ EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) : EditModel(parent) , global(global) { - auto const& params = global->get_current_state(); + auto params = global->get_current_state(); + domain::GlobalParams::switch_all_lengths_between_absolute_and_wavelength_relative(params); setRowCount(7); make_row(0, "Proximity limit", QString::number(params.proximity_limit), @@ -58,6 +59,8 @@ bool EditModelGlobal::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { + domain::GlobalParams::switch_all_lengths_between_absolute_and_wavelength_relative(params); + emit edit_from(app::Step::DETECT_DIAG_ZONES, [&]() { global->set_next_state(params); }); diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index 081e255..01b5022 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -22,7 +22,7 @@ EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent auto const& state = interval->get_current_state(); setRowCount(5); - make_row(0, "dmax", QString::number(state.dmax), + make_row(0, "dmax", QString::number(interval->global_params->switch_length_between_absolute_and_wavelength_relative(state.dmax)), "Maximum distance between two adjacent lines."); make_row(1, "Before.lmin", QString::number(state.before.lmin), "Minimum line number in the minimal interval half. " @@ -51,6 +51,8 @@ bool EditModelInterval::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { + state.dmax = interval->global_params->switch_length_between_absolute_and_wavelength_relative(state.dmax); + emit edit_from(app::Step::MESH, [&] { interval->set_next_state(state); }); diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index d395301..a40361a 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -39,7 +39,7 @@ EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshlin "Direction associated with Policy."); make_row(2, "Enabled", state.is_enabled, "Take into account in the meshing process."); - make_row(3, "d", QString::number(state.d), + make_row(3, "d", QString::number(meshline_policy->global_params->switch_length_between_absolute_and_wavelength_relative(state.d)), "Desired distance between policy lines (HALFS|THIRDS) or " "between policy line and adjacent lines (ONELINE).
" "Can be decreased by the meshing algorithm."); @@ -80,6 +80,8 @@ bool EditModelMeshlinePolicy::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { + state.d = meshline_policy->global_params->switch_length_between_absolute_and_wavelength_relative(state.d); + emit edit_from(app::Step::DETECT_INTERVALS, [&]() { meshline_policy->set_next_state(state); }); diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 68fb4e0..bfab0ab 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -70,6 +70,7 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) update_board_dependant_buttons_visibility(false); + ui->statusBar->addPermanentWidget(ui->l_wavelength_min_vacuum); ui->statusBar->addPermanentWidget(ui->l_cell_number); ui->a_edit->setShortcuts(ui->a_edit->shortcuts() += QKeySequence(Qt::Key_Space)); @@ -149,6 +150,7 @@ bool MainWindow::parse_and_display() { update_board_dependant_buttons_visibility(true); update_title(); + update_wavelength_min_vacuum(); ui->structure_view->init(&oemsh.get_board()); ui->processing_view->init(&oemsh.get_board()); run(); @@ -160,9 +162,17 @@ bool MainWindow::parse_and_display() { void MainWindow::update_cell_number(bool reset) { static QString const base_str(ui->l_cell_number->text()); // TODO space padding every 10^3 - ui->l_cell_number->setText(reset + ui->l_cell_number->setText(reset || !oemsh.get_board() ? base_str - : base_str + QString::number(oemsh.get_board().get_mesh_cell_number())); + : base_str + QString::number(oemsh.get_board()->get_mesh_cell_number())); +} + +//****************************************************************************** +void MainWindow::update_wavelength_min_vacuum(bool reset) { + static QString const base_str(ui->l_wavelength_min_vacuum->text()); + ui->l_wavelength_min_vacuum->setText(reset || !oemsh.get_board() + ? base_str + : base_str + QString::number(oemsh.get_board()->global_params->get_current_state().wavelength_min_vacuum, 'e', 2)); } //****************************************************************************** @@ -179,6 +189,7 @@ void MainWindow::clear() { ui->processing_view->clear(); ui->statusBar->clearMessage(); update_cell_number(true); + update_wavelength_min_vacuum(true); } //****************************************************************************** @@ -746,7 +757,7 @@ void MainWindow::closeEvent(QCloseEvent* event) { .level = Logger::Level::QUESTION, .user_actions = { Logger::UserAction::CANCEL, Logger::UserAction::SAVE, Logger::UserAction::CLOSE }, .message = std::format( - "You are about closing a unsaved document, do you want to save it before?") + "You are about closing an unsaved document, do you want to save it before?") }); if(res == Logger::UserAction::CANCEL) { event->ignore(); diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index c3cf363..6b5ba73 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -39,6 +39,7 @@ class MainWindow : public QMainWindow { void update_navigation_buttons_visibility(); void update_show_buttons_pressing(); void update_cell_number(bool reset = false); + void update_wavelength_min_vacuum(bool reset = false); void save_csx_file(); void go_to_current_state(); diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index b12a397..91ecd16 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -688,6 +688,17 @@ + + + λ<sub>min,0</sub>: + + + Qt::TextFormat::RichText + + + Minimal wavelength in vacuum (m) + + Mesh cells: diff --git a/src/ui/qt/processing_view/processing_conflict_diagonal_or_circular_zone.cpp b/src/ui/qt/processing_view/processing_conflict_diagonal_or_circular_zone.cpp index 1472572..d7c2206 100644 --- a/src/ui/qt/processing_view/processing_conflict_diagonal_or_circular_zone.cpp +++ b/src/ui/qt/processing_view/processing_conflict_diagonal_or_circular_zone.cpp @@ -60,7 +60,7 @@ ProcessingConflictDiagonalOrCircularZone::ProcessingConflictDiagonalOrCircularZo QString minimal_angle ("Minimal angle (°): "); if(conflict) { auto const& state = conflict->get_current_state(); - dmax += QString::number(state.dmax); + dmax += QString::number(conflict->global_params->switch_length_between_absolute_and_wavelength_relative(state.dmax)); lmin += QString::number(state.lmin); minimal_angle += QString::number(state.minimal_angle); for(auto const* angle : state.angles) diff --git a/src/ui/qt/processing_view/processing_interval.cpp b/src/ui/qt/processing_view/processing_interval.cpp index 8979ff8..a5e6131 100644 --- a/src/ui/qt/processing_view/processing_interval.cpp +++ b/src/ui/qt/processing_view/processing_interval.cpp @@ -100,7 +100,7 @@ ProcessingInterval::ProcessingInterval(domain::Interval const* interval, QGraphi auto const& state = interval->get_current_state(); if(state.main_material) type += QString::fromStdString(to_string(state.main_material->type)); - dmax += QString::number(state.dmax); + dmax += QString::number(interval->global_params->switch_length_between_absolute_and_wavelength_relative(state.dmax)); before_lmin += QString::number(state.before.lmin); before_smoothness += QString::number(state.before.smoothness); after_lmin += QString::number(state.after.lmin); diff --git a/src/ui/qt/processing_view/processing_meshline_policy.cpp b/src/ui/qt/processing_view/processing_meshline_policy.cpp index fb44603..2f9b329 100644 --- a/src/ui/qt/processing_view/processing_meshline_policy.cpp +++ b/src/ui/qt/processing_view/processing_meshline_policy.cpp @@ -84,7 +84,7 @@ ProcessingMeshlinePolicy::ProcessingMeshlinePolicy(domain::MeshlinePolicy const* normal += QString::fromStdString(to_string(meshline_policy->get_current_state().normal)); is_enabled += (meshline_policy->get_current_state().is_enabled ? "true" : "false"); policy += QString::fromStdString(to_string(meshline_policy->get_current_state().policy)); - d += QString::number(meshline_policy->get_current_state().d); + d += QString::number(meshline_policy->global_params->switch_length_between_absolute_and_wavelength_relative(meshline_policy->get_current_state().d)); } auto* text_normal = new nodegraph::Text(normal, this); From 5eb4d14bac4ff8a38ad3cb9c53b5a40a0cd115ad Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 8 Oct 2026 21:59:54 +0200 Subject: [PATCH 09/11] fix Board::find_ambient_material(Axis, Coord) & add Planes --- src/domain/board.cpp | 12 +----------- src/domain/geometrics/space.cpp | 8 ++++++++ src/domain/geometrics/space.hpp | 7 +++++++ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/domain/board.cpp b/src/domain/board.cpp index 6b2210c..56dc7a6 100644 --- a/src/domain/board.cpp +++ b/src/domain/board.cpp @@ -175,20 +175,10 @@ Board::Board( //****************************************************************************** shared_ptr Board::find_ambient_material(Axis axis, Coord const& coord) const { - // TODO is this order (view_axis) in phase with B1D cast(view_axis, B2D) ? - auto planes = [](Axis axis) -> array { - switch(axis) { - case X: return { ZX, XY }; - case Y: return { YZ, XY }; - case Z: return { YZ, ZX }; - default: ::unreachable(); - } - } (axis); - vector, remove_const_t>> materials; for(ViewAxis view_axis : AllViewAxis) { - for(shared_ptr const& polygon : get_current_state().polygons[planes[view_axis]]) { + for(shared_ptr const& polygon : get_current_state().polygons[Planes[axis][view_axis]]) { if(polygon->material && does_overlap(cast(view_axis, polygon->bounding), coord)) { materials.emplace_back(shared_ptr(polygon->material), polygon->priority); diff --git a/src/domain/geometrics/space.cpp b/src/domain/geometrics/space.cpp index 1a7ad88..98bdda7 100644 --- a/src/domain/geometrics/space.cpp +++ b/src/domain/geometrics/space.cpp @@ -20,6 +20,14 @@ static_assert(Axes[ZX][V] == X); static_assert(Axes[XY][H] == X); static_assert(Axes[XY][V] == Y); +//****************************************************************************** +static_assert(Planes[X][H] == XY); +static_assert(Planes[X][V] == ZX); +static_assert(Planes[Y][H] == YZ); +static_assert(Planes[Y][V] == XY); +static_assert(Planes[Z][H] == ZX); +static_assert(Planes[Z][V] == YZ); + //****************************************************************************** optional transpose(Plane const plane, Axis const axis) noexcept { switch(plane) { diff --git a/src/domain/geometrics/space.hpp b/src/domain/geometrics/space.hpp index 79a7bc9..6d90752 100644 --- a/src/domain/geometrics/space.hpp +++ b/src/domain/geometrics/space.hpp @@ -28,6 +28,13 @@ inline static PlaneSpace> constexpr Axes {{ { X, Y } }}; +//****************************************************************************** +inline static AxisSpace> constexpr Planes {{ + { XY, ZX }, + { YZ, XY }, + { ZX, YZ } +}}; + /// ViewAxis describe the orientation of Axis itself. ///***************************************************************************** std::optional transpose(Plane const plane, Axis const axis) noexcept; From 6e076fce02365550887f09cb1ed90e93893d5e66 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 8 Oct 2026 22:37:33 +0200 Subject: [PATCH 10/11] GUI : remove 'old value' edition column when overwhelming --- src/ui/qt/edit/edit_model.cpp | 7 +++---- src/ui/qt/edit/edit_model.hpp | 8 ++++---- src/ui/qt/edit/edit_model_angle.cpp | 2 +- .../edit_model_conflict_diagonal_or_circular_zone.cpp | 2 +- .../edit_model_conflict_too_close_meshline_policies.cpp | 2 +- src/ui/qt/edit/edit_model_edge.cpp | 2 +- src/ui/qt/edit/edit_model_global.cpp | 2 +- src/ui/qt/edit/edit_model_interval.cpp | 2 +- src/ui/qt/edit/edit_model_meshline_policy.cpp | 2 +- src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp | 2 +- 10 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/ui/qt/edit/edit_model.cpp b/src/ui/qt/edit/edit_model.cpp index c3dc524..3c33cad 100644 --- a/src/ui/qt/edit/edit_model.cpp +++ b/src/ui/qt/edit/edit_model.cpp @@ -80,14 +80,13 @@ EditModel* EditModel::make(nodegraph::Node* node, QObject* parent) { } //****************************************************************************** -EditModel::EditModel(QObject* parent) +EditModel::EditModel(bool with_old_value, QObject* parent) : QStandardItemModel(parent) +, V(with_old_value ? 2 : 1) { - static_assert(1 <= V && V <= 2, "The Value column index V must be either 1 or 2"); - setColumnCount(V + 1); - if constexpr(V == 2) + if(with_old_value) setHorizontalHeaderLabels({ "Property", "Old value", "New value" }); else setHorizontalHeaderLabels({ "Property", "Value" }); diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp index d0ab0c7..f008d11 100644 --- a/src/ui/qt/edit/edit_model.hpp +++ b/src/ui/qt/edit/edit_model.hpp @@ -24,11 +24,11 @@ class EditModel : public QStandardItemModel { public: static EditModel* make(nodegraph::Node* node, QObject* parent = nullptr); - explicit EditModel(QObject* parent = nullptr); + EditModel(bool with_old_value, QObject* parent = nullptr); virtual bool commit(); protected: - static auto constexpr V = 2; // Value column index. + int const V; // Value column index. template void make_row(int row, QString const& property, T const& old_value, T const& new_value, QString const& tool_tip); @@ -79,7 +79,7 @@ void EditModel::make_row(int row, QString const& property, T const& old_value, T item(row, 0)->setToolTip(tool_tip); set_uneditable(item(row, 0)); - if constexpr(V == 2) { + if(V == 2) { setItem(row, 1, new QStandardItem()); item(row, 1)->setToolTip(tool_tip); set_uneditable(item(row, 1)); @@ -88,7 +88,7 @@ void EditModel::make_row(int row, QString const& property, T const& old_value, T setItem(row, V, new QStandardItem()); item(row, V)->setToolTip(tool_tip); - if constexpr(V == 2) { + if(V == 2) { set_content(item(row, V), new_value); } else { set_content(item(row, V), old_value); diff --git a/src/ui/qt/edit/edit_model_angle.cpp b/src/ui/qt/edit/edit_model_angle.cpp index a36f07f..78c51e4 100644 --- a/src/ui/qt/edit/edit_model_angle.cpp +++ b/src/ui/qt/edit/edit_model_angle.cpp @@ -16,7 +16,7 @@ namespace ui::qt { //****************************************************************************** EditModelAngle::EditModelAngle(domain::Angle* angle, QObject* parent) -: EditModel(parent) +: EditModel(false, parent) , angle(angle) { auto const& state = angle->get_current_state(); diff --git a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp index 59fefa0..58ea358 100644 --- a/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp +++ b/src/ui/qt/edit/edit_model_conflict_diagonal_or_circular_zone.cpp @@ -16,7 +16,7 @@ namespace ui::qt { //****************************************************************************** EditModelConflictDiagonalOrCircularZone::EditModelConflictDiagonalOrCircularZone(domain::ConflictDiagonalOrCircularZone* conflict, QObject* parent) -: EditModel(parent) +: EditModel(false, parent) , conflict(conflict) { auto const& state = conflict->get_current_state(); diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp index 8a5ffaa..6bfa16d 100644 --- a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp @@ -16,7 +16,7 @@ namespace ui::qt { //****************************************************************************** EditModelConflictTooCloseMeshlinePolicies::EditModelConflictTooCloseMeshlinePolicies(domain::ConflictTooCloseMeshlinePolicies* conflict, QObject* parent) -: EditModel(parent) +: EditModel(false, parent) , conflict(conflict) { auto const& state = conflict->get_current_state(); diff --git a/src/ui/qt/edit/edit_model_edge.cpp b/src/ui/qt/edit/edit_model_edge.cpp index a0f3dc7..6032ba9 100644 --- a/src/ui/qt/edit/edit_model_edge.cpp +++ b/src/ui/qt/edit/edit_model_edge.cpp @@ -16,7 +16,7 @@ namespace ui::qt { //****************************************************************************** EditModelEdge::EditModelEdge(domain::Edge* edge, QObject* parent) -: EditModel(parent) +: EditModel(false, parent) , edge(edge) { auto const& state = edge->get_current_state(); diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index 2388618..4cdddd4 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -20,7 +20,7 @@ namespace ui::qt { // https://stackoverflow.com/questions/3135505/qstandarditem-qcombobox //****************************************************************************** EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) -: EditModel(parent) +: EditModel(false, parent) , global(global) { auto params = global->get_current_state(); diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index 01b5022..3715aa6 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -16,7 +16,7 @@ namespace ui::qt { //****************************************************************************** EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent) -: EditModel(parent) +: EditModel(true, parent) , interval(interval) { auto const& state = interval->get_current_state(); diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index a40361a..707bfea 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -25,7 +25,7 @@ using Policy = domain::MeshlinePolicy::Policy; //****************************************************************************** EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshline_policy, QObject* parent) -: EditModel(parent) +: EditModel(false, parent) , meshline_policy(meshline_policy) { auto const& state = meshline_policy->get_current_state(); diff --git a/src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp b/src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp index 41e02c8..7260c99 100644 --- a/src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp +++ b/src/ui/qt/edit/edit_model_per_axis_per_criteria.cpp @@ -10,7 +10,7 @@ namespace ui::qt { //****************************************************************************** IEditModelPerAxisPerCriteria::IEditModelPerAxisPerCriteria(QObject* parent) -: EditModel(parent) +: EditModel(false, parent) {} } // namespace ui::qt From 5d58e240f40fa8e45f8a27a0cf44011a44af97f9 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 8 Oct 2026 22:48:02 +0200 Subject: [PATCH 11/11] GUI : set Interval default smoothness from Global rules --- src/ui/qt/edit/edit_model_interval.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index 3715aa6..2495b8d 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -20,6 +20,7 @@ EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent , interval(interval) { auto const& state = interval->get_current_state(); + auto const& default_smoothness = interval->global_params->get_smoothness(interval->axis, state.main_material); setRowCount(5); make_row(0, "dmax", QString::number(interval->global_params->switch_length_between_absolute_and_wavelength_relative(state.dmax)), @@ -27,13 +28,13 @@ EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent make_row(1, "Before.lmin", QString::number(state.before.lmin), "Minimum line number in the minimal interval half. " "Note a line will always be placed at the interval center."); - make_row(2, "Before.Smoothness", QString::number(state.before.smoothness), QString("2"), + make_row(2, "Before.Smoothness", QString::number(state.before.smoothness), QString::number(default_smoothness), "Smoothness factor ]1;2] around the minimal side. " "Meshing algorithm will decrease it, better to start high."); make_row(3, "After.lmin", QString::number(state.after.lmin), "Minimum line number in the maximal interval half. " "Note a line will always be placed at the interval center."); - make_row(4, "After.Smoothness", QString::number(state.after.smoothness), QString("2"), + make_row(4, "After.Smoothness", QString::number(state.after.smoothness), QString::number(default_smoothness), "Smoothness factor ]1;2] around the maximal side. " "Meshing algorithm will decrease it, better to start high."); }