mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' into feature/maly
This commit is contained in:
commit
282f6e9d23
17
Changelog
17
Changelog
|
|
@ -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):
|
||||
* Bug: %GITHUB%/issues/1996 More robust triangulation
|
||||
* Bug: %GITHUB%/issues/2002 Path to polygon conversion issue
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
* New features and bugfixes
|
||||
|
|
|
|||
|
|
@ -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 "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, "QTimeZone")
|
||||
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_neqlt(self, "QUrl")
|
||||
add_native_operator_neq(self, "QUrlQuery")
|
||||
add_native_operator_neq(self, "QDomNodeList")
|
||||
add_native_operator_neq(self, "QXmlStreamAttribute")
|
||||
add_native_operator_neq(self, "QXmlStreamEntityDeclaration")
|
||||
add_native_operator_neq(self, "QXmlStreamNamespaceDeclaration")
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "dbReader.h"
|
||||
#include "dbWriter.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
namespace bd
|
||||
{
|
||||
|
|
@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format)
|
|||
|
||||
db::Layout layout;
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
{
|
||||
db::LoadLayoutOptions load_options;
|
||||
generic_reader_options.configure (load_options);
|
||||
|
|
|
|||
|
|
@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions ()
|
|||
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_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
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbSaveLayoutOptions.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
|
||||
struct ClipData
|
||||
|
|
@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[])
|
|||
|
||||
cmd.parse (argc, argv);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
clip (data);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "dbLayoutDiff.h"
|
||||
#include "dbReader.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
db::Layout layout_a;
|
||||
db::Layout layout_b;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "tlLog.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlTimer.h"
|
||||
#include "rba.h"
|
||||
#include "pya.h"
|
||||
#include "gsi.h"
|
||||
|
|
@ -97,5 +98,8 @@ BD_PUBLIC int strmrun (int argc, char *argv[])
|
|||
lym::Macro macro;
|
||||
macro.load_from (script);
|
||||
macro.set_file_path (script);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total")));
|
||||
|
||||
return macro.run ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#include "gsiExpression.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlThreads.h"
|
||||
#include "tlThreadedWorkers.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -319,7 +321,8 @@ struct XORData
|
|||
dont_summarize_missing_layers (false), silent (false), no_summary (false),
|
||||
threads (0),
|
||||
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;
|
||||
|
|
@ -336,6 +339,8 @@ struct XORData
|
|||
db::cell_index_type output_cell;
|
||||
std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc> l2l_map;
|
||||
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_b;
|
||||
|
||||
|
|
@ -572,14 +579,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
if (! silent && ! no_summary) {
|
||||
|
||||
if (result) {
|
||||
tl::info << "No differences found";
|
||||
tl::info << tl::to_string (tr ("No differences found"));
|
||||
} else {
|
||||
|
||||
const char *line_format = " %-10s %-12s %s";
|
||||
const char *sep = " -------------------------------------------------------";
|
||||
|
||||
tl::info << "Result summary (layers without differences are not shown):" << tl::endl;
|
||||
tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep;
|
||||
std::string headline;
|
||||
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;
|
||||
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) {
|
||||
ti = r->first.first;
|
||||
if (tolerances[ti] > db::epsilon) {
|
||||
tl::info << tl::endl << "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 << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl;
|
||||
tl::info << headline << tl::endl << sep;
|
||||
}
|
||||
}
|
||||
|
||||
std::string out ("-");
|
||||
std::string value;
|
||||
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) {
|
||||
value = "(no such layer in second layout)";
|
||||
value = tl::to_string (tr ("(no such layer in second layout)"));
|
||||
} else if (! r->second.is_empty ()) {
|
||||
if (r->second.layer_output >= 0 && r->second.layout) {
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
// 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 ());
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
|
|
@ -779,98 +953,18 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
xor_data.output_layout->dbu (dbu);
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
int index = 1;
|
||||
XORJob job (xor_data.threads);
|
||||
|
||||
for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) {
|
||||
|
||||
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;
|
||||
|
||||
job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
result = r->second.is_empty ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ TEST(1A_Flat)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 30\n"
|
||||
" 6/0 6/0 41\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"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 3\n"
|
||||
" 6/0 6/0 314\n"
|
||||
" 8/1 8/1 1\n"
|
||||
|
|
@ -177,7 +177,7 @@ TEST(1B_Flat)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" 6/0 - 41\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"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 3\n"
|
||||
" 6/0 - 314\n"
|
||||
" 8/1 - 1\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"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 31\n"
|
||||
" 6/0 - 217\n"
|
||||
" 8/1 - 168\n"
|
||||
|
|
@ -483,7 +483,7 @@ TEST(3_FlatCountHeal)
|
|||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (shape count)\n"
|
||||
" -------------------------------------------------------\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 30\n"
|
||||
" 6/0 - 41\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"
|
||||
);
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
trans_type m_trans;
|
||||
basic_array_iterator <Coord> *mp_base;
|
||||
|
|
|
|||
|
|
@ -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_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;
|
||||
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);
|
||||
|
|
@ -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);
|
||||
new_cells.push_back (new_cell);
|
||||
new_cells_b.push_back (*b);
|
||||
all_a2b.push_back (std::make_pair (new_cell, *b));
|
||||
|
||||
if (mapped_pairs) {
|
||||
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
|
||||
db::LayoutLocker locker (&layout_a);
|
||||
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
|
||||
db::LayoutLocker locker (&layout_a);
|
||||
|
||||
// Create instances for the new cells in layout A according to their instantiation in layout B
|
||||
double mag = layout_b.dbu () / layout_a.dbu ();
|
||||
for (size_t i = 0; i < new_cells.size (); ++i) {
|
||||
// Create instances for the new cells in layout A according to their instantiation in layout B
|
||||
double mag = layout_b.dbu () / layout_a.dbu ();
|
||||
for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) {
|
||||
|
||||
const db::Cell &b = layout_b.cell (new_cells_b [i]);
|
||||
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
|
||||
const db::Cell &b = layout_b.cell (i->second);
|
||||
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 ();
|
||||
bci.object ().cell_index (new_cells [i]);
|
||||
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);
|
||||
}
|
||||
db::CellInstArray bci = bi.cell_inst ();
|
||||
bci.object ().cell_index (i->first);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
|
|||
{
|
||||
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
|
||||
|
||||
if (empty ()) {
|
||||
|
||||
// Nothing to do
|
||||
return other.delegate ()->clone ();
|
||||
|
||||
} else if (other.empty ()) {
|
||||
if (other.empty ()) {
|
||||
|
||||
// Nothing to do
|
||||
return clone ();
|
||||
|
|
@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
|
|||
|
||||
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)) {
|
||||
|
||||
return new DeepRegion (deep_layer ().derived ());
|
||||
|
|
|
|||
|
|
@ -1321,7 +1321,7 @@ struct edge_xmin_at_yinterval_double_compare
|
|||
{
|
||||
if (edge_xmax (a) < edge_xmin (b)) {
|
||||
return true;
|
||||
} else if (edge_xmin (a) >= edge_xmax (b)) {
|
||||
} else if (edge_xmin (a) > edge_xmax (b)) {
|
||||
return false;
|
||||
} else {
|
||||
C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
: 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);
|
||||
reset ();
|
||||
}
|
||||
|
||||
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);
|
||||
reset ();
|
||||
}
|
||||
|
||||
HierarchyBuilder::~HierarchyBuilder ()
|
||||
|
|
@ -178,6 +180,8 @@ void
|
|||
HierarchyBuilder::reset ()
|
||||
{
|
||||
m_initial_pass = true;
|
||||
m_cm_new_entry = false;
|
||||
|
||||
mp_initial_cell = 0;
|
||||
|
||||
m_cells_to_be_filled.clear ();
|
||||
|
|
@ -186,7 +190,6 @@ HierarchyBuilder::reset ()
|
|||
m_cells_seen.clear ();
|
||||
m_cell_stack.clear ();
|
||||
m_cm_entry = null_iterator;
|
||||
m_cm_new_entry = false;
|
||||
}
|
||||
|
||||
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 ()) {
|
||||
cn += "$CLIP_VAR";
|
||||
description += "CLIP";
|
||||
|
||||
}
|
||||
if (key.inactive) {
|
||||
cn += "$DIS";
|
||||
|
|
@ -383,7 +385,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
|
|||
}
|
||||
|
||||
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) {
|
||||
|
||||
|
|
@ -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.
|
||||
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 {
|
||||
|
||||
|
|
@ -413,7 +415,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
|
|||
}
|
||||
|
||||
bool
|
||||
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, 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 ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes)
|
||||
{
|
||||
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 ();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -294,8 +294,8 @@ public:
|
|||
virtual void end (const RecursiveShapeIterator *iter);
|
||||
virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region);
|
||||
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 ®ion, 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 ®ion, 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 ®ion, 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 ®ion, 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 ®ion, const box_tree_type *complex_region);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db
|
|||
void PolygonRefToShapesGenerator::put (const db::Polygon &polygon)
|
||||
{
|
||||
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));
|
||||
} else {
|
||||
mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
|
||||
|
|
@ -58,7 +60,9 @@ PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size
|
|||
void
|
||||
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;
|
||||
db::split_polygon (poly, split_polygons);
|
||||
|
|
|
|||
|
|
@ -1771,6 +1771,14 @@ public:
|
|||
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
|
||||
*/
|
||||
|
|
@ -1879,6 +1887,7 @@ public:
|
|||
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);
|
||||
}
|
||||
m_bbox = m_ctrs [0].bbox ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -2804,6 +2813,7 @@ public:
|
|||
{
|
||||
// compress the polygon by employing the transform method
|
||||
m_hull.transform (db::unit_trans<C> (), true, remove_reflected);
|
||||
m_bbox = m_hull.bbox ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -3022,6 +3032,14 @@ public:
|
|||
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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI
|
|||
m_layer = d.m_layer;
|
||||
mp_cell = d.mp_cell;
|
||||
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_trans = d.m_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_region_stack = d.m_local_region_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_inst_quad_id = d.m_inst_quad_id;
|
||||
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_skip_shapes_stack.clear ();
|
||||
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 ();
|
||||
if (mp_complex_region.get ()) {
|
||||
|
|
@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const
|
|||
bool
|
||||
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);
|
||||
|
||||
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_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 ()) {
|
||||
|
||||
|
|
@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const
|
|||
m_inst = m_inst_iterators.back ();
|
||||
m_inst_array = m_inst_array_iterators.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_array_iterators.pop_back ();
|
||||
m_inst_quad_id_stack.pop_back ();
|
||||
|
|
@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const
|
|||
m_cells.pop_back ();
|
||||
m_local_region_stack.pop_back ();
|
||||
m_skip_shapes_stack.pop_back ();
|
||||
m_skip_shapes_member_stack.pop_back ();
|
||||
if (! m_local_complex_region_stack.empty ()) {
|
||||
m_local_complex_region_stack.pop_back ();
|
||||
}
|
||||
|
|
@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const
|
|||
void
|
||||
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 ();
|
||||
} 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);
|
||||
|
|
@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
|
|||
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
|
||||
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 ();
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
|
|
@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
|
|||
|
||||
// skip instance array members not part of the complex region
|
||||
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)) {
|
||||
break;
|
||||
} else {
|
||||
|
|
@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
|
|||
|
||||
}
|
||||
|
||||
while (! m_inst_array.at_end () && receiver) {
|
||||
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;
|
||||
} else {
|
||||
++m_inst_array;
|
||||
m_skip_shapes_member = false;
|
||||
|
||||
while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -868,6 +868,7 @@ private:
|
|||
mutable unsigned int m_layer;
|
||||
mutable const cell_type *mp_cell;
|
||||
mutable size_t m_current_layer;
|
||||
mutable bool m_skip_shapes, m_skip_shapes_member;
|
||||
mutable shape_iterator m_shape;
|
||||
mutable cplx_trans_type m_trans;
|
||||
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<box_tree_type> m_local_complex_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 size_t m_inst_quad_id;
|
||||
mutable std::vector<size_t> m_inst_quad_id_stack;
|
||||
|
|
@ -899,6 +900,8 @@ private:
|
|||
bool down (RecursiveShapeReceiver *receiver) 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;
|
||||
|
||||
void set_inactive (bool a) const
|
||||
|
|
@ -1013,8 +1016,11 @@ public:
|
|||
* - NI_all: iterate all members through "new_inst_member"
|
||||
* - NI_single: iterate a single member (the first one)
|
||||
* - 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
|
||||
|
|
@ -1026,8 +1032,11 @@ public:
|
|||
* "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.
|
||||
*
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
const TR &subject = interactions.subject_shape (i->first);
|
||||
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) {
|
||||
result.insert (subject);
|
||||
db::Polygon poly;
|
||||
subject.instantiate (poly);
|
||||
splitter.put (poly);
|
||||
}
|
||||
|
||||
} else if (i->second.empty ()) {
|
||||
|
||||
// shortcut (not: keep, and: drop)
|
||||
// Note that we still normalize and split the polygon, so we get a uniform
|
||||
// behavior.
|
||||
if (! m_is_and) {
|
||||
result.insert (subject);
|
||||
db::Polygon poly;
|
||||
subject.instantiate (poly);
|
||||
splitter.put (poly);
|
||||
}
|
||||
|
||||
} else {
|
||||
for (auto e = subject.begin_edge (); ! e.at_end(); ++e) {
|
||||
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::polygon_ref_generator<TR> pr (layout, result);
|
||||
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
|
||||
db::PolygonGenerator pg (splitter, true, true);
|
||||
ep.set_base_verbosity (50);
|
||||
ep.process (pg, op);
|
||||
|
|
|
|||
|
|
@ -485,8 +485,41 @@ TEST(7)
|
|||
cib.push_back (b1.cell_index ());
|
||||
cib.push_back (b2.cell_index ());
|
||||
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:");
|
||||
}
|
||||
|
||||
// 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:");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ TEST(1)
|
|||
|
||||
EXPECT_EQ (empty == p, true);
|
||||
EXPECT_EQ (p.is_box (), false);
|
||||
EXPECT_EQ (p.is_empty (), true);
|
||||
|
||||
std::vector <db::Point> c1, c2, c3;
|
||||
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, 0));
|
||||
p.assign_hull (c1.begin (), c1.end ());
|
||||
EXPECT_EQ (p.is_empty (), false);
|
||||
b = p.box ();
|
||||
EXPECT_EQ (p.holes (), size_t (0));
|
||||
EXPECT_EQ (p.area (), 1000*100);
|
||||
|
|
@ -1404,3 +1406,30 @@ TEST(28)
|
|||
db::Polygon b (db::Box (-1000000000, -1000000000, 1000000000, 1000000000));
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -756,15 +756,25 @@ namespace {
|
|||
: public db::RecursiveShapeReceiver
|
||||
{
|
||||
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*/)
|
||||
{
|
||||
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:
|
||||
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";
|
||||
}
|
||||
|
||||
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 ());
|
||||
if (all) {
|
||||
|
|
@ -1048,7 +1058,7 @@ public:
|
|||
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);
|
||||
if (all) {
|
||||
|
|
@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray
|
|||
public:
|
||||
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 ®ion, 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 ®ion, 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;
|
||||
}
|
||||
|
||||
|
|
@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne
|
|||
public:
|
||||
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 ®ion, 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 ®ion, 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;
|
||||
}
|
||||
|
||||
|
|
@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance
|
|||
public:
|
||||
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 ®ion, 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 ®ion, 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;
|
||||
}
|
||||
|
||||
|
|
@ -1586,49 +1596,174 @@ TEST(12_ForMerged)
|
|||
db::RecursiveShapeIterator i1 (*g, c0, 0);
|
||||
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 (collect_with_copy(i1, *g), x);
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
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;
|
||||
lv.push_back (0);
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, lv);
|
||||
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 (collect_with_copy(i1, *g), x);
|
||||
|
||||
i1.set_for_merged_input (true);
|
||||
x = collect(i1, *g);
|
||||
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
|
||||
i1 = db::RecursiveShapeIterator (*g, c0, lv);
|
||||
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 (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);
|
||||
x = collect(i1, *g);
|
||||
// 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 (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));
|
||||
x = collect(i1, *g);
|
||||
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);
|
||||
x = collect(i1, *g);
|
||||
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.set_overlapping (true);
|
||||
x = collect(i1, *g);
|
||||
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);
|
||||
x = collect(i1, *g);
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1585,7 +1585,8 @@ module DRC
|
|||
self._context("report") do
|
||||
|
||||
# finish what we got so far
|
||||
_finish(false)
|
||||
view = RBA::LayoutView::current
|
||||
@def_output && @def_output.finish(false, view)
|
||||
|
||||
@def_output = nil
|
||||
@def_output = _make_report(description, filename, cellname)
|
||||
|
|
|
|||
|
|
@ -1694,6 +1694,16 @@ TEST(93d_withAngle)
|
|||
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)
|
||||
{
|
||||
run_test (_this, "100", false);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,31 @@
|
|||
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)
|
||||
{
|
||||
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);
|
||||
if (le) {
|
||||
le->blockSignals (true);
|
||||
le->setText (value.to_qstring ());
|
||||
le->setText (tl::to_qstring (variant_list_to_string (value)));
|
||||
le->blockSignals (false);
|
||||
}
|
||||
}
|
||||
|
|
@ -905,8 +930,29 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool
|
|||
{
|
||||
QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
|
||||
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 ()));
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@
|
|||
|
||||
// -----------------------------------------------------------------------
|
||||
// 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()
|
||||
|
||||
|
|
@ -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 &)
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -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 ("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 ("!=", "@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 ("==", "@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);
|
||||
return methods;
|
||||
}
|
||||
|
||||
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 (),
|
||||
"@qt\n@brief Binding of QDomNodeList");
|
||||
|
||||
|
|
|
|||
|
|
@ -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_style1 ("grid-style1");
|
||||
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_micron ("grid-micron");
|
||||
static const std::string cfg_grid_show_ruler ("grid-show-ruler");
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>483</width>
|
||||
<height>341</height>
|
||||
<height>361</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
|
@ -59,6 +59,23 @@
|
|||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</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">
|
||||
<widget class="QComboBox" name="style1_cbx">
|
||||
<item>
|
||||
|
|
@ -108,7 +125,122 @@
|
|||
</item>
|
||||
</widget>
|
||||
</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">
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
|
|
@ -118,14 +250,17 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="5">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<item row="7" 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="6" column="2" colspan="2">
|
||||
<item row="7" column="2" colspan="2">
|
||||
<widget class="QComboBox" name="style0_cbx">
|
||||
<item>
|
||||
<property name="text">
|
||||
|
|
@ -169,30 +304,23 @@
|
|||
</item>
|
||||
</widget>
|
||||
</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="10" column="2">
|
||||
<widget class="QPushButton" name="grid_ruler_color_pb">
|
||||
<item row="8" column="1">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string/>
|
||||
<string>Color</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="5">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<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>
|
||||
|
|
@ -245,141 +373,37 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<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="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">
|
||||
<item row="9" column="0" colspan="5">
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ namespace lay
|
|||
// ------------------------------------------------------------
|
||||
// Helper functions to get and set the configuration
|
||||
|
||||
int default_density = 4;
|
||||
|
||||
static struct {
|
||||
lay::GridNet::GridStyle style;
|
||||
const char *string;
|
||||
|
|
@ -79,6 +81,20 @@ GridNetStyleConverter::to_string (lay::GridNet::GridStyle style)
|
|||
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
|
||||
|
||||
|
|
@ -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_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_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_show_ruler, tl::to_string (true)));
|
||||
// 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),
|
||||
mp_view (view),
|
||||
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 ..
|
||||
}
|
||||
|
|
@ -175,6 +193,12 @@ GridNet::configure (const std::string &name, const std::string &value)
|
|||
GridNetStyleConverter ().from_string (value, 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) {
|
||||
|
||||
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
|
||||
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);
|
||||
GridStyle style = m_style1;
|
||||
|
||||
// compute major grid and switch to secondary style if necessary
|
||||
int s = 0;
|
||||
while (dgrid < fwr * 4) {
|
||||
while (dgrid < threshold) {
|
||||
if (s == 0) {
|
||||
dgrid *= 2.0;
|
||||
} 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 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
|
||||
if (style == Dots || style == TenthDottedLines ||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ private:
|
|||
GridStyle m_style0;
|
||||
GridStyle m_style1;
|
||||
GridStyle m_style2;
|
||||
int m_density;
|
||||
};
|
||||
|
||||
class GridNetStyleConverter
|
||||
|
|
@ -96,6 +97,13 @@ public:
|
|||
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
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ GridNetConfigPage::setup (lay::Dispatcher *root)
|
|||
style = lay::GridNet::Invisible;
|
||||
root->config_get (cfg_grid_style2, style, GridNetStyleConverter ());
|
||||
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
|
||||
|
|
@ -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_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_density, mp_ui->grid_density_sb->value (), GridNetDensityConverter ());
|
||||
}
|
||||
|
||||
} // namespace lay
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace db
|
|||
// -----------------------------------------------------------------------------------
|
||||
// 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 ();
|
||||
|
||||
|
|
@ -64,19 +64,28 @@ std::string correct_path (const std::string &fn_in, const db::Layout &layout, co
|
|||
if (tech && ! tech->base_path ().empty ()) {
|
||||
std::string new_fn = tl::combine_path (tech->base_path (), 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 ()) {
|
||||
return tl::combine_path (base_path, fn);
|
||||
} else {
|
||||
return fn;
|
||||
fn = tl::combine_path (base_path, 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;
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ struct MacroDesc;
|
|||
* @brief Correct a path relative to the stream and technology
|
||||
*/
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
std::string lp = correct_path (*l, layout, base_path);
|
||||
|
||||
tl::InputStream lef_stream (lp);
|
||||
tl::log << tl::to_string (tr ("Reading")) << " " << lp;
|
||||
importer.read (lef_stream, layout, state);
|
||||
auto paths = correct_path (*l, layout, base_path, true);
|
||||
for (auto lp = paths.begin (); lp != paths.end (); ++lp) {
|
||||
tl::InputStream lef_stream (*lp);
|
||||
tl::log << tl::to_string (tr ("Reading")) << " " << *lp;
|
||||
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) {
|
||||
|
||||
std::string lp = correct_path (*l, layout, base_path);
|
||||
lef_files_read.insert (tl::normalize_path (lp));
|
||||
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 file: ")) + lp);
|
||||
if (lef_files_read.insert (tl::normalize_path (*lp)).second) {
|
||||
|
||||
tl::InputStream lef_stream (lp);
|
||||
tl::log << tl::to_string (tr ("Reading")) << " " << lp;
|
||||
importer.read_lef (lef_stream, layout, state);
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp);
|
||||
|
||||
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;
|
||||
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::log << tl::to_string (tr ("Reading")) << " " << lp;
|
||||
db::Layout *new_layout = new db::Layout (false);
|
||||
macro_layout_object_holder.push_back (new_layout);
|
||||
macro_layouts.push_back (new_layout);
|
||||
tl::InputStream macro_layout_stream (*lp);
|
||||
tl::log << tl::to_string (tr ("Reading")) << " " << *lp;
|
||||
db::Layout *new_layout = new db::Layout (false);
|
||||
macro_layout_object_holder.push_back (new_layout);
|
||||
macro_layouts.push_back (new_layout);
|
||||
|
||||
db::Reader reader (macro_layout_stream);
|
||||
reader.read (*new_layout, options);
|
||||
db::Reader reader (macro_layout_stream);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -661,15 +661,15 @@ D25ViewWidget::enter (const db::RecursiveShapeIterator *iter, double zstart, dou
|
|||
void
|
||||
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 ());
|
||||
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 ...")));
|
||||
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
|
||||
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 ());
|
||||
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 ...")));
|
||||
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
|
||||
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 ());
|
||||
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 ...")));
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11620,7 +11620,7 @@ class DEdgePair:
|
|||
...
|
||||
...
|
||||
|
||||
class DEdgePairWithProperties(EdgePair):
|
||||
class DEdgePairWithProperties(DEdgePair):
|
||||
r"""
|
||||
@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.
|
||||
|
|
@ -11685,7 +11685,7 @@ class DEdgePairWithProperties(EdgePair):
|
|||
@brief Returns a string representing the polygon
|
||||
"""
|
||||
...
|
||||
def _assign(self, other: EdgePair) -> None:
|
||||
def _assign(self, other: DEdgePair) -> None:
|
||||
r"""
|
||||
@brief Assigns another object to self
|
||||
"""
|
||||
|
|
@ -15609,8 +15609,7 @@ class DText:
|
|||
Setter:
|
||||
@brief Sets the horizontal alignment
|
||||
|
||||
This property specifies how the text is aligned relative to the anchor point.
|
||||
This property has been introduced in version 0.22 and extended to enums in 0.28.
|
||||
This is the version accepting integer values. It's provided for backward compatibility.
|
||||
"""
|
||||
size: float
|
||||
r"""
|
||||
|
|
@ -15646,8 +15645,7 @@ class DText:
|
|||
Setter:
|
||||
@brief Sets the vertical alignment
|
||||
|
||||
This property specifies how the text is aligned relative to the anchor point.
|
||||
This property has been introduced in version 0.22 and extended to enums in 0.28.
|
||||
This is the version accepting integer values. It's provided for backward compatibility.
|
||||
"""
|
||||
x: float
|
||||
r"""
|
||||
|
|
@ -25332,13 +25330,15 @@ class EdgePairs(ShapeCollection):
|
|||
@brief Creates a copy of self
|
||||
"""
|
||||
...
|
||||
def __getitem__(self, n: int) -> EdgePair:
|
||||
def __getitem__(self, n: int) -> Any:
|
||||
r"""
|
||||
@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.
|
||||
|
||||
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:
|
||||
|
|
@ -29095,7 +29095,7 @@ class Edges(ShapeCollection):
|
|||
@brief Creates a copy of self
|
||||
"""
|
||||
...
|
||||
def __getitem__(self, n: int) -> Edge:
|
||||
def __getitem__(self, n: int) -> Any:
|
||||
r"""
|
||||
@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).
|
||||
|
||||
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:
|
||||
|
|
@ -34907,11 +34909,11 @@ class Instance:
|
|||
|
||||
Starting with version 0.25 the displacement is of vector type.
|
||||
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
|
||||
r"""
|
||||
|
|
@ -34956,10 +34958,10 @@ class Instance:
|
|||
Getter:
|
||||
@brief Gets the basic \CellInstArray object associated with this instance reference.
|
||||
Setter:
|
||||
@brief Changes the \CellInstArray object to the given one.
|
||||
This method replaces the instance by the given CellInstArray object.
|
||||
@brief Returns the basic cell instance array object by giving a micrometer unit 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
|
||||
r"""
|
||||
|
|
@ -35455,7 +35457,7 @@ class Instance:
|
|||
r"""
|
||||
@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
|
||||
|
|
@ -35463,7 +35465,7 @@ class Instance:
|
|||
r"""
|
||||
@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:
|
||||
|
|
@ -47706,17 +47708,17 @@ class Netlist:
|
|||
@overload
|
||||
def circuit_by_cell_index(self, cell_index: int) -> Circuit:
|
||||
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.
|
||||
|
||||
This constness variant has been introduced in version 0.26.8.
|
||||
"""
|
||||
...
|
||||
@overload
|
||||
def circuit_by_cell_index(self, cell_index: int) -> Circuit:
|
||||
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.
|
||||
|
||||
This constness variant has been introduced in version 0.26.8.
|
||||
"""
|
||||
...
|
||||
@overload
|
||||
|
|
@ -47738,20 +47740,20 @@ class Netlist:
|
|||
@overload
|
||||
def circuits_by_name(self, name_pattern: str) -> List[Circuit]:
|
||||
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.
|
||||
|
||||
|
||||
This constness variant has been introduced in version 0.26.8.
|
||||
This method has been introduced in version 0.26.4.
|
||||
"""
|
||||
...
|
||||
@overload
|
||||
def circuits_by_name(self, name_pattern: str) -> List[Circuit]:
|
||||
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.
|
||||
|
||||
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:
|
||||
|
|
@ -47996,7 +47998,7 @@ class Netlist:
|
|||
@overload
|
||||
def top_circuit(self) -> Circuit:
|
||||
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 convenience method has been added in version 0.29.5.
|
||||
|
|
@ -48005,7 +48007,7 @@ class Netlist:
|
|||
@overload
|
||||
def top_circuit(self) -> Circuit:
|
||||
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 convenience method has been added in version 0.29.5.
|
||||
|
|
@ -58384,7 +58386,7 @@ class Region(ShapeCollection):
|
|||
@brief Creates a copy of self
|
||||
"""
|
||||
...
|
||||
def __getitem__(self, n: int) -> Polygon:
|
||||
def __getitem__(self, n: int) -> Any:
|
||||
r"""
|
||||
@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).
|
||||
|
||||
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:
|
||||
|
|
@ -63169,11 +63173,10 @@ class Shape:
|
|||
|
||||
Starting with version 0.23, this method returns nil, if the shape does not represent an edge.
|
||||
Setter:
|
||||
@brief Replaces the shape by the given edge
|
||||
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.
|
||||
Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes.
|
||||
@brief Replaces the shape by the given edge (in micrometer units)
|
||||
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.
|
||||
|
||||
This method has been introduced in version 0.22.
|
||||
This method has been introduced in version 0.25.
|
||||
"""
|
||||
edge_pair: Any
|
||||
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.
|
||||
|
||||
Setter:
|
||||
@brief Replaces the shape by the given simple polygon (in micrometer units)
|
||||
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.
|
||||
@brief Replaces the shape by the given simple polygon object
|
||||
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
|
||||
r"""
|
||||
|
|
@ -68247,8 +68251,7 @@ class Text:
|
|||
Setter:
|
||||
@brief Sets the horizontal alignment
|
||||
|
||||
This property specifies how the text is aligned relative to the anchor point.
|
||||
This property has been introduced in version 0.22 and extended to enums in 0.28.
|
||||
This is the version accepting integer values. It's provided for backward compatibility.
|
||||
"""
|
||||
size: int
|
||||
r"""
|
||||
|
|
@ -70054,13 +70057,15 @@ class Texts(ShapeCollection):
|
|||
@brief Creates a copy of self
|
||||
"""
|
||||
...
|
||||
def __getitem__(self, n: int) -> Text:
|
||||
def __getitem__(self, n: int) -> Any:
|
||||
r"""
|
||||
@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.
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -655,6 +655,14 @@ class RdbItem:
|
|||
"""
|
||||
...
|
||||
@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:
|
||||
r"""
|
||||
@brief Adds a numeric value to the values of this item
|
||||
|
|
|
|||
|
|
@ -2908,7 +2908,9 @@ class Timer:
|
|||
r"""
|
||||
@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
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public:
|
|||
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 ();
|
||||
if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "tlLog.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlEnv.h"
|
||||
#include "tlGlobPattern.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
|
|
@ -430,6 +431,63 @@ std::vector<std::string> dir_entries (const std::string &s, bool with_files, boo
|
|||
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)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -995,3 +995,94 @@ TEST (24)
|
|||
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"));
|
||||
}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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):
|
||||
|
||||
Layer Output Differences (shape count)
|
||||
-------------------------------------------------------
|
||||
----------------------------------------------------------------
|
||||
1/0 - (no such layer in second layout)
|
||||
2/0 - (no such layer in first layout)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -2,10 +2,10 @@
|
|||
# This script is sourced to define the main version parameters
|
||||
|
||||
# The main version
|
||||
KLAYOUT_VERSION="0.30.0"
|
||||
KLAYOUT_VERSION="0.30.1"
|
||||
|
||||
# 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
|
||||
KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")
|
||||
|
|
|
|||
Loading…
Reference in New Issue