Merge branch 'master' into devel

This commit is contained in:
Matthias Koefferlein 2025-10-26 16:36:03 +01:00
commit 289facd5ee
52 changed files with 1178 additions and 258 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

@ -46,8 +46,8 @@ ViaType::init ()
// ---------------------------------------------------------------------------------------
std::vector<SelectedViaDefinition>
find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir)
static std::vector<SelectedViaDefinition>
find_via_definitions_impl (const std::string &technology, const db::LayerProperties &layer, int dir, bool all)
{
std::vector<SelectedViaDefinition> via_defs;
@ -65,7 +65,8 @@ find_via_definitions_for (const std::string &technology, const db::LayerProperti
auto via_types = pcell->via_types ();
for (auto vt = via_types.begin (); vt != via_types.end (); ++vt) {
if ((dir >= 0 && vt->bottom.log_equal (layer) && vt->bottom_wired) ||
if (all ||
(dir >= 0 && vt->bottom.log_equal (layer) && vt->bottom_wired) ||
(dir <= 0 && vt->top.log_equal (layer) && vt->top_wired)) {
via_defs.push_back (SelectedViaDefinition (lib, pc->second, *vt));
}
@ -78,4 +79,16 @@ find_via_definitions_for (const std::string &technology, const db::LayerProperti
return via_defs;
}
std::vector<SelectedViaDefinition>
get_via_definitions (const std::string &technology)
{
return find_via_definitions_impl (technology, db::LayerProperties (), 0, true);
}
std::vector<SelectedViaDefinition>
find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir)
{
return find_via_definitions_impl (technology, layer, dir, false);
}
}

View File

@ -210,6 +210,9 @@ struct SelectedViaDefinition
DB_PUBLIC std::vector<SelectedViaDefinition>
find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir);
DB_PUBLIC std::vector<SelectedViaDefinition>
get_via_definitions (const std::string &technology);
}
#endif

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

@ -29,8 +29,9 @@
#include "layEditorUtils.h"
#include "layObjectInstPath.h"
#include "layCellView.h"
#include "layLayoutViewBase.h"
#include "layMarker.h"
#include "layFinder.h"
#include "layLayerTreeModel.h"
#include "tlException.h"
#include "tlExceptions.h"
@ -725,6 +726,93 @@ AreaAndPerimeterDialog::exec_dialog (double area, double perimeter)
return exec () != 0;
}
// --------------------------------------------------------------------------------
// popup_tap_layer_menu implementation
lay::LayerPropertiesConstIterator
popup_tap_layer_menu (lay::LayoutViewBase *view, const std::set<db::LayerProperties, db::LPLogicalLessFunc> *filter, int cv_index)
{
QWidget *view_widget = lay::widget_from_view (view);
if (! view_widget) {
return lay::LayerPropertiesConstIterator ();
}
if (! view->canvas ()->mouse_in_window ()) {
return lay::LayerPropertiesConstIterator ();
}
lay::ShapeFinder finder (true, // point mode
false, // all hierarchy levels
db::ShapeIterator::flags_type (db::ShapeIterator::All - db::ShapeIterator::Texts), // do not consider texts - their bounding box may be too large
0, // no excludes
true // capture all shapes
);
// capture all objects in point mode (default: capture one only)
finder.set_catch_all (true);
// go through all visible layers of all cellviews
db::DPoint pt = view->canvas ()->mouse_position_um ();
finder.find (view, db::DBox (pt, pt));
std::set<std::pair<unsigned int, unsigned int> > layers_in_selection;
for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) {
if (cv_index < 0 || f->cv_index () == cv_index) {
const db::Layout &ly = view->cellview (f->cv_index ())->layout ();
// ignore guiding shapes and only provide layers from the filter
if (f->layer () != ly.guiding_shape_layer ()
&& (! filter || filter->find (ly.get_properties (f->layer ())) != filter->end ())) {
layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ()));
}
}
}
std::vector<lay::LayerPropertiesConstIterator> tapped_layers;
for (lay::LayerPropertiesConstIterator lp = view->begin_layers (view->current_layer_list ()); ! lp.at_end (); ++lp) {
const lay::LayerPropertiesNode *ln = lp.operator-> ();
if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) {
tapped_layers.push_back (lp);
}
}
if (tapped_layers.empty ()) {
return lay::LayerPropertiesConstIterator ();
} else if (tapped_layers.size () == 1) {
return tapped_layers.front ();
}
// List the layers under the cursor as pop up a menu
#if QT_VERSION >= 0x050000
double dpr = view_widget->devicePixelRatio ();
#else
double dpr = 1.0;
#endif
std::unique_ptr<QMenu> menu (new QMenu (view_widget));
menu->show ();
int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
db::DPoint mp_local = view->canvas ()->mouse_position ();
QPoint mp = view->canvas ()->widget ()->mapToGlobal (QPoint (mp_local.x (), mp_local.y ()));
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view, icon_size, icon_size, dpr, 0, true), tl::to_qstring ((*l)->display_string (view, true, true /*with source*/)));
a->setData (int (l - tapped_layers.begin ()));
}
QAction *action = menu->exec (mp);
if (action) {
int index = action->data ().toInt ();
return tapped_layers [index];
} else {
return lay::LayerPropertiesConstIterator ();
}
}
}
#endif

View File

@ -34,6 +34,9 @@
#include "dbLayout.h"
#include "dbPoint.h"
#include "dbLayerProperties.h"
#include "layLayoutView.h"
#include "ui_InstantiationForm.h"
#include "ui_ChangeLayerOptionsDialog.h"
@ -47,7 +50,6 @@
namespace lay
{
class LayoutViewBase;
class Marker;
class ObjectInstPath;
}
@ -222,6 +224,17 @@ public:
bool exec_dialog (double area, double perimeter);
};
/**
* @brief Obtains an layer iterator for one of the layers present under the mouse cursor
*
* This is not really a dialog, but a popup menu.
*
* @param view The LayoutView object
* @param filter An optional set of layers to show. Only layers from this set are shown.
* @return A layer iterator which is at_end if no specific layer was selected
*/
lay::LayerPropertiesConstIterator popup_tap_layer_menu (lay::LayoutViewBase *view, const std::set<db::LayerProperties, db::LPLogicalLessFunc> *filter = 0, int cv_index = -1);
} // namespace edt
#endif

View File

@ -47,7 +47,6 @@
#if defined(HAVE_QT)
# include "layDialogs.h"
# include "layLayerTreeModel.h"
# include "layCellSelectionForm.h"
# include "edtDialogs.h"
# include "edtEditorOptionsPages.h"
@ -2361,88 +2360,16 @@ void
MainService::cm_tap ()
{
#if ! defined(HAVE_QT)
tl_assert (false); // see TODO
#endif
#if defined(HAVE_QT)
QWidget *view_widget = lay::widget_from_view (view ());
if (! view_widget) {
return;
}
#endif
if (! view ()->canvas ()->mouse_in_window ()) {
return;
}
lay::ShapeFinder finder (true, // point mode
false, // all hierarchy levels
db::ShapeIterator::flags_type (db::ShapeIterator::All - db::ShapeIterator::Texts), // do not consider texts - their bounding box may be too large
0, // no excludes
true // capture all shapes
);
// capture all objects in point mode (default: capture one only)
finder.set_catch_all (true);
// go through all visible layers of all cellviews
db::DPoint pt = view ()->canvas ()->mouse_position_um ();
finder.find (view (), db::DBox (pt, pt));
std::set<std::pair<unsigned int, unsigned int> > layers_in_selection;
for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) {
// ignore guiding shapes
if (f->layer () != view ()->cellview (f->cv_index ())->layout ().guiding_shape_layer ()) {
layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ()));
}
}
std::vector<lay::LayerPropertiesConstIterator> tapped_layers;
for (lay::LayerPropertiesConstIterator lp = view ()->begin_layers (view ()->current_layer_list ()); ! lp.at_end (); ++lp) {
const lay::LayerPropertiesNode *ln = lp.operator-> ();
if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) {
tapped_layers.push_back (lp);
}
}
if (tapped_layers.empty ()) {
return;
}
// List the layers under the cursor as pop up a menu
#if defined(HAVE_QT)
// TODO: what to do here in Qt-less case? Store results in configuration so they can be retrieved externally?
#if QT_VERSION >= 0x050000
double dpr = view_widget->devicePixelRatio ();
tl_assert (false); // TODO
#else
double dpr = 1.0;
#endif
lay::LayerPropertiesConstIterator iter = edt::popup_tap_layer_menu (view ());
if (! iter.at_end ()) {
std::unique_ptr<QMenu> menu (new QMenu (view_widget));
menu->show ();
int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
db::DPoint mp_local = view ()->canvas ()->mouse_position ();
QPoint mp = view ()->canvas ()->widget ()->mapToGlobal (QPoint (mp_local.x (), mp_local.y ()));
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, dpr, 0, true), tl::to_qstring ((*l)->display_string (view (), true, true /*with source*/)));
a->setData (int (l - tapped_layers.begin ()));
}
QAction *action = menu->exec (mp);
if (action) {
int index = action->data ().toInt ();
lay::LayerPropertiesConstIterator iter = tapped_layers [index];
view ()->set_current_layer (iter);
edt::Service *es = dynamic_cast<edt::Service *> (view ()->canvas ()->active_service ());
if (es) {
db::DPoint pt = view ()->canvas ()->mouse_position_um ();
es->tap (pt);
}
@ -2486,9 +2413,11 @@ MainService::via_impl (int dir)
return;
}
edt::Service *es = dynamic_cast<edt::Service *> (view ()->canvas ()->active_service ());
if (es) {
es->via (dir);
// via generation is delegated to the path service always
edt::PathService *ps = view ()->get_plugin<edt::PathService> ();
if (ps) {
view ()->switch_mode (ps->plugin_declaration ()->id ());
ps->via (dir);
}
}

View File

@ -22,15 +22,12 @@
#include "edtPathService.h"
#include "edtDialogs.h"
#include "edtPropertiesPages.h"
#include "layLayoutViewBase.h"
#include "layFinder.h"
#if defined(HAVE_QT)
# include "edtPropertiesPages.h"
# include "layLayoutView.h"
#endif
namespace edt
{
@ -268,6 +265,35 @@ PathService::via (int dir)
#endif
}
db::LayerProperties
PathService::get_layer_for_via (unsigned int cv_index)
{
#if defined(HAVE_QT)
const lay::CellView &cv = view ()->cellview (cv_index);
if (! cv.is_valid ()) {
return db::LayerProperties ();
}
std::vector<db::SelectedViaDefinition> via_defs = db::get_via_definitions (cv->layout ().technology_name ());
std::set<db::LayerProperties, db::LPLogicalLessFunc> lp_filter;
for (auto vd = via_defs.begin (); vd != via_defs.end (); ++vd) {
lp_filter.insert (vd->via_type.bottom);
lp_filter.insert (vd->via_type.top);
}
lay::LayerPropertiesConstIterator iter;
iter = edt::popup_tap_layer_menu (view (), &lp_filter, cv_index);
if (iter.at_end ()) {
return db::LayerProperties ();
}
return cv->layout ().get_properties (iter->layer_index ());
#else
return db::LayerProperties ();
#endif
}
bool
PathService::get_via_for (const db::LayerProperties &lp, unsigned int cv_index, int dir, db::SelectedViaDefinition &via_def)
{
@ -364,19 +390,20 @@ PathService::via_initial (int dir)
return;
}
const lay::CellView &cv = view ()->cellview (r->cv_index ());
if (! cv.is_valid ()) {
// the first found provides the cellview index
int cv_index = r->cv_index ();
db::LayerProperties lp = get_layer_for_via (cv_index);
if (lp.is_null ()) {
return;
}
db::LayerProperties lp = cv->layout ().get_properties (r->layer ());
db::SelectedViaDefinition via_def;
if (! get_via_for (lp, r->cv_index (), dir, via_def)) {
if (! get_via_for (lp, cv_index, dir, via_def)) {
return;
}
set_layer (lp, r->cv_index ());
set_layer (lp, cv_index);
bool is_bottom = via_def.via_type.bottom.log_equal (lp);
db::LayerProperties lp_new = is_bottom ? via_def.via_type.top : via_def.via_type.bottom;

View File

@ -89,6 +89,7 @@ private:
void via_initial (int dir);
void via_editing (int dir);
bool get_via_for (const db::LayerProperties &lp, unsigned int cv_index, int dir, db::SelectedViaDefinition &via_def);
db::LayerProperties get_layer_for_via (unsigned int cv_index);
void push_segment (const db::Shape &shape, const db::Instance &instance, const db::ViaType &via_type, db::Manager::transaction_id_t transaction_id);
void pop_segment ();
};

View File

@ -26,17 +26,13 @@
#include "dbLibrary.h"
#include "edtPlugin.h"
#include "edtService.h"
#if defined(HAVE_QT)
# include "edtEditorOptionsPages.h"
# include "edtDialogs.h"
#endif
#include "edtEditorOptionsPages.h"
#include "edtDialogs.h"
#include "layFinder.h"
#include "layLayoutView.h"
#include "laySnap.h"
#include "layConverters.h"
#if defined(HAVE_QT)
# include "layEditorOptionsPages.h"
#endif
#include "layEditorOptionsPages.h"
#include "tlProgress.h"
#include "tlTimer.h"
@ -1159,6 +1155,8 @@ Service::transient_select (const db::DPoint &pos)
if (m_cell_inst_service) {
lay::InstFinder finder (true, view ()->is_editable () && m_top_level_sel, view ()->is_editable () /*full arrays in editable mode*/, true /*enclose instances*/, &m_previous_selection, true /*visible layers only*/);
finder.consider_ghost_cells (view ()->ghost_cells_visible ());
finder.consider_normal_cells (view ()->cell_box_visible ());
// go through all transform variants
std::set< std::pair<db::DCplxTrans, int> > variants = view ()->cv_transform_variants_with_empty ();
@ -1208,7 +1206,7 @@ Service::transient_select (const db::DPoint &pos)
// In viewer mode, individual instances of arrays can be selected. Since that is not supported by
// InstanceMarker, we just indicate the individual instance's bounding box.
lay::Marker *marker = new lay::Marker (view (), r->cv_index ());
db::box_convert<db::CellInst> bc (cv->layout ());
db::box_convert<db::CellInst, false> bc (cv->layout ());
marker->set (bc (r->back ().inst_ptr.cell_inst ().object ()), gt * r->back ().inst_ptr.cell_inst ().complex_trans (*r->back ().array_inst), tv);
marker->set_vertex_size (view ()->default_transient_marker_vertex_size ());
marker->set_line_width (view ()->default_transient_marker_line_width ());
@ -1329,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;
@ -1493,6 +1498,8 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode)
} else if (m_cell_inst_service) {
lay::InstFinder finder (box.is_point (), view ()->is_editable () && m_top_level_sel, view ()->is_editable () /*full arrays in editable mode*/, true /*enclose_inst*/, exclude, true /*only visible layers*/);
finder.consider_ghost_cells (view ()->ghost_cells_visible ());
finder.consider_normal_cells (view ()->cell_box_visible ());
// go through all cell views
std::set< std::pair<db::DCplxTrans, int> > variants = view ()->cv_transform_variants_with_empty ();
@ -1709,13 +1716,13 @@ Service::begin_edit (const db::DPoint &p)
}
void
Service::tap (const db::DPoint & /*initial*/)
Service::tap (const db::DPoint & /*pt*/)
{
// .. nothing here ..
}
void
Service::via (int)
Service::via (int /*dir*/)
{
// .. nothing here ..
}
@ -1806,7 +1813,7 @@ Service::do_selection_to_view ()
if (r->seq () > 0 && m_indicate_secondary_selection) {
marker->set_dither_pattern (3);
}
db::box_convert<db::CellInst> bc (cv->layout ());
db::box_convert<db::CellInst, false> bc (cv->layout ());
marker->set (bc (r->back ().inst_ptr.cell_inst ().object ()), gt * r->back ().inst_ptr.cell_inst ().complex_trans (*r->back ().array_inst), *tv_list);
m_markers.push_back (std::make_pair (r.operator-> (), marker));

View File

@ -22,16 +22,18 @@
#include "edtShapeService.h"
#include "edtMainService.h"
#include "edtPathService.h"
#include "edtPropertiesPages.h"
#include "layLayoutView.h"
#include "dbEdgeProcessor.h"
#include "dbPolygonTools.h"
#if defined(HAVE_QT)
# include "edtPropertiesPages.h"
# include "layTipDialog.h"
# include "layEditorOptionsPages.h"
# include "layTipDialog.h"
#endif
#include "layEditorOptionsPages.h"
namespace edt
{

View File

@ -30,6 +30,7 @@
#include "dbLibraryManager.h"
#include "dbLibrary.h"
#include "dbReader.h"
#include "dbCellMapping.h"
#include "tlLog.h"
#include "tlStream.h"
#include "tlFileUtils.h"
@ -51,6 +52,12 @@ public:
set_description (tl::filename (path));
}
void merge_with_other_layout (const std::string &path)
{
m_other_paths.push_back (path);
merge_impl (path);
}
virtual std::string reload ()
{
std::string name = tl::basename (m_path);
@ -70,11 +77,42 @@ public:
}
}
for (auto p = m_other_paths.begin (); p != m_other_paths.end (); ++p) {
merge_impl (*p);
}
return name;
}
private:
std::string m_path;
std::list<std::string> m_other_paths;
void merge_impl (const std::string &path)
{
db::Layout ly;
tl::InputStream stream (path);
db::Reader reader (stream);
reader.read (ly);
std::vector<db::cell_index_type> target_cells, source_cells;
// collect the cells to pull in (all top cells of the library layout)
// NOTE: cells are not overwritten - the first layout wins, in terms
// of cell names and also in terms of database unit.
for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) {
std::string cn = ly.cell_name (*c);
if (! layout ().has_cell (cn.c_str ())) {
source_cells.push_back (*c);
target_cells.push_back (layout ().add_cell (cn.c_str ()));
}
}
db::CellMapping cm;
cm.create_multi_mapping_full (layout (), target_cells, ly, source_cells);
layout ().copy_tree_shapes (ly, cm);
}
};
// -------------------------------------------------------------------------------------------
@ -201,60 +239,111 @@ LibraryController::sync_files ()
m_file_watcher->add_file (tl::to_string (lp.absolutePath ()));
}
tl::log << "Scanning library path '" << tl::to_string (lp.absolutePath ()) << "'";
QStringList name_filters;
name_filters << QString::fromUtf8 ("*");
// NOTE: this should return a list sorted by name
QStringList libs = lp.entryList (name_filters, QDir::Files);
for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) {
bool needs_load = false;
for (QStringList::const_iterator im = libs.begin (); im != libs.end () && ! needs_load; ++im) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im));
QFileInfo fi (tl::to_qstring (lib_path));
try {
auto ll = m_lib_files.find (lib_path);
if (ll == m_lib_files.end ()) {
needs_load = true;
} else if (fi.lastModified () > ll->second.time) {
needs_load = true;
}
QFileInfo fi (tl::to_qstring (lib_path));
}
bool needs_load = false;
std::map<std::string, LibInfo>::iterator ll = m_lib_files.find (lib_path);
if (ll == m_lib_files.end ()) {
needs_load = true;
} else {
if (fi.lastModified () > ll->second.time) {
needs_load = true;
} else {
new_lib_files.insert (*ll);
}
if (! needs_load) {
// If not reloading, register the existing files as known ones - this allows detecting if
// a file got removed.
for (QStringList::const_iterator im = libs.begin (); im != libs.end () && ! needs_load; ++im) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im));
auto ll = m_lib_files.find (lib_path);
if (ll != m_lib_files.end ()) {
new_lib_files.insert (*ll);
}
if (needs_load) {
}
std::unique_ptr<db::Library> lib (new FileBasedLibrary (lib_path));
} else {
std::map<std::string, FileBasedLibrary *> libs_by_name_here;
// Reload all files
for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im));
QFileInfo fi (tl::to_qstring (lib_path));
try {
std::unique_ptr<FileBasedLibrary> lib (new FileBasedLibrary (lib_path));
if (! p->second.empty ()) {
lib->set_technology (p->second);
}
tl::log << "Reading library '" << lib_path << "'";
lib->set_name (lib->reload ());
std::string libname = lib->reload ();
// merge with existing lib if there is already one in this folder with the right name
auto il = libs_by_name_here.find (libname);
if (il != libs_by_name_here.end ()) {
tl::log << "Merging with other library file with the same name: " << libname;
il->second->merge_with_other_layout (lib_path);
// now, we can forget the new library as it is included in the first one
if (! p->second.empty ()) {
tl::log << "Registering as '" << lib->get_name () << "' for tech '" << p->second << "'";
} else {
tl::log << "Registering as '" << lib->get_name () << "'";
// otherwise register the new library
if (! p->second.empty ()) {
tl::log << "Registering as '" << libname << "' for tech '" << p->second << "'";
} else {
tl::log << "Registering as '" << libname << "'";
}
LibInfo li;
li.name = libname;
li.time = fi.lastModified ();
if (! p->second.empty ()) {
li.tech.insert (p->second);
}
new_lib_files.insert (std::make_pair (lib_path, li));
lib->set_name (libname);
libs_by_name_here.insert (std::make_pair (libname, lib.release ()));
}
LibInfo li;
li.name = lib->get_name ();
li.time = fi.lastModified ();
if (! p->second.empty ()) {
li.tech.insert (p->second);
}
new_lib_files.insert (std::make_pair (lib_path, li));
db::LibraryManager::instance ().register_lib (lib.release ());
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
}
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
}
// Register the libs (NOTE: this needs to happen after the merge)
for (auto l = libs_by_name_here.begin (); l != libs_by_name_here.end (); ++l) {
try {
db::LibraryManager::instance ().register_lib (l->second);
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (...) {
}
}
}
@ -280,8 +369,15 @@ LibraryController::sync_files ()
try {
std::pair<bool, db::lib_id_type> li = db::LibraryManager::instance ().lib_by_name (lf->second.name, lf->second.tech);
if (li.first) {
if (! lf->second.tech.empty ()) {
tl::log << "Unregistering lib '" << lf->second.name << "' for technology '" << *lf->second.tech.begin () << "' as the file no longer exists: " << lf->first;
} else {
tl::log << "Unregistering lib '" << lf->second.name << "' as the file no longer exists: " << lf->first;
}
db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (li.second));
}
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (...) {
}
}

View File

@ -4507,6 +4507,7 @@ public:
menu_entries.push_back (lay::config_menu_item ("show_markers", at, tl::to_string (QObject::tr ("Show Markers")), cfg_markers_visible, "?"));
menu_entries.push_back (lay::config_menu_item ("show_texts", at, tl::to_string (QObject::tr ("Show Texts")), cfg_text_visible, "?"));
menu_entries.push_back (lay::config_menu_item ("show_cell_boxes", at, tl::to_string (QObject::tr ("Show Cell Frames")), cfg_cell_box_visible, "?"));
menu_entries.push_back (lay::config_menu_item ("show_ghost_cells", at, tl::to_string (QObject::tr ("Show Unresolved References")), cfg_ghost_cells_visible, "?"));
menu_entries.push_back (lay::config_menu_item ("no_stipples", at, tl::to_string (QObject::tr ("Show Layers Without Fill")), cfg_no_stipple, "?"));
menu_entries.push_back (lay::config_menu_item ("synchronized_views", at, tl::to_string (QObject::tr ("Synchronized Views")), cfg_synchronized_views, "?"));
menu_entries.push_back (lay::config_menu_item ("edit_top_level_selection:edit_mode", at, tl::to_string (QObject::tr ("Select Top Level Objects")), edt::cfg_edit_top_level_selection, "?"));

View File

@ -139,7 +139,7 @@ public:
}
db::DCplxTrans vp_trans = mp_view->viewport ().trans () * tv_trans;
db::DCplxTrans vp_trans = db::DCplxTrans (double (mp_view->canvas ()->oversampling ())) * mp_view->viewport ().trans () * tv_trans;
db::DBox box = m_textinfo.bbox (ctx_trans * dtext, vp_trans);
double b = m_border / vp_trans.mag ();

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);
}
@ -721,6 +724,8 @@ InstFinder::InstFinder (bool point_mode, bool top_level_sel, bool full_arrays, b
m_full_arrays (full_arrays),
m_enclose_insts (enclose_inst),
m_visible_layers (visible_layers),
m_consider_ghost_cells (true),
m_consider_normal_cells (true),
mp_view (0),
mp_progress (0)
{
@ -805,28 +810,40 @@ InstFinder::checkpoint ()
}
}
bool
InstFinder::consider_cell (const db::Cell &cell) const
{
return cell.is_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells;
}
void
InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::Box & /*scan_box*/, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level)
{
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 ()) {
++*mp_progress;
// look for instances to check here ..
db::Cell::touching_iterator inst = cell.begin_touching (search_box);
while (! inst.at_end ()) {
for (db::Cell::touching_iterator inst = cell.begin_touching (search_box); ! inst.at_end (); ++inst) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ());
++*mp_progress;
// just consider the instances exactly at the last level of
if (! consider_cell (inst_cell)) {
continue;
}
// just consider the instances exactly at the last level of
// hierarchy (this is where the boxes are drawn) or of cells that
// are hidden.
if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
@ -887,21 +904,22 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
}
++inst;
}
} else {
// look for instances to check here ..
db::Cell::touching_iterator inst = cell.begin_touching (search_box);
while (! inst.at_end ()) {
for (db::Cell::touching_iterator inst = cell.begin_touching (search_box); ! inst.at_end (); ++inst) {
checkpoint ();
const db::CellInstArray &cell_inst = inst->cell_inst ();
const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ());
if (! consider_cell (inst_cell)) {
continue;
}
// just consider the instances exactly at the last level of
// hierarchy (this is where the boxes are drawn) or if of cells that
// are hidden.
@ -909,7 +927,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
db::box_convert <db::CellInst, false> bc (layout ());
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
checkpoint ();
bool match = false;
@ -940,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);
}
@ -1000,8 +1018,6 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
}
++inst;
}
}

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;
@ -343,7 +363,17 @@ public:
bool find (LayoutViewBase *view, unsigned int cv_index, const db::DCplxTrans &trans, const db::DBox &region_mu);
bool find (LayoutViewBase *view, const db::DBox &region_mu);
void consider_ghost_cells (bool f)
{
m_consider_ghost_cells = f;
}
void consider_normal_cells (bool f)
{
m_consider_normal_cells = f;
}
iterator begin () const
{
return m_founds.begin ();
@ -360,6 +390,7 @@ private:
virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level);
bool find_internal (LayoutViewBase *view, unsigned int cv_index, const db::DCplxTrans &trans_mu, const db::DBox &region_mu);
bool consider_cell (const db::Cell &cell) const;
unsigned int m_cv_index;
db::cell_index_type m_topcell;
@ -369,6 +400,8 @@ private:
bool m_full_arrays;
bool m_enclose_insts;
bool m_visible_layers;
bool m_consider_ghost_cells;
bool m_consider_normal_cells;
std::vector<int> m_visible_layer_indexes;
LayoutViewBase *mp_view;
tl::AbsoluteProgress *mp_progress;

View File

@ -343,6 +343,7 @@ LayoutViewBase::init (db::Manager *mgr)
m_box_font = 0;
m_min_size_for_label = 16;
m_cell_box_visible = true;
m_ghost_cells_visible = true;
m_text_visible = true;
m_default_font_size = lay::FixedFont::default_font_size ();
m_text_lazy_rendering = true;
@ -964,6 +965,13 @@ LayoutViewBase::configure (const std::string &name, const std::string &value)
cell_box_visible (flag);
return true;
} else if (name == cfg_ghost_cells_visible) {
bool flag;
tl::from_string (value, flag);
ghost_cells_visible (flag);
return true;
} else if (name == cfg_cell_box_color) {
tl::Color color;
@ -4333,6 +4341,62 @@ LayoutViewBase::set_view_ops ()
}
}
// ghost cells
if (m_ghost_cells_visible) {
lay::ViewOp vop, vopv;
// context level
if (m_ctx_color.is_valid ()) {
vop = lay::ViewOp (m_ctx_color.rgb (), lay::ViewOp::Copy, 0, 0, 0);
} else {
vop = lay::ViewOp (lay::LayerProperties::brighter (box_color.rgb (), brightness_for_context), lay::ViewOp::Copy, 0, 0, 0);
}
vopv = vop;
vopv.shape (lay::ViewOp::Cross);
vopv.width (mark_size);
// fill, frame, text, vertex
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
view_ops.push_back (vop);
view_ops.push_back (vop);
view_ops.push_back (vopv);
// child level
if (m_child_ctx_color.is_valid ()) {
vop = lay::ViewOp (m_child_ctx_color.rgb (), lay::ViewOp::Copy, 0, 0, 0);
} else {
vop = lay::ViewOp (lay::LayerProperties::brighter (box_color.rgb (), brightness_for_context), lay::ViewOp::Copy, 0, 0, 0);
}
vopv = vop;
vopv.shape (lay::ViewOp::Cross);
vopv.width (mark_size);
// fill, frame, text, vertex
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
view_ops.push_back (vop);
view_ops.push_back (vop);
view_ops.push_back (vopv);
// current level
vop = lay::ViewOp (box_color.rgb (), lay::ViewOp::Copy, 0, 0, 0);
vopv = vop;
vopv.shape (lay::ViewOp::Cross);
vopv.width (mark_size);
// fill, frame, text, vertex
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
view_ops.push_back (vop);
view_ops.push_back (vop);
view_ops.push_back (vopv);
} else {
// invisible
for (unsigned int i = 0; i < (unsigned int) planes_per_layer; ++i) { // frame, fill, vertex, text
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
}
}
// sanity check: number of planes defined in layRedrawThreadWorker must match to view_ops layout
tl_assert (view_ops.size () == (size_t)cell_box_planes);
@ -5385,7 +5449,16 @@ LayoutViewBase::cell_box_visible (bool vis)
}
}
void
void
LayoutViewBase::ghost_cells_visible (bool vis)
{
if (m_ghost_cells_visible != vis) {
m_ghost_cells_visible = vis;
update_content ();
}
}
void
LayoutViewBase::text_font (unsigned int f)
{
if (m_text_font != f) {

View File

@ -1123,6 +1123,19 @@ public:
return m_cell_box_visible;
}
/**
* @brief Visibility of ghost cells
*/
void ghost_cells_visible (bool vis);
/**
* @brief Visibility of ghost cells
*/
bool ghost_cells_visible () const
{
return m_ghost_cells_visible;
}
/**
* @brief Min instance label size setter
*/
@ -2929,6 +2942,7 @@ private:
unsigned int m_box_font;
int m_min_size_for_label;
bool m_cell_box_visible;
bool m_ghost_cells_visible;
tl::Color m_marker_color;
int m_marker_line_width;

View File

@ -59,6 +59,7 @@ public:
options.push_back (std::pair<std::string, std::string> (cfg_cell_box_text_transform, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_cell_box_color, "auto"));
options.push_back (std::pair<std::string, std::string> (cfg_cell_box_visible, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_ghost_cells_visible, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_text_color, "auto"));
options.push_back (std::pair<std::string, std::string> (cfg_text_visible, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_text_lazy_rendering, "true"));

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

@ -316,6 +316,73 @@ RedrawThreadWorker::perform_task (tl::Task *task)
transfer ();
// Draw the ghost cells
// HINT: the order in which the planes are delivered (the index stored in the first member of the pair below)
// must correspond with the order by which the ViewOp's are created inside LayoutView::set_view_ops
m_buffers.clear ();
for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) {
// context level planes
unsigned int i1 = plain_cell_box_planes + i;
mp_canvas->initialize_plane (m_planes[i], i1);
m_buffers.push_back (std::make_pair (i1, m_planes [i]));
// child level planes (if used)
unsigned int i2 = plain_cell_box_planes + i + planes_per_layer / 3;
mp_canvas->initialize_plane (m_planes [i + planes_per_layer / 3], i2);
m_buffers.push_back (std::make_pair (i2, m_planes [i + planes_per_layer / 3]));
// current level planes
unsigned int i3 = plain_cell_box_planes + i + 2 * (planes_per_layer / 3);
mp_canvas->initialize_plane (m_planes [i + 2 * (planes_per_layer / 3)], i3);
m_buffers.push_back (std::make_pair (i3, m_planes [i + 2 * (planes_per_layer / 3)]));
}
// detect whether the text planes are empty. If not, the whole text plane must be redrawn to account for clipped texts
text_planes_empty = true;
for (unsigned int i = 0; i < (unsigned int) planes_per_layer && text_planes_empty; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text && ! text->empty ()) {
text_planes_empty = false;
}
}
text_redraw_regions = m_redraw_region;
if (! text_planes_empty) {
// if there are non-empty text planes, redraw the whole area for texts
text_redraw_regions.clear ();
text_redraw_regions.push_back(db::Box(0, 0, mp_canvas->canvas_width (), mp_canvas->canvas_height ()));
for (unsigned int i = 0; i < (unsigned int) planes_per_layer; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text) {
text->clear ();
}
}
}
for (std::set< std::pair<db::DCplxTrans, int> >::const_iterator b = m_box_variants.begin (); b != m_box_variants.end (); ++b) {
const lay::CellView &cv = m_cellviews [b->second];
if (cv.is_valid () && ! cv->layout ().under_construction () && ! (cv->layout ().manager () && cv->layout ().manager ()->transacting ())) {
mp_layout = &cv->layout ();
m_cv_index = b->second;
db::CplxTrans trans = m_vp_trans * b->first * db::CplxTrans (mp_layout->dbu ());
iterate_variants (m_redraw_region, cv.cell_index (), trans, &RedrawThreadWorker::draw_boxes_for_ghosts);
iterate_variants (text_redraw_regions, cv.cell_index (), trans, &RedrawThreadWorker::draw_box_properties_for_ghosts);
}
}
transfer ();
// draw guiding and error shapes
// HINT: the order in which the planes are delivered (the index stored in the first member of the pair below)
// must correspond with the order by which the ViewOp's are created inside LayoutView::set_view_ops
m_buffers.clear ();
@ -689,31 +756,51 @@ cells_in (const db::Layout *layout, const db::Cell &cell,
}
bool
RedrawThreadWorker::need_draw_box (const db::Layout *layout, const db::Cell &cell, int level)
RedrawThreadWorker::need_draw_box (const db::Layout *layout, const db::Cell &cell, int level, bool for_ghosts)
{
if (level > m_to_level) {
return false;
}
if (m_ghost_cells.size () > (size_t) m_cv_index && ! m_ghost_cells [m_cv_index].empty ()) {
std::set <std::pair <int, db::cell_index_type> > cache;
if (cells_in (layout, cell, m_ghost_cells [m_cv_index], m_to_level - level, cache)) {
return true;
} else if (for_ghosts) {
if (m_ghost_cells.size () > (size_t) m_cv_index && ! m_ghost_cells [m_cv_index].empty ()) {
std::set <std::pair <int, db::cell_index_type> > cache;
if (cells_in (layout, cell, m_ghost_cells [m_cv_index], m_to_level - level, cache)) {
return true;
}
}
}
if (m_hidden_cells.size () > (size_t) m_cv_index && ! m_hidden_cells [m_cv_index].empty ()) {
std::set <std::pair <int, db::cell_index_type> > cache;
if (cells_in (layout, cell, m_hidden_cells [m_cv_index], m_to_level - level, cache)) {
return true;
return false;
} else {
if (m_hidden_cells.size () > (size_t) m_cv_index && ! m_hidden_cells [m_cv_index].empty ()) {
std::set <std::pair <int, db::cell_index_type> > cache;
if (cells_in (layout, cell, m_hidden_cells [m_cv_index], m_to_level - level, cache)) {
return true;
}
}
}
return int (cell.hierarchy_levels ()) + level >= m_to_level;
return int (cell.hierarchy_levels ()) + level >= m_to_level;
}
}
void
RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &redraw_regions, int level)
{
draw_boxes_impl (drawing_context, ci, trans, redraw_regions, level, false);
}
void
RedrawThreadWorker::draw_boxes_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &redraw_regions, int level)
{
draw_boxes_impl (drawing_context, ci, trans, redraw_regions, level, true);
}
void
RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &redraw_regions, int level, bool for_ghosts)
{
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || redraw_regions.empty ()) {
@ -723,7 +810,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
const db::Cell &cell = mp_layout->cell (ci);
// we will never come to a valid level ..
if (! need_draw_box (mp_layout, cell, level)) {
if (! need_draw_box (mp_layout, cell, level, for_ghosts)) {
return;
}
if (cell_var_cached (ci, trans)) {
@ -731,12 +818,12 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
}
for (std::vector<db::Box>::const_iterator b = redraw_regions.begin (); b != redraw_regions.end (); ++b) {
draw_boxes (drawing_context, ci, trans, *b, level);
draw_boxes_impl (drawing_context, ci, trans, *b, level, for_ghosts);
}
}
void
RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level)
RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, bool for_ghosts)
{
lay::Renderer &r = *mp_renderer;
const db::Cell &cell = mp_layout->cell (ci);
@ -749,7 +836,12 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
// small cell dropped
} else if (level == m_to_level || cell.is_ghost_cell () || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) {
} else if (for_ghosts && cell.is_ghost_cell ()) {
// paint the box on this level
draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci));
} else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) {
// paint the box on this level
draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci));
@ -759,7 +851,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
db::DBox dbbox = trans * bbox;
if (!empty_cell && (dbbox.width () < 1.5 && dbbox.height () < 1.5)) {
if (need_draw_box (mp_layout, cell, level)) {
if (need_draw_box (mp_layout, cell, level, for_ghosts)) {
// the cell is a very small box and we know there must be
// some level at which to draw the boundary: just draw it
// here and stop looking further down ..
@ -836,7 +928,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
if (simplify) {
// The array can be simplified if there are levels below to draw
if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1)) {
if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1, for_ghosts)) {
unsigned int plane_group = 2;
if (drawing_context) {
@ -865,7 +957,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
test_snapshot (0);
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = safe_transformed_box (*v, t.inverted ());
draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1);
draw_boxes_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, for_ghosts);
if (p.quad_id () > 0 && p.quad_id () != qid) {
@ -902,18 +994,30 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
}
void
void
RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level)
{
draw_box_properties_impl (drawing_context, ci, trans, vp, level, false);
}
void
RedrawThreadWorker::draw_box_properties_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level)
{
draw_box_properties_impl (drawing_context, ci, trans, vp, level, true);
}
void
RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level, bool for_ghosts)
{
if (! m_text_visible) {
return;
}
draw_box_properties (drawing_context, ci, trans, vp, level, 0);
draw_box_properties_impl (drawing_context, ci, trans, vp, level, 0, for_ghosts);
}
void
RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level, db::properties_id_type prop_id)
RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level, db::properties_id_type prop_id, bool for_ghosts)
{
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || vp.empty ()) {
@ -923,7 +1027,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
const db::Cell &cell = mp_layout->cell (ci);
// we will never come to a valid level ..
if (! need_draw_box (mp_layout, cell, level)) {
if (! need_draw_box (mp_layout, cell, level, for_ghosts)) {
return;
}
if (cell_var_cached (ci, trans)) {
@ -931,12 +1035,12 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
}
for (std::vector<db::Box>::const_iterator b = vp.begin (); b != vp.end (); ++b) {
draw_box_properties (drawing_context, ci, trans, *b, level, prop_id);
draw_box_properties_impl (drawing_context, ci, trans, *b, level, prop_id, for_ghosts);
}
}
void
RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, db::properties_id_type prop_id)
RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, db::properties_id_type prop_id, bool for_ghosts)
{
const db::Cell &cell = mp_layout->cell (ci);
@ -948,7 +1052,12 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
// small cell dropped
} else if (level == m_to_level || cell.is_ghost_cell () || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) {
} else if (for_ghosts && cell.is_ghost_cell ()) {
// paint the box on this level
draw_cell_properties (drawing_context, level, trans, bbox, prop_id);
} else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) {
// paint the box on this level
draw_cell_properties (drawing_context, level, trans, bbox, prop_id);
@ -1036,7 +1145,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
test_snapshot (0);
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = safe_transformed_box (*v, t.inverted ());
draw_box_properties (drawing_context, new_ci, trans * t, new_vp, level + 1, cell_inst_prop);
draw_box_properties_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, cell_inst_prop, for_ghosts);
}

View File

@ -44,7 +44,8 @@ class CanvasPlane;
// some helpful constants
const int planes_per_layer = 12;
const int cell_box_planes = planes_per_layer; // for cell boxes
const int plain_cell_box_planes = planes_per_layer; // for cell boxes, not including ghost_cells
const int cell_box_planes = planes_per_layer * 2; // for cell boxes, including ghost cells
const int guiding_shape_planes = planes_per_layer; // for guiding shapes
const int special_planes_before = cell_box_planes + guiding_shape_planes;
const int special_planes_after = 1;
@ -181,11 +182,15 @@ private:
void draw_layer_wo_cache (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vv, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot);
void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, Bitmap *opt_bitmap);
void draw_boxes_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level);
void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level, bool for_ghosts);
void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, bool for_ghosts);
void draw_box_properties_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level, db::properties_id_type prop_id);
void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, db::properties_id_type prop_id);
void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level, bool for_ghosts);
void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level, db::properties_id_type prop_id, bool for_ghosts);
void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, db::properties_id_type prop_id, bool for_ghosts);
void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, bool empty_cell, const std::string &txt);
void draw_cell_properties (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, db::properties_id_type prop_id);
void draw_cell_shapes (const db::CplxTrans &trans, const db::Cell &cell, const db::Box &vp, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text);
@ -199,7 +204,7 @@ private:
bool any_shapes (db::cell_index_type cell_index, unsigned int levels);
bool any_text_shapes (db::cell_index_type cell_index, unsigned int levels);
bool any_cell_box (db::cell_index_type cell_index, unsigned int levels);
bool need_draw_box (const db::Layout *layout, const db::Cell &cell, int level);
bool need_draw_box (const db::Layout *layout, const db::Cell &cell, int level, bool for_ghosts);
RedrawThread *mp_redraw_thread;
std::vector <db::Box> m_redraw_region;

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

@ -95,6 +95,7 @@ static const std::string cfg_cell_box_text_font ("inst-label-font");
static const std::string cfg_cell_box_text_transform ("inst-label-transform");
static const std::string cfg_cell_box_color ("inst-color");
static const std::string cfg_cell_box_visible ("inst-visible");
static const std::string cfg_ghost_cells_visible ("ghost-cells-visible");
static const std::string cfg_text_color ("text-color");
static const std::string cfg_text_visible ("text-visible");
static const std::string cfg_text_lazy_rendering ("text-lazy-rendering");

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>631</width>
<height>320</height>
<width>656</width>
<height>397</height>
</rect>
</property>
<property name="windowTitle">
@ -21,13 +21,33 @@
<number>9</number>
</property>
<item>
<widget class="QGroupBox" name="cell_group">
<property name="title">
<widget class="QCheckBox" name="cell_boxes_visible">
<property name="text">
<string>Show cell boxes</string>
</property>
<property name="checkable">
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ghost_cells_visible">
<property name="text">
<string>Show unresolved references (ghost cells) </string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="cell_group">
<property name="title">
<string>Cell box appearance</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>

View File

@ -207,7 +207,10 @@ LayoutViewConfigPage2a::setup (lay::Dispatcher *root)
mp_ui->cell_xform_text_cbx->setChecked (flag);
root->config_get (cfg_cell_box_visible, flag);
mp_ui->cell_group->setChecked (flag);
mp_ui->cell_boxes_visible->setChecked (flag);
root->config_get (cfg_ghost_cells_visible, flag);
mp_ui->ghost_cells_visible->setChecked (flag);
int font = 0;
root->config_get (cfg_cell_box_text_font, font);
@ -247,7 +250,8 @@ LayoutViewConfigPage2a::commit (lay::Dispatcher *root)
root->config_set (cfg_cell_box_text_transform, mp_ui->cell_xform_text_cbx->isChecked ());
root->config_set (cfg_cell_box_text_font, mp_ui->cell_font_cb->currentIndex ());
root->config_set (cfg_cell_box_color, mp_ui->cell_box_color_pb->get_color (), ColorConverter ());
root->config_set (cfg_cell_box_visible, mp_ui->cell_group->isChecked ());
root->config_set (cfg_cell_box_visible, mp_ui->cell_boxes_visible->isChecked ());
root->config_set (cfg_ghost_cells_visible, mp_ui->ghost_cells_visible->isChecked ());
root->config_set (cfg_guiding_shape_visible, mp_ui->pcell_gs_group->isChecked ());
root->config_set (cfg_guiding_shape_line_width, mp_ui->pcell_gs_lw->value ());

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