Merge branch 'master' into feature/maly

This commit is contained in:
Matthias Koefferlein 2025-04-27 20:57:09 +02:00
commit 282f6e9d23
53 changed files with 1340 additions and 552 deletions

View File

@ -1,3 +1,20 @@
0.30.1 (2025-04-27):
* Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed
* Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader
* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties
* Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9
* Bugfix: %GITHUB%/issues/2020 String weak ordering issue fixed in edge processor
* Enhancement: %GITHUB%/issues/2024 Option to configure grid density
* Bugfix: %GITHUB%/issues/2025 Brackets get added in List type PCell parameter edit field
* Bugfix: %GITHUB%/issues/2026 Display is dead after opening 2.5d view
* Bugfix/Enhancement: some updates of "strmxor" tool
- strmxor was giving wrong results if cell variants are
present where one variant is covered entirely by a large shape
- parallelization now happens on a per-layer basis (same as for
XOR tool in KLayout)
- Shape count was not consistent in deep mode
- All buddy tools print total runtime with -d11
0.30.0 (2025-03-25): 0.30.0 (2025-03-25):
* Bug: %GITHUB%/issues/1996 More robust triangulation * Bug: %GITHUB%/issues/1996 More robust triangulation
* Bug: %GITHUB%/issues/2002 Path to polygon conversion issue * Bug: %GITHUB%/issues/2002 Path to polygon conversion issue

View File

@ -1,3 +1,10 @@
klayout (0.30.1-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Sun, 27 Apr 2025 14:26:50 +0200
klayout (0.30.0-1) unstable; urgency=low klayout (0.30.0-1) unstable; urgency=low
* New features and bugfixes * New features and bugfixes

View File

@ -547,7 +547,7 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m
drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?) drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?)
drop_method "QNoDebug", /QNoDebug::operator<</ # nothing usable (TODO: how to map?) drop_method "QNoDebug", /QNoDebug::operator<</ # nothing usable (TODO: how to map?)
# No longer supported operator== and operator!= in Qt 6.7/6.8 # No longer supported operator== and operator!= in Qt 6.7/6.8/6.9
add_native_operator_neq(self, "QEasingCurve") add_native_operator_neq(self, "QEasingCurve")
add_native_operator_neq(self, "QTimeZone") add_native_operator_neq(self, "QTimeZone")
add_native_operator_neq(self, "QDir") add_native_operator_neq(self, "QDir")
@ -569,6 +569,7 @@ add_native_operator_neq(self, "QProcessEnvironment")
add_native_operator_neq(self, "QRegularExpression") add_native_operator_neq(self, "QRegularExpression")
add_native_operator_neqlt(self, "QUrl") add_native_operator_neqlt(self, "QUrl")
add_native_operator_neq(self, "QUrlQuery") add_native_operator_neq(self, "QUrlQuery")
add_native_operator_neq(self, "QDomNodeList")
add_native_operator_neq(self, "QXmlStreamAttribute") add_native_operator_neq(self, "QXmlStreamAttribute")
add_native_operator_neq(self, "QXmlStreamEntityDeclaration") add_native_operator_neq(self, "QXmlStreamEntityDeclaration")
add_native_operator_neq(self, "QXmlStreamNamespaceDeclaration") add_native_operator_neq(self, "QXmlStreamNamespaceDeclaration")

View File

@ -27,6 +27,7 @@
#include "dbReader.h" #include "dbReader.h"
#include "dbWriter.h" #include "dbWriter.h"
#include "tlCommandLineParser.h" #include "tlCommandLineParser.h"
#include "tlTimer.h"
namespace bd namespace bd
{ {
@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format)
db::Layout layout; db::Layout layout;
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
{ {
db::LoadLayoutOptions load_options; db::LoadLayoutOptions load_options;
generic_reader_options.configure (load_options); generic_reader_options.configure (load_options);

View File

@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions ()
m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool (); m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool ();
m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool (); m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool ();
m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string (); m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string ();
m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); // Don't take the default, as in practice, it's more common to substitute LEF macros by layouts
// m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int ();
m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always"
} }
void void

View File

@ -29,6 +29,7 @@
#include "dbSaveLayoutOptions.h" #include "dbSaveLayoutOptions.h"
#include "tlLog.h" #include "tlLog.h"
#include "tlCommandLineParser.h" #include "tlCommandLineParser.h"
#include "tlTimer.h"
struct ClipData struct ClipData
@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[])
cmd.parse (argc, argv); cmd.parse (argc, argv);
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
clip (data); clip (data);
return 0; return 0;

View File

@ -25,6 +25,7 @@
#include "dbLayoutDiff.h" #include "dbLayoutDiff.h"
#include "dbReader.h" #include "dbReader.h"
#include "tlCommandLineParser.h" #include "tlCommandLineParser.h"
#include "tlTimer.h"
BD_PUBLIC int strmcmp (int argc, char *argv[]) BD_PUBLIC int strmcmp (int argc, char *argv[])
{ {
@ -141,6 +142,8 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
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");
} }
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
db::Layout layout_a; db::Layout layout_a;
db::Layout layout_b; db::Layout layout_b;

View File

@ -28,6 +28,7 @@
#include "tlLog.h" #include "tlLog.h"
#include "tlCommandLineParser.h" #include "tlCommandLineParser.h"
#include "tlFileUtils.h" #include "tlFileUtils.h"
#include "tlTimer.h"
#include "rba.h" #include "rba.h"
#include "pya.h" #include "pya.h"
#include "gsi.h" #include "gsi.h"
@ -97,5 +98,8 @@ BD_PUBLIC int strmrun (int argc, char *argv[])
lym::Macro macro; lym::Macro macro;
macro.load_from (script); macro.load_from (script);
macro.set_file_path (script); macro.set_file_path (script);
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
return macro.run (); return macro.run ();
} }

View File

@ -32,6 +32,8 @@
#include "gsiExpression.h" #include "gsiExpression.h"
#include "tlCommandLineParser.h" #include "tlCommandLineParser.h"
#include "tlThreads.h" #include "tlThreads.h"
#include "tlThreadedWorkers.h"
#include "tlTimer.h"
namespace { namespace {
@ -319,7 +321,8 @@ struct XORData
dont_summarize_missing_layers (false), silent (false), no_summary (false), dont_summarize_missing_layers (false), silent (false), no_summary (false),
threads (0), threads (0),
tile_size (0.0), heal_results (false), tile_size (0.0), heal_results (false),
output_layout (0), output_cell (0) output_layout (0), output_cell (0),
layers_missing (0)
{ } { }
db::Layout *layout_a, *layout_b; db::Layout *layout_a, *layout_b;
@ -336,6 +339,8 @@ struct XORData
db::cell_index_type output_cell; db::cell_index_type output_cell;
std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc> l2l_map; std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc> l2l_map;
std::map<std::pair<int, db::LayerProperties>, ResultDescriptor> *results; std::map<std::pair<int, db::LayerProperties>, ResultDescriptor> *results;
mutable int layers_missing;
mutable tl::Mutex lock;
}; };
} }
@ -455,6 +460,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
} }
} }
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
db::Layout layout_a; db::Layout layout_a;
db::Layout layout_b; db::Layout layout_b;
@ -572,14 +579,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
if (! silent && ! no_summary) { if (! silent && ! no_summary) {
if (result) { if (result) {
tl::info << "No differences found"; tl::info << tl::to_string (tr ("No differences found"));
} else { } else {
const char *line_format = " %-10s %-12s %s"; const char *line_format = " %-10s %-12s %s";
const char *sep = " -------------------------------------------------------";
tl::info << "Result summary (layers without differences are not shown):" << tl::endl; std::string headline;
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; if (deep) {
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)")));
} else {
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)")));
}
const char *sep = " ----------------------------------------------------------------";
tl::info << tl::to_string (tr ("Result summary (layers without differences are not shown):")) << tl::endl;
tl::info << headline << tl::endl << sep;
int ti = -1; int ti = -1;
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) { for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) {
@ -587,17 +602,17 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
if (r->first.first != ti) { if (r->first.first != ti) {
ti = r->first.first; ti = r->first.first;
if (tolerances[ti] > db::epsilon) { if (tolerances[ti] > db::epsilon) {
tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; tl::info << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; tl::info << headline << tl::endl << sep;
} }
} }
std::string out ("-"); std::string out ("-");
std::string value; std::string value;
if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) { if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) {
value = "(no such layer in first layout)"; value = tl::to_string (tr ("(no such layer in first layout)"));
} else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) { } else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) {
value = "(no such layer in second layout)"; value = tl::to_string (tr ("(no such layer in second layout)"));
} else if (! r->second.is_empty ()) { } else if (! r->second.is_empty ()) {
if (r->second.layer_output >= 0 && r->second.layout) { if (r->second.layer_output >= 0 && r->second.layout) {
out = r->second.layout->get_properties (r->second.layer_output).to_string (); out = r->second.layout->get_properties (r->second.layer_output).to_string ();
@ -758,15 +773,174 @@ bool run_tiled_xor (const XORData &xor_data)
return result; return result;
} }
bool run_deep_xor (const XORData &xor_data)
{
db::DeepShapeStore dss;
dss.set_threads (xor_data.threads);
class XORJob
: public tl::JobBase
{
public:
XORJob (int nworkers)
: tl::JobBase (nworkers)
{
}
virtual tl::Worker *create_worker ();
};
class XORWorker
: public tl::Worker
{
public:
XORWorker (XORJob *job);
void perform_task (tl::Task *task);
db::DeepShapeStore &dss ()
{
return m_dss;
}
private:
XORJob *mp_job;
db::DeepShapeStore m_dss;
};
class XORTask
: public tl::Task
{
public:
XORTask (const XORData *xor_data, const db::LayerProperties &layer_props, int la, int lb, double dbu)
: mp_xor_data (xor_data), m_layer_props (layer_props), m_la (la), m_lb (lb), m_dbu (dbu)
{
// .. nothing yet ..
}
void run (XORWorker *worker) const
{
if ((m_la < 0 || m_lb < 0) && ! mp_xor_data->dont_summarize_missing_layers) {
if (m_la < 0) {
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in first layout, but in second";
} else {
(mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in second layout, but in first";
}
tl::MutexLocker locker (&mp_xor_data->lock);
mp_xor_data->layers_missing += 1;
int tol_index = 0;
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
result.layer_a = m_la;
result.layer_b = m_lb;
result.layout = mp_xor_data->output_layout;
result.top_cell = mp_xor_data->output_cell;
++tol_index;
}
} else {
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ());
db::RecursiveShapeIterator ri_a, ri_b;
if (m_la >= 0) {
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la);
} else {
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector<unsigned int> ());
}
ri_a.set_for_merged_input (true);
if (m_lb >= 0) {
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb);
} else {
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector<unsigned int> ());
}
ri_b.set_for_merged_input (true);
db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu));
db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu));
db::Region xor_res;
{
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ());
xor_res = in_a ^ in_b;
}
int tol_index = 0;
for (std::vector<double>::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) {
db::LayerProperties lp = m_layer_props;
if (lp.layer >= 0) {
lp.layer += tol_index * mp_xor_data->tolerance_bump;
}
if (*t > db::epsilon) {
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + m_layer_props.to_string ());
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / m_dbu));
}
{
tl::MutexLocker locker (&mp_xor_data->lock);
ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second;
result.layer_a = m_la;
result.layer_b = m_lb;
result.layout = mp_xor_data->output_layout;
result.top_cell = mp_xor_data->output_cell;
if (mp_xor_data->output_layout) {
result.layer_output = result.layout->insert_layer (lp);
xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output);
} else {
result.shape_count = xor_res.hier_count ();
}
}
++tol_index;
}
}
}
private:
const XORData *mp_xor_data;
const db::LayerProperties &m_layer_props;
int m_la;
int m_lb;
double m_dbu;
};
XORWorker::XORWorker (XORJob *job)
: tl::Worker (), mp_job (job)
{
// TODO: this conflicts with the "set_for_merged_input" optimization below. // TODO: this conflicts with the "set_for_merged_input" optimization below.
// It seems not to be very effective then. Why? // It seems not to be very effective then. Why?
dss.set_wants_all_cells (true); // saves time for less cell mapping operations m_dss.set_wants_all_cells (true); // saves time for less cell mapping operations
}
void
XORWorker::perform_task (tl::Task *task)
{
XORTask *xor_task = dynamic_cast <XORTask *> (task);
if (xor_task) {
xor_task->run (this);
}
}
tl::Worker *
XORJob::create_worker ()
{
return new XORWorker (this);
}
bool run_deep_xor (const XORData &xor_data)
{
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ()); double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
if (tl::verbosity () >= 20) { if (tl::verbosity () >= 20) {
@ -779,98 +953,18 @@ bool run_deep_xor (const XORData &xor_data)
xor_data.output_layout->dbu (dbu); xor_data.output_layout->dbu (dbu);
} }
bool result = true; XORJob job (xor_data.threads);
int index = 1;
for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) { for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) {
job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu));
if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) {
if (ll->second.first < 0) {
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second";
} else {
(xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first";
}
result = false;
int tol_index = 0;
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
result.layer_a = ll->second.first;
result.layer_b = ll->second.second;
result.layout = xor_data.output_layout;
result.top_cell = xor_data.output_cell;
++tol_index;
}
} else {
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ());
db::RecursiveShapeIterator ri_a, ri_b;
if (ll->second.first >= 0) {
ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first);
ri_a.set_for_merged_input (true);
}
if (ll->second.second >= 0) {
ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second);
ri_b.set_for_merged_input (true);
}
db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu));
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
db::Region xor_res;
{
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ());
xor_res = in_a ^ in_b;
}
int tol_index = 0;
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
db::LayerProperties lp = ll->first;
if (lp.layer >= 0) {
lp.layer += tol_index * xor_data.tolerance_bump;
}
ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second;
result.layer_a = ll->second.first;
result.layer_b = ll->second.second;
result.layout = xor_data.output_layout;
result.top_cell = xor_data.output_cell;
if (*t > db::epsilon) {
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ());
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
}
if (xor_data.output_layout) {
result.layer_output = result.layout->insert_layer (lp);
xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output);
} else {
result.shape_count = xor_res.count ();
}
++tol_index;
}
}
++index;
} }
// Determines the output status job.start ();
job.wait ();
// Determine the output status
bool result = (xor_data.layers_missing == 0);
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) { for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) {
result = r->second.is_empty (); result = r->second.is_empty ();
} }

View File

@ -105,7 +105,7 @@ TEST(1A_Flat)
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
"\n" "\n"
" Layer Output Differences (shape count)\n" " Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n" " ----------------------------------------------------------------\n"
" 3/0 3/0 30\n" " 3/0 3/0 30\n"
" 6/0 6/0 41\n" " 6/0 6/0 41\n"
" 8/1 8/1 1\n" " 8/1 8/1 1\n"
@ -146,8 +146,8 @@ TEST(1A_Deep)
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
"\n" "\n"
" Layer Output Differences (shape count)\n" " Layer Output Differences (hierarchical shape count)\n"
" -------------------------------------------------------\n" " ----------------------------------------------------------------\n"
" 3/0 3/0 3\n" " 3/0 3/0 3\n"
" 6/0 6/0 314\n" " 6/0 6/0 314\n"
" 8/1 8/1 1\n" " 8/1 8/1 1\n"
@ -177,7 +177,7 @@ TEST(1B_Flat)
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
"\n" "\n"
" Layer Output Differences (shape count)\n" " Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n" " ----------------------------------------------------------------\n"
" 3/0 - 30\n" " 3/0 - 30\n"
" 6/0 - 41\n" " 6/0 - 41\n"
" 8/1 - 1\n" " 8/1 - 1\n"
@ -206,9 +206,9 @@ TEST(1B_Deep)
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
"\n" "\n"
" Layer Output Differences (shape count)\n" " Layer Output Differences (hierarchical shape count)\n"
" -------------------------------------------------------\n" " ----------------------------------------------------------------\n"
" 3/0 - 30\n" " 3/0 - 3\n"
" 6/0 - 314\n" " 6/0 - 314\n"
" 8/1 - 1\n" " 8/1 - 1\n"
" 10/0 - (no such layer in first layout)\n" " 10/0 - (no such layer in first layout)\n"
@ -417,7 +417,7 @@ TEST(3_FlatCount)
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
"\n" "\n"
" Layer Output Differences (shape count)\n" " Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n" " ----------------------------------------------------------------\n"
" 3/0 - 31\n" " 3/0 - 31\n"
" 6/0 - 217\n" " 6/0 - 217\n"
" 8/1 - 168\n" " 8/1 - 168\n"
@ -483,7 +483,7 @@ TEST(3_FlatCountHeal)
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
"\n" "\n"
" Layer Output Differences (shape count)\n" " Layer Output Differences (shape count)\n"
" -------------------------------------------------------\n" " ----------------------------------------------------------------\n"
" 3/0 - 30\n" " 3/0 - 30\n"
" 6/0 - 41\n" " 6/0 - 41\n"
" 8/1 - 1\n" " 8/1 - 1\n"
@ -756,3 +756,42 @@ TEST(6_Deep)
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
} }
TEST(7_OptimizeDeep)
{
tl::CaptureChannel cap;
std::string input_a = tl::testdata ();
input_a += "/bd/strmxor_covered1.gds";
std::string input_b = tl::testdata ();
input_b += "/bd/strmxor_covered2.gds";
std::string au = tl::testdata ();
au += "/bd/strmxor_au7d.oas";
std::string output = this->tmp_file ("tmp.oas");
const char *argv[] = { "x", "-u", 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 (),
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (hierarchical shape count)\n"
" ----------------------------------------------------------------\n"
" 2/0 2/0 1\n"
" 3/0 3/0 8\n"
"\n"
);
}

View File

@ -1571,6 +1571,17 @@ struct array_iterator
} }
} }
/**
* @brief Gets a value indicating whether the iterator is a synthetic one
*
* "is_singular" is true, if the iterator was default-created or with a single
* transformation.
*/
bool is_singular () const
{
return mp_base == 0;
}
private: private:
trans_type m_trans; trans_type m_trans;
basic_array_iterator <Coord> *mp_base; basic_array_iterator <Coord> *mp_base;

View File

@ -354,6 +354,13 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
std::vector<db::cell_index_type> &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int); std::vector<db::cell_index_type> &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int);
std::vector<db::cell_index_type> new_cells_b; std::vector<db::cell_index_type> new_cells_b;
std::vector<std::pair<db::cell_index_type, db::cell_index_type> > all_a2b;
for (std::vector<db::cell_index_type>::const_iterator b = cell_index_b.begin (); b != cell_index_b.end (); ++b) {
auto m = m_b2a_mapping.find (*b);
tl_assert (m != m_b2a_mapping.end ());
all_a2b.push_back (std::make_pair (m->second, *b));
}
std::set<db::cell_index_type> called_b; std::set<db::cell_index_type> called_b;
for (std::vector<db::cell_index_type>::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) { for (std::vector<db::cell_index_type>::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) {
layout_b.cell (*i).collect_called_cells (called_b); layout_b.cell (*i).collect_called_cells (called_b);
@ -368,6 +375,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b); db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b);
new_cells.push_back (new_cell); new_cells.push_back (new_cell);
new_cells_b.push_back (*b); new_cells_b.push_back (*b);
all_a2b.push_back (std::make_pair (new_cell, *b));
if (mapped_pairs) { if (mapped_pairs) {
mapped_pairs->push_back (std::make_pair (*b, new_cell)); mapped_pairs->push_back (std::make_pair (*b, new_cell));
@ -378,34 +386,34 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
} }
} }
if (! new_cells.empty ()) { if (all_a2b.empty ()) {
return;
}
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical // Note: this avoids frequent cell index table rebuilds if source and target layout are identical
db::LayoutLocker locker (&layout_a); db::LayoutLocker locker (&layout_a);
// Create instances for the new cells in layout A according to their instantiation in layout B // Create instances for the new cells in layout A according to their instantiation in layout B
double mag = layout_b.dbu () / layout_a.dbu (); double mag = layout_b.dbu () / layout_a.dbu ();
for (size_t i = 0; i < new_cells.size (); ++i) { for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) {
const db::Cell &b = layout_b.cell (new_cells_b [i]); const db::Cell &b = layout_b.cell (i->second);
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) { for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) { if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]); db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
db::Instance bi = pb->child_inst (); db::Instance bi = pb->child_inst ();
db::CellInstArray bci = bi.cell_inst (); db::CellInstArray bci = bi.cell_inst ();
bci.object ().cell_index (new_cells [i]); bci.object ().cell_index (i->first);
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
if (bi.has_prop_id ()) {
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
} else {
pa.insert (bci);
}
if (bi.has_prop_id ()) {
pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ()));
} else {
pa.insert (bci);
} }
} }

View File

@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
{ {
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ()); const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) { if (other.empty ()) {
// Nothing to do
return other.delegate ()->clone ();
} else if (other.empty ()) {
// Nothing to do // Nothing to do
return clone (); return clone ();
@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
return AsIfFlatRegion::xor_with (other, property_constraint); return AsIfFlatRegion::xor_with (other, property_constraint);
} else if (empty ()) {
// Nothing to do, but to maintain the normal behavior, we have to map the other
// input to our layout if neccessary
if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) {
return other.delegate ()->clone ();
} else {
std::unique_ptr<DeepRegion> other_deep_mapped (dynamic_cast<DeepRegion *> (clone ()));
other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ());
return other_deep_mapped.release ();
}
} else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) {
return new DeepRegion (deep_layer ().derived ()); return new DeepRegion (deep_layer ().derived ());

View File

@ -1321,7 +1321,7 @@ struct edge_xmin_at_yinterval_double_compare
{ {
if (edge_xmax (a) < edge_xmin (b)) { if (edge_xmax (a) < edge_xmin (b)) {
return true; return true;
} else if (edge_xmin (a) >= edge_xmax (b)) { } else if (edge_xmin (a) > edge_xmax (b)) {
return false; return false;
} else { } else {
C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2); C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2);

View File

@ -152,15 +152,17 @@ static std::pair<bool, std::set<db::Box> > compute_clip_variant (const db::Box &
} }
HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans) : mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
{ {
set_shape_receiver (pipe); set_shape_receiver (pipe);
reset ();
} }
HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_wants_all_cells (false), m_trans (trans) : mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
{ {
set_shape_receiver (pipe); set_shape_receiver (pipe);
reset ();
} }
HierarchyBuilder::~HierarchyBuilder () HierarchyBuilder::~HierarchyBuilder ()
@ -178,6 +180,8 @@ void
HierarchyBuilder::reset () HierarchyBuilder::reset ()
{ {
m_initial_pass = true; m_initial_pass = true;
m_cm_new_entry = false;
mp_initial_cell = 0; mp_initial_cell = 0;
m_cells_to_be_filled.clear (); m_cells_to_be_filled.clear ();
@ -186,7 +190,6 @@ HierarchyBuilder::reset ()
m_cells_seen.clear (); m_cells_seen.clear ();
m_cell_stack.clear (); m_cell_stack.clear ();
m_cm_entry = null_iterator; m_cm_entry = null_iterator;
m_cm_new_entry = false;
} }
const std::pair<db::cell_index_type, std::string> & const std::pair<db::cell_index_type, std::string> &
@ -351,7 +354,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
if (! key.clip_region.empty ()) { if (! key.clip_region.empty ()) {
cn += "$CLIP_VAR"; cn += "$CLIP_VAR";
description += "CLIP"; description += "CLIP";
} }
if (key.inactive) { if (key.inactive) {
cn += "$DIS"; cn += "$DIS";
@ -383,7 +385,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
} }
HierarchyBuilder::new_inst_mode HierarchyBuilder::new_inst_mode
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes)
{ {
if (all) { if (all) {
@ -402,7 +404,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
} }
// To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array.
return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
} else { } else {
@ -413,7 +415,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
} }
bool bool
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all) HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{ {
if (all) { if (all) {
@ -441,7 +443,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
} }
} }
return (m_cells_seen.find (key) == m_cells_seen.end ()); return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ();
} }
} }

View File

@ -294,8 +294,8 @@ public:
virtual void end (const RecursiveShapeIterator *iter); virtual void end (const RecursiveShapeIterator *iter);
virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box &region, const box_tree_type *complex_region); virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box &region, const box_tree_type *complex_region);
virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell); virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell);
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all); virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes);
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all); virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes);
virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region); virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region);
/** /**

View File

@ -39,7 +39,9 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db
void PolygonRefToShapesGenerator::put (const db::Polygon &polygon) void PolygonRefToShapesGenerator::put (const db::Polygon &polygon)
{ {
tl::MutexLocker locker (&mp_layout->lock ()); tl::MutexLocker locker (&mp_layout->lock ());
if (m_prop_id != 0) { if (polygon.is_empty ()) {
// ignore empty polygons
} else if (m_prop_id != 0) {
mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id)); mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id));
} else { } else {
mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
@ -58,7 +60,9 @@ PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size
void void
PolygonSplitter::put (const db::Polygon &poly) PolygonSplitter::put (const db::Polygon &poly)
{ {
if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) { if (poly.is_empty ()) {
// ignore empty polygons
} else if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) {
std::vector <db::Polygon> split_polygons; std::vector <db::Polygon> split_polygons;
db::split_polygon (poly, split_polygons); db::split_polygon (poly, split_polygons);

View File

@ -1771,6 +1771,14 @@ public:
return true; return true;
} }
/**
* @brief Returns a value indicating that the polygon is an empty one
*/
bool is_empty () const
{
return m_ctrs.size () == size_t (1) && m_ctrs[0].size () == 0;
}
/** /**
* @brief Returns the number of points in the polygon * @brief Returns the number of points in the polygon
*/ */
@ -1879,6 +1887,7 @@ public:
for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) { for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) {
h->transform (db::unit_trans<C> (), true /*compress*/, remove_reflected); h->transform (db::unit_trans<C> (), true /*compress*/, remove_reflected);
} }
m_bbox = m_ctrs [0].bbox ();
return *this; return *this;
} }
@ -2804,6 +2813,7 @@ public:
{ {
// compress the polygon by employing the transform method // compress the polygon by employing the transform method
m_hull.transform (db::unit_trans<C> (), true, remove_reflected); m_hull.transform (db::unit_trans<C> (), true, remove_reflected);
m_bbox = m_hull.bbox ();
return *this; return *this;
} }
@ -3022,6 +3032,14 @@ public:
return m_hull.is_halfmanhattan (); return m_hull.is_halfmanhattan ();
} }
/**
* @brief Returns a value indicating that the polygon is an empty one
*/
bool is_empty () const
{
return m_hull.size () == 0;
}
/** /**
* @brief The number of holes * @brief The number of holes
* *

View File

@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
m_layer = d.m_layer; m_layer = d.m_layer;
mp_cell = d.mp_cell; mp_cell = d.mp_cell;
m_current_layer = d.m_current_layer; m_current_layer = d.m_current_layer;
m_skip_shapes = d.m_skip_shapes;
m_skip_shapes_member = d.m_skip_shapes_member;
m_shape = d.m_shape; m_shape = d.m_shape;
m_trans = d.m_trans; m_trans = d.m_trans;
m_global_trans = d.m_global_trans; m_global_trans = d.m_global_trans;
@ -85,6 +87,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
m_local_complex_region_stack = d.m_local_complex_region_stack; m_local_complex_region_stack = d.m_local_complex_region_stack;
m_local_region_stack = d.m_local_region_stack; m_local_region_stack = d.m_local_region_stack;
m_skip_shapes_stack = d.m_skip_shapes_stack; m_skip_shapes_stack = d.m_skip_shapes_stack;
m_skip_shapes_member_stack = d.m_skip_shapes_member_stack;
m_needs_reinit = d.m_needs_reinit; m_needs_reinit = d.m_needs_reinit;
m_inst_quad_id = d.m_inst_quad_id; m_inst_quad_id = d.m_inst_quad_id;
m_inst_quad_id_stack = d.m_inst_quad_id_stack; m_inst_quad_id_stack = d.m_inst_quad_id_stack;
@ -469,6 +472,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const
m_local_region_stack.push_back (m_global_trans.inverted () * m_region); m_local_region_stack.push_back (m_global_trans.inverted () * m_region);
m_skip_shapes_stack.clear (); m_skip_shapes_stack.clear ();
m_skip_shapes_stack.push_back (false); m_skip_shapes_stack.push_back (false);
m_skip_shapes_member_stack.clear ();
m_skip_shapes_member_stack.push_back (false);
m_local_complex_region_stack.clear (); m_local_complex_region_stack.clear ();
if (mp_complex_region.get ()) { if (mp_complex_region.get ()) {
@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const
bool bool
RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
{ {
bool skip_shapes = false;
if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) {
// Try some optimization: if the instance we're looking at is entirely covered
// by a rectangle (other objects are too expensive to check), then we skip it
//
// We check 10 shapes max.
box_type inst_bx;
if (m_inst->size () == 1) {
inst_bx = m_inst->bbox (m_box_convert);
} else {
inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ());
}
unsigned int l = m_has_layers ? m_layers.front () : m_layer;
auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
size_t nmax = 10;
while (! si.at_end () && nmax-- > 0) {
if (inst_bx.inside (si->rectangle ())) {
skip_shapes = true;
break;
}
++si;
}
}
if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) {
return false;
}
tl_assert (mp_layout); tl_assert (mp_layout);
m_trans_stack.push_back (m_trans); m_trans_stack.push_back (m_trans);
@ -874,7 +846,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
} }
m_local_region_stack.push_back (new_region); m_local_region_stack.push_back (new_region);
m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); m_skip_shapes_stack.push_back (m_skip_shapes);
m_skip_shapes_member_stack.push_back (m_skip_shapes_member);
if (! m_local_complex_region_stack.empty ()) { if (! m_local_complex_region_stack.empty ()) {
@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const
m_inst = m_inst_iterators.back (); m_inst = m_inst_iterators.back ();
m_inst_array = m_inst_array_iterators.back (); m_inst_array = m_inst_array_iterators.back ();
m_inst_quad_id = m_inst_quad_id_stack.back (); m_inst_quad_id = m_inst_quad_id_stack.back ();
m_skip_shapes = m_skip_shapes_stack.back ();
m_skip_shapes_member = m_skip_shapes_member_stack.back ();
m_inst_iterators.pop_back (); m_inst_iterators.pop_back ();
m_inst_array_iterators.pop_back (); m_inst_array_iterators.pop_back ();
m_inst_quad_id_stack.pop_back (); m_inst_quad_id_stack.pop_back ();
@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const
m_cells.pop_back (); m_cells.pop_back ();
m_local_region_stack.pop_back (); m_local_region_stack.pop_back ();
m_skip_shapes_stack.pop_back (); m_skip_shapes_stack.pop_back ();
m_skip_shapes_member_stack.pop_back ();
if (! m_local_complex_region_stack.empty ()) { if (! m_local_complex_region_stack.empty ()) {
m_local_complex_region_stack.pop_back (); m_local_complex_region_stack.pop_back ();
} }
@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const
void void
RecursiveShapeIterator::new_layer () const RecursiveShapeIterator::new_layer () const
{ {
if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { if (skip_shapes () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) {
m_shape = shape_iterator (); m_shape = shape_iterator ();
} else if (! m_overlapping) { } else if (! m_overlapping) {
m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
new_inst (receiver); new_inst (receiver);
} }
bool
RecursiveShapeIterator::instance_is_covered (const box_type &inst_bx, unsigned int layer) const
{
// Try some optimization: if the instance we're looking at is entirely covered
// by a rectangle (other objects are too expensive to check), then we skip it
//
// We check 10 shapes max.
auto si = cell ()->shapes (layer).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel);
size_t nmax = 10;
while (! si.at_end () && nmax-- > 0) {
if (inst_bx.inside (si->rectangle ())) {
return true;
}
++si;
}
return false;
}
bool
RecursiveShapeIterator::skip_shapes () const
{
return m_skip_shapes_stack.back () || m_skip_shapes_member_stack.back ();
}
void void
RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
{ {
@ -1055,9 +1057,19 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const
all_of_instance = m_local_complex_region_stack.empty (); all_of_instance = m_local_complex_region_stack.empty ();
} }
m_skip_shapes = skip_shapes ();
m_skip_shapes_member = false;
if (m_for_merged_input && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
box_type inst_bx = m_inst->bbox (m_box_convert);
m_skip_shapes = instance_is_covered (inst_bx, m_has_layers ? m_layers.front () : m_layer);
}
RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all;
if (receiver) { if (receiver) {
ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance, m_skip_shapes);
} else if (m_skip_shapes) {
ni = RecursiveShapeReceiver::NI_skip;
} }
if (ni == RecursiveShapeReceiver::NI_skip) { if (ni == RecursiveShapeReceiver::NI_skip) {
@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
// skip instance array members not part of the complex region // skip instance array members not part of the complex region
while (! m_inst_array.at_end ()) { while (! m_inst_array.at_end ()) {
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
if (! is_outside_complex_region (ia_box)) { if (! is_outside_complex_region (ia_box)) {
break; break;
} else { } else {
@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
} }
while (! m_inst_array.at_end () && receiver) { m_skip_shapes_member = false;
if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) {
break; while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) {
} else {
++m_inst_array; m_skip_shapes_member = m_skip_shapes;
if (m_for_merged_input && ! m_inst_array.is_singular () && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) {
box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
m_skip_shapes_member = instance_is_covered (ia_box, m_has_layers ? m_layers.front () : m_layer);
} }
bool skip = false;
if (receiver) {
skip = ! receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance (), m_skip_shapes_member);
} else {
skip = m_skip_shapes_member;
}
if (skip) {
++m_inst_array;
} else {
break;
}
} }
} }

View File

@ -868,6 +868,7 @@ private:
mutable unsigned int m_layer; mutable unsigned int m_layer;
mutable const cell_type *mp_cell; mutable const cell_type *mp_cell;
mutable size_t m_current_layer; mutable size_t m_current_layer;
mutable bool m_skip_shapes, m_skip_shapes_member;
mutable shape_iterator m_shape; mutable shape_iterator m_shape;
mutable cplx_trans_type m_trans; mutable cplx_trans_type m_trans;
mutable std::vector<cplx_trans_type> m_trans_stack; mutable std::vector<cplx_trans_type> m_trans_stack;
@ -876,7 +877,7 @@ private:
mutable std::vector<const cell_type *> m_cells; mutable std::vector<const cell_type *> m_cells;
mutable std::vector<box_tree_type> m_local_complex_region_stack; mutable std::vector<box_tree_type> m_local_complex_region_stack;
mutable std::vector<box_type> m_local_region_stack; mutable std::vector<box_type> m_local_region_stack;
mutable std::vector<bool> m_skip_shapes_stack; mutable std::vector<bool> m_skip_shapes_stack, m_skip_shapes_member_stack;
mutable bool m_needs_reinit; mutable bool m_needs_reinit;
mutable size_t m_inst_quad_id; mutable size_t m_inst_quad_id;
mutable std::vector<size_t> m_inst_quad_id_stack; mutable std::vector<size_t> m_inst_quad_id_stack;
@ -899,6 +900,8 @@ private:
bool down (RecursiveShapeReceiver *receiver) const; bool down (RecursiveShapeReceiver *receiver) const;
void pop () const; void pop () const;
bool instance_is_covered (const box_type &inst_bx, unsigned int layer) const;
bool skip_shapes () const;
bool is_outside_complex_region (const db::Box &box) const; bool is_outside_complex_region (const db::Box &box) const;
void set_inactive (bool a) const void set_inactive (bool a) const
@ -1013,8 +1016,11 @@ public:
* - NI_all: iterate all members through "new_inst_member" * - NI_all: iterate all members through "new_inst_member"
* - NI_single: iterate a single member (the first one) * - NI_single: iterate a single member (the first one)
* - NI_skip: skips the whole array (not a single instance is iterated) * - NI_skip: skips the whole array (not a single instance is iterated)
*
* The "skip_shapes" parameter indicates that the instance is visited with the
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
*/ */
virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return NI_all; }
/** /**
* @brief Enters a new array member of the instance * @brief Enters a new array member of the instance
@ -1026,8 +1032,11 @@ public:
* "all" is true, if an instance array is iterated in "all" mode (see new_inst). * "all" is true, if an instance array is iterated in "all" mode (see new_inst).
* *
* If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered.
*
* The "skip_shapes" parameter indicates that the instance member is visited with the
* purpose of skipping all shapes. This is used to implement the "for_merged" optimization.
*/ */
virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return true; }
/** /**
* @brief Delivers a shape * @brief Delivers a shape

View File

@ -1618,18 +1618,34 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
} }
} }
db::polygon_ref_generator<TR> pr (layout, result);
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
for (auto i = interactions.begin (); i != interactions.end (); ++i) { for (auto i = interactions.begin (); i != interactions.end (); ++i) {
const TR &subject = interactions.subject_shape (i->first); const TR &subject = interactions.subject_shape (i->first);
if (others.find (subject) != others.end ()) { if (others.find (subject) != others.end ()) {
// shortcut (and: keep, not: drop)
// Note that we still normalize and split the polygon, so we get a uniform
// behavior.
if (m_is_and) { if (m_is_and) {
result.insert (subject); db::Polygon poly;
subject.instantiate (poly);
splitter.put (poly);
} }
} else if (i->second.empty ()) { } else if (i->second.empty ()) {
// shortcut (not: keep, and: drop) // shortcut (not: keep, and: drop)
// Note that we still normalize and split the polygon, so we get a uniform
// behavior.
if (! m_is_and) { if (! m_is_and) {
result.insert (subject); db::Polygon poly;
subject.instantiate (poly);
splitter.put (poly);
} }
} else { } else {
for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { for (auto e = subject.begin_edge (); ! e.at_end(); ++e) {
ep.insert (*e, p1); ep.insert (*e, p1);
@ -1649,8 +1665,6 @@ bool_and_or_not_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layou
} }
db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB);
db::polygon_ref_generator<TR> pr (layout, result);
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
db::PolygonGenerator pg (splitter, true, true); db::PolygonGenerator pg (splitter, true, true);
ep.set_base_verbosity (50); ep.set_base_verbosity (50);
ep.process (pg, op); ep.process (pg, op);

View File

@ -485,8 +485,41 @@ TEST(7)
cib.push_back (b1.cell_index ()); cib.push_back (b1.cell_index ());
cib.push_back (b2.cell_index ()); cib.push_back (b2.cell_index ());
cm.create_multi_mapping_full (h, cib, *g, cia); cm.create_multi_mapping_full (h, cib, *g, cia);
EXPECT_EQ (m2s (cm, *g, h), "a0->b0;a1->b1;a2->b2;a3->a3;a4->a4;a5->a5"); EXPECT_EQ (m2s (cm, h, *g), "b0->a0;b1->a1;b2->a2;a3->a3;a4->a4;a5->a5");
EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:"); EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:");
} }
// Issue #2014
TEST(8)
{
std::unique_ptr<db::Layout> g (new db::Layout ());
db::Cell &a (g->cell (g->add_cell ("a")));
db::Cell &b (g->cell (g->add_cell ("b")));
db::Cell &b1 (g->cell (g->add_cell ("b1")));
db::Cell &b2 (g->cell (g->add_cell ("b2")));
db::Cell &c (g->cell (g->add_cell ("c")));
b.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans ()));
b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans ()));
b.insert (db::CellInstArray (db::CellInst (b1.cell_index ()), db::Trans ()));
b.insert (db::CellInstArray (db::CellInst (b2.cell_index ()), db::Trans ()));
db::Layout h;
db::Cell &ha (h.cell (h.add_cell ("a")));
db::Cell &hb (h.cell (h.add_cell ("b")));
db::Cell &hc (h.cell (h.add_cell ("c")));
db::CellMapping cm;
std::vector<db::cell_index_type> cib, cia;
cia.push_back (a.cell_index ());
cia.push_back (b.cell_index ());
cia.push_back (c.cell_index ());
cib.push_back (ha.cell_index ());
cib.push_back (hb.cell_index ());
cib.push_back (hc.cell_index ());
cm.create_multi_mapping_full (h, cib, *g, cia);
EXPECT_EQ (m2s (cm, h, *g), "a->a;b->b;b1->b1;b2->b2;c->c");
EXPECT_EQ (l2s (h), "a#0:;b#1:cell_index=0 r0 0,0,cell_index=2 r0 0,0,cell_index=3 r0 0,0,cell_index=4 r0 0,0;c#2:;b1#3:;b2#4:");
}

View File

@ -69,6 +69,7 @@ TEST(1)
EXPECT_EQ (empty == p, true); EXPECT_EQ (empty == p, true);
EXPECT_EQ (p.is_box (), false); EXPECT_EQ (p.is_box (), false);
EXPECT_EQ (p.is_empty (), true);
std::vector <db::Point> c1, c2, c3; std::vector <db::Point> c1, c2, c3;
c1.push_back (db::Point (0, 0)); c1.push_back (db::Point (0, 0));
@ -76,6 +77,7 @@ TEST(1)
c1.push_back (db::Point (100, 1000)); c1.push_back (db::Point (100, 1000));
c1.push_back (db::Point (100, 0)); c1.push_back (db::Point (100, 0));
p.assign_hull (c1.begin (), c1.end ()); p.assign_hull (c1.begin (), c1.end ());
EXPECT_EQ (p.is_empty (), false);
b = p.box (); b = p.box ();
EXPECT_EQ (p.holes (), size_t (0)); EXPECT_EQ (p.holes (), size_t (0));
EXPECT_EQ (p.area (), 1000*100); EXPECT_EQ (p.area (), 1000*100);
@ -1404,3 +1406,30 @@ TEST(28)
db::Polygon b (db::Box (-1000000000, -1000000000, 1000000000, 1000000000)); db::Polygon b (db::Box (-1000000000, -1000000000, 1000000000, 1000000000));
EXPECT_EQ (b.perimeter (), 8000000000.0); EXPECT_EQ (b.perimeter (), 8000000000.0);
} }
TEST(29)
{
// Degenerated boxes and compress
db::Polygon b (db::Box (10, 20, 10, 20));
EXPECT_EQ (b.is_empty (), false);
EXPECT_EQ (b == db::Polygon (), false);
EXPECT_EQ (b.to_string (), "(10,20;10,20;10,20;10,20)");
EXPECT_EQ (double (b.area ()), 0.0);
b.compress (true);
EXPECT_EQ (b.is_empty (), true);
EXPECT_EQ (b == db::Polygon (), true);
db::SimplePolygon sb (db::Box (10, 20, 10, 20));
EXPECT_EQ (sb.is_empty (), false);
EXPECT_EQ (sb == db::SimplePolygon (), false);
EXPECT_EQ (sb.to_string (), "(10,20;10,20;10,20;10,20)");
EXPECT_EQ (double (sb.area ()), 0.0);
sb.compress (true);
EXPECT_EQ (sb.is_empty (), true);
EXPECT_EQ (sb == db::SimplePolygon (), true);
}

View File

@ -756,15 +756,25 @@ namespace {
: public db::RecursiveShapeReceiver : public db::RecursiveShapeReceiver
{ {
public: public:
FlatPusher (std::set<db::Box> *boxes) : mp_boxes (boxes) { } FlatPusher (std::set<db::Box> *boxes = 0) : mp_boxes (boxes ? boxes : &m_boxes) { }
void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/)
{ {
mp_boxes->insert (trans * shape.bbox ()); mp_boxes->insert (trans * shape.bbox ());
} }
std::string to_string () const
{
std::vector<std::string> s;
for (auto i = mp_boxes->begin (); i != mp_boxes->end (); ++i) {
s.push_back (i->to_string ());
}
return tl::join (s.begin (), s.end (), ";");
}
private: private:
std::set<db::Box> *mp_boxes; std::set<db::Box> *mp_boxes;
std::set<db::Box> m_boxes;
}; };
} }
@ -1038,7 +1048,7 @@ public:
m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n";
} }
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
{ {
m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ());
if (all) { if (all) {
@ -1048,7 +1058,7 @@ public:
return NI_all; return NI_all;
} }
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/)
{ {
m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans); m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans);
if (all) { if (all) {
@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray
public: public:
ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { }
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all) virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{ {
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip;
} }
@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne
public: public:
ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { }
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all) virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{ {
LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes);
return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; return inst.object ().cell_index () != m_rejected ? NI_all : NI_single;
} }
@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance
public: public:
ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { }
virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all) virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region, bool all, bool skip_shapes)
{ {
LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all); LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all, skip_shapes);
return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected;
} }
@ -1586,49 +1596,174 @@ TEST(12_ForMerged)
db::RecursiveShapeIterator i1 (*g, c0, 0); db::RecursiveShapeIterator i1 (*g, c0, 0);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
i1.set_for_merged_input (true); i1.set_for_merged_input (true);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
std::vector<unsigned int> lv; std::vector<unsigned int> lv;
lv.push_back (0); lv.push_back (0);
i1 = db::RecursiveShapeIterator (*g, c0, lv); i1 = db::RecursiveShapeIterator (*g, c0, lv);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
i1.set_for_merged_input (true); i1.set_for_merged_input (true);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
lv.push_back (1); // empty, but kills "for merged" optimization lv.push_back (1); // empty, but kills "for merged" optimization
i1 = db::RecursiveShapeIterator (*g, c0, lv); i1 = db::RecursiveShapeIterator (*g, c0, lv);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
}
i1.set_for_merged_input (true); i1.set_for_merged_input (true);
x = collect(i1, *g); x = collect(i1, *g);
// no longer optimized // no longer optimized
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)");
}
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50)); i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50));
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)");
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100)");
}
i1.set_for_merged_input (true); i1.set_for_merged_input (true);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
}
i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50)); i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50));
i1.set_overlapping (true); i1.set_overlapping (true);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
}
i1.set_for_merged_input (true); i1.set_for_merged_input (true);
x = collect(i1, *g); x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)");
}
} }
TEST(12b_ForMerged)
{
std::unique_ptr<db::Layout> g (new db::Layout ());
g->insert_layer (0);
g->insert_layer (1);
db::Cell &c0 (g->cell (g->add_cell ()));
db::Cell &c1 (g->cell (g->add_cell ()));
db::Box b (0, 100, 1000, 1200);
c0.shapes (0).insert (db::Box (0, 0, 3000, 2200));
c1.shapes (0).insert (b);
db::Trans tt;
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt));
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (2000, 1000)), db::Vector (0, 2000), db::Vector (2000, 0), 2l, 2l));
std::string x;
db::RecursiveShapeIterator i1 (*g, c0, 0);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
i1.set_for_merged_input (true);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
{
FlatPusher f;
i1.reset ();
i1.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
i1.set_for_merged_input (false);
x = collect(i1, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)");
EXPECT_EQ (collect_with_copy(i1, *g), x);
c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 2000))));
db::RecursiveShapeIterator i2 (*g, c0, 0);
x = collect(i2, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
EXPECT_EQ (collect_with_copy(i2, *g), x);
{
FlatPusher f;
i2.reset ();
i2.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
i2.set_for_merged_input (true);
x = collect(i2, *g);
EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)");
EXPECT_EQ (collect_with_copy(i2, *g), x);
{
FlatPusher f;
i2.reset ();
i2.push (&f);
EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)");
}
}
TEST(13_ForMergedPerformance) TEST(13_ForMergedPerformance)
{ {

View File

@ -1585,7 +1585,8 @@ module DRC
self._context("report") do self._context("report") do
# finish what we got so far # finish what we got so far
_finish(false) view = RBA::LayoutView::current
@def_output && @def_output.finish(false, view)
@def_output = nil @def_output = nil
@def_output = _make_report(description, filename, cellname) @def_output = _make_report(description, filename, cellname)

View File

@ -1694,6 +1694,16 @@ TEST(93d_withAngle)
run_test (_this, "93", true); run_test (_this, "93", true);
} }
TEST(94_texts_in_region_xor)
{
run_test (_this, "94", false);
}
TEST(94d_texts_in_region_xor)
{
run_test (_this, "94", true);
}
TEST(100_edge_interaction_with_count) TEST(100_edge_interaction_with_count)
{ {
run_test (_this, "100", false); run_test (_this, "100", false);

View File

@ -47,6 +47,31 @@
namespace edt namespace edt
{ {
static std::string variant_list_to_string (const tl::Variant &value)
{
if (! value.is_list ()) {
tl::Variant v = tl::Variant::empty_list ();
v.push (value);
return v.to_parsable_string ();
}
for (auto i = value.begin (); i != value.end (); ++i) {
if (! i->is_a_string () || std::string (i->to_string ()).find (",") != std::string::npos) {
return value.to_parsable_string ();
}
}
// otherwise we can plainly combine the strings with ","
std::string res;
for (auto i = value.begin (); i != value.end (); ++i) {
if (i != value.begin ()) {
res += ",";
}
res += i->to_string ();
}
return res;
}
static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, const tl::Variant &value) static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, const tl::Variant &value)
{ {
if (p.get_choices ().empty ()) { if (p.get_choices ().empty ()) {
@ -91,7 +116,7 @@ static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget,
QLineEdit *le = dynamic_cast<QLineEdit *> (widget); QLineEdit *le = dynamic_cast<QLineEdit *> (widget);
if (le) { if (le) {
le->blockSignals (true); le->blockSignals (true);
le->setText (value.to_qstring ()); le->setText (tl::to_qstring (variant_list_to_string (value)));
le->blockSignals (false); le->blockSignals (false);
} }
} }
@ -905,8 +930,29 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool
{ {
QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]); QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
if (le) { if (le) {
std::vector<std::string> values = tl::split (tl::to_string (le->text ()), ",");
std::string s = tl::to_string (le->text ());
// try parsing a bracketed expression
tl::Extractor ex (s.c_str ());
if (*ex.skip () == '(') {
tl::Variant v;
try {
ex.read (v);
ps.set_value (v);
break;
} catch (...) {
// ignore errors
}
} else if (ex.at_end ()) {
ps.set_value (tl::Variant::empty_list ());
break;
}
// otherwise: plain splitting at comma
std::vector<std::string> values = tl::split (s, ",");
ps.set_value (tl::Variant (values.begin (), values.end ())); ps.set_value (tl::Variant (values.begin (), values.end ()));
} }
} }
break; break;

View File

@ -35,6 +35,12 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// class QDomNodeList // class QDomNodeList
static bool QDomNodeList_operator_eq(const QDomNodeList *a, const QDomNodeList &b) {
return *a == b;
}
static bool QDomNodeList_operator_ne(const QDomNodeList *a, const QDomNodeList &b) {
return !(*a == b);
}
// Constructor QDomNodeList::QDomNodeList() // Constructor QDomNodeList::QDomNodeList()
@ -153,25 +159,6 @@ static void _call_f_length_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cls
} }
// bool QDomNodeList::operator!=(const QDomNodeList &)
static void _init_f_operator_excl__eq__c2484 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("arg1");
decl->add_arg<const QDomNodeList & > (argspec_0);
decl->set_return<bool > ();
}
static void _call_f_operator_excl__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const QDomNodeList &arg1 = gsi::arg_reader<const QDomNodeList & >() (args, heap);
ret.write<bool > ((bool)((QDomNodeList *)cls)->operator!= (arg1));
}
// QDomNodeList &QDomNodeList::operator=(const QDomNodeList &) // QDomNodeList &QDomNodeList::operator=(const QDomNodeList &)
@ -191,25 +178,6 @@ static void _call_f_operator_eq__2484 (const qt_gsi::GenericMethod * /*decl*/, v
} }
// bool QDomNodeList::operator==(const QDomNodeList &)
static void _init_f_operator_eq__eq__c2484 (qt_gsi::GenericMethod *decl)
{
static gsi::ArgSpecBase argspec_0 ("arg1");
decl->add_arg<const QDomNodeList & > (argspec_0);
decl->set_return<bool > ();
}
static void _call_f_operator_eq__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret)
{
__SUPPRESS_UNUSED_WARNING(args);
tl::Heap heap;
const QDomNodeList &arg1 = gsi::arg_reader<const QDomNodeList & >() (args, heap);
ret.write<bool > ((bool)((QDomNodeList *)cls)->operator== (arg1));
}
// int QDomNodeList::size() // int QDomNodeList::size()
@ -238,14 +206,15 @@ static gsi::Methods methods_QDomNodeList () {
methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QDomNodeList::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0); methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QDomNodeList::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0);
methods += new qt_gsi::GenericMethod ("item", "@brief Method QDomNode QDomNodeList::item(int index)\n", true, &_init_f_item_c767, &_call_f_item_c767); methods += new qt_gsi::GenericMethod ("item", "@brief Method QDomNode QDomNodeList::item(int index)\n", true, &_init_f_item_c767, &_call_f_item_c767);
methods += new qt_gsi::GenericMethod ("length", "@brief Method int QDomNodeList::length()\n", true, &_init_f_length_c0, &_call_f_length_c0); methods += new qt_gsi::GenericMethod ("length", "@brief Method int QDomNodeList::length()\n", true, &_init_f_length_c0, &_call_f_length_c0);
methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &)\n", true, &_init_f_operator_excl__eq__c2484, &_call_f_operator_excl__eq__c2484);
methods += new qt_gsi::GenericMethod ("assign", "@brief Method QDomNodeList &QDomNodeList::operator=(const QDomNodeList &)\n", false, &_init_f_operator_eq__2484, &_call_f_operator_eq__2484); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QDomNodeList &QDomNodeList::operator=(const QDomNodeList &)\n", false, &_init_f_operator_eq__2484, &_call_f_operator_eq__2484);
methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QDomNodeList::operator==(const QDomNodeList &)\n", true, &_init_f_operator_eq__eq__c2484, &_call_f_operator_eq__eq__c2484);
methods += new qt_gsi::GenericMethod ("size", "@brief Method int QDomNodeList::size()\n", true, &_init_f_size_c0, &_call_f_size_c0); methods += new qt_gsi::GenericMethod ("size", "@brief Method int QDomNodeList::size()\n", true, &_init_f_size_c0, &_call_f_size_c0);
return methods; return methods;
} }
gsi::Class<QDomNodeList> decl_QDomNodeList ("QtXml", "QDomNodeList", gsi::Class<QDomNodeList> decl_QDomNodeList ("QtXml", "QDomNodeList",
gsi::method_ext("==", &QDomNodeList_operator_eq, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator==(const QDomNodeList &) const") +
gsi::method_ext("!=", &QDomNodeList_operator_ne, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &) const")
+
methods_QDomNodeList (), methods_QDomNodeList (),
"@qt\n@brief Binding of QDomNodeList"); "@qt\n@brief Binding of QDomNodeList");

View File

@ -43,6 +43,7 @@ static const std::string cfg_grid_grid_color ("grid-grid-color");
static const std::string cfg_grid_style0 ("grid-style0"); static const std::string cfg_grid_style0 ("grid-style0");
static const std::string cfg_grid_style1 ("grid-style1"); static const std::string cfg_grid_style1 ("grid-style1");
static const std::string cfg_grid_style2 ("grid-style2"); static const std::string cfg_grid_style2 ("grid-style2");
static const std::string cfg_grid_density ("grid-density");
static const std::string cfg_grid_visible ("grid-visible"); static const std::string cfg_grid_visible ("grid-visible");
static const std::string cfg_grid_micron ("grid-micron"); static const std::string cfg_grid_micron ("grid-micron");
static const std::string cfg_grid_show_ruler ("grid-show-ruler"); static const std::string cfg_grid_show_ruler ("grid-show-ruler");

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>483</width> <width>483</width>
<height>341</height> <height>361</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -59,6 +59,23 @@
<property name="spacing"> <property name="spacing">
<number>6</number> <number>6</number>
</property> </property>
<item row="3" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Far style</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="10" column="2" colspan="2">
<widget class="QCheckBox" name="show_ruler">
<property name="text">
<string>Show ruler</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2"> <item row="2" column="2" colspan="2">
<widget class="QComboBox" name="style1_cbx"> <widget class="QComboBox" name="style1_cbx">
<item> <item>
@ -108,7 +125,122 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="2" column="4">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="5">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QPushButton" name="grid_axis_color_pb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Grid </string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="5">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="grid_net_color_pb">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Ruler</string>
</property>
</widget>
</item>
<item row="11" column="2">
<widget class="QPushButton" name="grid_ruler_color_pb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Axis</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="grid_grid_color_pb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="Close style">
<property name="text">
<string>Close style</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Color (all)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>Color</string> <string>Color</string>
@ -118,14 +250,17 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="5"> <item row="7" column="1">
<widget class="Line" name="line_2"> <widget class="QLabel" name="label_4">
<property name="orientation"> <property name="text">
<enum>Qt::Horizontal</enum> <string>Style</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="2" colspan="2"> <item row="7" column="2" colspan="2">
<widget class="QComboBox" name="style0_cbx"> <widget class="QComboBox" name="style0_cbx">
<item> <item>
<property name="text"> <property name="text">
@ -169,30 +304,23 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="0" column="3"> <item row="8" column="1">
<spacer> <widget class="QLabel" name="label_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="10" column="2">
<widget class="QPushButton" name="grid_ruler_color_pb">
<property name="text"> <property name="text">
<string/> <string>Color</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="5"> <item row="4" column="1">
<widget class="Line" name="line"> <widget class="QLabel" name="label">
<property name="orientation"> <property name="text">
<enum>Qt::Horizontal</enum> <string>Color</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -245,141 +373,37 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="9" column="0" colspan="5">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Far style</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="Close style">
<property name="text">
<string>Close style</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Color (all)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="grid_grid_color_pb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Grid </string>
</property>
</widget>
</item>
<item row="9" column="2" colspan="2">
<widget class="QCheckBox" name="show_ruler">
<property name="text">
<string>Show Ruler</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QPushButton" name="grid_axis_color_pb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="4">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Style</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Color</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="grid_net_color_pb">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Axis</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Color</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Ruler</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="5">
<widget class="Line" name="line_3"> <widget class="Line" name="line_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="2">
<widget class="QSpinBox" name="grid_density_sb">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Min. spacing</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="3" colspan="2">
<widget class="QLabel" name="label_11">
<property name="text">
<string>(Font height units)</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -41,6 +41,8 @@ namespace lay
// ------------------------------------------------------------ // ------------------------------------------------------------
// Helper functions to get and set the configuration // Helper functions to get and set the configuration
int default_density = 4;
static struct { static struct {
lay::GridNet::GridStyle style; lay::GridNet::GridStyle style;
const char *string; const char *string;
@ -79,6 +81,20 @@ GridNetStyleConverter::to_string (lay::GridNet::GridStyle style)
return ""; return "";
} }
void
GridNetDensityConverter::from_string (const std::string &value, int &density)
{
density = default_density; // original default
tl::Extractor ex (value.c_str ());
ex.try_read (density);
}
std::string
GridNetDensityConverter::to_string (int density)
{
return tl::to_string (density);
}
// ------------------------------------------------------------ // ------------------------------------------------------------
// Implementation of the GridNetPluginDeclaration // Implementation of the GridNetPluginDeclaration
@ -92,6 +108,7 @@ GridNetPluginDeclaration::get_options (std::vector < std::pair<std::string, std:
options.push_back (std::pair<std::string, std::string> (cfg_grid_style0, GridNetStyleConverter ().to_string (lay::GridNet::Invisible))); options.push_back (std::pair<std::string, std::string> (cfg_grid_style0, GridNetStyleConverter ().to_string (lay::GridNet::Invisible)));
options.push_back (std::pair<std::string, std::string> (cfg_grid_style1, GridNetStyleConverter ().to_string (lay::GridNet::Dots))); options.push_back (std::pair<std::string, std::string> (cfg_grid_style1, GridNetStyleConverter ().to_string (lay::GridNet::Dots)));
options.push_back (std::pair<std::string, std::string> (cfg_grid_style2, GridNetStyleConverter ().to_string (lay::GridNet::TenthDottedLines))); options.push_back (std::pair<std::string, std::string> (cfg_grid_style2, GridNetStyleConverter ().to_string (lay::GridNet::TenthDottedLines)));
options.push_back (std::pair<std::string, std::string> (cfg_grid_density, ""));
options.push_back (std::pair<std::string, std::string> (cfg_grid_visible, tl::to_string (true))); options.push_back (std::pair<std::string, std::string> (cfg_grid_visible, tl::to_string (true)));
options.push_back (std::pair<std::string, std::string> (cfg_grid_show_ruler, tl::to_string (true))); options.push_back (std::pair<std::string, std::string> (cfg_grid_show_ruler, tl::to_string (true)));
// grid-micron is not configured here since some other entity is supposed to do this. // grid-micron is not configured here since some other entity is supposed to do this.
@ -122,7 +139,8 @@ GridNet::GridNet (LayoutViewBase *view)
lay::Plugin (view), lay::Plugin (view),
mp_view (view), mp_view (view),
m_visible (false), m_show_ruler (true), m_grid (1.0), m_visible (false), m_show_ruler (true), m_grid (1.0),
m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible) m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible),
m_density (default_density)
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -175,6 +193,12 @@ GridNet::configure (const std::string &name, const std::string &value)
GridNetStyleConverter ().from_string (value, style); GridNetStyleConverter ().from_string (value, style);
need_update = test_and_set (m_style2, style); need_update = test_and_set (m_style2, style);
} else if (name == cfg_grid_density) {
int density = 0;
GridNetDensityConverter ().from_string (value, density);
need_update = test_and_set (m_density, density);
} else if (name == cfg_grid_show_ruler) { } else if (name == cfg_grid_show_ruler) {
bool sr = false; bool sr = false;
@ -246,13 +270,14 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas)
// fw is the basic unit of the ruler geometry // fw is the basic unit of the ruler geometry
int fwr = lay::FixedFont::get_font (bmp_canvas->font_resolution ()).width (); int fwr = lay::FixedFont::get_font (bmp_canvas->font_resolution ()).width ();
int threshold = std::min (1000, m_density * fwr);
double dgrid = trans.ctrans (m_grid); double dgrid = trans.ctrans (m_grid);
GridStyle style = m_style1; GridStyle style = m_style1;
// compute major grid and switch to secondary style if necessary // compute major grid and switch to secondary style if necessary
int s = 0; int s = 0;
while (dgrid < fwr * 4) { while (dgrid < threshold) {
if (s == 0) { if (s == 0) {
dgrid *= 2.0; dgrid *= 2.0;
} else if (s == 1) { } else if (s == 1) {
@ -279,56 +304,6 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas)
int nx = int (dbworld.width () / grid + eps) + 2; int nx = int (dbworld.width () / grid + eps) + 2;
int ny = int (dbworld.height () / grid + eps) + 2; int ny = int (dbworld.height () / grid + eps) + 2;
if (m_show_ruler && dgrid < vp.width () * 0.2) {
int rh = int (floor (0.5 + fwr * 0.8));
int xoffset = int (floor (0.5 + fwr * 2.5));
int yoffset = int (floor (0.5 + fwr * 2.5));
painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2),
db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2),
ruler_color);
painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2),
db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2),
ruler_color);
painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (),
db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2),
ruler_color, -1, 1);
if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) {
// draw a small "F" indicating any global transformation
db::Point pts[] = {
db::Point (-4, -5),
db::Point (-4, 5),
db::Point (4, 5),
db::Point (4, 3),
db::Point (-2, 3),
db::Point (-2, 1),
db::Point (3, 1),
db::Point (3, -1),
db::Point (-2, -1),
db::Point (-2, -5),
db::Point (-4, -5)
};
db::Polygon poly;
poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0])));
poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ()));
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) {
db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5);
db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4)));
db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4)));
painter.draw_line (p1, p2, ruler_color);
}
}
}
// draw grid // draw grid
if (style == Dots || style == TenthDottedLines || if (style == Dots || style == TenthDottedLines ||
style == DottedLines || style == LightDottedLines) { style == DottedLines || style == LightDottedLines) {
@ -549,6 +524,56 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas)
} }
if (m_show_ruler && dgrid < vp.width () * 0.4) {
int rh = int (floor (0.5 + fwr * 0.8));
int xoffset = int (floor (0.5 + fwr * 2.5));
int yoffset = int (floor (0.5 + fwr * 2.5));
painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2),
db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2),
ruler_color);
painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2),
db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2),
ruler_color);
painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (),
db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2),
ruler_color, -1, 1);
if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) {
// draw a small "F" indicating any global transformation
db::Point pts[] = {
db::Point (-4, -5),
db::Point (-4, 5),
db::Point (4, 5),
db::Point (4, 3),
db::Point (-2, 3),
db::Point (-2, 1),
db::Point (3, 1),
db::Point (3, -1),
db::Point (-2, -1),
db::Point (-2, -5),
db::Point (-4, -5)
};
db::Polygon poly;
poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0])));
poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ()));
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) {
db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5);
db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4)));
db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4)));
painter.draw_line (p1, p2, ruler_color);
}
}
}
} }
} }

View File

@ -87,6 +87,7 @@ private:
GridStyle m_style0; GridStyle m_style0;
GridStyle m_style1; GridStyle m_style1;
GridStyle m_style2; GridStyle m_style2;
int m_density;
}; };
class GridNetStyleConverter class GridNetStyleConverter
@ -96,6 +97,13 @@ public:
std::string to_string (lay::GridNet::GridStyle style); std::string to_string (lay::GridNet::GridStyle style);
}; };
class GridNetDensityConverter
{
public:
void from_string (const std::string &value, int &density);
std::string to_string (int density);
};
} }
#endif #endif

View File

@ -94,6 +94,10 @@ GridNetConfigPage::setup (lay::Dispatcher *root)
style = lay::GridNet::Invisible; style = lay::GridNet::Invisible;
root->config_get (cfg_grid_style2, style, GridNetStyleConverter ()); root->config_get (cfg_grid_style2, style, GridNetStyleConverter ());
mp_ui->style2_cbx->setCurrentIndex (int (style)); mp_ui->style2_cbx->setCurrentIndex (int (style));
int density = 0;
root->config_get (cfg_grid_density, density, GridNetDensityConverter ());
mp_ui->grid_density_sb->setValue (density);
} }
void void
@ -108,6 +112,7 @@ GridNetConfigPage::commit (lay::Dispatcher *root)
root->config_set (cfg_grid_style0, lay::GridNet::GridStyle (mp_ui->style0_cbx->currentIndex ()), GridNetStyleConverter ()); root->config_set (cfg_grid_style0, lay::GridNet::GridStyle (mp_ui->style0_cbx->currentIndex ()), GridNetStyleConverter ());
root->config_set (cfg_grid_style1, lay::GridNet::GridStyle (mp_ui->style1_cbx->currentIndex ()), GridNetStyleConverter ()); root->config_set (cfg_grid_style1, lay::GridNet::GridStyle (mp_ui->style1_cbx->currentIndex ()), GridNetStyleConverter ());
root->config_set (cfg_grid_style2, lay::GridNet::GridStyle (mp_ui->style2_cbx->currentIndex ()), GridNetStyleConverter ()); root->config_set (cfg_grid_style2, lay::GridNet::GridStyle (mp_ui->style2_cbx->currentIndex ()), GridNetStyleConverter ());
root->config_set (cfg_grid_density, mp_ui->grid_density_sb->value (), GridNetDensityConverter ());
} }
} // namespace lay } // namespace lay

View File

@ -38,7 +38,7 @@ namespace db
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Path resolution utility // Path resolution utility
std::string correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path) std::vector<std::string> correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path, bool glob)
{ {
const db::Technology *tech = layout.technology (); const db::Technology *tech = layout.technology ();
@ -64,19 +64,28 @@ std::string correct_path (const std::string &fn_in, const db::Layout &layout, co
if (tech && ! tech->base_path ().empty ()) { if (tech && ! tech->base_path ().empty ()) {
std::string new_fn = tl::combine_path (tech->base_path (), fn); std::string new_fn = tl::combine_path (tech->base_path (), fn);
if (tl::file_exists (new_fn)) { if (tl::file_exists (new_fn)) {
return new_fn; std::vector<std::string> res;
res.push_back (new_fn);
return res;
} else if (glob) {
return tl::glob_expand (new_fn);
} }
} }
if (! base_path.empty ()) { if (! base_path.empty ()) {
return tl::combine_path (base_path, fn); fn = tl::combine_path (base_path, fn);
} else {
return fn;
} }
} else {
return fn;
} }
if (tl::file_exists (fn) || ! glob) {
std::vector<std::string> res;
res.push_back (fn);
return res;
} else {
return tl::glob_expand (fn);
}
} }
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
@ -1059,7 +1068,7 @@ LEFDEFReaderState::read_map_file (const std::string &filename, db::Layout &layou
std::map<std::pair<std::string, LayerDetailsKey>, std::vector<db::LayerProperties> > layer_map; std::map<std::pair<std::string, LayerDetailsKey>, std::vector<db::LayerProperties> > layer_map;
for (std::vector<std::string>::const_iterator p = paths.begin (); p != paths.end (); ++p) { for (std::vector<std::string>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
read_single_map_file (correct_path (*p, layout, base_path), layer_map); read_single_map_file (correct_path (*p, layout, base_path, false).front (), layer_map);
} }
// build an explicit layer mapping now. // build an explicit layer mapping now.

View File

@ -52,7 +52,7 @@ struct MacroDesc;
* @brief Correct a path relative to the stream and technology * @brief Correct a path relative to the stream and technology
*/ */
DB_PLUGIN_PUBLIC DB_PLUGIN_PUBLIC
std::string correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path); std::vector<std::string> correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path, bool glob);
/** /**
* @brief Convers a string to a MASKSHIFT index list * @brief Convers a string to a MASKSHIFT index list

View File

@ -141,11 +141,12 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti
for (std::vector<std::string>::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { for (std::vector<std::string>::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) {
std::string lp = correct_path (*l, layout, base_path); auto paths = correct_path (*l, layout, base_path, true);
for (auto lp = paths.begin (); lp != paths.end (); ++lp) {
tl::InputStream lef_stream (lp); tl::InputStream lef_stream (*lp);
tl::log << tl::to_string (tr ("Reading")) << " " << lp; tl::log << tl::to_string (tr ("Reading")) << " " << *lp;
importer.read (lef_stream, layout, state); importer.read (lef_stream, layout, state);
}
} }
@ -164,14 +165,20 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti
for (std::vector<std::string>::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { for (std::vector<std::string>::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) {
std::string lp = correct_path (*l, layout, base_path); auto paths = correct_path (*l, layout, base_path, true);
lef_files_read.insert (tl::normalize_path (lp)); for (auto lp = paths.begin (); lp != paths.end (); ++lp) {
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + lp); if (lef_files_read.insert (tl::normalize_path (*lp)).second) {
tl::InputStream lef_stream (lp); tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp);
tl::log << tl::to_string (tr ("Reading")) << " " << lp;
importer.read_lef (lef_stream, layout, state); tl::InputStream lef_stream (*lp);
tl::log << tl::to_string (tr ("Reading")) << " " << *lp;
importer.read_lef (lef_stream, layout, state);
}
}
} }
@ -223,22 +230,25 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti
tl::shared_collection<db::Layout> macro_layout_object_holder; tl::shared_collection<db::Layout> macro_layout_object_holder;
for (std::vector<std::string>::const_iterator l = effective_options.begin_macro_layout_files (); l != effective_options.end_macro_layout_files (); ++l) { for (std::vector<std::string>::const_iterator l = effective_options.begin_macro_layout_files (); l != effective_options.end_macro_layout_files (); ++l) {
std::string lp = correct_path (*l, layout, base_path); auto paths = correct_path (*l, layout, base_path, true);
for (auto lp = paths.begin (); lp != paths.end (); ++lp) {
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + lp); tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp);
tl::InputStream macro_layout_stream (lp); tl::InputStream macro_layout_stream (*lp);
tl::log << tl::to_string (tr ("Reading")) << " " << lp; tl::log << tl::to_string (tr ("Reading")) << " " << *lp;
db::Layout *new_layout = new db::Layout (false); db::Layout *new_layout = new db::Layout (false);
macro_layout_object_holder.push_back (new_layout); macro_layout_object_holder.push_back (new_layout);
macro_layouts.push_back (new_layout); macro_layouts.push_back (new_layout);
db::Reader reader (macro_layout_stream); db::Reader reader (macro_layout_stream);
reader.read (*new_layout, options); reader.read (*new_layout, options);
if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) {
importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")),
*lp, new_layout->dbu (), layout.dbu ()));
}
if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) {
importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")),
lp, new_layout->dbu (), layout.dbu ()));
} }
} }
@ -266,6 +276,14 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti
} }
// Warn about cells that could not be resolved
for (std::map<std::string, db::cell_index_type>::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) {
if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) {
importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")),
f->first));
}
}
} }
state.finish (layout); state.finish (layout);

View File

@ -661,15 +661,15 @@ D25ViewWidget::enter (const db::RecursiveShapeIterator *iter, double zstart, dou
void void
D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop) D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop)
{ {
// try to establish a default color from the region's origin if required
const db::RecursiveShapeIterator *iter = 0;
const db::OriginalLayerRegion *original = dynamic_cast<const db::OriginalLayerRegion *> (data.delegate ()); const db::OriginalLayerRegion *original = dynamic_cast<const db::OriginalLayerRegion *> (data.delegate ());
if (original) { if (original) {
iter = original->iter (); // try to establish a default color from the region's origin if required
auto it = original->begin_iter ();
enter (&it.first, zstart, zstop);
} else {
enter (0, zstart, zstop);
} }
enter (iter, zstart, zstop);
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
render_region (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); render_region (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop);
} }
@ -677,15 +677,15 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double
void void
D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double zstop) D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double zstop)
{ {
// try to establish a default color from the region's origin if required
const db::RecursiveShapeIterator *iter = 0;
const db::OriginalLayerEdges *original = dynamic_cast<const db::OriginalLayerEdges *> (data.delegate ()); const db::OriginalLayerEdges *original = dynamic_cast<const db::OriginalLayerEdges *> (data.delegate ());
if (original) { if (original) {
iter = original->iter (); // try to establish a default color from the region's origin if required
auto it = original->begin_iter ();
enter (&it.first, zstart, zstop);
} else {
enter (0, zstart, zstop);
} }
enter (iter, zstart, zstop);
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
render_edges (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); render_edges (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop);
} }
@ -693,15 +693,15 @@ D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double z
void void
D25ViewWidget::entry (const db::EdgePairs &data, double dbu, double zstart, double zstop) D25ViewWidget::entry (const db::EdgePairs &data, double dbu, double zstart, double zstop)
{ {
// try to establish a default color from the region's origin if required
const db::RecursiveShapeIterator *iter = 0;
const db::OriginalLayerEdgePairs *original = dynamic_cast<const db::OriginalLayerEdgePairs *> (data.delegate ()); const db::OriginalLayerEdgePairs *original = dynamic_cast<const db::OriginalLayerEdgePairs *> (data.delegate ());
if (original) { if (original) {
iter = original->iter (); // try to establish a default color from the region's origin if required
auto it = original->begin_iter ();
enter (&it.first, zstart, zstop);
} else {
enter (0, zstart, zstop);
} }
enter (iter, zstart, zstop);
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
render_edge_pairs (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); render_edge_pairs (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop);
} }

View File

@ -11620,7 +11620,7 @@ class DEdgePair:
... ...
... ...
class DEdgePairWithProperties(EdgePair): class DEdgePairWithProperties(DEdgePair):
r""" r"""
@brief A DEdgePair object with properties attached. @brief A DEdgePair object with properties attached.
This class represents a combination of a DEdgePair object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. This class represents a combination of a DEdgePair object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly.
@ -11685,7 +11685,7 @@ class DEdgePairWithProperties(EdgePair):
@brief Returns a string representing the polygon @brief Returns a string representing the polygon
""" """
... ...
def _assign(self, other: EdgePair) -> None: def _assign(self, other: DEdgePair) -> None:
r""" r"""
@brief Assigns another object to self @brief Assigns another object to self
""" """
@ -15609,8 +15609,7 @@ class DText:
Setter: Setter:
@brief Sets the horizontal alignment @brief Sets the horizontal alignment
This property specifies how the text is aligned relative to the anchor point. This is the version accepting integer values. It's provided for backward compatibility.
This property has been introduced in version 0.22 and extended to enums in 0.28.
""" """
size: float size: float
r""" r"""
@ -15646,8 +15645,7 @@ class DText:
Setter: Setter:
@brief Sets the vertical alignment @brief Sets the vertical alignment
This property specifies how the text is aligned relative to the anchor point. This is the version accepting integer values. It's provided for backward compatibility.
This property has been introduced in version 0.22 and extended to enums in 0.28.
""" """
x: float x: float
r""" r"""
@ -25332,13 +25330,15 @@ class EdgePairs(ShapeCollection):
@brief Creates a copy of self @brief Creates a copy of self
""" """
... ...
def __getitem__(self, n: int) -> EdgePair: def __getitem__(self, n: int) -> Any:
r""" r"""
@brief Returns the nth edge pair @brief Returns the nth edge pair
This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. those for which \has_valid_edge_pairs? is true. Use \flatten to explicitly flatten an edge pair collection. This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. those for which \has_valid_edge_pairs? is true. Use \flatten to explicitly flatten an edge pair collection.
The \each iterator is the more general approach to access the edge pairs. The \each iterator is the more general approach to access the edge pairs.
Since version 0.30.1, this method returns a \EdgePairWithProperties object.
""" """
... ...
def __iadd__(self, other: EdgePairs) -> EdgePairs: def __iadd__(self, other: EdgePairs) -> EdgePairs:
@ -29095,7 +29095,7 @@ class Edges(ShapeCollection):
@brief Creates a copy of self @brief Creates a copy of self
""" """
... ...
def __getitem__(self, n: int) -> Edge: def __getitem__(self, n: int) -> Any:
r""" r"""
@brief Returns the nth edge of the collection @brief Returns the nth edge of the collection
@ -29103,6 +29103,8 @@ class Edges(ShapeCollection):
This method returns the raw edge (not merged edges, even if merged semantics is enabled). This method returns the raw edge (not merged edges, even if merged semantics is enabled).
The \each iterator is the more general approach to access the edges. The \each iterator is the more general approach to access the edges.
Since version 0.30.1, this method returns an \EdgeWithProperties object.
""" """
... ...
def __iadd__(self, other: Edges) -> Edges: def __iadd__(self, other: Edges) -> Edges:
@ -34907,11 +34909,11 @@ class Instance:
Starting with version 0.25 the displacement is of vector type. Starting with version 0.25 the displacement is of vector type.
Setter: Setter:
@brief Sets the displacement vector for the 'a' axis in micrometer units @brief Sets the displacement vector for the 'a' axis
Like \a= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. If the instance was not an array instance before it is made one.
This method has been introduced in version 0.25. This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type.
""" """
b: Vector b: Vector
r""" r"""
@ -34956,10 +34958,10 @@ class Instance:
Getter: Getter:
@brief Gets the basic \CellInstArray object associated with this instance reference. @brief Gets the basic \CellInstArray object associated with this instance reference.
Setter: Setter:
@brief Changes the \CellInstArray object to the given one. @brief Returns the basic cell instance array object by giving a micrometer unit object.
This method replaces the instance by the given CellInstArray object. This method replaces the instance by the given CellInstArray object and it internally transformed into database units.
This method has been introduced in version 0.22 This method has been introduced in version 0.25
""" """
cplx_trans: ICplxTrans cplx_trans: ICplxTrans
r""" r"""
@ -35455,7 +35457,7 @@ class Instance:
r""" r"""
@brief Gets the layout this instance is contained in @brief Gets the layout this instance is contained in
This method has been introduced in version 0.22. This const version of the method has been introduced in version 0.25.
""" """
... ...
@overload @overload
@ -35463,7 +35465,7 @@ class Instance:
r""" r"""
@brief Gets the layout this instance is contained in @brief Gets the layout this instance is contained in
This const version of the method has been introduced in version 0.25. This method has been introduced in version 0.22.
""" """
... ...
def pcell_declaration(self) -> PCellDeclaration_Native: def pcell_declaration(self) -> PCellDeclaration_Native:
@ -47706,17 +47708,17 @@ class Netlist:
@overload @overload
def circuit_by_cell_index(self, cell_index: int) -> Circuit: def circuit_by_cell_index(self, cell_index: int) -> Circuit:
r""" r"""
@brief Gets the circuit object for a given cell index (const version). @brief Gets the circuit object for a given cell index.
If the cell index is not valid or no circuit is registered with this index, nil is returned. If the cell index is not valid or no circuit is registered with this index, nil is returned.
This constness variant has been introduced in version 0.26.8.
""" """
... ...
@overload @overload
def circuit_by_cell_index(self, cell_index: int) -> Circuit: def circuit_by_cell_index(self, cell_index: int) -> Circuit:
r""" r"""
@brief Gets the circuit object for a given cell index. @brief Gets the circuit object for a given cell index (const version).
If the cell index is not valid or no circuit is registered with this index, nil is returned. If the cell index is not valid or no circuit is registered with this index, nil is returned.
This constness variant has been introduced in version 0.26.8.
""" """
... ...
@overload @overload
@ -47738,20 +47740,20 @@ class Netlist:
@overload @overload
def circuits_by_name(self, name_pattern: str) -> List[Circuit]: def circuits_by_name(self, name_pattern: str) -> List[Circuit]:
r""" r"""
@brief Gets the circuit objects for a given name filter (const version). @brief Gets the circuit objects for a given name filter.
The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern.
This method has been introduced in version 0.26.4.
This constness variant has been introduced in version 0.26.8.
""" """
... ...
@overload @overload
def circuits_by_name(self, name_pattern: str) -> List[Circuit]: def circuits_by_name(self, name_pattern: str) -> List[Circuit]:
r""" r"""
@brief Gets the circuit objects for a given name filter. @brief Gets the circuit objects for a given name filter (const version).
The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern.
This method has been introduced in version 0.26.4.
This constness variant has been introduced in version 0.26.8.
""" """
... ...
def combine_devices(self) -> None: def combine_devices(self) -> None:
@ -47996,7 +47998,7 @@ class Netlist:
@overload @overload
def top_circuit(self) -> Circuit: def top_circuit(self) -> Circuit:
r""" r"""
@brief Gets the top circuit. @brief Gets the top circuit (const version).
This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit.
This convenience method has been added in version 0.29.5. This convenience method has been added in version 0.29.5.
@ -48005,7 +48007,7 @@ class Netlist:
@overload @overload
def top_circuit(self) -> Circuit: def top_circuit(self) -> Circuit:
r""" r"""
@brief Gets the top circuit (const version). @brief Gets the top circuit.
This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit.
This convenience method has been added in version 0.29.5. This convenience method has been added in version 0.29.5.
@ -58384,7 +58386,7 @@ class Region(ShapeCollection):
@brief Creates a copy of self @brief Creates a copy of self
""" """
... ...
def __getitem__(self, n: int) -> Polygon: def __getitem__(self, n: int) -> Any:
r""" r"""
@brief Returns the nth polygon of the region @brief Returns the nth polygon of the region
@ -58392,6 +58394,8 @@ class Region(ShapeCollection):
This method returns the raw polygon (not merged polygons, even if merged semantics is enabled). This method returns the raw polygon (not merged polygons, even if merged semantics is enabled).
The \each iterator is the more general approach to access the polygons. The \each iterator is the more general approach to access the polygons.
Since version 0.30.1, this method returns a \PolygonWithProperties object.
""" """
... ...
def __iadd__(self, other: Region) -> Region: def __iadd__(self, other: Region) -> Region:
@ -63169,11 +63173,10 @@ class Shape:
Starting with version 0.23, this method returns nil, if the shape does not represent an edge. Starting with version 0.23, this method returns nil, if the shape does not represent an edge.
Setter: Setter:
@brief Replaces the shape by the given edge @brief Replaces the shape by the given edge (in micrometer units)
This method replaces the shape by the given edge. This method can only be called for editable layouts. It does not change the user properties of the shape. This method replaces the shape by the given edge, like \edge= with a \Edge argument does. This version translates the edge from micrometer units to database units internally.
Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes.
This method has been introduced in version 0.22. This method has been introduced in version 0.25.
""" """
edge_pair: Any edge_pair: Any
r""" r"""
@ -63377,10 +63380,11 @@ class Shape:
Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical primitive that can be converted to a simple polygon. Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical primitive that can be converted to a simple polygon.
Setter: Setter:
@brief Replaces the shape by the given simple polygon (in micrometer units) @brief Replaces the shape by the given simple polygon object
This method replaces the shape by the given text, like \simple_polygon= with a \SimplePolygon argument does. This version translates the polygon from micrometer units to database units internally. This method replaces the shape by the given simple polygon object. This method can only be called for editable layouts. It does not change the user properties of the shape.
Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes.
This method has been introduced in version 0.25. This method has been introduced in version 0.22.
""" """
text: Any text: Any
r""" r"""
@ -68247,8 +68251,7 @@ class Text:
Setter: Setter:
@brief Sets the horizontal alignment @brief Sets the horizontal alignment
This property specifies how the text is aligned relative to the anchor point. This is the version accepting integer values. It's provided for backward compatibility.
This property has been introduced in version 0.22 and extended to enums in 0.28.
""" """
size: int size: int
r""" r"""
@ -70054,13 +70057,15 @@ class Texts(ShapeCollection):
@brief Creates a copy of self @brief Creates a copy of self
""" """
... ...
def __getitem__(self, n: int) -> Text: def __getitem__(self, n: int) -> Any:
r""" r"""
@brief Returns the nth text @brief Returns the nth text
This method returns nil if the index is out of range. It is available for flat texts only - i.e. those for which \has_valid_texts? is true. Use \flatten to explicitly flatten an text collection. This method returns nil if the index is out of range. It is available for flat texts only - i.e. those for which \has_valid_texts? is true. Use \flatten to explicitly flatten an text collection.
The \each iterator is the more general approach to access the texts. The \each iterator is the more general approach to access the texts.
Since version 0.30.1, this method returns a \TextWithProperties object.
""" """
... ...
def __iadd__(self, other: Texts) -> Texts: def __iadd__(self, other: Texts) -> Texts:

View File

@ -655,6 +655,14 @@ class RdbItem:
""" """
... ...
@overload @overload
def add_value(self, value: db.DText) -> None:
r"""
@brief Adds a text object to the values of this item
@param value The text to add.
This method has been introduced in version 0.30.1 to support text objects with properties.
"""
...
@overload
def add_value(self, value: float) -> None: def add_value(self, value: float) -> None:
r""" r"""
@brief Adds a numeric value to the values of this item @brief Adds a numeric value to the values of this item

View File

@ -2908,7 +2908,9 @@ class Timer:
r""" r"""
@brief Gets the current memory usage of the process in Bytes @brief Gets the current memory usage of the process in Bytes
This method has been introduced in version 0.27. The returned value is the resident memory size on Linux and MacOS and the working set size on Windows.
This method has been introduced in version 0.27. The value has been changed to be resident size (instead of virtual size) on Linux in version 0.30.
""" """
... ...
@classmethod @classmethod

View File

@ -146,7 +146,7 @@ public:
m_cell_stack.pop_back (); m_cell_stack.pop_back ();
} }
virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/)
{ {
db::cell_index_type ci = inst.object ().cell_index (); db::cell_index_type ci = inst.object ().cell_index ();
if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) { if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) {

View File

@ -25,6 +25,7 @@
#include "tlLog.h" #include "tlLog.h"
#include "tlInternational.h" #include "tlInternational.h"
#include "tlEnv.h" #include "tlEnv.h"
#include "tlGlobPattern.h"
#include <cctype> #include <cctype>
#include <fstream> #include <fstream>
@ -430,6 +431,63 @@ std::vector<std::string> dir_entries (const std::string &s, bool with_files, boo
return ee; return ee;
} }
static void glob_partial (const std::string &where, std::vector<std::string>::const_iterator pfrom, std::vector<std::string>::const_iterator pto, std::vector<std::string> &res)
{
if (pfrom == pto) {
if (! is_dir (where)) {
res.push_back (where);
}
return;
}
auto p = where + *pfrom;
if (file_exists (p)) {
glob_partial (p, pfrom + 1, pto, res);
return;
}
if (tl::trimmed_part (*pfrom) == "**") {
if (pfrom + 1 == pto) {
// a glob pattern can't be "**" without anything after that
return;
}
auto subdirs = dir_entries (where, false, true, true);
for (auto s = subdirs.begin (); s != subdirs.end (); ++s) {
glob_partial (combine_path (where, *s), pfrom, pto, res);
}
++pfrom;
}
#if defined(_WIN32)
if (where.empty ()) {
// On Windows, we cannot iterate the drives
std::string root = *pfrom;
++pfrom;
glob_partial (root, pfrom, pto, res);
return;
}
#endif
tl::GlobPattern glob (tl::trimmed_part (*pfrom));
++pfrom;
auto entries = dir_entries (where, true, true, true);
for (auto e = entries.begin (); e != entries.end (); ++e) {
if (glob.match (*e)) {
glob_partial (combine_path (where, *e), pfrom, pto, res);
}
}
}
std::vector<std::string> glob_expand (const std::string &path)
{
auto apath = absolute_file_path (path);
auto parts = split_path (apath);
std::vector<std::string> res;
glob_partial (std::string (), parts.begin (), parts.end (), res);
return res;
}
bool mkdir (const std::string &path) bool mkdir (const std::string &path)
{ {
#if defined(_WIN32) #if defined(_WIN32)

View File

@ -138,6 +138,14 @@ bool TL_PUBLIC is_dir (const std::string &s);
*/ */
std::vector<std::string> TL_PUBLIC dir_entries (const std::string &s, bool with_files = true, bool with_dirs = true, bool without_dotfiles = false); std::vector<std::string> TL_PUBLIC dir_entries (const std::string &s, bool with_files = true, bool with_dirs = true, bool without_dotfiles = false);
/**
* @brief Expands a glob pattern into a set of files
*
* This version supports "**" for recursive directory expansion.
* Apart from that the features of tl::GlobPattern are supported.
*/
std::vector<std::string> TL_PUBLIC glob_expand (const std::string &path);
/** /**
* @brief Rename the given file * @brief Rename the given file
*/ */

View File

@ -995,3 +995,94 @@ TEST (24)
EXPECT_EQ (tl::file_exists (p), false); EXPECT_EQ (tl::file_exists (p), false);
} }
// glob_expand
TEST (25)
{
tl::TemporaryDirectory tmpdir ("tl_tests");
auto p = tmpdir.path ();
auto ad = tl::combine_path (p, "a");
tl::mkpath (ad);
auto aad = tl::combine_path (ad, "a");
tl::mkpath (aad);
auto aaad = tl::combine_path (aad, "a");
tl::mkpath (aaad);
auto bd = tl::combine_path (p, "b");
tl::mkpath (bd);
{
std::ofstream os (tl::combine_path (ad, "test.txt"));
os << "A test";
os.close ();
}
{
std::ofstream os (tl::combine_path (aad, "test.txt"));
os << "A test";
os.close ();
}
{
std::ofstream os (tl::combine_path (aaad, "test.txt"));
os << "A test";
os.close ();
}
{
std::ofstream os (tl::combine_path (aaad, "test2.txt"));
os << "A test";
os.close ();
}
{
std::ofstream os (tl::combine_path (bd, "test.txt"));
os << "A test";
os.close ();
}
{
std::ofstream os (tl::combine_path (p, "test2.txt"));
os << "A test";
os.close ();
}
std::vector<std::string> au;
auto res = tl::glob_expand (tl::combine_path (p, "*.txt"));
au.push_back (tl::combine_path (p, "test2.txt"));
std::sort (res.begin (), res.end ());
std::sort (au.begin (), au.end ());
EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n"));
res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*.txt"));
au.clear ();
au.push_back (tl::combine_path (p, "test2.txt"));
au.push_back (tl::combine_path (ad, "test.txt"));
au.push_back (tl::combine_path (aad, "test.txt"));
au.push_back (tl::combine_path (aaad, "test.txt"));
au.push_back (tl::combine_path (aaad, "test2.txt"));
au.push_back (tl::combine_path (bd, "test.txt"));
std::sort (res.begin (), res.end ());
std::sort (au.begin (), au.end ());
EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n"));
res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*2.txt"));
au.clear ();
au.push_back (tl::combine_path (p, "test2.txt"));
au.push_back (tl::combine_path (aaad, "test2.txt"));
std::sort (res.begin (), res.end ());
std::sort (au.begin (), au.end ());
EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n"));
res = tl::glob_expand (tl::combine_path (tl::combine_path (tl::combine_path (p, "**"), "a"), "*2.txt"));
au.clear ();
au.push_back (tl::combine_path (aaad, "test2.txt"));
std::sort (res.begin (), res.end ());
std::sort (au.begin (), au.end ());
EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n"));
}

BIN
testdata/bd/strmxor_au7d.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strmxor_covered1.gds vendored Normal file

Binary file not shown.

BIN
testdata/bd/strmxor_covered2.gds vendored Normal file

Binary file not shown.

View File

@ -113,7 +113,7 @@ Warning: Layer 2/0 is not present in first layout, but in second
Result summary (layers without differences are not shown): Result summary (layers without differences are not shown):
Layer Output Differences (shape count) Layer Output Differences (shape count)
------------------------------------------------------- ----------------------------------------------------------------
1/0 - (no such layer in second layout) 1/0 - (no such layer in second layout)
2/0 - (no such layer in first layout) 2/0 - (no such layer in first layout)

26
testdata/drc/drcSimpleTests_94.drc vendored Normal file
View File

@ -0,0 +1,26 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l1.output(1, 0)
l2.output(2, 0)
x = l1 ^ l2
# we detect point-like polygons here by explicitly
# iterating. The enlargement makes sure, we have something
# to write to the output layout.
boxes = x.data.each.collect do |p|
RBA::Box::new(p.bbox.enlarged(10, 10))
end
x.data = RBA::Region::new(boxes)
x.output(10, 0)

BIN
testdata/drc/drcSimpleTests_94.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au94.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au94d.gds vendored Normal file

Binary file not shown.

View File

@ -2,10 +2,10 @@
# This script is sourced to define the main version parameters # This script is sourced to define the main version parameters
# The main version # The main version
KLAYOUT_VERSION="0.30.0" KLAYOUT_VERSION="0.30.1"
# The version used for PyPI (don't use variables here!) # The version used for PyPI (don't use variables here!)
KLAYOUT_PYPI_VERSION="0.30.0" KLAYOUT_PYPI_VERSION="0.30.1"
# The build date # The build date
KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d") KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")