Compare commits

..

11 Commits

Author SHA1 Message Date
Matthias Köfferlein ad1a3a0be3
Merge 4eaae15ce0 into 3a069427cd 2025-10-26 16:45:54 +01:00
Matthias Köfferlein 3a069427cd
Merge pull request #2202 from KLayout/bugfix/issue-2201
Fixed issue #2201 (trace path)
2025-10-26 16:35:43 +01:00
Matthias Köfferlein fdeab5d104
Merge pull request #2200 from KLayout/issue/issue-2183
Implementing request from issue #2183
2025-10-26 16:35:29 +01:00
Matthias Köfferlein ff49a81723
Merge pull request #2199 from KLayout/feature/issue-2195
Feature/issue 2195
2025-10-26 16:35:14 +01:00
Matthias Köfferlein d015b98534
Merge pull request #2198 from KLayout/bugfix/issue-2194
Fixing issue #2194 (can't attach key binding to 'forward'/'backward')
2025-10-26 16:34:57 +01:00
Matthias Koefferlein b454d2ae42 Fixed issue #2201 (trace path)
* you can zoom in now to select the end point. Problem was
  actually that zooming in was a problem when the start point
  went out of the viewport

In addition:
* Messages are sticky now ("Click on second point")
* "Esc" will cancel path trace mode
* The cursor switches back to normal after tracing
2025-10-25 23:12:22 +02:00
Matthias Koefferlein b9906180e8 Implementing request from issue #2183
For strmxor, --drop-empty-cells now is default.
To explicitly turn it OFF, use

strmxor --drop-empty-cells=false ...
2025-10-25 17:54:26 +02:00
Matthias Koefferlein fc3185165f Added some tests 2025-10-25 17:16:13 +02:00
Matthias Koefferlein b523f05f80 Some typos fixed 2025-10-25 00:33:25 +02:00
Matthias Koefferlein e8e2858af3 Implemented a solution for #2195
A new PCell method cell_name/cell_name_impl that
delivers a cell name, which is used for "convert to
static cell" and as cell name in general.

Needs testing.
2025-10-25 00:21:04 +02:00
Matthias Koefferlein e6e85ab3b3 FIxing issue #2194 (can't attach key binding to 'forward'/'backward') 2025-10-23 21:49:24 +02:00
33 changed files with 562 additions and 65 deletions

View File

@ -30,9 +30,23 @@ namespace bd
{
GenericWriterOptions::GenericWriterOptions ()
: m_scale_factor (1.0)
{
db::SaveLayoutOptions save_options;
db::SaveLayoutOptions options;
init_from_options (options);
}
GenericWriterOptions::GenericWriterOptions (const db::SaveLayoutOptions &options)
{
init_from_options (options);
}
void
GenericWriterOptions::init_from_options (const db::SaveLayoutOptions &save_options_nc)
{
// const_cast needed because "get_option_by_name" is not const as it should be
db::SaveLayoutOptions &save_options = const_cast<db::SaveLayoutOptions &> (save_options_nc);
m_scale_factor = 1.0;
m_dbu = save_options.get_option_by_name ("dbu").to_double ();
m_libname = save_options.get_option_by_name ("libname").to_string ();

View File

@ -53,6 +53,13 @@ public:
*/
GenericWriterOptions ();
/**
* @brief Constructor
*
* The "options" object specifies the defaults to be used.
*/
GenericWriterOptions (const db::SaveLayoutOptions &options);
/**
* @brief Adds the generic options to the command line parser object
* The format string gives a hint about the target format. Certain options will be
@ -142,6 +149,7 @@ private:
int m_dxf_polygon_mode;
void set_oasis_substitution_char (const std::string &text);
void init_from_options (const db::SaveLayoutOptions &options);
};
}

View File

@ -380,7 +380,9 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
generic_reader_options_a.add_options (cmd);
generic_reader_options_b.add_options (cmd);
bd::GenericWriterOptions writer_options;
db::SaveLayoutOptions def_writer_options;
def_writer_options.set_dont_write_empty_cells (true);
bd::GenericWriterOptions writer_options (def_writer_options);
writer_options.add_options (cmd);
cmd << tl::arg ("input_a", &infile_a, "The first input file (any format, may be gzip compressed)")
@ -447,7 +449,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
cmd.parse (argc, argv);
if (top_a.empty () != top_b.empty ()) {
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given");
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given, not just one of them");
}
if (tolerances.empty ()) {

View File

@ -129,6 +129,48 @@ TEST(1A_Deep)
std::string output = this->tmp_file ("tmp.oas");
const char *argv[] = { "x", "--deep", "--drop-empty-cells=false", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (hierarchical shape count)\n"
" ----------------------------------------------------------------\n"
" 3/0 3/0 3\n"
" 6/0 6/0 314\n"
" 8/1 8/1 1\n"
" 10/0 - (no such layer in first layout)\n"
"\n"
);
}
TEST(1A_DeepNoEmptyCells)
{
tl::CaptureChannel cap;
std::string input_a = tl::testdata ();
input_a += "/bd/strmxor_in1.gds";
std::string input_b = tl::testdata ();
input_b += "/bd/strmxor_in2.gds";
std::string au = tl::testdata ();
au += "/bd/strmxor_au1d2.oas";
std::string output = this->tmp_file ("tmp.oas");
const char *argv[] = { "x", "--deep", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
@ -342,7 +384,7 @@ TEST(2_Deep)
std::string output = this->tmp_file ("tmp.oas");
const char *argv[] = { "x", "-u", "--no-summary", "-l", input_a.c_str (), input_b.c_str (), output.c_str () };
const char *argv[] = { "x", "-u", "--no-summary", "--drop-empty-cells=false", "-l", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
@ -508,7 +550,7 @@ TEST(3_Deep)
std::string output = this->tmp_file ("tmp.oas");
// NOTE: -p is ignored in deep mode
const char *argv[] = { "x", "-u", "--no-summary", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str (), output.c_str () };
const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
@ -607,7 +649,7 @@ TEST(4_Deep)
std::string output = this->tmp_file ("tmp.oas");
const char *argv[] = { "x", "-u", "--no-summary", "-p=1.0", "-n=4", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-p=1.0", "-n=4", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
@ -673,7 +715,7 @@ TEST(5_Deep)
std::string output = this->tmp_file ("tmp.oas");
const char *argv[] = { "x", "-u", "--no-summary", "-b=1000", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-b=1000", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);

View File

@ -149,6 +149,18 @@ created as well.
This method must be reimplemented in a PCell class to identify the PCell in human-readable form.
This text is shown in the cell tree for the PCell for example.
@method cell_name_impl
@brief Delivers the cell name to be used for the PCell variant
A PCell variant is represented in the cell tree by a placeholder cell. By
default, the name of this cell is the PCell name. Since multiple variants
may exist, usually a disambiguator is added to the name (e.g. "..$1").
This method allows encoding the PCell parameters into that cell name,
so the PCell variant is easier to identify in the cell tree - for example
in the GDS file - instead of the unspecific disambiguator.
@method produce_impl
@brief Produces the layout
@ -466,6 +478,19 @@ module RBA
text
end
# implementation of cell_name
def cell_name(parameters)
self._start
@param_values = parameters
text = ""
begin
text = cell_name_impl
ensure
self._finish
end
text
end
# get the parameters
def get_parameters
@param_decls
@ -568,6 +593,11 @@ module RBA
""
end
# default implementation
def cell_name_impl
self.name
end
# default implementation
def coerce_parameters_impl
end

View File

@ -158,6 +158,18 @@ created as well.
This method must be reimplemented in a PCell class to identify the PCell in human-readable form.
This text is shown in the cell tree for the PCell for example.
@method cell_name_impl
@brief Delivers the cell name to be used for the PCell variant
A PCell variant is represented in the cell tree by a placeholder cell. By
default, the name of this cell is the PCell name. Since multiple variants
may exist, usually a disambiguator is added to the name (e.g. "..$1").
This method allows encoding the PCell parameters into that cell name,
so the PCell variant is easier to identify in the cell tree - for example
in the GDS file - instead of the unspecific disambiguator.
@method produce_impl
@brief Produces the layout

View File

@ -862,6 +862,12 @@ Cell::get_basic_name () const
return layout ()->cell_name (cell_index ());
}
std::string
Cell::get_variant_name () const
{
return get_basic_name ();
}
std::string
Cell::get_qualified_name () const
{

View File

@ -890,7 +890,7 @@ public:
void set_name (const std::string &name);
/**
* @brief Get the basic name
* @brief Gets the basic name
*
* The basic name of the cell is either the cell name or the cell name in the
* target library (for library proxies) or the PCell name (for PCell proxies).
@ -898,6 +898,14 @@ public:
*/
virtual std::string get_basic_name () const;
/**
* @brief Gets the variant name
*
* The variant name is the PCell's "cell_name" - which may encode the PCell parameters
* into a formal name. Usually that is identical to the PCell name.
*/
virtual std::string get_variant_name () const;
/**
* @brief Gets the display name
*

View File

@ -89,7 +89,7 @@ ColdProxy::get_basic_name () const
}
}
std::string
std::string
ColdProxy::get_display_name () const
{
if (! mp_context_info->lib_name.empty ()) {

View File

@ -2462,7 +2462,7 @@ Layout::get_pcell_variant_dict (pcell_id_type pcell_id, const std::map<std::stri
pcell_variant_type *variant = header->get_variant (*this, parameters);
if (! variant) {
std::string b (header->get_name ());
std::string b (header->declaration ()->get_cell_name (parameters));
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
b = uniquify_cell_name (b.c_str ());
}
@ -2501,7 +2501,7 @@ Layout::get_pcell_variant (pcell_id_type pcell_id, const std::vector<tl::Variant
pcell_variant_type *variant = header->get_variant (*this, parameters);
if (! variant) {
std::string b (header->get_name ());
std::string b (header->declaration ()->get_cell_name (parameters));
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
b = uniquify_cell_name (b.c_str ());
}
@ -2627,9 +2627,18 @@ Layout::convert_cell_to_static (db::cell_index_type ci)
const cell_type &org_cell = cell (ci);
// Note: convert to static cell by explicitly cloning to the db::Cell class
ret_ci = add_cell (org_cell.get_basic_name ().c_str ());
std::string vn = org_cell.get_variant_name ();
if (vn == std::string (cell_name (ci), vn.size ())) {
// there is a cell name conflict: give priority to the static cell, so it
// will see the variant name or at least the original disambiguated name
std::string rename_org = uniquify_cell_name (vn.c_str ());
vn = cell_name (ci);
rename_cell (ci, rename_org.c_str ());
}
ret_ci = add_cell (vn.c_str ());
cell_type &new_cell = cell (ret_ci);
// Note: we convert to static cell by explicitly converting to the db::Cell class
new_cell = org_cell;
new_cell.set_cell_index (ret_ci);
@ -3126,6 +3135,12 @@ Layout::basic_name (cell_index_type cell_index) const
return cell (cell_index).get_basic_name ();
}
std::string
Layout::variant_name (cell_index_type cell_index) const
{
return cell (cell_index).get_variant_name ();
}
void
Layout::register_lib_proxy (db::LibraryProxy *lib_proxy)
{
@ -3161,7 +3176,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
} else {
// create a new unique name
std::string b (lib->layout ().basic_name (cell_index));
std::string b (lib->layout ().variant_name (cell_index));
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
b = uniquify_cell_name (b.c_str ());
}

View File

@ -675,7 +675,7 @@ public:
std::string display_name (cell_index_type cell_index) const;
/**
* @brief Return the basic name for the given cell
* @brief Returns the basic name for the given cell
*
* This method is forwarded to the respective method of the cell.
* The basic name is the "original" cell name within the library or
@ -684,7 +684,16 @@ public:
*/
std::string basic_name (cell_index_type cell_index) const;
/**
/**
* @brief Returns the variant name for the given cell
*
* The variant name usually is the basic name. For PCells, this name
* can encode PCell parameters, depending on the definition of the
* PCell.
*/
std::string variant_name (cell_index_type cell_index) const;
/**
* @brief Add a cell object with the given ID and name
*
* This method is basically supposed to be used for "undo" and "redo".

View File

@ -257,7 +257,23 @@ LibraryProxy::get_basic_name () const
}
}
std::string
std::string
LibraryProxy::get_variant_name () const
{
Library *lib = LibraryManager::instance ().lib (lib_id ());
if (lib) {
if (! lib->layout ().is_valid_cell_index (library_cell_index ())) {
return "<defunct>";
} else {
const db::Cell &lib_cell = lib->layout ().cell (library_cell_index ());
return lib_cell.get_variant_name ();
}
} else {
return Cell::get_variant_name ();
}
}
std::string
LibraryProxy::get_display_name () const
{
Library *lib = LibraryManager::instance ().lib (lib_id ());

View File

@ -96,25 +96,28 @@ public:
/**
* @brief Gets the basic name
*
* The basic name of the cell is either the cell name or the cell name in the
* target library (for library proxies) or the PCell name (for PCell proxies).
* The actual name may be different by a extension to make it unique.
* This returns the basic name of the proxy target
*/
virtual std::string get_basic_name () const;
/**
* @brief Gets the variant name
*
* This returns the basic name of the proxy target
*/
virtual std::string get_variant_name () const;
/**
* @brief Gets the display name
*
* The display name is some "nice" descriptive name of the cell (variant)
* For normal cells this name is equivalent to the normal cell name.
* This returns the basic name of the proxy target
*/
virtual std::string get_display_name () const;
/**
* @brief Gets the qualified name
*
* The qualified name for a library proxy is made from the library name, a
* dot and the cell's name.
* Gets a combination of the library name and the target cell's qualified name
*/
virtual std::string get_qualified_name () const;

View File

@ -673,6 +673,18 @@ public:
return std::string ();
}
/**
* @brief Gets a cell name for the PCell, which can depend on the parameters
*
* The actual cell name in the layout may differ by disambiguation. This method
* delivers a proposal for a cell name.
* By default, the PCell name is returned.
*/
virtual std::string get_cell_name (const pcell_parameters_type &) const
{
return m_name;
}
/**
* @brief Returns the description text of the PCell
*

View File

@ -86,7 +86,18 @@ PCellVariant::get_basic_name () const
}
}
std::string
std::string
PCellVariant::get_variant_name () const
{
const PCellHeader *header = pcell_header ();
if (header) {
return m_variant_name;
} else {
return Cell::get_basic_name ();
}
}
std::string
PCellVariant::get_display_name () const
{
const PCellHeader *header = pcell_header ();
@ -172,6 +183,7 @@ PCellVariant::update (ImportLayerMapping *layer_mapping)
header->declaration ()->produce (*layout (), layer_ids, plist, *this);
m_display_name = header->declaration ()->get_display_name (plist);
m_variant_name = header->declaration ()->get_cell_name (plist);
} catch (tl::Exception &ex) {

View File

@ -94,42 +94,37 @@ public:
}
/**
* @brief Get the basic name
*
* The basic name of the cell is either the cell name or the cell name in the
* target library (for library proxies) or the PCell name (for PCell proxies).
* The actual name may be different by a extension to make it unique.
* @brief Gets the basic name
*/
virtual std::string get_basic_name () const;
/**
* @brief Get the display name
*
* The display name is some "nice" descriptive name of the cell (variant)
* For normal cells this name is equivalent to the normal cell name.
* @brief Gets the variant name
*/
virtual std::string get_variant_name () const;
/**
* @brief Gets the display name
*/
virtual std::string get_display_name () const;
/**
* @brief Unregister a cell from it's context.
* @brief Unregisters a cell from its context.
*/
virtual void unregister ();
/**
* @brief Reregister a cell inside it's context.
* @brief Re-registers a cell inside its context.
*/
virtual void reregister ();
/**
* @brief Update the layout
* @brief Updates the layout
*/
virtual void update (ImportLayerMapping *layer_mapping = 0);
/**
* @brief Tell, if this cell is a proxy cell
*
* Proxy cells are such whose layout represents a snapshot of another entity.
* Such cells can be PCell variants or library references for example.
* @brief Gets a value indicating if this cell is a proxy cell
*/
virtual bool is_proxy () const
{
@ -138,7 +133,7 @@ public:
protected:
/**
* @brief Get the PCell header for this variant
* @brief Gets the PCell header for this variant
*/
PCellHeader *pcell_header ()
{
@ -146,7 +141,7 @@ protected:
}
/**
* @brief Get the PCell header for this variant
* @brief Gets the PCell header for this variant
*/
const PCellHeader *pcell_header () const
{
@ -156,6 +151,7 @@ protected:
private:
pcell_parameters_type m_parameters;
mutable std::string m_display_name;
mutable std::string m_variant_name;
db::pcell_id_type m_pcell_id;
bool m_registered;
};

View File

@ -368,6 +368,7 @@ Class<db::PCellDeclaration> decl_PCellDeclaration_Native ("db", "PCellDeclaratio
gsi::method ("via_types", &db::PCellDeclaration::via_types) +
gsi::method ("description", &db::PCellDeclaration::get_description) +
gsi::method ("display_text", &db::PCellDeclaration::get_display_name, gsi::arg ("parameters")) +
gsi::method ("cell_name", &db::PCellDeclaration::get_cell_name, gsi::arg ("parameters")) +
gsi::method ("layout", &db::PCellDeclaration::layout,
"@brief Gets the Layout object the PCell is registered in or nil if it is not registered yet.\n"
"This attribute has been added in version 0.27.5."
@ -659,6 +660,20 @@ public:
}
}
std::string get_cell_name_fb (const db::pcell_parameters_type &parameters) const
{
return db::PCellDeclaration::get_cell_name (parameters);
}
virtual std::string get_cell_name (const db::pcell_parameters_type &parameters) const
{
if (cb_get_cell_name.can_issue ()) {
return cb_get_cell_name.issue<db::PCellDeclaration, std::string, const db::pcell_parameters_type &> (&db::PCellDeclaration::get_cell_name, parameters);
} else {
return db::PCellDeclaration::get_cell_name (parameters);
}
}
gsi::Callback cb_get_layer_declarations;
gsi::Callback cb_get_parameter_declarations;
gsi::Callback cb_produce;
@ -669,6 +684,7 @@ public:
gsi::Callback cb_coerce_parameters;
gsi::Callback cb_callback;
gsi::Callback cb_get_display_name;
gsi::Callback cb_get_cell_name;
gsi::Callback cb_get_description;
gsi::Callback cb_via_types;
};
@ -682,6 +698,7 @@ Class<PCellDeclarationImpl> decl_PCellDeclaration (decl_PCellDeclaration_Native,
gsi::method ("parameters_from_shape", &PCellDeclarationImpl::parameters_from_shape_fb, "@hide") +
gsi::method ("transformation_from_shape", &PCellDeclarationImpl::transformation_from_shape_fb, "@hide") +
gsi::method ("display_text", &PCellDeclarationImpl::get_display_name_fb, "@hide") +
gsi::method ("cell_name", &PCellDeclarationImpl::get_cell_name_fb, "@hide") +
gsi::method ("wants_lazy_evaluation", &PCellDeclarationImpl::wants_lazy_evaluation_fb, "@hide") +
gsi::method ("description", &PCellDeclarationImpl::get_description_fb, "@hide") +
gsi::method ("via_types", &PCellDeclarationImpl::via_types_fb, "@hide") +
@ -809,6 +826,13 @@ Class<PCellDeclarationImpl> decl_PCellDeclaration (decl_PCellDeclaration_Native,
"@brief Returns the display text for this PCell given a certain parameter set\n"
"Reimplement this method to create a distinct display text for a PCell variant with \n"
"the given parameter set. If this method is not implemented, a default text is created. \n"
) +
gsi::callback ("cell_name", &PCellDeclarationImpl::get_cell_name, &PCellDeclarationImpl::cb_get_cell_name, gsi::arg ("parameters"),
"@brief Returns a cell name used for the PCell variant\n"
"Reimplement this method to create a cell name the system uses for the PCell variant. By default that is the PCell name.\n"
"This feature allows encoding the PCell parameters into the cell name for easier identification of the PCell variant in a cell tree.\n"
"\n"
"This feature has been added in version 0.30.5.\n"
),
"@brief A PCell declaration providing the parameters and code to produce the PCell\n"
"\n"

View File

@ -88,6 +88,13 @@ class PD
cell.shapes (l_metal0).insert (db::Box (0, 0, width, height));
}
virtual std::string get_display_name (const db::pcell_parameters_type &parameters) const
{
db::Coord width = db::coord_traits<db::Coord>::rounded (parameters[0].to_double () * 1000.0);
db::Coord height = db::coord_traits<db::Coord>::rounded (parameters[1].to_double () * 1000.0);
return tl::sprintf ("PD(W=%d,H=%d)", width, height);
}
};
TEST(0)
@ -252,3 +259,114 @@ TEST(1)
}
}
// PCell names, convert PCell to static
class PD2
: public PD
{
public:
virtual std::string get_cell_name (const db::pcell_parameters_type &parameters) const
{
db::Coord width = db::coord_traits<db::Coord>::rounded (parameters[0].to_double () * 1000.0);
db::Coord height = db::coord_traits<db::Coord>::rounded (parameters[1].to_double () * 1000.0);
return tl::sprintf ("PD_W%d_H%d", width, height);
}
};
TEST(2)
{
db::Manager m (true);
db::Layout layout(&m);
layout.dbu (0.001);
db::LayerProperties p;
p.layer = 23;
p.datatype = 0;
unsigned int l_cont = layout.insert_layer (p);
p.layer = 16;
p.datatype = 0;
unsigned int l_gate = layout.insert_layer (p);
db::Cell &cell_a = layout.cell (layout.add_cell ("A"));
cell_a.shapes(l_cont).insert(db::Box (50, 50, 150, 150));
cell_a.shapes(l_gate).insert(db::Box (0, 0, 200, 1000));
db::Cell &top = layout.cell (layout.add_cell ("TOP"));
db::pcell_id_type pd = layout.register_pcell ("PD", new PD2 ());
std::vector<tl::Variant> parameters;
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
tl::Variant &width = parameters[0];
tl::Variant &height = parameters[1];
tl::Variant &orientation = parameters[2];
width = 0.5;
height = 1.0;
orientation = long (0);
db::cell_index_type pd1 = layout.get_pcell_variant (pd, parameters);
top.insert (db::CellInstArray (db::CellInst (pd1), db::Trans (db::Vector (0, 0))));
EXPECT_EQ (layout.display_name (pd1), "PD(W=500,H=1000)");
EXPECT_EQ (layout.cell_name (pd1), "PD_W500_H1000");
EXPECT_EQ (layout.variant_name (pd1), "PD_W500_H1000");
width = 0.4;
height = 0.8;
db::cell_index_type pd2 = layout.get_pcell_variant (pd, parameters);
top.insert (db::CellInstArray (db::CellInst (pd2), db::Trans (db::Vector (0, 2000))));
EXPECT_EQ (layout.display_name (pd2), "PD(W=400,H=800)");
EXPECT_EQ (layout.cell_name (pd2), "PD_W400_H800");
EXPECT_EQ (layout.variant_name (pd2), "PD_W400_H800");
EXPECT_NE (pd1, pd2);
width = 0.4;
height = 0.8;
orientation = long (1);
db::cell_index_type pd3 = layout.get_pcell_variant (pd, parameters);
auto i3 = top.insert (db::CellInstArray (db::CellInst (pd3), db::Trans (db::Vector (2000, 0))));
EXPECT_EQ (layout.display_name (pd3), "PD(W=400,H=800)");
EXPECT_EQ (layout.cell_name (pd3), "PD_W400_H800$1");
EXPECT_EQ (layout.variant_name (pd3), "PD_W400_H800");
EXPECT_NE (pd2, pd3);
auto pd3_org = pd3;
pd3 = layout.convert_cell_to_static (pd3);
EXPECT_NE (pd3, pd3_org);
auto ci3 = i3.cell_inst ();
ci3.object ().cell_index (pd3);
top.replace (i3, ci3);
EXPECT_EQ (layout.cell (pd3_org).is_proxy (), true);
EXPECT_EQ (layout.cell (pd3).is_proxy (), false);
EXPECT_EQ (layout.display_name (pd3_org), "PD(W=400,H=800)");
EXPECT_EQ (layout.cell_name (pd3_org), "PD_W400_H800$2");
EXPECT_EQ (layout.variant_name (pd3_org), "PD_W400_H800");
layout.do_cleanup (true);
layout.cleanup ();
EXPECT_EQ (layout.is_valid_cell_index (pd3_org), false);
EXPECT_EQ (layout.display_name (pd3), "PD_W400_H800$1");
EXPECT_EQ (layout.cell_name (pd3), "PD_W400_H800$1");
EXPECT_EQ (layout.variant_name (pd3), "PD_W400_H800$1");
CHECKPOINT ();
db::compare_layouts (_this, layout, tl::testdata () + "/gds/pcell_test20.gds", db::NoNormalization);
}

View File

@ -1327,6 +1327,13 @@ static std::string path_to_string (const db::Layout &layout, const lay::ObjectIn
void
Service::display_status (bool transient)
{
if (transient && view ()->canvas ()->begin_mouse_receivers () != view ()->canvas ()->end_mouse_receivers ()
&& (*view ()->canvas ()->begin_mouse_receivers ())->claims_message_bar ()) {
// do not display transient if there is a plugin active that has captured the mouse and claims the message bar -
// this way we can use messages in active plugins and still get visual feedback by the transient selection.
return;
}
EditableSelectionIterator r = transient ? begin_transient_selection () : begin_selection ();
EditableSelectionIterator rr = r;

View File

@ -621,6 +621,11 @@ struct LAYBASIC_PUBLIC AbstractMenuItem
return m_primary;
}
void set_primary (bool p)
{
m_primary = p;
}
std::list <AbstractMenuItem> children;
private:

View File

@ -63,7 +63,7 @@ static int inst_point_sel_tests = 10000;
Finder::Finder (bool point_mode, bool top_level_sel)
: m_min_level (0), m_max_level (0),
mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_top_level_sel (top_level_sel)
mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_consider_viewport (true), m_top_level_sel (top_level_sel)
{
m_distance = std::numeric_limits<double>::max ();
}
@ -482,7 +482,10 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
checkpoint ();
// Viewport in current cell coordinate space (DBU)
db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
db::Box viewport_box;
if (consider_viewport ()) {
viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
}
if (! m_context_layers.empty ()) {
@ -583,7 +586,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
bool any_valid_edge = m_capture_all_shapes;
for (db::Shape::polygon_edge_iterator e = shape->begin_edge (); ! e.at_end (); ++e) {
if ((*e).clipped (viewport_box).first) {
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
any_valid_edge = true;
test_edge (t, *e, d, match);
}
@ -606,7 +609,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
++pt;
for (; pt != shape->end_point (); ++pt) {
db::Edge e (p, *pt);
if (e.clipped (viewport_box).first) {
if (viewport_box.empty () || e.clipped (viewport_box).first) {
any_valid_edge = true;
test_edge (t, e, d, match);
}
@ -618,7 +621,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
db::Polygon poly;
shape->polygon (poly);
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
if ((*e).clipped (viewport_box).first) {
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
any_valid_edge = true;
test_edge (t, *e, d, match);
}
@ -651,7 +654,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
// convert to polygon and test those edges
db::Polygon poly (box);
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
if ((*e).clipped (viewport_box).first) {
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
any_valid_edge = true;
test_edge (t, *e, d, match);
}
@ -819,7 +822,10 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
checkpoint ();
// Viewport in current cell coordinate space (DBU)
db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
db::Box viewport_box;
if (consider_viewport ()) {
viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
}
if (! point_mode ()) {
@ -952,7 +958,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
bool any_valid_edge = false;
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
// only consider edges that cut through the viewport
if ((*e).clipped (viewport_box).first) {
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
any_valid_edge = true;
test_edge (t, *e, d, match);
}

View File

@ -96,6 +96,25 @@ public:
m_catch_all = f;
}
/**
* @brief Gets a flag indicating that the viewport will be considered
*/
bool consider_viewport () const
{
return m_consider_viewport;
}
/**
* @brief Sets a flag indicating that the viewport will be considered
* If this flag is true (the default), only shapes and instances will be considered
* if edges (or polygons) or boundary edges (for instances) are visible in the
* viewport. If this flag is false, shapes or instances are considered always.
*/
void set_consider_viewport (bool f)
{
m_consider_viewport = f;
}
/**
* @brief Destructor (just provided to please the compiler)
*/
@ -217,6 +236,7 @@ private:
double m_distance;
bool m_point_mode;
bool m_catch_all;
bool m_consider_viewport;
bool m_top_level_sel;
db::box_convert <db::CellInst, false> m_box_convert;
db::box_convert <db::Cell, false> m_cell_box_convert;

View File

@ -197,6 +197,12 @@ PluginDeclaration::init_menu (lay::Dispatcher *dispatcher)
if (! m->copy_from.empty ()) {
// As a general strategy for now we take primary ownership from the copy source as pass it to
// the copy. This is important for "next/prev_display_state" as the first registry is @toolbar
// (which is not accessible by Setup menu) and the second one is "zoom_menu" which should be
// the primary one. For later, we may want to make this configurable (move/leave primary flag).
menu.find_item_exact (m->copy_from)->set_primary (false);
menu.insert_item (m->insert_pos, m->menu_name, menu.action (m->copy_from));
} else if (m->separator) {

View File

@ -621,7 +621,10 @@ END_PROTECTED
void
ViewObjectUI::set_cursor (lay::Cursor::cursor_shape cursor)
{
m_cursor = cursor;
if (m_cursor != cursor) {
m_cursor = cursor;
realize_cursor ();
}
}
void
@ -629,15 +632,7 @@ ViewObjectUI::set_default_cursor (lay::Cursor::cursor_shape cursor)
{
if (cursor != m_default_cursor) {
m_default_cursor = cursor;
#if defined(HAVE_QT)
if (m_cursor == lay::Cursor::none && mp_widget) {
if (m_default_cursor == lay::Cursor::none) {
mp_widget->unsetCursor ();
} else {
mp_widget->setCursor (lay::Cursor::qcursor (m_default_cursor));
}
}
#endif
realize_cursor ();
}
}
@ -652,11 +647,17 @@ ViewObjectUI::ensure_entered ()
void
ViewObjectUI::begin_mouse_event (lay::Cursor::cursor_shape cursor)
{
m_cursor = cursor;
set_cursor (cursor);
}
void
ViewObjectUI::end_mouse_event ()
{
realize_cursor ();
}
void
ViewObjectUI::realize_cursor ()
{
#if defined(HAVE_QT)
if (mp_widget) {

View File

@ -285,6 +285,14 @@ public:
*/
virtual void drag_cancel () { }
/**
* @brief Gets a value indicating whether the mouse receiver claims the view message bar
*
* If this method returns true, other services are not supposed to emit transient
* messages.
*/
virtual bool claims_message_bar () const { return false; }
/**
* @brief Gets a value indicating whether a cursor position it set
*/
@ -1121,6 +1129,7 @@ private:
void objects_changed ();
int widget_height () const;
int widget_width () const;
void realize_cursor ();
/**
* @brief Register a service

View File

@ -202,13 +202,32 @@ NetTracerDialog::item_double_clicked (QListWidgetItem *item)
}
}
void
NetTracerDialog::drag_cancel ()
{
if (m_mouse_state > 0) {
view ()->message ();
ui ()->ungrab_mouse (this);
set_cursor (lay::Cursor::none);
m_mouse_state = 0;
}
}
bool
NetTracerDialog::claims_message_bar () const
{
return true;
}
bool
NetTracerDialog::mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio)
{
if (prio && m_mouse_state != 0) {
set_cursor (lay::Cursor::cross);
}
return false;
}
@ -397,11 +416,13 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
{
unsigned int start_layer = 0;
db::Point start_point;
db::Shape start_shape;
// locate the seed
{
lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All);
finder.set_consider_viewport (false);
// go through all visible layers of all cellviews and find a seed shape
for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) {
@ -417,7 +438,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
}
m_cv_index = r->cv_index ();
start_shape = r->shape ();
start_layer = r->layer ();
}
@ -440,6 +461,12 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
start_point = tt.inverted ().trans (start_search_box.center ());
// stop if the center start point is not inside the start polygon
db::Polygon poly;
if (start_shape.polygon (poly) && db::inside_poly (poly.begin_edge (), start_point) < 0) {
return 0;
}
}
// Set up the net tracer environment
@ -455,6 +482,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
if (trace_path) {
lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All);
finder.set_consider_viewport (false);
// go through all visible layers of all cellviews and find a seed shape
for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) {
@ -483,6 +511,12 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
stop_point = tt.inverted ().trans (stop_search_box.center ());
stop_layer = r->layer ();
// stop if the center stop point is not inside the stop polygon
db::Polygon poly;
if (r->shape ().polygon (poly) && db::inside_poly (poly.begin_edge (), stop_point) < 0) {
return 0;
}
}
db::NetTracer net_tracer;
@ -1261,6 +1295,7 @@ NetTracerDialog::release_mouse ()
m_mouse_state = 0;
view ()->message ();
ui ()->ungrab_mouse (this);
set_cursor (lay::Cursor::none);
}
void

View File

@ -60,6 +60,8 @@ public:
NetTracerDialog (lay::Dispatcher *root, lay::LayoutViewBase *view);
virtual ~NetTracerDialog ();
virtual void drag_cancel ();
virtual bool claims_message_bar () const;
virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual void menu_activated (const std::string &symbol);

View File

@ -138,6 +138,21 @@ class _PCellDeclarationHelperMixin:
self._finish()
return text
def cell_name(self, parameters):
"""
Reimplementation of PCellDeclaration.cell_name
This function delegates the implementation to self.cell_name_impl
after configuring the PCellDeclaration object.
"""
self._start()
self._param_values = parameters
try:
text = self.cell_name_impl()
finally:
self._finish()
return text
def get_parameters(self):
"""
Reimplementation of PCellDeclaration.get_parameters
@ -333,6 +348,12 @@ class _PCellDeclarationHelperMixin:
"""
return ""
def cell_name_impl(self):
"""
default implementation
"""
return self.name()
def coerce_parameters_impl(self):
"""
default implementation

BIN
testdata/bd/strmxor_au1d2.oas vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
testdata/gds/pcell_test20.gds vendored Normal file

Binary file not shown.

View File

@ -27,6 +27,10 @@ class BoxPCell(pya.PCellDeclaration):
# provide a descriptive text for the cell
return "Box(L=" + str(parameters[0]) + ",W=" + ('%.3f' % parameters[1]) + ",H=" + ('%.3f' % parameters[2]) + ")"
def cell_name(self, parameters):
# provide a descriptive text for the cell
return "Box_L" + str(parameters[0]).replace("/", "d") + "_W" + ('%.3f' % parameters[1]).replace(".", "p") + "_H" + ('%.3f' % parameters[2]).replace(".", "p")
def get_parameters(self):
# prepare a set of parameter declarations
@ -100,6 +104,10 @@ if "PCellDeclarationHelper" in pya.__dict__:
# provide a descriptive text for the cell
return "Box2(L=" + str(self.layer) + ",W=" + ('%.3f' % self.width) + ",H=" + ('%.3f' % self.height) + ")"
def cell_name_impl(self):
# provide a descriptive text for the cell
return "Box2_L" + str(self.layer).replace("/", "d") + "_W" + ('%.3f' % self.width).replace(".", "p") + "_H" + ('%.3f' % self.height).replace(".", "p")
def wants_lazy_evaluation(self):
return True
@ -260,6 +268,8 @@ class DBPCellTests(unittest.TestCase):
self.assertEqual(pcell_var.is_pcell_variant(), True)
self.assertEqual(pcell_var.display_title(), "PCellTestLib.Box(L=1/0,W=1.000,H=1.000)")
self.assertEqual(pcell_var.basic_name(), "Box")
self.assertEqual(pcell_var.qname(), "PCellTestLib.Box")
self.assertEqual(pcell_var.name, "Box_L1d0_W1p000_H1p000")
self.assertEqual(pcell_var.pcell_declaration().wants_lazy_evaluation(), False)
self.assertEqual(c1.is_pcell_variant(), False)
self.assertEqual(c1.is_pcell_variant(pcell_inst), True)
@ -394,6 +404,8 @@ class DBPCellTests(unittest.TestCase):
pcell_var = ly.cell(pcell_var_id)
pcell_inst = c1.insert(pya.CellInstArray(pcell_var_id, pya.Trans()))
self.assertEqual(pcell_var.basic_name(), "Box2")
self.assertEqual(pcell_var.name, "Box2_L1d0_W1p000_H1p000")
self.assertEqual(pcell_var.qname(), "PCellTestLib2.Box2")
self.assertEqual(pcell_var.pcell_parameters().__repr__(), "[<1/0>, 1.0, 1.0]")
self.assertEqual(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)")
self.assertEqual(nh(pcell_var.pcell_parameters_by_name()), "{'height': 1.0, 'layer': <1/0>, 'width': 1.0}")

View File

@ -39,6 +39,11 @@ class BoxPCell < RBA::PCellDeclaration
# provide a descriptive text for the cell
return "Box(L=#{parameters[0].to_s},W=#{'%.3f' % parameters[1].to_s},H=#{'%.3f' % parameters[2].to_s})"
end
def cell_name(parameters)
# provide a cell name for the PCell
return "Box_L#{parameters[0].to_s.gsub('/','d')}_W#{('%.3f' % parameters[1]).gsub('.','p')}_H#{('%.3f' % parameters[2]).gsub('.','p')}"
end
def get_parameters
@ -129,6 +134,11 @@ if RBA.constants.member?(:PCellDeclarationHelper)
return "Box2(L=" + layer.to_s + ",W=" + ('%.3f' % width) + ",H=" + ('%.3f' % height) + ")"
end
def cell_name_impl
# provide a cell name for the PCell
return "Box2_L" + layer.to_s.gsub('/', 'd') + "_W" + ('%.3f' % width).gsub('.', 'p') + "_H" + ('%.3f' % height).gsub('.', 'p')
end
def produce_impl
# fetch the parameters
@ -371,6 +381,9 @@ class DBPCell_TestClass < TestBase
param = [ RBA::LayerInfo::new(1, 0) ] # rest is filled with defaults
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl_id, param)
pcell_var = ly.cell(pcell_var_id)
assert_equal(pcell_var.name, "Box_L1d0_W1p000_H1p000")
assert_equal(pcell_var.qname, "PCellTestLib.Box")
assert_equal(pcell_var.basic_name, "Box")
pcell_inst = c1.insert(RBA::CellInstArray::new(pcell_var_id, RBA::Trans::new))
assert_equal(pcell_var.layout.inspect, ly.inspect)
assert_equal(pcell_var.library.inspect, lib.inspect)
@ -512,6 +525,8 @@ class DBPCell_TestClass < TestBase
pcell_var = ly.cell(pcell_var_id)
pcell_inst = c1.insert(RBA::CellInstArray::new(pcell_var_id, RBA::Trans::new))
assert_equal(pcell_var.basic_name, "Box2")
assert_equal(pcell_var.name, "Box2_L1d0_W1p000_H1p000")
assert_equal(pcell_var.qname, "PCellTestLib2.Box2")
assert_equal(pcell_var.pcell_parameters().inspect, "[<1/0>, 1.0, 1.0, 0]")
assert_equal(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)")
assert_equal(norm_hash(pcell_var.pcell_parameters_by_name()), "{\"height\"=>1.0, \"layer\"=><1/0>, \"secret\"=>0, \"width\"=>1.0}")
@ -908,6 +923,37 @@ class DBPCell_TestClass < TestBase
end
# convert to static cell
def test_13
if !RBA.constants.member?(:PCellDeclarationHelper)
return
end
# instantiate and register the library
tl = PCellTestLib2::new
ly = RBA::Layout::new(true)
ly.dbu = 0.01
ci1 = ly.add_cell("c1")
c1 = ly.cell(ci1)
lib = RBA::Library.library_by_name("PCellTestLib2")
assert_equal(lib != nil, true)
pcell_decl = lib.layout().pcell_declaration("Box2")
param = [ RBA::LayerInfo::new(1, 0) ] # rest is filled with defaults
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl.id(), param)
static_id = ly.convert_cell_to_static(pcell_var_id)
static = ly.cell(static_id)
assert_equal(static.basic_name, "Box2_L1d0_W1p000_H1p000")
assert_equal(static.name, "Box2_L1d0_W1p000_H1p000")
assert_equal(static.qname, "Box2_L1d0_W1p000_H1p000")
assert_equal(static.is_proxy?, false)
end
end
class DBPCellParameterStates_TestClass < TestBase