mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' into devel
This commit is contained in:
commit
289facd5ee
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ()) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 ()) {
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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".
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ¶meters) const
|
||||
{
|
||||
return db::PCellDeclaration::get_cell_name (parameters);
|
||||
}
|
||||
|
||||
virtual std::string get_cell_name (const db::pcell_parameters_type ¶meters) 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"
|
||||
|
|
|
|||
|
|
@ -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 ¶meters) 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 ¶meters) 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (...) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, "?"));
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -621,6 +621,11 @@ struct LAYBASIC_PUBLIC AbstractMenuItem
|
|||
return m_primary;
|
||||
}
|
||||
|
||||
void set_primary (bool p)
|
||||
{
|
||||
m_primary = p;
|
||||
}
|
||||
|
||||
std::list <AbstractMenuItem> children;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ®ion_mu);
|
||||
bool find (LayoutViewBase *view, const db::DBox ®ion_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 ®ion_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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue