diff --git a/src/db/db/dbPCellDeclaration.h b/src/db/db/dbPCellDeclaration.h index 08e058683..1d68acce1 100644 --- a/src/db/db/dbPCellDeclaration.h +++ b/src/db/db/dbPCellDeclaration.h @@ -30,6 +30,7 @@ #include "dbLayout.h" #include "tlVariant.h" #include "tlObject.h" +#include "tlOptional.h" namespace db { @@ -69,7 +70,7 @@ public: * @brief The default constructor */ PCellParameterDeclaration () - : m_hidden (false), m_readonly (false), m_type (t_none) + : m_hidden (false), m_readonly (false), m_type (t_none), m_range() { // .. nothing yet .. } @@ -78,7 +79,7 @@ public: * @brief The constructor with a name */ PCellParameterDeclaration (const std::string &name) - : m_hidden (false), m_readonly (false), m_type (t_none), m_name (name) + : m_hidden (false), m_readonly (false), m_type (t_none), m_name (name), m_range() { // .. nothing yet .. } @@ -87,7 +88,7 @@ public: * @brief The constructor with a name, type and description */ PCellParameterDeclaration (const std::string &name, type t, const std::string &description) - : m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description) + : m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_range() { // .. nothing yet .. } @@ -96,7 +97,7 @@ public: * @brief The constructor with a name, type, description and default value */ PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const tl::Variant &def) - : m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description) + : m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_range() { // .. nothing yet .. } @@ -105,7 +106,7 @@ public: * @brief The constructor with a name, type, description, default value and unit */ PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const tl::Variant &def, const std::string &unit) - : m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_unit (unit) + : m_default (def), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_unit (unit), m_range() { // .. nothing yet .. } @@ -114,7 +115,7 @@ public: * @brief The constructor with a name, type and description and choice values / choice descriptions */ PCellParameterDeclaration (const std::string &name, type t, const std::string &description, const std::vector &choices, const std::vector &choice_descriptions) - : m_choices (choices), m_choice_descriptions (choice_descriptions), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description) + : m_choices (choices), m_choice_descriptions (choice_descriptions), m_hidden (false), m_readonly (false), m_type (t), m_name (name), m_description (description), m_range() { // .. nothing yet .. } @@ -248,6 +249,53 @@ public: } /** + * @brief A enum describing the action of range violated + */ + enum Action { + t_Reject = 1, // reject the parameter + t_Accept, // accept the parameter + t_Use_Default // use a default parameter (currently not supported) + }; + + void set_range (const tl::Variant& low, const tl::Variant& high, const tl::Variant& resolution, Action action = t_Reject) + { + m_range = Range(low, high, resolution, action); + } + + typedef struct _Range { + _Range() : + m_low(), + m_high(), + m_resolution(), + m_action(t_Reject) {} + + _Range(const tl::Variant& low, const tl::Variant& high, const tl::Variant& resolution, Action action = t_Reject) : + m_low(low), + m_high(high), + m_resolution(resolution), + m_action(action) {} + + bool operator== (const _Range& other) const + { + return + m_low == other.m_low && + m_high == other.m_high && + m_resolution == other.m_resolution && + m_action == other.m_action; + } + + tl::Variant m_low; + tl::Variant m_high; + tl::Variant m_resolution; + Action m_action; + } Range; + + const tl::optional& get_range() const + { + return m_range; + } + + /** * @brief Getter for the choice descriptions * * The choice descriptions correspond to choice values. The descriptions @@ -280,7 +328,8 @@ public: m_type == d.m_type && m_name == d.m_name && m_description == d.m_description && - m_unit == d.m_unit; + m_unit == d.m_unit && + m_range == d.m_range; } private: @@ -291,6 +340,7 @@ private: type m_type; std::string m_name; std::string m_description, m_unit; + tl::optional m_range; }; /** diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 8f739a5ee..2f4c57c11 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -29,6 +29,7 @@ #include "dbPCellDeclaration.h" #include "dbLibrary.h" #include "dbLibraryManager.h" +#include "tlLog.h" namespace gsi { @@ -719,12 +720,41 @@ void clear_choices (db::PCellParameterDeclaration *pd) void add_choice (db::PCellParameterDeclaration *pd, const std::string &d, const tl::Variant &v) { - std::vector vv = pd->get_choices (); - std::vector dd = pd->get_choice_descriptions (); - vv.push_back (v); - dd.push_back (d); - pd->set_choice_descriptions (dd); - pd->set_choices (vv); + if (!pd->get_range().has_value()) + { + std::vector vv = pd->get_choices (); + std::vector dd = pd->get_choice_descriptions (); + vv.push_back (v); + dd.push_back (d); + pd->set_choice_descriptions (dd); + pd->set_choices (vv); + } + else + { + tl::warn + << "PCell parameter '" << pd->get_name() << "' has a range constraint, could not add choice '" + << v.to_string() << "'"; + } +} + +void set_range (db::PCellParameterDeclaration *pd, const tl::Variant& low, const tl::Variant& high, const tl::Variant& resolution, unsigned int action) +{ + if (pd->get_choices().empty() && pd->get_choice_descriptions().empty()) + { + if ((pd->get_type() == db::PCellParameterDeclaration::t_int + || pd->get_type() == db::PCellParameterDeclaration::t_double) && + !low.is_nil() && low.can_convert_to_double() && + !high.is_nil() && high.can_convert_to_double()) + { + pd->set_range(low, high, resolution, db::PCellParameterDeclaration::Action(action)); + } + } + else + { + tl::warn + << "PCell parameter '" << pd->get_name() << "' has a choice constraint, could not add range '[" + << low.to_string() << ", " << high.to_string() << "]'"; + } } static unsigned int pd_type_int () @@ -772,6 +802,21 @@ static unsigned int pd_type_none () return (unsigned int) db::PCellParameterDeclaration::t_none; } +static unsigned int pd_action_reject () +{ + return (unsigned int) db::PCellParameterDeclaration::t_Reject; +} + +static unsigned int pd_action_accept () +{ + return (unsigned int) db::PCellParameterDeclaration::t_Accept; +} + +static unsigned int pd_action_use_default () +{ + return (unsigned int) db::PCellParameterDeclaration::t_Use_Default; +} + db::PCellParameterDeclaration *ctor_pcell_parameter (const std::string &name, unsigned int type, const std::string &description) { db::PCellParameterDeclaration *pd = new db::PCellParameterDeclaration (); @@ -874,6 +919,19 @@ Class decl_PCellParameterDeclaration ("db", "PCel "This method will add the given value with the given description to the list of\n" "choices. If choices are defined, KLayout will show a drop-down box instead of an\n" "entry field in the parameter user interface.\n" + "If a range is already set for this parameter the choice will not be added and a warning message is showed.\n" + ) + + gsi::method_ext ("set_range", &set_range, gsi::arg ("low"), gsi::arg ("high"), gsi::arg ("resolution"), gsi::arg ("action"), + "@brief Set a range constraint\n" + "This method will set a range constraint to the parameter with 'low' and 'high' as minimum and maximum value.\n" + "This range constraint will only be set if the parameter-, the low- and the high-type are numeric.\n" + "If a choice is already set for this parameter the range will not be set and a warning message is showed.\n" + "The optional parameter 'resolution' will give a desired resolution value (currently not used).\n" + "The optional parameter 'action' determines the action to be invoked.\n" + "This action can be one of three values: REJECT, ACCEPT, USE_DEFAULT. If this failure action\n" + "parameter is not specified, then it will be REJECT by default.\n" + "If a range constraint is violated this parameter is marked wrong with violation hint in the\n" + "parameter user interface.\n" ) + gsi::method ("choice_values", &db::PCellParameterDeclaration::get_choices, "@brief Returns a list of choice values\n" @@ -897,7 +955,10 @@ Class decl_PCellParameterDeclaration ("db", "PCel gsi::method ("TypeLayer", &pd_type_layer, "@brief Type code: a layer (a \\LayerInfo object)") + gsi::method ("TypeShape", &pd_type_shape, "@brief Type code: a guiding shape (Box, Edge, Point, Polygon or Path)") + gsi::method ("TypeCallback", &pd_type_callback, "@brief Type code: a button triggering a callback\n\nThis code has been introduced in version 0.28.") + - gsi::method ("TypeNone", &pd_type_none, "@brief Type code: unspecific type") + gsi::method ("TypeNone", &pd_type_none, "@brief Type code: unspecific type") + + gsi::method ("REJECT", &pd_action_reject, "@brief Action type: reject violating parameter") + + gsi::method ("ACCEPT", &pd_action_accept, "@brief Action type: accept violating parameter") + + gsi::method ("USE_DEFAULT", &pd_action_use_default, "@brief Action type: use default instead violating parameter (currently not supported)") , "@brief A PCell parameter declaration\n" "\n" diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 2e5488d2e..7d9c2e4cb 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -398,6 +398,18 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P inner_grid->addWidget (icon_label, row, 0); m_icon_widgets.push_back (icon_label); m_all_widgets.back ().push_back (icon_label); + std::string range; + + if (p->get_range().has_value()) + { + const tl::Variant& low(p->get_range().value().m_low); + const tl::Variant& high(p->get_range().value().m_high); + + range = tl::sprintf( + " [%s, %s]" , + low.is_nil() ? "-\u221e" : low.to_string(), + high.is_nil() ? "\u221e" : high.to_string()); + } if (p->get_type () != db::PCellParameterDeclaration::t_callback) { @@ -406,7 +418,8 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P leader = tl::sprintf ("[%s] ", p->get_name ()); } - QLabel *l = new QLabel (tl::to_qstring (leader + description), inner_frame); + QLabel *l = new QLabel (tl::to_qstring (leader + description + range), inner_frame); + inner_grid->addWidget (l, row, 1); m_all_widgets.back ().push_back (l); @@ -762,6 +775,11 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool ps.set_value (tl::Variant (v)); lay::indicate_error (le, (tl::Exception *) 0); + if (p->get_range().has_value()) + { + check_range(tl::Variant(v), p->get_range().value()); + } + } catch (tl::Exception &ex) { lay::indicate_error (le, &ex); @@ -786,6 +804,11 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool ps.set_value (tl::Variant (v)); lay::indicate_error (le, (tl::Exception *) 0); + if (p->get_range().has_value()) + { + check_range(tl::Variant(v), p->get_range().value()); + } + } catch (tl::Exception &ex) { lay::indicate_error (le, &ex); @@ -1085,6 +1108,23 @@ PCellParametersPage::states_from_parameters (db::ParameterStates &states, const } } +void +PCellParametersPage::check_range(const tl::Variant& value, const db::PCellParameterDeclaration::Range& range) +{ + if (db::PCellParameterDeclaration::Action(range.m_action) == db::PCellParameterDeclaration::t_Reject) + { + if (!range.m_low.is_nil() && value < range.m_low) + { + throw tl::Exception(tl::to_string (tr("Range violation: value < low"))); + } + + if (!range.m_high.is_nil() && range.m_high < value) + { + throw tl::Exception(tl::to_string (tr("Range violation: value > high"))); + } + } +} + } #endif diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index e6e501399..0e961b8e1 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -181,6 +181,7 @@ private: void get_parameters_internal (db::ParameterStates &states, bool &edit_error); std::vector parameter_from_states (const db::ParameterStates &states) const; void states_from_parameters (db::ParameterStates &states, const std::vector ¶meters); + void check_range(const tl::Variant& value, const db::PCellParameterDeclaration::Range& range); }; } diff --git a/src/gsi/gsi/gsiTypes.h b/src/gsi/gsi/gsiTypes.h index cf02ea391..250a8db2c 100644 --- a/src/gsi/gsi/gsiTypes.h +++ b/src/gsi/gsi/gsiTypes.h @@ -35,6 +35,7 @@ #include #include #include +#include #if defined(HAVE_QT) #include diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 8cd688a4f..e205ca7fb 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -36569,6 +36569,18 @@ class PCellParameterDeclaration: r""" @brief Type code: string data """ + REJECT: ClassVar[int] + r""" + @brief Action type: reject violating parameter + """ + ACCEPT: ClassVar[int] + r""" + @brief Action type: accept violating parameter + """ + USE_DEFAULT: ClassVar[int] + r""" + @brief Action type: use default instead violating parameter (currently not supported) + """ default: Any r""" Getter: diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 5a9f73562..7d1cf0c15 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -55,7 +55,8 @@ SOURCES = \ tlEquivalenceClusters.cc \ tlUniqueName.cc \ tlRecipe.cc \ - tlEnv.cc + tlEnv.cc \ + tlOptional.cc HEADERS = \ tlAlgorithm.h \ @@ -121,7 +122,8 @@ HEADERS = \ tlUniqueName.h \ tlRecipe.h \ tlSelect.h \ - tlEnv.h + tlEnv.h \ + tlOptional.h equals(HAVE_GIT2, "1") { diff --git a/src/tl/tl/tlOptional.cc b/src/tl/tl/tlOptional.cc new file mode 100644 index 000000000..02269651a --- /dev/null +++ b/src/tl/tl/tlOptional.cc @@ -0,0 +1,32 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlOptional.h" + +namespace tl +{ + +#if __cplusplus < 201703L +extern const nullopt_t nullopt = nullopt_t(); +#endif + +} // namespace tl diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h new file mode 100644 index 000000000..7dcc59565 --- /dev/null +++ b/src/tl/tl/tlOptional.h @@ -0,0 +1,160 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlOptional +#define HDR_tlOptional + +#include "tlAssert.h" + +#include +#include + +namespace tl +{ + +#if __cplusplus >= 201703L + +template +class optional : public std::optional {}; + +#else + +struct nullopt_t {}; + +extern const nullopt_t nullopt; + +/* + * Poor man's partly implementation of C++17's std::optional + */ +template +class optional +{ +public: + optional() : + a_value(), + a_isValid(false) + {} + + optional(const nullopt_t&) : + a_value(), + a_isValid(false) + {} + + optional(const T &value) : + a_value(value), + a_isValid(true) + {} + + void reset() + { + a_isValid = false; + } + + bool has_value() const { return a_isValid; } + + T &value() + { + tl_assert(a_isValid); + + return a_value; + } + + const T &value() const + { + tl_assert(a_isValid); + + return a_value; + } + + T& operator* () + { + return value(); + } + + const T& operator* () const + { + return value(); + } + + T* operator-> () + { + return &value(); + } + + const T* operator-> () const + { + return &value(); + } + +private: + T a_value; + bool a_isValid; +}; + +template +optional make_optional(const T& value) +{ + return optional(value); +} + +template +bool operator==(const optional &lhs, const optional &rhs) +{ + if (lhs.has_value() != rhs.has_value()) + { + return false; + } + + if (!lhs.has_value()) + { + return true; + } + + return lhs.value() == rhs.value(); +} + +template +bool operator!=(const optional &lhs, const optional &rhs) +{ + return !(lhs == rhs); +} + +template +std::ostream &operator<<(std::ostream &ostr, const optional &rhs) +{ + if (rhs.has_value()) + { + ostr << rhs.value(); + } + else + { + ostr << ""; + } + + return ostr; +} + +#endif /* __cplusplus >= 201703L */ + +} // namespace tl + +#endif /* HDR_tlOptional */