Merge remote-tracking branch 'origin/master' into master-mac-qt6 discarding changes in the macbuild/ dir.

This commit is contained in:
Kazunari Sekigawa 2022-09-04 18:18:45 +09:00
commit 1cb8bdae0b
428 changed files with 53264 additions and 2812 deletions

View File

@ -12,6 +12,7 @@ recursive-include src/pymod *.cc *.h
recursive-include src/rbastub *.cc *.h
recursive-include src/rdb/rdb *.cc *.h
recursive-include src/tl/tl *.cc *.h
recursive-include src/pymod *.pyi
include src/plugins/*/db_plugin/*.cc
include src/plugins/*/*/db_plugin/*.cc
include src/plugins/*/db_plugin/*.h

View File

@ -11,12 +11,12 @@ For more details see http://www.klayout.org.
Building on Linux:
* Qt 4.7 or later (4.6 with some restrictions) or Qt 5
* Qt 4.7 or later (4.6 with some restrictions), Qt 5 or Qt 6
* gcc 4.6 or later or clang 3.8 or later
Building on Windows with MSYS2:
* MSYS2 with gcc, Qt4 or 5, zlib, ruby and python packages installed
* MSYS2 with gcc, Qt4, 5 or 6, zlib, ruby and python packages installed
Building on Windows with MSVC 2017:
@ -34,14 +34,10 @@ For more build instructions see http://www.klayout.de/build.html.
## Building instructions (Linux)
### Plain building for Qt4
### Plain building for Qt4, Qt5 and Qt6 (one Qt version installed)
./build.sh
### Plain building for Qt5
./build.sh -qt5
### Building without Qt binding
./build.sh -without-qtbinding

View File

@ -600,6 +600,32 @@ run_demo gen, "input1.edges.not(input2)", "drc_not3.png"
run_demo gen, "input1.edges.inside_part(input2)", "drc_inside_part.png"
run_demo gen, "input1.edges.outside_part(input2)", "drc_outside_part.png"
class Gen
def produce(s1, s2)
pts = [
RBA::Point::new(0 , 0 ),
RBA::Point::new(0 , 3000),
RBA::Point::new(2000, 3000),
RBA::Point::new(2000, 5000),
RBA::Point::new(6000, 5000),
RBA::Point::new(6000, 0 )
];
s1.insert(RBA::Polygon::new(pts))
s2.insert(RBA::Box::new(0, 0, 4000, 6000))
end
end
gen = Gen::new
run_demo gen, "input1.edges.inside(input2)", "drc_inside_ep.png"
run_demo gen, "input1.edges.inside(input2.edges)", "drc_inside_ee.png"
run_demo gen, "input1.edges.not_inside(input2)", "drc_not_inside_ep.png"
run_demo gen, "input1.edges.not_inside(input2.edges)", "drc_not_inside_ee.png"
run_demo gen, "input1.edges.outside(input2)", "drc_outside_ep.png"
run_demo gen, "input1.edges.outside(input2.edges)", "drc_outside_ee.png"
run_demo gen, "input1.edges.not_outside(input2)", "drc_not_outside_ep.png"
run_demo gen, "input1.edges.not_outside(input2.edges)", "drc_not_outside_ee.png"
class Gen
def produce(s1, s2)
s1.insert(RBA::Box::new(-1000, 0, 1000, 2000))

View File

@ -759,4 +759,6 @@ if __name__ == '__main__':
url='https://github.com/klayout/klayout',
packages=find_packages('src/pymod/distutils_src'),
package_dir={'': 'src/pymod/distutils_src'}, # https://github.com/pypa/setuptools/issues/230
package_data={config.root: ["src/pymod/distutils_src/klayout/*.pyi"]},
include_package_data=True,
ext_modules=[_tl, _gsi, _pya, _rba, _db, _lib, _rdb, _lym, _laybasic, _layview, _ant, _edt, _img] + db_plugins + [tl, db, lib, rdb, lay])

View File

@ -91,7 +91,7 @@ PluginDeclaration::get_options (std::vector < std::pair<std::string, std::string
options.push_back (std::pair<std::string, std::string> (cfg_ruler_snap_mode, ACConverter ().to_string (lay::AC_Any)));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_obj_snap, tl::to_string (true)));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_grid_snap, tl::to_string (false)));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_templates, ant::TemplatesConverter ().to_string (make_standard_templates ())));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_templates, std::string ()));
options.push_back (std::pair<std::string, std::string> (cfg_current_ruler_template, "0"));
// grid-micron is not configured here since some other entity is supposed to do this.
}
@ -183,13 +183,10 @@ PluginDeclaration::config_finalize ()
if (m_templates_updated) {
update_menu ();
m_templates_updated = false;
m_current_template_updated = false;
} else if (m_current_template_updated) {
update_current_template ();
m_current_template_updated = false;
}
}
@ -198,21 +195,16 @@ void
PluginDeclaration::initialized (lay::Dispatcher *root)
{
// Check if we already have templates (initial setup)
// NOTE: this is not done by using a default value for the configuration item but dynamically.
// This provides a migration path from earlier versions (not having templates) to recent ones.
bool any_templates = false;
for (std::vector<ant::Template>::iterator i = m_templates.begin (); ! any_templates && i != m_templates.end (); ++i) {
any_templates = ! i->category ().empty ();
}
if (! any_templates) {
// This is the migration path from <= 0.24 to 0.25: clear all templates unless we
// have categorized ones there. Those can't be deleted, so we know we have a 0.25
// setup if there are some
m_templates = make_standard_templates ();
root->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (m_templates));
root->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (make_standard_templates ()));
root->config_end ();
}
}
@ -250,6 +242,8 @@ PluginDeclaration::update_current_template ()
}
}
m_current_template_updated = false;
}
void
@ -294,6 +288,9 @@ PluginDeclaration::update_menu ()
}
}
}
m_templates_updated = false;
m_current_template_updated = false;
}
void

View File

@ -95,11 +95,11 @@ AsIfFlatEdges::to_string (size_t nmax) const
}
EdgesDelegate *
AsIfFlatEdges::selected_interacting_generic (const Region &other, bool inverse) const
AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const
{
// shortcuts
if (other.empty () || empty ()) {
return new EmptyEdges ();
return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone ();
}
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner (report_progress (), progress_desc ());
@ -110,7 +110,7 @@ AsIfFlatEdges::selected_interacting_generic (const Region &other, bool inverse)
scanner.insert1 (e.operator-> (), 0);
}
AddressablePolygonDelivery p = other.addressable_polygons ();
AddressablePolygonDelivery p = (mode == EdgesInside ? other.addressable_merged_polygons () : other.addressable_polygons ());
for ( ; ! p.at_end (); ++p) {
scanner.insert2 (p.operator-> (), 1);
@ -120,17 +120,17 @@ AsIfFlatEdges::selected_interacting_generic (const Region &other, bool inverse)
if (! inverse) {
edge_to_region_interaction_filter<FlatEdges> filter (*output);
edge_to_region_interaction_filter<FlatEdges> filter (output.get (), mode);
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
} else {
std::set<db::Edge> interacting;
edge_to_region_interaction_filter<std::set<db::Edge> > filter (interacting);
std::set<db::Edge> result;
edge_to_region_interaction_filter<std::set<db::Edge> > filter (&result, mode);
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
if (interacting.find (*o) == interacting.end ()) {
if (result.find (*o) == result.end ()) {
output->insert (*o);
}
}
@ -141,8 +141,13 @@ AsIfFlatEdges::selected_interacting_generic (const Region &other, bool inverse)
}
EdgesDelegate *
AsIfFlatEdges::selected_interacting_generic (const Edges &edges, bool inverse) const
AsIfFlatEdges::selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const
{
// shortcuts
if (edges.empty () || empty ()) {
return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone ();
}
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ());
@ -151,7 +156,8 @@ AsIfFlatEdges::selected_interacting_generic (const Edges &edges, bool inverse) c
scanner.insert (e.operator-> (), 0);
}
AddressableEdgeDelivery ee = edges.addressable_edges ();
// NOTE: "inside" needs merged edges for the other edges as the algorithm works edge by edge
AddressableEdgeDelivery ee = (mode == EdgesInside ? edges.addressable_merged_edges () : edges.addressable_edges ());
for ( ; ! ee.at_end (); ++ee) {
scanner.insert (ee.operator-> (), 1);
@ -161,17 +167,17 @@ AsIfFlatEdges::selected_interacting_generic (const Edges &edges, bool inverse) c
if (! inverse) {
edge_interaction_filter<FlatEdges> filter (*output);
edge_interaction_filter<FlatEdges> filter (*output, mode);
scanner.process (filter, 1, db::box_convert<db::Edge> ());
} else {
std::set<db::Edge> interacting;
edge_interaction_filter<std::set<db::Edge> > filter (interacting);
std::set<db::Edge> result;
edge_interaction_filter<std::set<db::Edge> > filter (result, mode);
scanner.process (filter, 1, db::box_convert<db::Edge> ());
for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
if (interacting.find (*o) == interacting.end ()) {
if (result.find (*o) == result.end ()) {
output->insert (*o);
}
}
@ -181,6 +187,95 @@ AsIfFlatEdges::selected_interacting_generic (const Edges &edges, bool inverse) c
return output.release ();
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_interacting_pair_generic (const Region &region, EdgeInteractionMode mode) const
{
// shortcuts
if (region.empty () || empty ()) {
if (mode != EdgesOutside) {
return std::make_pair (new EmptyEdges (), clone ());
} else {
return std::make_pair (clone (), new EmptyEdges ());
}
}
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner (report_progress (), progress_desc ());
AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ());
for ( ; ! e.at_end (); ++e) {
scanner.insert1 (e.operator-> (), 0);
}
AddressablePolygonDelivery p = region.addressable_merged_polygons ();
for ( ; ! p.at_end (); ++p) {
scanner.insert2 (p.operator-> (), 1);
}
std::unique_ptr<FlatEdges> output (new FlatEdges (true));
std::unique_ptr<FlatEdges> output2 (new FlatEdges (true));
std::set<db::Edge> result;
edge_to_region_interaction_filter<std::set<db::Edge> > filter (&result, mode);
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
if (result.find (*o) == result.end ()) {
output2->insert (*o);
} else {
output->insert (*o);
}
}
return std::make_pair (output.release (), output2.release ());
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const
{
// shortcuts
if (other.empty () || empty ()) {
if (mode != EdgesOutside) {
return std::make_pair (new EmptyEdges (), clone ());
} else {
return std::make_pair (clone (), new EmptyEdges ());
}
}
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ());
for ( ; ! e.at_end (); ++e) {
scanner.insert (e.operator-> (), 0);
}
AddressableEdgeDelivery ee = other.addressable_merged_edges ();
for ( ; ! ee.at_end (); ++ee) {
scanner.insert (ee.operator-> (), 1);
}
std::unique_ptr<FlatEdges> output (new FlatEdges (true));
std::unique_ptr<FlatEdges> output2 (new FlatEdges (true));
std::set<db::Edge> results;
edge_interaction_filter<std::set<db::Edge> > filter (results, mode);
scanner.process (filter, 1, db::box_convert<db::Edge> ());
for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
if (results.find (*o) == results.end ()) {
output2->insert (*o);
} else {
output->insert (*o);
}
}
return std::make_pair (output.release (), output2.release ());
}
EdgesDelegate *
AsIfFlatEdges::pull_generic (const Edges &edges) const
{
@ -199,7 +294,7 @@ AsIfFlatEdges::pull_generic (const Edges &edges) const
}
std::unique_ptr<FlatEdges> output (new FlatEdges (true));
edge_interaction_filter<FlatEdges> filter (*output);
edge_interaction_filter<FlatEdges> filter (*output, EdgesInteract);
scanner.process (filter, 1, db::box_convert<db::Edge> ());
return output.release ();
@ -229,7 +324,7 @@ AsIfFlatEdges::pull_generic (const Region &other) const
std::unique_ptr<FlatRegion> output (new FlatRegion (true));
edge_to_region_interaction_filter<FlatRegion> filter (*output);
edge_to_region_interaction_filter<FlatRegion> filter (output.get (), EdgesInteract);
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
return output.release ();
@ -250,27 +345,112 @@ AsIfFlatEdges::pull_interacting (const Region &other) const
EdgesDelegate *
AsIfFlatEdges::selected_interacting (const Edges &other) const
{
return selected_interacting_generic (other, false);
return selected_interacting_generic (other, EdgesInteract, false);
}
EdgesDelegate *
AsIfFlatEdges::selected_not_interacting (const Edges &other) const
{
return selected_interacting_generic (other, true);
return selected_interacting_generic (other, EdgesInteract, true);
}
EdgesDelegate *
AsIfFlatEdges::selected_interacting (const Region &other) const
{
return selected_interacting_generic (other, false);
return selected_interacting_generic (other, EdgesInteract, false);
}
EdgesDelegate *
AsIfFlatEdges::selected_not_interacting (const Region &other) const
{
return selected_interacting_generic (other, true);
return selected_interacting_generic (other, EdgesInteract, true);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_interacting_pair (const Region &other) const
{
return selected_interacting_pair_generic (other, EdgesInteract);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_interacting_pair (const Edges &other) const
{
return selected_interacting_pair_generic (other, EdgesInteract);
}
EdgesDelegate *
AsIfFlatEdges::selected_outside (const Region &other) const
{
return selected_interacting_generic (other, EdgesOutside, false);
}
EdgesDelegate *
AsIfFlatEdges::selected_not_outside (const Region &other) const
{
return selected_interacting_generic (other, EdgesOutside, true);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_outside_pair (const Region &other) const
{
return selected_interacting_pair_generic (other, EdgesOutside);
}
EdgesDelegate *
AsIfFlatEdges::selected_inside (const Region &other) const
{
return selected_interacting_generic (other, EdgesInside, false);
}
EdgesDelegate *
AsIfFlatEdges::selected_not_inside (const Region &other) const
{
return selected_interacting_generic (other, EdgesInside, true);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_inside_pair (const Region &other) const
{
return selected_interacting_pair_generic (other, EdgesInside);
}
EdgesDelegate *
AsIfFlatEdges::selected_outside (const Edges &other) const
{
return selected_interacting_generic (other, EdgesOutside, false);
}
EdgesDelegate *
AsIfFlatEdges::selected_not_outside (const Edges &other) const
{
return selected_interacting_generic (other, EdgesOutside, true);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_outside_pair (const Edges &other) const
{
return selected_interacting_pair_generic (other, EdgesOutside);
}
EdgesDelegate *
AsIfFlatEdges::selected_inside (const Edges &other) const
{
return selected_interacting_generic (other, EdgesInside, false);
}
EdgesDelegate *
AsIfFlatEdges::selected_not_inside (const Edges &other) const
{
return selected_interacting_generic (other, EdgesInside, true);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::selected_inside_pair (const Edges &other) const
{
return selected_interacting_pair_generic (other, EdgesInside);
}
namespace
{
@ -584,18 +764,52 @@ AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const
return output.release ();
}
EdgesDelegate *
AsIfFlatEdges::edge_region_op (const Region &other, bool outside, bool include_borders) const
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::boolean_andnot (const Edges *other) const
{
std::unique_ptr<FlatEdges> output (new FlatEdges (true));
std::unique_ptr<FlatEdges> output2 (new FlatEdges (true));
EdgeBooleanClusterCollectorToShapes cluster_collector (&output->raw_edges (), EdgeAndNot, &output2->raw_edges ());
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
scanner.reserve (count () + (other ? other->count () : 0));
AddressableEdgeDelivery e (begin (), has_valid_edges ());
for ( ; ! e.at_end (); ++e) {
if (! e->is_degenerate ()) {
scanner.insert (e.operator-> (), 0);
}
}
AddressableEdgeDelivery ee;
if (other) {
ee = other->addressable_edges ();
for ( ; ! ee.at_end (); ++ee) {
if (! ee->is_degenerate ()) {
scanner.insert (ee.operator-> (), 1);
}
}
}
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
return std::make_pair (output.release (), output2.release ());
}
std::pair<EdgesDelegate *, EdgesDelegate *>
AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mode, bool include_borders) const
{
// shortcuts
if (other.empty ()) {
if (! outside) {
return new EmptyEdges ();
if (other.empty () || empty ()) {
if (mode == db::EdgePolygonOp::Both) {
return std::make_pair (new EmptyEdges (), clone ());
} else if (mode == db::EdgePolygonOp::Inside) {
return std::make_pair (new EmptyEdges (), (EdgesDelegate *) 0);
} else {
return clone ();
return std::make_pair (clone (), (EdgesDelegate *) 0);
}
} else if (empty ()) {
return new EmptyEdges ();
}
db::EdgeProcessor ep (report_progress (), progress_desc ());
@ -610,12 +824,19 @@ AsIfFlatEdges::edge_region_op (const Region &other, bool outside, bool include_b
ep.insert (*e, 1);
}
std::unique_ptr<FlatEdges> output_second;
std::unique_ptr<db::EdgeShapeGenerator> cc_second;
if (mode == db::EdgePolygonOp::Both) {
output_second.reset (new FlatEdges (false));
cc_second.reset (new db::EdgeShapeGenerator (output_second->raw_edges (), true /*clear*/, 2 /*second tag*/));
}
std::unique_ptr<FlatEdges> output (new FlatEdges (false));
db::EdgeShapeGenerator cc (output->raw_edges (), true /*clear*/);
db::EdgePolygonOp op (outside, include_borders);
db::EdgeShapeGenerator cc (output->raw_edges (), true /*clear*/, 1 /*tag*/, cc_second.get ());
db::EdgePolygonOp op (mode, include_borders);
ep.process (cc, op);
return output.release ();
return std::make_pair (output.release (), output_second.release ());
}
EdgesDelegate *

View File

@ -28,6 +28,8 @@
#include "dbBoxScanner.h"
#include "dbEdgesDelegate.h"
#include "dbEdgeBoolean.h"
#include "dbEdgeProcessor.h"
#include "dbEdgesUtils.h"
#include "dbBoxScanner.h"
#include "dbPolygonTools.h"
@ -115,19 +117,29 @@ public:
return boolean (&other, EdgeAnd);
}
virtual EdgesDelegate *and_with (const Region &other) const
{
return edge_region_op (other, false /*inside*/, true /*include borders*/);
}
virtual EdgesDelegate *not_with (const Edges &other) const
{
return boolean (&other, EdgeNot);
}
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Edges &other) const
{
return boolean_andnot (&other);
}
virtual EdgesDelegate *and_with (const Region &other) const
{
return edge_region_op (other, db::EdgePolygonOp::Inside, true /*include borders*/).first;
}
virtual EdgesDelegate *not_with (const Region &other) const
{
return edge_region_op (other, true /*outside*/, true /*include borders*/);
return edge_region_op (other, db::EdgePolygonOp::Outside, true /*include borders*/).first;
}
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Region &other) const
{
return edge_region_op (other, db::EdgePolygonOp::Both, true /*include borders*/);
}
virtual EdgesDelegate *xor_with (const Edges &other) const
@ -154,12 +166,17 @@ public:
virtual EdgesDelegate *inside_part (const Region &other) const
{
return edge_region_op (other, false /*inside*/, false /*don't include borders*/);
return edge_region_op (other, db::EdgePolygonOp::Inside, false /*don't include borders*/).first;
}
virtual EdgesDelegate *outside_part (const Region &other) const
{
return edge_region_op (other, true /*outside*/, false /*don't include borders*/);
return edge_region_op (other, db::EdgePolygonOp::Outside, false /*don't include borders*/).first;
}
virtual std::pair<EdgesDelegate *, EdgesDelegate *> inside_outside_part_pair (const Region &other) const
{
return edge_region_op (other, db::EdgePolygonOp::Both, false /*don't include borders*/);
}
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const;
@ -170,6 +187,21 @@ public:
virtual EdgesDelegate *selected_not_interacting (const Edges &) const;
virtual EdgesDelegate *selected_interacting (const Region &) const;
virtual EdgesDelegate *selected_not_interacting (const Region &) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Edges &other) const;
virtual EdgesDelegate *selected_outside (const Edges &other) const;
virtual EdgesDelegate *selected_not_outside (const Edges &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_outside_pair (const Edges &other) const;
virtual EdgesDelegate *selected_inside (const Edges &other) const;
virtual EdgesDelegate *selected_not_inside (const Edges &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_inside_pair (const Edges &other) const;
virtual EdgesDelegate *selected_outside (const Region &other) const;
virtual EdgesDelegate *selected_not_outside (const Region &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_outside_pair (const Region &other) const;
virtual EdgesDelegate *selected_inside (const Region &other) const;
virtual EdgesDelegate *selected_not_inside (const Region &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_inside_pair (const Region &other) const;
virtual EdgesDelegate *in (const Edges &, bool) const;
@ -184,8 +216,10 @@ protected:
EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const EdgesCheckOptions &options) const;
virtual EdgesDelegate *pull_generic (const Edges &edges) const;
virtual RegionDelegate *pull_generic (const Region &region) const;
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, bool inverse) const;
virtual EdgesDelegate *selected_interacting_generic (const Region &region, bool inverse) const;
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const;
virtual EdgesDelegate *selected_interacting_generic (const Region &region, EdgeInteractionMode mode, bool inverse) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair_generic (const Region &region, EdgeInteractionMode mode) const;
AsIfFlatEdges &operator= (const AsIfFlatEdges &other);
AsIfFlatEdges (const AsIfFlatEdges &other);
@ -195,7 +229,8 @@ private:
virtual db::Box compute_bbox () const;
EdgesDelegate *boolean (const Edges *other, EdgeBoolOp op) const;
EdgesDelegate *edge_region_op (const Region &other, bool outside, bool include_borders) const;
std::pair<EdgesDelegate *, EdgesDelegate *> boolean_andnot (const Edges *other) const;
std::pair<EdgesDelegate *, EdgesDelegate *> edge_region_op(const Region &other, db::EdgePolygonOp::mode_t mode, bool include_borders) const;
};
}

View File

@ -556,6 +556,7 @@ CommonReader::read (db::Layout &layout)
void
CommonReader::init (const LoadLayoutOptions &options)
{
ReaderBase::init (options);
CommonReaderBase::init ();
db::CommonReaderOptions common_options = options.get_options<db::CommonReaderOptions> ();

View File

@ -237,7 +237,7 @@ protected:
friend class CommonReaderLayerMapping;
virtual void common_reader_error (const std::string &msg) = 0;
virtual void common_reader_warn (const std::string &msg) = 0;
virtual void common_reader_warn (const std::string &msg, int warn_level = 1) = 0;
/**
* @brief Merge (and delete) the src_cell into target_cell

View File

@ -387,7 +387,7 @@ DeepEdges::has_valid_edges () const
bool
DeepEdges::has_valid_merged_edges () const
{
return merged_semantics ();
return false;
}
const db::RecursiveShapeIterator *
@ -797,12 +797,21 @@ DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const
return dl_out;
}
DeepLayer
DeepEdges::edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const
std::pair<DeepLayer, DeepLayer>
DeepEdges::edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t mode, bool include_borders) const
{
DeepLayer dl_out (deep_layer ().derived ());
std::vector<unsigned int> output_layers;
db::EdgeToPolygonLocalOperation op (outside, include_borders);
DeepLayer dl_out (deep_layer ().derived ());
output_layers.push_back (dl_out.layer ());
DeepLayer dl_out2;
if (mode == EdgePolygonOp::Both) {
dl_out2 = DeepLayer (deep_layer ().derived ());
output_layers.push_back (dl_out2.layer ());
}
db::EdgeToPolygonLocalOperation op (mode, include_borders);
db::local_processor<db::Edge, db::PolygonRef, db::Edge> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ());
proc.set_base_verbosity (base_verbosity ());
@ -810,9 +819,9 @@ DeepEdges::edge_region_op (const DeepRegion *other, bool outside, bool include_b
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
proc.run (&op, deep_layer ().layer (), other->deep_layer ().layer (), dl_out.layer ());
proc.run (&op, deep_layer ().layer (), other->deep_layer ().layer (), output_layers);
return dl_out;
return std::make_pair (dl_out, dl_out2);
}
EdgesDelegate *DeepEdges::intersections (const Edges &other) const
@ -821,8 +830,7 @@ EdgesDelegate *DeepEdges::intersections (const Edges &other) const
if (empty () || other.empty ()) {
// Nothing to do
return new EmptyEdges ();
return clone ();
} else if (! other_deep) {
@ -839,10 +847,14 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const
{
const DeepEdges *other_deep = dynamic_cast <const DeepEdges *> (other.delegate ());
if (empty () || other.empty ()) {
if (empty ()) {
// Nothing to do
return new EmptyEdges ();
return clone ();
} else if (other.empty ()) {
// NOTE: we do not use "EmptyEdges" as we want to maintain
return new DeepEdges (deep_layer ().derived ());
} else if (! other_deep) {
@ -855,43 +867,12 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const
}
}
EdgesDelegate *DeepEdges::and_with (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
// Nothing to do
return new EmptyEdges ();
} else if (other.empty ()) {
// Nothing to do
return clone ();
} else if (! other_deep) {
return AsIfFlatEdges::not_with (other);
} else {
return new DeepEdges (edge_region_op (other_deep, false /*outside*/, true /*include borders*/));
}
}
EdgesDelegate *DeepEdges::not_with (const Edges &other) const
{
const DeepEdges *other_deep = dynamic_cast <const DeepEdges *> (other.delegate ());
if (empty ()) {
if (empty () || other.empty ()) {
// Nothing to do
return new EmptyEdges ();
} else if (other.empty ()) {
// Nothing to do
return clone ();
} else if (! other_deep) {
@ -905,18 +886,61 @@ EdgesDelegate *DeepEdges::not_with (const Edges &other) const
}
}
EdgesDelegate *DeepEdges::not_with (const Region &other) const
EdgesDelegate *DeepEdges::and_with (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
// Nothing to do
return new EmptyEdges ();
return clone ();
} else if (other.empty ()) {
// Nothing to do
// NOTE: we do not use "EmptyEdges" as we want to maintain
return new DeepEdges (deep_layer ().derived ());
} else if (! other_deep) {
return AsIfFlatEdges::and_with (other);
} else {
return new DeepEdges (edge_region_op (other_deep, EdgePolygonOp::Inside, true /*include borders*/).first);
}
}
std::pair<EdgesDelegate *, EdgesDelegate *> DeepEdges::andnot_with (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
return std::make_pair (clone (), clone ());
} else if (other.empty ()) {
// NOTE: we do not use "EmptyEdges" as we want to maintain "deepness"
return std::make_pair (new DeepEdges (deep_layer ().derived ()), clone ());
} else if (! other_deep) {
return AsIfFlatEdges::andnot_with (other);
} else {
auto res = edge_region_op (other_deep, EdgePolygonOp::Both, true /*include borders*/);
return std::make_pair (new DeepEdges (res.first), new DeepEdges (res.second));
}
}
EdgesDelegate *DeepEdges::not_with (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty () || other.empty ()) {
return clone ();
} else if (! other_deep) {
@ -925,7 +949,33 @@ EdgesDelegate *DeepEdges::not_with (const Region &other) const
} else {
return new DeepEdges (edge_region_op (other_deep, true /*outside*/, true /*include borders*/));
return new DeepEdges (edge_region_op (other_deep, EdgePolygonOp::Outside, true /*include borders*/).first);
}
}
std::pair<EdgesDelegate *, EdgesDelegate *>
DeepEdges::andnot_with (const Edges &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
return std::make_pair (clone (), clone ());
} else if (other.empty ()) {
// NOTE: we do not use "EmptyEdges" as we want to maintain
return std::make_pair (new DeepEdges (deep_layer ().derived ()), clone ());
} else if (! other_deep) {
return AsIfFlatEdges::andnot_with (other);
} else {
auto res = edge_region_op (other_deep, EdgePolygonOp::Both, true /*include borders*/);
return std::make_pair (new DeepEdges (res.first), new DeepEdges (res.second));
}
}
@ -936,12 +986,10 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const
if (empty ()) {
// Nothing to do
return other.delegate ()->clone ();
} else if (other.empty ()) {
// Nothing to do
return clone ();
} else if (! other_deep) {
@ -1013,21 +1061,20 @@ EdgesDelegate *DeepEdges::inside_part (const Region &other) const
if (empty ()) {
// Nothing to do
return new EmptyEdges ();
return clone ();
} else if (other.empty ()) {
// Nothing to do
return clone ();
// NOTE: we do not use "EmptyEdges" as we want to maintain
return new DeepEdges (deep_layer ().derived ());
} else if (! other_deep) {
return AsIfFlatEdges::not_with (other);
return AsIfFlatEdges::inside_part (other);
} else {
return new DeepEdges (edge_region_op (other_deep, false /*outside*/, false /*include borders*/));
return new DeepEdges (edge_region_op (other_deep, db::EdgePolygonOp::Inside, false /*include borders*/).first);
}
}
@ -1036,23 +1083,42 @@ EdgesDelegate *DeepEdges::outside_part (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
if (empty () || other.empty ()) {
// Nothing to do
return new EmptyEdges ();
} else if (other.empty ()) {
// Nothing to do
return clone ();
} else if (! other_deep) {
return AsIfFlatEdges::not_with (other);
return AsIfFlatEdges::outside_part (other);
} else {
return new DeepEdges (edge_region_op (other_deep, true /*outside*/, false /*include borders*/));
return new DeepEdges (edge_region_op (other_deep, db::EdgePolygonOp::Outside, false /*include borders*/).first);
}
}
std::pair<EdgesDelegate *, EdgesDelegate *> DeepEdges::inside_outside_part_pair (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
return std::make_pair (clone (), clone ());
} else if (other.empty ()) {
// NOTE: we do not use "EmptyEdges" as we want to maintain "deepness"
return std::make_pair (new DeepEdges (deep_layer ().derived ()), clone ());
} else if (! other_deep) {
return AsIfFlatEdges::inside_outside_part_pair (other);
} else {
auto res = edge_region_op (other_deep, EdgePolygonOp::Both, false /*include borders*/);
return std::make_pair (new DeepEdges (res.first), new DeepEdges (res.second));
}
}
@ -1157,8 +1223,10 @@ class Edge2EdgeInteractingLocalOperation
: public local_operation<db::Edge, db::Edge, db::Edge>
{
public:
Edge2EdgeInteractingLocalOperation (bool inverse)
: m_inverse (inverse)
enum output_mode_t { Normal, Inverse, Both };
Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode)
: m_mode (mode), m_output_mode (output_mode)
{
// .. nothing yet ..
}
@ -1171,9 +1239,15 @@ public:
virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{
tl_assert (results.size () == 1);
tl_assert (results.size () == (m_output_mode == Both ? 2 : 1));
std::unordered_set<db::Edge> &result = results.front ();
std::unordered_set<db::Edge> *result2 = 0;
if (m_output_mode == Both) {
result2 = &results[1];
}
db::box_scanner<db::Edge, size_t> scanner;
std::set<db::Edge> others;
@ -1192,22 +1266,30 @@ public:
scanner.insert (o.operator-> (), 1);
}
if (m_inverse) {
if (m_output_mode == Inverse || m_output_mode == Both) {
std::unordered_set<db::Edge> interacting;
edge_interaction_filter<std::unordered_set<db::Edge> > filter (interacting);
edge_interaction_filter<std::unordered_set<db::Edge> > filter (interacting, m_mode);
scanner.process (filter, 1, db::box_convert<db::Edge> ());
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
const db::Edge &subject = interactions.subject_shape (i->first);
if (interacting.find (subject) == interacting.end ()) {
if (m_output_mode != Both) {
result.insert (subject);
} else {
result2->insert (subject);
}
} else if (m_output_mode == Both) {
result.insert (subject);
}
}
} else {
edge_interaction_filter<std::unordered_set<db::Edge> > filter (result);
edge_interaction_filter<std::unordered_set<db::Edge> > filter (result, m_mode);
scanner.process (filter, 1, db::box_convert<db::Edge> ());
}
@ -1216,10 +1298,10 @@ public:
virtual OnEmptyIntruderHint on_empty_intruder_hint () const
{
if (m_inverse) {
return Copy;
if (m_mode == EdgesOutside) {
return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy);
} else {
return Drop;
return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop);
}
}
@ -1229,7 +1311,8 @@ public:
}
private:
bool m_inverse;
EdgeInteractionMode m_mode;
output_mode_t m_output_mode;
};
class Edge2EdgePullLocalOperation
@ -1270,7 +1353,7 @@ public:
scanner.insert (o.operator-> (), 0);
}
edge_interaction_filter<std::unordered_set<db::Edge> > filter (result);
edge_interaction_filter<std::unordered_set<db::Edge> > filter (result, EdgesInteract);
scanner.process (filter, 1, db::box_convert<db::Edge> ());
}
@ -1290,8 +1373,10 @@ class Edge2PolygonInteractingLocalOperation
: public local_operation<db::Edge, db::PolygonRef, db::Edge>
{
public:
Edge2PolygonInteractingLocalOperation (bool inverse)
: m_inverse (inverse)
enum output_mode_t { Normal, Inverse, Both };
Edge2PolygonInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode)
: m_mode (mode), m_output_mode (output_mode)
{
// .. nothing yet ..
}
@ -1304,9 +1389,15 @@ public:
virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::Edge, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{
tl_assert (results.size () == 1);
tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1));
std::unordered_set<db::Edge> &result = results.front ();
std::unordered_set<db::Edge> *result2 = 0;
if (m_output_mode == Both) {
result2 = &results[1];
}
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner;
std::set<db::PolygonRef> others;
@ -1327,22 +1418,31 @@ public:
scanner.insert2 (& heap.back (), 1);
}
if (m_inverse) {
if (m_output_mode == Inverse || m_output_mode == Both) {
std::unordered_set<db::Edge> interacting;
edge_to_region_interaction_filter<std::unordered_set<db::Edge> > filter (interacting);
edge_to_region_interaction_filter<std::unordered_set<db::Edge> > filter (&interacting, m_mode);
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
const db::Edge &subject = interactions.subject_shape (i->first);
if (interacting.find (subject) == interacting.end ()) {
if (m_output_mode != Both) {
result.insert (subject);
} else {
result2->insert (subject);
}
} else if (m_output_mode == Both) {
result.insert (subject);
}
}
} else {
edge_to_region_interaction_filter<std::unordered_set<db::Edge> > filter (result);
edge_to_region_interaction_filter<std::unordered_set<db::Edge> > filter (&result, m_mode);
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
}
@ -1350,20 +1450,46 @@ public:
virtual OnEmptyIntruderHint on_empty_intruder_hint () const
{
if (m_inverse) {
return Copy;
if (m_mode == EdgesOutside) {
return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy);
} else {
return Drop;
return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop);
}
}
virtual std::string description () const
{
return tl::to_string (tr ("Select interacting edges"));
if (m_mode == EdgesInteract) {
if (m_output_mode == Inverse) {
return tl::to_string (tr ("Select non-interacting edges"));
} else if (m_output_mode == Normal) {
return tl::to_string (tr ("Select interacting edges"));
} else {
return tl::to_string (tr ("Select interacting and non-interacting edges"));
}
} else if (m_mode == EdgesInside) {
if (m_output_mode == Inverse) {
return tl::to_string (tr ("Select non-inside edges"));
} else if (m_output_mode == Normal) {
return tl::to_string (tr ("Select inside edges"));
} else {
return tl::to_string (tr ("Select inside and non-inside edges"));
}
} else if (m_mode == EdgesOutside) {
if (m_output_mode == Inverse) {
return tl::to_string (tr ("Select non-outside edges"));
} else if (m_output_mode == Normal) {
return tl::to_string (tr ("Select outside edges"));
} else {
return tl::to_string (tr ("Select outside and non-outside edges"));
}
}
return std::string ();
}
private:
bool m_inverse;
EdgeInteractionMode m_mode;
output_mode_t m_output_mode;
};
struct ResultInserter
@ -1427,7 +1553,7 @@ public:
}
ResultInserter inserter (layout, result);
edge_to_region_interaction_filter<ResultInserter> filter (inserter);
edge_to_region_interaction_filter<ResultInserter> filter (&inserter, EdgesInteract);
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
}
@ -1445,7 +1571,7 @@ public:
}
EdgesDelegate *
DeepEdges::selected_interacting_generic (const Region &other, bool inverse) const
DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const
{
std::unique_ptr<db::DeepRegion> dr_holder;
const db::DeepRegion *other_deep = dynamic_cast<const db::DeepRegion *> (other.delegate ());
@ -1459,19 +1585,51 @@ DeepEdges::selected_interacting_generic (const Region &other, bool inverse) cons
DeepLayer dl_out (edges.derived ());
db::Edge2PolygonInteractingLocalOperation op (inverse);
db::Edge2PolygonInteractingLocalOperation op (mode, inverse ? db::Edge2PolygonInteractingLocalOperation::Inverse : db::Edge2PolygonInteractingLocalOperation::Normal);
db::local_processor<db::Edge, db::PolygonRef, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (edges.store ()->threads ());
proc.run (&op, edges.layer (), other_deep->deep_layer ().layer (), dl_out.layer ());
proc.run (&op, edges.layer (), (mode == EdgesInside ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ());
return new db::DeepEdges (dl_out);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode) const
{
std::unique_ptr<db::DeepRegion> dr_holder;
const db::DeepRegion *other_deep = dynamic_cast<const db::DeepRegion *> (other.delegate ());
if (! other_deep) {
// if the other region isn't deep, turn into a top-level only deep region to facilitate re-hierarchization
dr_holder.reset (new db::DeepRegion (other, const_cast<db::DeepShapeStore &> (*deep_layer ().store ())));
other_deep = dr_holder.get ();
}
const db::DeepLayer &edges = merged_deep_layer ();
DeepLayer dl_out (edges.derived ());
DeepLayer dl_out2 (edges.derived ());
std::vector<unsigned int> output_layers;
output_layers.reserve (2);
output_layers.push_back (dl_out.layer ());
output_layers.push_back (dl_out2.layer ());
db::Edge2PolygonInteractingLocalOperation op (mode, db::Edge2PolygonInteractingLocalOperation::Both);
db::local_processor<db::Edge, db::PolygonRef, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (edges.store ()->threads ());
proc.run (&op, edges.layer (), other_deep->merged_deep_layer ().layer (), output_layers);
return std::make_pair (new db::DeepEdges (dl_out), new db::DeepEdges (dl_out2));
}
EdgesDelegate *
DeepEdges::selected_interacting_generic (const Edges &other, bool inverse) const
DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse) const
{
std::unique_ptr<db::DeepEdges> dr_holder;
const db::DeepEdges *other_deep = dynamic_cast<const db::DeepEdges *> (other.delegate ());
@ -1485,17 +1643,49 @@ DeepEdges::selected_interacting_generic (const Edges &other, bool inverse) const
DeepLayer dl_out (edges.derived ());
db::Edge2EdgeInteractingLocalOperation op (inverse);
db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal);
db::local_processor<db::Edge, db::Edge, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (edges.store ()->threads ());
proc.run (&op, edges.layer (), other_deep->deep_layer ().layer (), dl_out.layer ());
proc.run (&op, edges.layer (), (mode == EdgesInside ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ());
return new db::DeepEdges (dl_out);
}
std::pair<EdgesDelegate *, EdgesDelegate *>
DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const
{
std::unique_ptr<db::DeepEdges> dr_holder;
const db::DeepEdges *other_deep = dynamic_cast<const db::DeepEdges *> (other.delegate ());
if (! other_deep) {
// if the other edge collection isn't deep, turn into a top-level only deep edge collection to facilitate re-hierarchization
dr_holder.reset (new db::DeepEdges (other, const_cast<db::DeepShapeStore &> (*deep_layer ().store ())));
other_deep = dr_holder.get ();
}
const db::DeepLayer &edges = merged_deep_layer ();
DeepLayer dl_out (edges.derived ());
DeepLayer dl_out2 (edges.derived ());
std::vector<unsigned int> output_layers;
output_layers.reserve (2);
output_layers.push_back (dl_out.layer ());
output_layers.push_back (dl_out2.layer ());
db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both);
db::local_processor<db::Edge, db::Edge, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (edges.store ()->threads ());
proc.run (&op, edges.layer (), other_deep->merged_deep_layer ().layer (), output_layers);
return std::make_pair (new db::DeepEdges (dl_out), new db::DeepEdges (dl_out2));
}
RegionDelegate *DeepEdges::pull_generic (const Region &other) const
{
std::unique_ptr<db::DeepRegion> dr_holder;

View File

@ -133,10 +133,12 @@ public:
virtual EdgesDelegate *merged () const;
virtual EdgesDelegate *and_with (const Edges &other) const;
virtual EdgesDelegate *and_with (const Region &other) const;
virtual EdgesDelegate *not_with (const Edges &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Edges &) const;
virtual EdgesDelegate *and_with (const Region &other) const;
virtual EdgesDelegate *not_with (const Region &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Region &) const;
virtual EdgesDelegate *xor_with (const Edges &other) const;
@ -149,6 +151,7 @@ public:
virtual EdgesDelegate *inside_part (const Region &other) const;
virtual EdgesDelegate *outside_part (const Region &other) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> inside_outside_part_pair (const Region &) const;
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const;
@ -179,12 +182,14 @@ private:
void ensure_merged_edges_valid () const;
const DeepLayer &merged_deep_layer () const;
DeepLayer and_or_not_with(const DeepEdges *other, EdgeBoolOp op) const;
DeepLayer edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const;
std::pair<DeepLayer, DeepLayer> edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t op, bool include_borders) const;
EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const db::EdgesCheckOptions &options) const;
virtual EdgesDelegate *pull_generic (const Edges &edges) const;
virtual RegionDelegate *pull_generic (const Region &region) const;
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, bool invert) const;
virtual EdgesDelegate *selected_interacting_generic (const Region &region, bool invert) const;
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const;
virtual EdgesDelegate *selected_interacting_generic (const Region &region, EdgeInteractionMode mode, bool inverse) const;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair_generic (const Region &region, EdgeInteractionMode mode) const;
DeepEdges *apply_filter (const EdgeFilterBase &filter) const;
template <class Result, class OutputContainer> OutputContainer *processed_impl (const edge_processor<Result> &filter) const;

View File

@ -36,7 +36,7 @@ namespace db
/**
* @brief A common definition for the boolean operations available on edges
*/
enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd, EdgeIntersections };
enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd, EdgeIntersections, EdgeAndNot /*not always supported*/ };
struct OrJoinOp
{
@ -87,7 +87,13 @@ struct EdgeBooleanCluster
typedef db::Edge::coord_type coord_type;
EdgeBooleanCluster (OutputContainer *output, EdgeBoolOp op)
: mp_output (output), m_op (op)
: mp_output (output), mp_output2 (0), m_op (op)
{
// .. nothing yet ..
}
EdgeBooleanCluster (OutputContainer *output, OutputContainer *output2, EdgeBoolOp op)
: mp_output (output), mp_output2 (output2), m_op (op)
{
// .. nothing yet ..
}
@ -99,11 +105,13 @@ struct EdgeBooleanCluster
// shortcut for single edge
if (begin () + 1 == end ()) {
if (begin ()->second == 0) {
if (m_op != EdgeAnd) {
if (m_op == EdgeAndNot) {
mp_output2->insert (*(begin ()->first));
} else if (m_op != EdgeAnd) {
mp_output->insert (*(begin ()->first));
}
} else {
if (m_op != EdgeAnd && m_op != EdgeNot) {
if (m_op != EdgeAnd && m_op != EdgeNot && m_op != EdgeAndNot) {
mp_output->insert (*(begin ()->first));
}
}
@ -174,19 +182,34 @@ struct EdgeBooleanCluster
if (b.begin () == b.end ()) {
// optimize for empty b
if (m_op != EdgeAnd) {
OutputContainer *oc = 0;
if (m_op == EdgeAndNot) {
oc = mp_output;
} else if (m_op != EdgeAnd) {
oc = mp_output;
}
if (oc) {
for (tl::interval_map<db::Coord, int>::const_iterator ib = b.begin (); ib != b.end (); ++ib) {
if (ib->second > 0) {
mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n))));
oc->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n))));
} else if (ib->second < 0) {
mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n))));
oc->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n))));
}
}
}
} else {
if (m_op == EdgeAnd) {
tl::interval_map<db::Coord, int> q2;
if (m_op == EdgeAnd || m_op == EdgeAndNot) {
if (m_op == EdgeAndNot) {
q2 = q;
for (tl::interval_map<db::Coord, int>::const_iterator ib = b.begin (); ib != b.end (); ++ib) {
q2.add (ib->first.first, ib->first.second, ib->second, not_jop);
}
}
for (tl::interval_map<db::Coord, int>::const_iterator ib = b.begin (); ib != b.end (); ++ib) {
q.add (ib->first.first, ib->first.second, ib->second, and_jop);
}
@ -208,12 +231,20 @@ struct EdgeBooleanCluster
}
}
for (tl::interval_map<db::Coord, int>::const_iterator iq = q2.begin (); iq != q2.end (); ++iq) {
if (iq->second > 0) {
mp_output2->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n))));
} else if (iq->second < 0) {
mp_output2->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n))));
}
}
}
}
private:
OutputContainer *mp_output;
OutputContainer *mp_output, *mp_output2;
db::EdgeBoolOp m_op;
};
@ -221,8 +252,8 @@ template <class OutputContainer>
struct EdgeBooleanClusterCollector
: public db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> >
{
EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op)
: db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (output, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/),
EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op, OutputContainer *output2 = 0)
: db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (output, output2, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/),
mp_output (output), mp_intersections (op == EdgeIntersections ? output : 0)
{
// .. nothing yet ..
@ -377,6 +408,12 @@ public:
typedef Iterator const_iterator;
ShapesToOutputContainerAdaptor ()
: mp_shapes (0)
{
// .. nothing yet ..
}
ShapesToOutputContainerAdaptor (db::Shapes &shapes)
: mp_shapes (&shapes)
{
@ -413,8 +450,14 @@ struct DB_PUBLIC EdgeBooleanClusterCollectorToShapes
{
}
EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op, db::Shapes *output2)
: EdgeBooleanClusterCollector<ShapesToOutputContainerAdaptor> (&m_adaptor, op, &m_adaptor2), m_adaptor (*output), m_adaptor2 (*output2)
{
}
private:
ShapesToOutputContainerAdaptor m_adaptor;
ShapesToOutputContainerAdaptor m_adaptor2;
};
}

View File

@ -560,8 +560,8 @@ private:
// -------------------------------------------------------------------------------
// EdgePolygonOp implementation
EdgePolygonOp::EdgePolygonOp (bool outside, bool include_touching, int polygon_mode)
: m_outside (outside), m_include_touching (include_touching),
EdgePolygonOp::EdgePolygonOp (EdgePolygonOp::mode_t mode, bool include_touching, int polygon_mode)
: m_mode (mode), m_include_touching (include_touching),
m_function (polygon_mode),
m_wcp_n (0), m_wcp_s (0)
{
@ -572,27 +572,30 @@ void EdgePolygonOp::reset ()
m_wcp_n = m_wcp_s = 0;
}
bool EdgePolygonOp::select_edge (bool horizontal, property_type p)
int EdgePolygonOp::select_edge (bool horizontal, property_type p)
{
if (p == 0) {
return 0;
}
return false;
bool inside;
} else if (horizontal) {
bool res;
if (horizontal) {
if (m_include_touching) {
res = (m_function (m_wcp_n) || m_function (m_wcp_s));
inside = (m_function (m_wcp_n) || m_function (m_wcp_s));
} else {
res = (m_function (m_wcp_n) && m_function (m_wcp_s));
inside = (m_function (m_wcp_n) && m_function (m_wcp_s));
}
return m_outside ? !res : res;
} else {
inside = m_function (m_wcp_n);
}
return m_outside ? !m_function (m_wcp_n) : m_function (m_wcp_n);
if (m_mode == Inside) {
return inside ? 1 : 0;
} else if (m_mode == Outside) {
return inside ? 0 : 1;
} else {
return inside ? 1 : 2;
}
}
@ -1626,6 +1629,7 @@ public:
void reset ()
{
mp_es->reset_stop ();
mp_op->reset ();
}
@ -1634,6 +1638,11 @@ public:
return mp_op->is_reset ();
}
bool can_stop ()
{
return mp_es->can_stop ();
}
void reserve (size_t n)
{
mp_op->reserve (n);
@ -1705,10 +1714,11 @@ public:
void select_edge (const WorkEdge &e)
{
if (mp_op->select_edge (e.dy () == 0, e.prop)) {
mp_es->put (e);
int tag = mp_op->select_edge (e.dy () == 0, e.prop);
if (tag > 0) {
mp_es->put (e, (unsigned int) tag);
#ifdef DEBUG_EDGE_PROCESSOR
printf ("put(%s)\n", e.to_string().c_str());
printf ("put(%s, %d)\n", e.to_string().c_str(), tag);
#endif
}
}
@ -1937,6 +1947,19 @@ public:
return true;
}
/**
* @brief Gets a value indicating whether the generator wants to stop
*/
bool can_stop ()
{
for (std::vector<EdgeProcessorState>::iterator s = m_states.begin (); s != m_states.end (); ++s) {
if (s->can_stop ()) {
return true;
}
}
return false;
}
/**
* @brief Reserve memory n edges
*/
@ -2410,7 +2433,7 @@ EdgeProcessor::redo_or_process (const std::vector<std::pair<db::EdgeSink *, db::
y = edge_ymin ((*mp_work_edges) [0]);
future = mp_work_edges->begin ();
for (std::vector <WorkEdge>::iterator current = mp_work_edges->begin (); current != mp_work_edges->end (); ) {
for (std::vector <WorkEdge>::iterator current = mp_work_edges->begin (); current != mp_work_edges->end () && ! gs.can_stop (); ) {
if (m_report_progress) {
double p = double (std::distance (mp_work_edges->begin (), current)) / double (mp_work_edges->size ());

View File

@ -56,7 +56,7 @@ public:
/**
* @brief Constructor
*/
EdgeSink () { }
EdgeSink () : m_can_stop (false) { }
/**
* @brief Destructor
@ -87,6 +87,15 @@ public:
*/
virtual void put (const db::Edge &) { }
/**
* @brief Deliver a tagged edge
*
* This method delivers an edge that ends or starts at the current scanline.
* This version includes a tag which is generated when using "select_edge".
* A tag is a value > 0 returned by "select_edge".
*/
virtual void put (const db::Edge &, int /*tag*/) { }
/**
* @brief Deliver an edge that crosses the scanline
*
@ -114,6 +123,38 @@ public:
* @brief Signal the end of a scanline at the given y coordinate
*/
virtual void end_scanline (db::Coord /*y*/) { }
/**
* @brief Gets a value indicating that the generator wants to stop
*/
bool can_stop () const
{
return m_can_stop;
}
/**
* @brief Resets the stop request
*/
void reset_stop ()
{
m_can_stop = false;
}
protected:
/**
* @brief Sets the stop request
*
* The scanner can choose to stop once the request is set.
* This is useful for implementing receivers that can stop once a
* specific condition is found.
*/
void request_stop ()
{
m_can_stop = true;
}
private:
bool m_can_stop;
};
/**
@ -130,15 +171,15 @@ public:
/**
* @brief Constructor connecting this receiver to an external edge vector
*/
EdgeContainer (std::vector<db::Edge> &edges, bool clear = false)
: EdgeSink (), mp_edges (&edges), m_clear (clear)
EdgeContainer (std::vector<db::Edge> &edges, bool clear = false, int tag = 0, EdgeContainer *chained = 0)
: EdgeSink (), mp_edges (&edges), m_clear (clear), m_tag (tag), mp_chained (chained)
{ }
/**
* @brief Constructor using an internal edge vector
*/
EdgeContainer ()
: EdgeSink (), mp_edges (&m_edges), m_clear (false)
EdgeContainer (int tag = 0, EdgeContainer *chained = 0)
: EdgeSink (), mp_edges (&m_edges), m_clear (false), m_tag (tag), mp_chained (chained)
{ }
/**
@ -167,6 +208,9 @@ public:
// The single-shot scheme is a easy way to overcome problems with multiple start/flush brackets (i.e. on size filter)
m_clear = false;
}
if (mp_chained) {
mp_chained->start ();
}
}
/**
@ -175,12 +219,30 @@ public:
virtual void put (const db::Edge &e)
{
mp_edges->push_back (e);
if (mp_chained) {
mp_chained->put (e);
}
}
/**
* @brief Implementation of the EdgeSink interface
*/
virtual void put (const db::Edge &e, int tag)
{
if (m_tag == 0 || tag == m_tag) {
mp_edges->push_back (e);
}
if (mp_chained) {
mp_chained->put (e, tag);
}
}
private:
std::vector<db::Edge> m_edges;
std::vector<db::Edge> *mp_edges;
bool m_clear;
int m_tag;
EdgeContainer *mp_chained;
};
/**
@ -204,7 +266,7 @@ public:
virtual void reset () { }
virtual void reserve (size_t /*n*/) { }
virtual int edge (bool /*north*/, bool /*enter*/, property_type /*p*/) { return 0; }
virtual bool select_edge (bool /*horizontal*/, property_type /*p*/) { return false; }
virtual int select_edge (bool /*horizontal*/, property_type /*p*/) { return 0; }
virtual int compare_ns () const { return 0; }
virtual bool is_reset () const { return false; }
virtual bool prefer_touch () const { return false; }
@ -215,8 +277,8 @@ public:
* @brief An intersection detector
*
* This edge evaluator will not produce output edges but rather record the
* property pairs of polygons intersecting. Only intersections (overlaps)
* are recorded. Touching contacts are not recorded.
* property pairs of polygons intersecting or interacting in the specified
* way.
*
* It will build a set of property pairs, where the lower property value
* is the first one of the pairs.
@ -491,6 +553,15 @@ class DB_PUBLIC EdgePolygonOp
: public db::EdgeEvaluatorBase
{
public:
/**
* @brief The operation mode
*/
enum mode_t {
Inside = 0, // Selects inside edges
Outside = 1, // Selects outside edges
Both = 2 // Selects both (inside -> tag #1, outside -> tag #2)
};
/**
* @brief Constructor
*
@ -498,17 +569,18 @@ public:
* @param include_touching If true, edges on the polygon's border will be considered "inside" of polygons
* @param polygon_mode Determines how the polygon edges on property 0 are interpreted (see merge operators)
*/
EdgePolygonOp (bool outside = false, bool include_touching = true, int polygon_mode = -1);
EdgePolygonOp (mode_t mode = Inside, bool include_touching = true, int polygon_mode = -1);
virtual void reset ();
virtual bool select_edge (bool horizontal, property_type p);
virtual int select_edge (bool horizontal, property_type p);
virtual int edge (bool north, bool enter, property_type p);
virtual bool is_reset () const;
virtual bool prefer_touch () const;
virtual bool selects_edges () const;
private:
bool m_outside, m_include_touching;
mode_t m_mode;
bool m_include_touching;
db::ParametrizedInsideFunc m_function;
int m_wcp_n, m_wcp_s;
};

View File

@ -274,6 +274,16 @@ public:
return mp_delegate;
}
/**
* @brief Takes the underlying delegate object
*/
EdgesDelegate *take_delegate ()
{
EdgesDelegate *delegate = mp_delegate;
mp_delegate = 0;
return delegate;
}
/**
* @brief Sets the base verbosity
*
@ -794,6 +804,24 @@ public:
return *this;
}
/**
* @brief Boolean AND/NOT operator in the same operation
*/
std::pair<Edges, Edges> andnot (const Edges &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->andnot_with (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Boolean AND/NOT operator with a region in the same operation
*/
std::pair<Edges, Edges> andnot (const Region &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->andnot_with (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Boolean XOR operator
*/
@ -974,6 +1002,17 @@ public:
return Edges (mp_delegate->outside_part (other));
}
/**
* @brief Returns the eges inside the given region and the ones outside the region.
*
* This method combined both inside_part and outside_part.
*/
std::pair<Edges, Edges> inside_outside_part (const Region &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->inside_outside_part_pair (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Selects all polygons of the other region set which overlap or touch edges from this edge set
*
@ -1037,6 +1076,251 @@ public:
return Edges (mp_delegate->selected_not_interacting (other));
}
/**
* @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region together with the ones that do not
*/
std::pair<Edges, Edges> selected_interacting_differential (const Region &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_interacting_pair (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Selects all edges of this edge collection which are completely outside polygons from the region
*
* Merged semantics applies.
*/
Edges &select_outside (const Region &other)
{
set_delegate (mp_delegate->selected_outside (other));
return *this;
}
/**
* @brief Selects all edges of this edge collection which are not completely outside polygons from the region
*
* Merged semantics applies.
*/
Edges &select_not_outside (const Region &other)
{
set_delegate (mp_delegate->selected_not_outside (other));
return *this;
}
/**
* @brief Returns all edges of this edge collection which are completely outside polygons from the region
*
* This method is an out-of-place version of select_outside.
*
* Merged semantics applies.
*/
Edges selected_outside (const Region &other) const
{
return Edges (mp_delegate->selected_outside (other));
}
/**
* @brief Returns all edges of this edge collection which are not completely outside polygons from the region
*
* This method is an out-of-place version of select_not_outside.
*
* Merged semantics applies.
*/
Edges selected_not_outside (const Region &other) const
{
return Edges (mp_delegate->selected_not_outside (other));
}
/**
* @brief Returns all edges of this which are completely outside polygons from the region and the opposite ones at the same time
*
* This method is equivalent to calling selected_outside and selected_not_outside, but faster.
*
* Merged semantics applies.
*/
std::pair<Edges, Edges> selected_outside_differential (const Region &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_outside_pair (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Selects all edges of this edge collection which are completely inside polygons from the region
*
* Merged semantics applies.
*/
Edges &select_inside (const Region &other)
{
set_delegate (mp_delegate->selected_inside (other));
return *this;
}
/**
* @brief Selects all edges of this edge collection which are not completely inside polygons from the region
*
* Merged semantics applies.
*/
Edges &select_not_inside (const Region &other)
{
set_delegate (mp_delegate->selected_not_inside (other));
return *this;
}
/**
* @brief Returns all edges of this which are completely inside polygons from the region
*
* This method is an out-of-place version of select_inside.
*
* Merged semantics applies.
*/
Edges selected_inside (const Region &other) const
{
return Edges (mp_delegate->selected_inside (other));
}
/**
* @brief Returns all edges of this which are not completely inside polygons from the region
*
* This method is an out-of-place version of select_not_inside.
*
* Merged semantics applies.
*/
Edges selected_not_inside (const Region &other) const
{
return Edges (mp_delegate->selected_not_inside (other));
}
/**
* @brief Returns all edges of this which are completely inside polygons from the region and the opposite ones at the same time
*
* This method is equivalent to calling selected_inside and selected_not_inside, but faster.
*
* Merged semantics applies.
*/
std::pair<Edges, Edges> selected_inside_differential (const Region &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_inside_pair (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Selects all edges of this edge collection which are completely outside edges from the other edge collection
*
* Merged semantics applies.
*/
Edges &select_outside (const Edges &other)
{
set_delegate (mp_delegate->selected_outside (other));
return *this;
}
/**
* @brief Selects all edges of this edge collection which are not completely outside edges from the other edge collection
*
* Merged semantics applies.
*/
Edges &select_not_outside (const Edges &other)
{
set_delegate (mp_delegate->selected_not_outside (other));
return *this;
}
/**
* @brief Returns all edges of this edge collection which are completely outside edges from the other edge collection
*
* This method is an out-of-place version of select_outside.
*
* Merged semantics applies.
*/
Edges selected_outside (const Edges &other) const
{
return Edges (mp_delegate->selected_outside (other));
}
/**
* @brief Returns all edges of this edge collection which are not completely outside edges from the other edge collection
*
* This method is an out-of-place version of select_not_outside.
*
* Merged semantics applies.
*/
Edges selected_not_outside (const Edges &other) const
{
return Edges (mp_delegate->selected_not_outside (other));
}
/**
* @brief Returns all edges of this which are completely outside edges from the other edge collection and the opposite ones at the same time
*
* This method is equivalent to calling selected_outside and selected_not_outside, but faster.
*
* Merged semantics applies.
*/
std::pair<Edges, Edges> selected_outside_differential (const Edges &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_outside_pair (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Selects all edges of this edge collection which are completely inside edges from the other edge collection
*
* Merged semantics applies.
*/
Edges &select_inside (const Edges &other)
{
set_delegate (mp_delegate->selected_inside (other));
return *this;
}
/**
* @brief Selects all edges of this edge collection which are not completely inside edges from the other edge collection
*
* Merged semantics applies.
*/
Edges &select_not_inside (const Edges &other)
{
set_delegate (mp_delegate->selected_not_inside (other));
return *this;
}
/**
* @brief Returns all edges of this which are completely inside edgess from the other edge collection
*
* This method is an out-of-place version of select_inside.
*
* Merged semantics applies.
*/
Edges selected_inside (const Edges &other) const
{
return Edges (mp_delegate->selected_inside (other));
}
/**
* @brief Returns all edges of this which are not completely inside edges from the other edge collection
*
* This method is an out-of-place version of select_not_inside.
*
* Merged semantics applies.
*/
Edges selected_not_inside (const Edges &other) const
{
return Edges (mp_delegate->selected_not_inside (other));
}
/**
* @brief Returns all edges of this which are completely inside edges from the other edge collection and the opposite ones at the same time
*
* This method is equivalent to calling selected_inside and selected_not_inside, but faster.
*
* Merged semantics applies.
*/
std::pair<Edges, Edges> selected_inside_differential (const Edges &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_inside_pair (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Selects all edges of this edge set which overlap or touch with edges from the other edge set
*
@ -1059,6 +1343,15 @@ public:
return Edges (mp_delegate->selected_interacting (other));
}
/**
* @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set together with the ones that do not
*/
std::pair<Edges, Edges> selected_interacting_differential (const Edges &other) const
{
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_interacting_pair (other);
return std::pair<Edges, Edges> (Edges (p.first), Edges (p.second));
}
/**
* @brief Selects all edges of this edge set which do not overlap or touch with edges from the other edge set
*

View File

@ -294,9 +294,11 @@ public:
virtual EdgesDelegate *merged () const = 0;
virtual EdgesDelegate *and_with (const Edges &other) const = 0;
virtual EdgesDelegate *and_with (const Region &other) const = 0;
virtual EdgesDelegate *not_with (const Edges &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Edges &) const = 0;
virtual EdgesDelegate *and_with (const Region &other) const = 0;
virtual EdgesDelegate *not_with (const Region &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Region &) const = 0;
virtual EdgesDelegate *xor_with (const Edges &other) const = 0;
virtual EdgesDelegate *or_with (const Edges &other) const = 0;
virtual EdgesDelegate *add_in_place (const Edges &other) = 0;
@ -307,12 +309,28 @@ public:
virtual EdgesDelegate *inside_part (const Region &other) const = 0;
virtual EdgesDelegate *outside_part (const Region &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> inside_outside_part_pair (const Region &other) const = 0;
virtual RegionDelegate *pull_interacting (const Region &) const = 0;
virtual EdgesDelegate *pull_interacting (const Edges &) const = 0;
virtual EdgesDelegate *selected_interacting (const Region &other) const = 0;
virtual EdgesDelegate *selected_not_interacting (const Region &other) const = 0;
virtual EdgesDelegate *selected_interacting (const Edges &other) const = 0;
virtual EdgesDelegate *selected_not_interacting (const Edges &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Edges &other) const = 0;
virtual EdgesDelegate *selected_outside (const Region &other) const = 0;
virtual EdgesDelegate *selected_not_outside (const Region &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_outside_pair (const Region &other) const = 0;
virtual EdgesDelegate *selected_inside (const Region &other) const = 0;
virtual EdgesDelegate *selected_not_inside (const Region &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_inside_pair (const Region &other) const = 0;
virtual EdgesDelegate *selected_outside (const Edges &other) const = 0;
virtual EdgesDelegate *selected_not_outside (const Edges &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_outside_pair (const Edges &other) const = 0;
virtual EdgesDelegate *selected_inside (const Edges &other) const = 0;
virtual EdgesDelegate *selected_not_inside (const Edges &other) const = 0;
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_inside_pair (const Edges &other) const = 0;
virtual EdgesDelegate *in (const Edges &other, bool invert) const = 0;

View File

@ -177,7 +177,7 @@ extended_edge (const db::Edge &edge, db::Coord ext_b, db::Coord ext_e, db::Coord
// -------------------------------------------------------------------------------------------------------------
// EdgeSegmentSelector processor
EdgeSegmentSelector::EdgeSegmentSelector (int mode, db::Edges::length_type length, double fraction)
EdgeSegmentSelector::EdgeSegmentSelector (int mode, Edge::distance_type length, double fraction)
: m_mode (mode), m_length (length), m_fraction (fraction)
{ }
@ -274,4 +274,96 @@ EdgeOrientationFilter::selected (const db::Edge &edge) const
}
}
// -------------------------------------------------------------------------------------------------------------
// Edge to Edge relation implementation
bool edge_interacts (const db::Edge &a, const db::Edge &b)
{
return a.intersect (b);
}
bool edge_is_inside (const db::Edge &a, const db::Edge &b)
{
return b.contains (a.p1 ()) && b.contains (a.p2 ());
}
bool edge_is_outside (const db::Edge &a, const db::Edge &b)
{
if (a.parallel (b)) {
return ! a.coincident (b);
} else {
auto pt = a.intersect_point (b);
if (! pt.first) {
// no intersection -> outside
return true;
}
return ! b.contains_excl (pt.second) || ! a.contains_excl (pt.second);
}
}
// -------------------------------------------------------------------------------------------------------------
// Edge to Polygon relation implementation
bool edge_interacts (const db::Edge &a, const db::Polygon &b)
{
return db::interact (b, a);
}
namespace {
struct DetectTagEdgeSink
: public db::EdgeSink
{
DetectTagEdgeSink (int tag)
: fail_tag (tag), result (true) { }
virtual void put (const db::Edge &, int tag)
{
if (tag == fail_tag) {
result = false;
request_stop ();
}
}
int fail_tag;
bool result;
};
}
static bool
edge_is_inside_or_outside (bool outside, const db::Edge &a, const db::Polygon &b)
{
db::EdgeProcessor ep;
ep.insert (b, 0);
ep.insert (a, 1);
DetectTagEdgeSink es (outside ? 1 : 2); // 2 is the "outside" tag in "Both" mode -> this makes inside fail
db::EdgePolygonOp op (db::EdgePolygonOp::Both, true /*include borders*/);
ep.process (es, op);
return es.result;
}
bool edge_is_inside (const db::Edge &a, const db::Polygon &b)
{
// shortcuts
if (!a.bbox ().inside (b.box ())) {
return false;
}
return edge_is_inside_or_outside (false, a, b);
}
bool edge_is_outside (const db::Edge &a, const db::Polygon &b)
{
// shortcuts
if (! a.bbox ().overlaps (b.box ())) {
return true;
}
return edge_is_inside_or_outside (true, a, b);
}
}

View File

@ -34,6 +34,11 @@
namespace db {
/**
* @brief The operation mode for the interaction filters
*/
enum EdgeInteractionMode { EdgesInteract, EdgesInside, EdgesOutside };
class PolygonSink;
/**
@ -241,6 +246,21 @@ private:
EdgeAngleChecker m_checker;
};
/**
* @brief A predicate defining edge a interacts with b
*/
DB_PUBLIC bool edge_interacts (const db::Edge &a, const db::Edge &b);
/**
* @brief A predicate defining edge a is "inside" b
*/
DB_PUBLIC bool edge_is_inside (const db::Edge &a, const db::Edge &b);
/**
* @brief A predicate defining edge a is "outside" b
*/
DB_PUBLIC bool edge_is_outside (const db::Edge &a, const db::Edge &b);
/**
* @brief A helper class for the edge interaction functionality which acts as an edge pair receiver
*/
@ -249,31 +269,65 @@ class edge_interaction_filter
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
edge_interaction_filter (OutputContainer &output)
: mp_output (&output)
edge_interaction_filter (OutputContainer &output, EdgeInteractionMode mode)
: mp_output (&output), m_mode (mode)
{
// .. nothing yet ..
}
void finish (const db::Edge *o, size_t p)
{
if (p == 0 && m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) {
mp_output->insert (*o);
}
}
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
// Select the edges which intersect
if (p1 != p2) {
const db::Edge *o = p1 > p2 ? o2 : o1;
const db::Edge *oo = p1 > p2 ? o1 : o2;
if (o->intersect (*oo)) {
if ((m_mode == EdgesInteract && db::edge_interacts (*o, *oo)) ||
(m_mode == EdgesInside && db::edge_is_inside (*o, *oo))) {
if (m_seen.insert (o).second) {
mp_output->insert (*o);
}
} else if (m_mode == EdgesOutside && ! db::edge_is_outside (*o, *oo)) {
// In this case we need to collect edges which are outside always - we report those on "finished".
m_seen.insert (o);
}
}
}
private:
OutputContainer *mp_output;
std::set<const db::Edge *> m_seen;
EdgeInteractionMode m_mode;
};
/**
* @brief A predicate defining edge a interacts with polygon b
*/
DB_PUBLIC bool edge_interacts (const db::Edge &a, const db::Polygon &b);
/**
* @brief A predicate defining edge a is "inside" polygon b
*/
DB_PUBLIC bool edge_is_inside (const db::Edge &a, const db::Polygon &b);
/**
* @brief A predicate defining edge a is "outside" polygon b
*/
DB_PUBLIC bool edge_is_outside (const db::Edge &a, const db::Polygon &b);
/**
* @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver
*
@ -288,28 +342,64 @@ class edge_to_region_interaction_filter
: public db::box_scanner_receiver2<db::Edge, size_t, db::Polygon, size_t>
{
public:
edge_to_region_interaction_filter (OutputContainer &output)
: mp_output (&output)
edge_to_region_interaction_filter (OutputContainer *output, EdgeInteractionMode mode)
: mp_output (output), m_mode (mode)
{
// .. nothing yet ..
}
void finish (const OutputType *o)
{
if (m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) {
mp_output->insert (*o);
}
}
void finish1 (const db::Edge *o, size_t /*p*/)
{
const OutputType *ep = 0;
tl::select (ep, o, (const db::Polygon *) 0);
if (ep) {
finish (ep);
}
}
void finish2 (const db::Polygon *o, size_t /*p*/)
{
const OutputType *ep = 0;
tl::select (ep, (const db::Edge *) 0, o);
if (ep) {
finish (ep);
}
}
void add (const db::Edge *e, size_t, const db::Polygon *p, size_t)
{
const OutputType *ep = 0;
tl::select (ep, e, p);
if (m_seen.find (ep) == m_seen.end ()) {
if (db::interact (*p, *e)) {
if ((m_mode == EdgesInteract && db::edge_interacts (*e, *p)) ||
(m_mode == EdgesInside && db::edge_is_inside (*e, *p))) {
m_seen.insert (ep);
mp_output->insert (*ep);
} else if (m_mode == EdgesOutside && ! db::edge_is_outside (*e, *p)) {
// In this case we need to collect edges which are outside always - we report those on "finished".
m_seen.insert (ep);
}
}
}
private:
OutputContainer *mp_output;
std::set<const OutputType *> m_seen;
EdgeInteractionMode m_mode;
};
/**
@ -407,7 +497,7 @@ class DB_PUBLIC EdgeSegmentSelector
: public EdgeProcessorBase
{
public:
EdgeSegmentSelector (int mode, db::Edges::length_type length, double fraction);
EdgeSegmentSelector (int mode, Edge::distance_type length, double fraction);
~EdgeSegmentSelector ();
virtual void process (const db::Edge &edge, std::vector<db::Edge> &res) const;
@ -420,7 +510,7 @@ public:
private:
int m_mode;
db::Edges::length_type m_length;
db::Edge::distance_type m_length;
double m_fraction;
db::MagnificationReducer m_vars;
};

View File

@ -75,9 +75,11 @@ public:
virtual EdgesDelegate *merged () const { return new EmptyEdges (); }
virtual EdgesDelegate *and_with (const Edges &) const { return new EmptyEdges (); }
virtual EdgesDelegate *and_with (const Region &) const { return new EmptyEdges (); }
virtual EdgesDelegate *not_with (const Edges &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual EdgesDelegate *and_with (const Region &) const { return new EmptyEdges (); }
virtual EdgesDelegate *not_with (const Region &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> andnot_with (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual EdgesDelegate *xor_with (const Edges &other) const;
virtual EdgesDelegate *or_with (const Edges &other) const;
virtual EdgesDelegate *add_in_place (const Edges &other);
@ -88,12 +90,29 @@ public:
virtual EdgesDelegate *inside_part (const Region &) const { return new EmptyEdges (); }
virtual EdgesDelegate *outside_part (const Region &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> inside_outside_part_pair (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual RegionDelegate *pull_interacting (const Region &) const;
virtual EdgesDelegate *pull_interacting (const Edges &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_interacting (const Edges &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_not_interacting (const Edges &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_interacting (const Region &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_not_interacting (const Region &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual EdgesDelegate *selected_outside (const Region &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_not_outside (const Region &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_outside_pair (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual EdgesDelegate *selected_inside (const Region &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_not_inside (const Region &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_inside_pair (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual EdgesDelegate *selected_outside (const Edges &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_not_outside (const Edges &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_outside_pair (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual EdgesDelegate *selected_inside (const Edges &) const { return new EmptyEdges (); }
virtual EdgesDelegate *selected_not_inside (const Edges &) const { return new EmptyEdges (); }
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_inside_pair (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
virtual EdgesDelegate *in (const Edges &, bool) const { return new EmptyEdges (); }

View File

@ -152,30 +152,6 @@ public:
}
}
size_t filled_pixels () const
{
size_t n = 0;
for (std::vector<db::AreaMap>::const_iterator a = m_area_maps.begin (); a != m_area_maps.end (); ++a) {
db::Coord nx = db::Coord (a->nx ());
db::Coord ny = db::Coord (a->ny ());
db::AreaMap::area_type amax = a->pixel_area ();
double n = 0;
for (size_t i = 0; i < size_t (nx); ++i) {
for (size_t j = 0; j < size_t (ny); ++j) {
if (a->get (i, j) >= amax) {
n += 1;
}
}
}
}
return n;
}
const db::Point &p0 () const { return m_origin; }
unsigned int row_steps () const { return m_row_steps; }

View File

@ -1366,9 +1366,10 @@ compute_area_and_perimeter_of_net_shapes (const db::hier_clusters<db::NetShape>
perimeter = ap_collector.perimeter ();
}
static void
static db::Point
get_merged_shapes_of_net (const db::hier_clusters<db::NetShape> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Shapes &shapes)
{
db::Point ref;
db::EdgeProcessor ep;
// count vertices and reserve space
@ -1380,16 +1381,76 @@ get_merged_shapes_of_net (const db::hier_clusters<db::NetShape> &clusters, db::c
size_t p = 0;
for (db::recursive_cluster_shape_iterator<db::NetShape> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
ep.insert (rci.trans () * rci->polygon_ref (), ++p);
if (p == 0) {
db::PolygonRef pr = (rci.trans () * rci->polygon_ref ());
db::PolygonRef::polygon_edge_iterator e = pr.begin_edge ();
if (! e.at_end ()) {
// pick one reference point for the label
ref = (*e).p1 ();
ep.insert (pr, ++p);
}
} else {
ep.insert (rci.trans () * rci->polygon_ref (), ++p);
}
}
db::ShapeGenerator sg (shapes);
db::PolygonGenerator pg (sg, false);
db::SimpleMerge op;
ep.process (pg, op);
return ref;
}
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes)
static std::string
create_antenna_msg (double agate, db::Polygon::area_type agate_int, double gate_area_factor, db::Polygon::perimeter_type pgate_int, double gate_perimeter_factor,
double ametal, db::Polygon::area_type ametal_int, double metal_area_factor, db::Polygon::perimeter_type pmetal_int, double metal_perimeter_factor,
const std::vector<std::pair<const db::Region *, double> > &diodes,
const std::vector<db::Polygon::area_type> &adiodes_int,
double r, double ratio, double dbu)
{
std::string msg;
msg += tl::sprintf ("agate_eff: %.12g, ", agate);
if (fabs (gate_area_factor) > 1e-6) {
msg += tl::sprintf ("agate: %.12g, agate_factor: %.12g, ", agate_int * dbu * dbu, gate_area_factor);
}
if (fabs (gate_perimeter_factor) > 1e-6) {
msg += tl::sprintf ("pgate: %.12g, pgate_factor: %.12g, ", pgate_int * dbu * dbu, gate_perimeter_factor);
}
msg += tl::sprintf ("ametal_eff: %.12g, ", ametal);
if (fabs (metal_area_factor) > 1e-6) {
msg += tl::sprintf ("ametal: %.12g, ametal_factor: %.12g, ", ametal_int * dbu * dbu, metal_area_factor);
}
if (fabs (metal_perimeter_factor) > 1e-6) {
msg += tl::sprintf ("pmetal: %.12g, pmetal_factor: %.12g, ", pmetal_int * dbu * dbu, metal_perimeter_factor);
}
if (! adiodes_int.empty ()) {
msg += "adiodes: [";
for (auto d = adiodes_int.begin (); d != adiodes_int.end (); ++d) {
if (d != adiodes_int.begin ()) {
msg += ", ";
}
msg += tl::sprintf ("%.12g", *d * dbu * dbu);
}
msg += "], ";
}
if (! diodes.empty ()) {
msg += "diode_factors: [";
for (auto d = diodes.begin (); d != diodes.end (); ++d) {
if (d != diodes.begin ()) {
msg += ", ";
}
msg += tl::sprintf ("%.12g", d->second);
}
msg += "], ";
}
msg += tl::sprintf ("ratio: %.12g, ", ametal / agate);
msg += tl::sprintf ("max_ratio_eff: %.12g, ", r);
msg += tl::sprintf ("max_ratio: %.12g", ratio);
return msg;
}
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes, db::Texts *values)
{
// TODO: that's basically too much .. we only need the clusters
if (! m_netlist_extracted) {
@ -1401,6 +1462,11 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
db::DeepLayer dl (&dss (), m_layout_index, ly.insert_layer ());
db::DeepLayer dlv;
if (values) {
dlv = db::DeepLayer (&dss (), m_layout_index, ly.insert_layer ());
}
for (db::Layout::bottom_up_const_iterator cid = ly.begin_bottom_up (); cid != ly.end_bottom_up (); ++cid) {
const connected_clusters<db::NetShape> &clusters = m_net_clusters.clusters_per_cell (*cid);
@ -1408,6 +1474,8 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
continue;
}
std::vector<db::Polygon::area_type> adiodes_int;
for (connected_clusters<db::NetShape>::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) {
if (! clusters.is_root (*c)) {
@ -1417,13 +1485,18 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
double r = ratio;
bool skip = false;
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
adiodes_int.clear ();
adiodes_int.reserve (diodes.size ());
for (auto d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
db::Polygon::area_type adiode_int = 0;
db::Polygon::perimeter_type pdiode_int = 0;
compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (*d->first), adiode_int, pdiode_int);
adiodes_int.push_back (adiode_int);
if (fabs (d->second) < db::epsilon) {
if (adiode_int > 0) {
skip = true;
@ -1465,12 +1538,29 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
}
if (tl::verbosity () >= 50) {
tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r);
tl::info << "cell [" << ly.cell_name (*cid) << "]: " <<
create_antenna_msg (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
diodes, adiodes_int, r, ratio, dbu);
}
if (ametal / agate > r + db::epsilon) {
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), shapes);
db::Point ref = get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), shapes);
if (values) {
// generate a data string with the details of the antenna computation (intentionally like JSON)
std::string msg = create_antenna_msg (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
diodes, adiodes_int, r, ratio, dbu);
db::Shapes &shapesv = ly.cell (*cid).shapes (dlv.layer ());
shapesv.insert (db::Text (msg, db::Trans (ref - db::Point ())));
}
}
}
@ -1481,6 +1571,10 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
}
if (values) {
*values = db::Texts (new db::DeepTexts (dlv));
}
return db::Region (new db::DeepRegion (dl));
}

View File

@ -856,18 +856,18 @@ public:
* regardless of the diode's area.
* In other words: any diode will make the net safe against antenna discharge.
*/
db::Region antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ())
db::Region antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > (), db::Texts *values = 0)
{
return antenna_check (gate, 1.0, gate_perimeter_factor, metal, 1.0, metal_perimeter_factor, ratio, diodes);
return antenna_check (gate, 1.0, gate_perimeter_factor, metal, 1.0, metal_perimeter_factor, ratio, diodes, values);
}
/**
* @brief Variant of the antenna check not using the perimeter
* This version uses 0 for the perimeter factor hence not taking into account the perimeter at all.
*/
db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ())
db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > (), db::Texts *values = 0)
{
return antenna_check (gate, 1.0, 0.0, metal, 1.0, 0.0, ratio, diodes);
return antenna_check (gate, 1.0, 0.0, metal, 1.0, 0.0, ratio, diodes, values);
}
/**
@ -879,8 +879,10 @@ public:
*
* where f is the area scale factor and t the perimeter scale factor. This version allows to ignore the
* area contribution entirely and switch to a perimeter-based antenna check by setting f to zero.
*
* If values is non-null, texts explaining the violations are placed there.
*/
db::Region antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ());
db::Region antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > (), Texts *values = 0);
/**
* @brief Saves the database to the given path

View File

@ -46,55 +46,76 @@ namespace db
* The file follows the declaration-before-use principle
* (circuits before subcircuits, nets before use ...)
*
* Global statements:
* Main body:
* [version|description|unit|top|layer|connect|global|circuit|class|device|any]*
*
* [version]:
* version(<number>) - file format version [short key: V]
*
* [description]:
* description(<text>) - an arbitrary description text [short key: B]
*
* [unit]:
* unit(<unit>) - specifies the database unit [short key: U]
*
* [top]:
* top(<circuit>) - specifies the name of the top circuit [short key: W]
*
* [layer]:
* layer(<name> <source-spec>?) - define a layer [short key: L]
*
* [connect]:
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
*
* [global]:
* global(<layer> <net-name> ...)
* - connects the shapes of the layer with the given global
* nets [short key: G]
*
* [circuit]:
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
*
* [class]:
* class(<name> <template> [template-def]) - a device class definition (template: RES,CAP,...) [short key: K]
* device(<name> <class> [device-abstract-def])
*
* [device]:
* device(<name> <class> [device-abstract-terminal|any]*)
* - device abstract [short key: D]
*
* [circuit-def]:
* [boundary|property|circuit-net|circuit-pin|circuit-device|subcircuit|any]*
*
* [boundary-def]
*
* [property-def]*
*
* net(<id> [name]? [property-def]* [geometry-def]*)
* [circuit-net]:
* net(<id> [name]? [geometries-def])
* - net geometry [short key: N]
* A net declaration shall be there also if no geometry
* is present. The ID is a numerical shortcut for the net.
*
* [circuit-pin]:
* pin(<net-id> [name]?) - outgoing pin connection [short key: P]
* Statement order specifies pin order.
* device(<id> <abstract-or-class> [name]? [combined-device]* [terminal-route]* [device-def])
*
* [circuit-device]:
* device(<id> <abstract-or-class> [name|trans|combined-device|terminal-route|param|device-terminal|any]*)
* - device with connections [short key: D]
* circuit(<id> [name]? [subcircuit-def])
*
* [subcircuit]:
* circuit(<id> [name]? [property|trans|subcircuit-pin|any])
* - subcircuit with connections [short key: X]
*
* [boundary-def]:
*
* polygon([coord] ...) - defines a polygon [short key: Q]
* [boundary]:
* polygon([coord] ...) | - defines a polygon [short key: Q]
* "*" for <x> or <y> means take previous
* rect([coord] [coord]) - defines a rectangle [short key: R]
* coordinates are bottom/left and top/right
*
* [combined-device]:
*
* device(<abstract> [trans-def])
* device(<abstract> [trans])
* - specifies an additional device component
* (for combined devices) with abstract <abstract>
* and offset dx, dy.
*
* [terminal-route]:
*
* connect(<device-index> <outer-terminal-name> <inner-terminal-name>)
* - connects the outer terminal with the terminal
* of the device component with <device-index>:
@ -102,68 +123,70 @@ namespace db
* device etc.
*
* [name]:
*
* name(<name>) - specify net name [short key: I]
*
* [property-def]:
* [geometries-def]:
* [property|polygon|rect|text|any]*
*
* [property]:
* property(<prop-name> <prop-value>)
* - specifies a property value/key pair [short key: F]
* prop-name and prop-value are variant specifications
* in klayout notation: #x is an integer, ##y a floating-point
* value, a word or quoted literal is a string.
*
* [geometry-def]:
*
* [polygon]:
* polygon(<layer> [coord] ...) - defines a polygon [short key: Q]
* "*" for <x> or <y> means take previous
*
* [rect]:
* rect(<layer> [coord] [coord]) - defines a rectangle [short key: R]
* coordinates are bottom/left and top/right
*
* [text]:
* text(<layer> [text] [coord]) - defines a rectangle [short key: J]
*
* [coord]
*
* [coord]:
* <x> <y> - absolute coordinates
* (<x> <y>) - relative coordinates (reference is reset to 0,0
* for each net or terminal in device abstract)
*
* [template-def]:
* [template-param|template-terminal|any]*
*
* [template-param]:
* param(<name> <primary>? <default-value>*) - defines a template parameter [short key: E]
* ('primary' is a value: 0 or 1)
*
* [template-terminal]:
* terminal(<name>) - defines a terminal [short key: T]
*
* [device-abstract-def]:
*
* [device-abstract-terminal-def]*
*
* [device-abstract-terminal-def]:
*
* terminal(<terminal-name> [geometry-def]*)
* [device-abstract-terminal]:
* terminal(<terminal-name> [geometries-def])
* - specifies the terminal geometry [short key: T]
*
* [device-def]:
*
* [property-def]* - user properties
* [trans-def] - location of the device
* must be before terminal
* [param]:
* param(<name> <value>) - defines a parameter [short key: E]
*
* [device-terminal]:
* terminal(<terminal-name> <net-id>)
* - specifies connection of the terminal with
* a net (short key: T)
* - specifies connection of the terminal with a net (short key: T)
*
* [subcircuit-def]:
*
* [property-def]* - user properties
* [trans-def] - location of the subcircuit
* [subcircuit-pin]:
* pin(<pin-id> <net-id>) - specifies connection of the pin with a net [short key: P]
*
* [trans-def]:
*
* [trans]:
* location(<x> <y>) - location of the instance [short key: Y]
* rotation(<angle>) - rotation angle (in degree, default is 0) [short key: O]
* mirror - if specified, the instance is mirrored before rotation [short key: M]
* scale(<mag>) - magnification (default is 1) [short key: S]
*
* [any]:
* * |
* <token> |
* <token> ( [any]* ) |
* <float> |
* <quoted-string>
*/
namespace l2n_std_format

View File

@ -97,6 +97,13 @@ LayoutToNetlistStandardReader::read_int ()
return i;
}
bool
LayoutToNetlistStandardReader::try_read_int (int &i)
{
i = 0;
return m_ex.try_read (i);
}
db::Coord
LayoutToNetlistStandardReader::read_coord ()
{
@ -133,6 +140,50 @@ LayoutToNetlistStandardReader::skip ()
}
}
void LayoutToNetlistStandardReader::skip_element ()
{
std::string s;
double f;
if (m_ex.try_read_word (s)) {
// skip bracket elements after token key
Brace br (this);
while (br) {
skip_element ();
}
br.done ();
} else if (m_ex.test ("*")) {
// asterisk is allowed as element (e.g. inside point)
} else if (m_ex.try_read_quoted (s)) {
// skip string
} else if (m_ex.try_read (f)) {
// skip numeric value
} else {
Brace br (this);
if (br) {
// skip bracket elements without token
while (br) {
skip_element ();
}
br.done ();
} else {
throw tl::Exception (tl::to_string (tr ("Unexpected token")));
}
}
}
void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n)
{
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("File read: ")) + m_path);
@ -153,7 +204,7 @@ static db::Region &layer_by_name (db::LayoutToNetlist *l2n, const std::string &n
return *l;
}
void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, ObjectMap> *map_per_circuit)
void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::LayoutToNetlist *l2n, LayoutToNetlistStandardReader::Brace *nested, std::map<const db::Circuit *, ObjectMap> *map_per_circuit)
{
m_dbu = 0.001;
int version = 0;
@ -179,7 +230,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
db::LayoutLocker layout_locker (l2n ? l2n->internal_layout () : 0);
while (! at_end ()) {
while (nested ? *nested : ! at_end ()) {
if (test (skeys::version_key) || test (lkeys::version_key)) {
@ -289,7 +340,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
br.done ();
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword")));
skip_element ();
}
}
@ -369,7 +420,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (rect, polygon, net, pin, device or circuit expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (rect, polygon, net, pin, device or circuit expected)")));
skip_element ();
}
}
@ -430,19 +481,17 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside device abstract definition (terminal expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device abstract definition (terminal expected)")));
skip_element ();
}
}
br.done ();
} else if (nested) {
break;
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file")));
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside device abstract definition (terminal expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword")));
skip_element ();
}
}
@ -450,6 +499,10 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
if (l2n) {
l2n->set_netlist_extracted ();
}
if (version > 1) {
throw tl::Exception (tl::to_string (tr ("This program version only supports version 1 of the L2N DB format. File version is: ")) + tl::to_string (version));
}
}
db::Point
@ -490,65 +543,6 @@ LayoutToNetlistStandardReader::read_property (db::NetlistObject *obj)
br.done ();
}
std::pair<unsigned int, NetShape> LayoutToNetlistStandardReader::read_geometry(db::LayoutToNetlist *l2n)
{
std::string lname;
if (test (skeys::rect_key) || test (lkeys::rect_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
db::Point lb = read_point ();
db::Point rt = read_point ();
db::Box box (lb, rt);
br.done ();
return std::make_pair (lid, db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ()));
} else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::vector<db::Point> pt;
while (br) {
pt.push_back (read_point ());
}
br.done ();
db::Polygon poly;
poly.assign_hull (pt.begin (), pt.end ());
return std::make_pair (lid, db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ()));
} else if (test (skeys::text_key) || test (lkeys::text_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::string text;
read_word_or_quoted (text);
db::Point pt = read_point ();
br.done ();
return std::make_pair (lid, db::TextRef (db::Text (text, db::Trans (pt - db::Point ())), l2n->internal_layout ()->shape_repository ()));
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file (polygon or rect expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword (polygon or rect expected)")));
}
}
db::Box
LayoutToNetlistStandardReader::read_rect ()
{
@ -587,14 +581,75 @@ void
LayoutToNetlistStandardReader::read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster<db::NetShape> &lc, db::Cell &cell)
{
m_ref = db::Point ();
std::string lname;
while (br) {
if (test (skeys::property_key) || test (lkeys::property_key)) {
read_property (obj);
} else if (test (skeys::rect_key) || test (lkeys::rect_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
db::Point lb = read_point ();
db::Point rt = read_point ();
db::Box box (lb, rt);
br.done ();
NetShape n (db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ()));
lc.add (n, lid);
n.insert_into (cell.shapes (lid));
} else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::vector<db::Point> pt;
while (br) {
pt.push_back (read_point ());
}
br.done ();
db::Polygon poly;
poly.assign_hull (pt.begin (), pt.end ());
NetShape n (db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ()));
lc.add (n, lid);
n.insert_into (cell.shapes (lid));
} else if (test (skeys::text_key) || test (lkeys::text_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::string text;
read_word_or_quoted (text);
db::Point pt = read_point ();
br.done ();
NetShape n (db::TextRef (db::Text (text, db::Trans (pt - db::Point ())), l2n->internal_layout ()->shape_repository ()));
lc.add (n, lid);
n.insert_into (cell.shapes (lid));
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file (polygon, text or rect expected)")));
} else {
std::pair<unsigned int, db::NetShape> pr = read_geometry (l2n);
lc.add (pr.second, pr.first);
pr.second.insert_into (cell.shapes (pr.first));
skip_element ();
}
}
}
@ -641,6 +696,7 @@ LayoutToNetlistStandardReader::read_pin (db::Netlist * /*netlist*/, db::LayoutTo
db::Net *net = 0;
db::Pin pin;
int netid = 0;
while (br) {
@ -660,18 +716,19 @@ LayoutToNetlistStandardReader::read_pin (db::Netlist * /*netlist*/, db::LayoutTo
read_property (&pin);
} else {
} else if (try_read_int (netid)) {
if (net) {
throw tl::Exception (tl::to_string (tr ("Duplicate net ID")));
}
unsigned int netid = (unsigned int) read_int ();
net = map.id2net [netid];
net = map.id2net [(unsigned int) netid];
if (!net) {
throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid));
}
} else {
skip_element ();
}
}
@ -851,7 +908,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside device definition (location, scale, mirror, rotation, param or terminal expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device definition (location, scale, mirror, rotation, param or terminal expected)")));
skip_element ();
}
}
@ -1032,7 +1089,7 @@ LayoutToNetlistStandardReader::read_subcircuit (db::Netlist *netlist, db::Layout
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside subcircuit definition (location, rotation, mirror, scale or pin expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside subcircuit definition (location, rotation, mirror, scale or pin expected)")));
skip_element ();
}
}

View File

@ -44,6 +44,11 @@ namespace l2n_std_reader {
operator bool ();
void done ();
bool has_brace () const
{
return m_has_brace;
}
private:
db::LayoutToNetlistStandardReader *mp_reader;
bool m_checked;
@ -101,7 +106,7 @@ protected:
friend class l2n_std_reader::Brace;
typedef l2n_std_reader::Brace Brace;
void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, bool nested = false, std::map<const db::Circuit *, ObjectMap> *map_per_circuit = 0);
void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, Brace *nested = 0, std::map<const db::Circuit *, ObjectMap> *map_per_circuit = 0);
static size_t terminal_id (const db::DeviceClass *device_class, const std::string &tname);
static std::pair<db::DeviceAbstract *, const db::DeviceClass *> device_model_by_name (db::Netlist *netlist, const std::string &dmname);
@ -126,12 +131,14 @@ protected:
bool test (const std::string &token);
void expect (const std::string &token);
void read_word_or_quoted(std::string &s);
void read_word_or_quoted (std::string &s);
int read_int ();
bool try_read_int (int &i);
db::Coord read_coord ();
double read_double ();
bool at_end ();
void skip ();
void skip_element ();
void read_net (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map);
void read_pin (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map);
@ -139,7 +146,6 @@ protected:
void read_subcircuit (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map, std::map<db::CellInstArray, std::list<Connections> > &connections);
bool read_trans_part (db::DCplxTrans &tr);
void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc);
std::pair<unsigned int, db::NetShape> read_geometry(db::LayoutToNetlist *l2n);
void read_property (db::NetlistObject *obj);
db::Polygon read_polygon ();
db::Box read_rect ();

View File

@ -29,6 +29,9 @@
namespace db
{
static const std::string endl ("\n");
static const std::string indent1 (" ");
// -------------------------------------------------------------------------------------------
// LayoutToNetlistWriterBase implementation
@ -47,15 +50,89 @@ void LayoutToNetlistWriterBase::write (const db::LayoutToNetlist *l2n)
do_write (l2n);
}
// -------------------------------------------------------------------------------------------
// TokenizedOutput implementation
TokenizedOutput::TokenizedOutput (tl::OutputStream &s)
: mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false), m_indent (-1)
{
// .. nothing yet ..
}
TokenizedOutput::TokenizedOutput (tl::OutputStream &s, const std::string &token)
: mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false), m_indent (0)
{
stream () << token << "(";
}
TokenizedOutput::TokenizedOutput (tl::OutputStream &s, int indent, const std::string &token)
: mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false)
{
m_indent = indent;
for (int i = 0; i < m_indent; ++i) {
stream () << indent1;
}
stream () << token << "(";
}
TokenizedOutput::TokenizedOutput (TokenizedOutput &output, const std::string &token, bool inl)
: mp_stream (&output.stream ()), mp_parent (&output), m_first (true), m_inline (inl), m_newline (false)
{
m_indent = output.indent () + 1;
output.emit_sep ();
stream () << token << "(";
}
TokenizedOutput::~TokenizedOutput ()
{
if (m_newline) {
for (int i = 0; i < m_indent; ++i) {
stream () << indent1;
}
}
if (m_indent >= 0) {
stream () << ")";
if (! m_inline) {
if (mp_parent) {
*mp_parent << endl;
} else {
stream () << endl;
}
}
}
}
void TokenizedOutput::emit_sep ()
{
if (m_newline) {
for (int i = 0; i <= m_indent; ++i) {
stream () << indent1;
}
m_newline = false;
} else if (! m_first) {
stream () << " ";
}
m_first = false;
}
TokenizedOutput &TokenizedOutput::operator<< (const std::string &s)
{
if (s == endl) {
m_newline = true;
stream () << s;
} else if (! s.empty ()) {
emit_sep ();
stream () << s;
}
return *this;
}
// -------------------------------------------------------------------------------------------
namespace l2n_std_format
{
static const std::string endl ("\n");
static const std::string indent1 (" ");
static const std::string indent2 (" ");
template <class Keys>
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
: mp_stream (&stream), m_dbu (dbu), mp_netlist (0),
@ -82,7 +159,10 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n)
mp_netlist = l2n->netlist ();
mp_l2n = l2n;
write (false, 0);
{
TokenizedOutput stream (*mp_stream);
write (false, stream, 0);
}
mp_netlist = 0;
mp_l2n = 0;
@ -95,14 +175,14 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n)
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, bool nested, const db::Netlist *netlist, const db::LayoutToNetlist *l2n, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
try {
mp_netlist = netlist;
mp_l2n = l2n;
write (nested, net2id_per_circuit);
write (nested, stream, net2id_per_circuit);
mp_netlist = 0;
mp_l2n = 0;
@ -126,16 +206,20 @@ static bool same_parameter (const DeviceParameterDefinition &a, const DevicePara
}
template <class Keys>
void std_writer_impl<Keys>::write_device_class (const std::string &indent, const db::DeviceClass *cls, const std::string &temp_name, const db::DeviceClass *temp_class)
void std_writer_impl<Keys>::write_device_class (TokenizedOutput &stream, const db::DeviceClass *cls, const std::string &temp_name, const db::DeviceClass *temp_class)
{
*mp_stream << indent << Keys::class_key << "(" << tl::to_word_or_quoted_string (cls->name ()) << " " << tl::to_word_or_quoted_string (temp_name);
TokenizedOutput out (stream, Keys::class_key);
out << tl::to_word_or_quoted_string (cls->name ()) << tl::to_word_or_quoted_string (temp_name);
bool any_def = false;
const std::vector<DeviceParameterDefinition> &pd = cls->parameter_definitions ();
for (std::vector<DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
if (! temp_class->has_parameter_with_name (p->name ()) || !same_parameter (*p, *temp_class->parameter_definition (temp_class->parameter_id_for_name (p->name ())))) {
*mp_stream << endl << indent << indent1 << Keys::param_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_string (p->is_primary () ? 1 : 0) << " " << tl::to_string (p->default_value ()) << ")";
if (! any_def) {
out << endl;
}
TokenizedOutput (out, Keys::param_key) << tl::to_word_or_quoted_string (p->name ()) << tl::to_string (p->is_primary () ? 1 : 0) << tl::to_string (p->default_value ());
any_def = true;
}
}
@ -143,73 +227,70 @@ void std_writer_impl<Keys>::write_device_class (const std::string &indent, const
const std::vector<DeviceTerminalDefinition> &td = cls->terminal_definitions ();
for (std::vector<DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (! temp_class->has_terminal_with_name (t->name ())) {
*mp_stream << endl << indent << indent1 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (t->name ()) << ")";
if (! any_def) {
out << endl;
}
TokenizedOutput (out, Keys::terminal_key) << tl::to_word_or_quoted_string (t->name ());
any_def = true;
}
}
if (any_def) {
*mp_stream << endl << indent << ")" << endl;
} else {
*mp_stream << ")" << endl;
}
}
template <class Keys>
void std_writer_impl<Keys>::write (bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
void std_writer_impl<Keys>::write (bool nested, TokenizedOutput &stream, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
bool any = false;
const int version = 0;
const db::Layout *ly = mp_l2n ? mp_l2n->internal_layout () : 0;
const std::string indent (nested ? indent1 : "");
if (! nested) {
*mp_stream << Keys::l2n_magic_string << endl;
stream << Keys::l2n_magic_string << endl;
}
if (version > 0) {
*mp_stream << indent << Keys::version_key << "(" << version << ")" << endl;
TokenizedOutput (stream, Keys::version_key) << tl::to_string (version);
stream << endl;
}
if (ly) {
*mp_stream << indent << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (mp_l2n->internal_top_cell ()->cell_index ())) << ")" << endl;
*mp_stream << indent << Keys::unit_key << "(" << m_dbu << ")" << endl;
TokenizedOutput (stream, Keys::top_key) << tl::to_word_or_quoted_string (ly->cell_name (mp_l2n->internal_top_cell ()->cell_index ()));
TokenizedOutput (stream, Keys::unit_key) << tl::to_string (m_dbu);
}
bool any = false;
if (mp_l2n) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Layer section" << endl;
*mp_stream << indent << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
stream << endl << "# Layer section" << endl;
stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
}
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Mask layers" << endl;
stream << endl << "# Mask layers" << endl;
}
for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
*mp_stream << indent << Keys::layer_key << "(" << name_for_layer (mp_l2n, *l);
TokenizedOutput out (stream, Keys::layer_key);
out << name_for_layer (mp_l2n, *l);
db::LayerProperties lp = ly->get_properties (*l);
if (! lp.is_null ()) {
*mp_stream << " " << tl::to_word_or_quoted_string (lp.to_string ());
out << tl::to_word_or_quoted_string (lp.to_string ());
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Mask layer connectivity" << endl;
stream << endl << "# Mask layer connectivity" << endl;
}
for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
db::Connectivity::layer_iterator ce = mp_l2n->connectivity ().end_connected (*l);
db::Connectivity::layer_iterator cb = mp_l2n->connectivity ().begin_connected (*l);
if (cb != ce) {
*mp_stream << indent << Keys::connect_key << "(" << name_for_layer (mp_l2n, *l);
TokenizedOutput out (stream, Keys::connect_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
*mp_stream << " " << name_for_layer (mp_l2n, *c);
out << name_for_layer (mp_l2n, *c);
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
@ -223,15 +304,15 @@ void std_writer_impl<Keys>::write (bool nested, std::map<const db::Circuit *, st
if (gb != ge) {
if (! any) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Global nets and connectivity" << endl;
stream << endl << "# Global nets and connectivity" << endl;
}
any = true;
}
*mp_stream << indent << Keys::global_key << "(" << name_for_layer (mp_l2n, *l);
TokenizedOutput out (stream, Keys::global_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
*mp_stream << " " << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (*g));
out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (*g));
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
@ -240,68 +321,64 @@ void std_writer_impl<Keys>::write (bool nested, std::map<const db::Circuit *, st
}
if (mp_netlist->begin_device_classes () != mp_netlist->end_device_classes () && ! Keys::is_short ()) {
*mp_stream << endl << indent << "# Device class section" << endl;
stream << endl << "# Device class section" << endl;
}
for (db::Netlist::const_device_class_iterator c = mp_netlist->begin_device_classes (); c != mp_netlist->end_device_classes (); ++c) {
db::DeviceClassTemplateBase *temp = db::DeviceClassTemplateBase::is_a (c.operator-> ());
if (temp) {
std::unique_ptr<db::DeviceClass> temp_class (temp->create ());
write_device_class (indent, c.operator-> (), temp->name (), temp_class.get ());
write_device_class (stream, c.operator-> (), temp->name (), temp_class.get ());
} else {
db::DeviceClass empty;
write_device_class (indent, c.operator-> (), std::string (), &empty);
write_device_class (stream, c.operator-> (), std::string (), &empty);
}
m_progress.set (mp_stream->pos ());
}
if (mp_netlist->begin_device_abstracts () != mp_netlist->end_device_abstracts () && ! Keys::is_short ()) {
*mp_stream << endl << indent << "# Device abstracts section" << endl;
*mp_stream << indent << "# Device abstracts list the pin shapes of the devices." << endl;
stream << endl << "# Device abstracts section" << endl;
stream << "# Device abstracts list the pin shapes of the devices." << endl;
}
for (db::Netlist::const_abstract_model_iterator m = mp_netlist->begin_device_abstracts (); m != mp_netlist->end_device_abstracts (); ++m) {
if (m->device_class ()) {
*mp_stream << indent << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
write (*m, indent);
*mp_stream << indent << ")" << endl;
TokenizedOutput out (stream, Keys::device_key);
out << tl::to_word_or_quoted_string (m->name ()) << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
write (out, *m);
m_progress.set (mp_stream->pos ());
}
}
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Circuit section" << endl;
*mp_stream << indent << "# Circuits are the hierarchical building blocks of the netlist." << endl;
stream << endl << "# Circuit section" << endl;
stream << "# Circuits are the hierarchical building blocks of the netlist." << endl;
}
for (db::Netlist::const_bottom_up_circuit_iterator i = mp_netlist->begin_bottom_up (); i != mp_netlist->end_bottom_up (); ++i) {
const db::Circuit *x = i.operator-> ();
*mp_stream << indent << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
write (*x, indent, net2id_per_circuit);
*mp_stream << indent << ")" << endl;
TokenizedOutput out (stream, Keys::circuit_key);
out << tl::to_word_or_quoted_string (x->name ()) << endl;
write (out, *x, net2id_per_circuit);
m_progress.set (mp_stream->pos ());
}
}
void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref, bool relative)
static void write_point (TokenizedOutput &out, const db::Point &pt, db::Point &ref, bool relative)
{
if (relative) {
stream << "(";
stream << pt.x () - ref.x ();
stream << " ";
stream << pt.y () - ref.y ();
stream << ")";
TokenizedOutput (out, std::string (), true) << tl::to_string (pt.x () - ref.x ()) << tl::to_string (pt.y () - ref.y ());
} else {
if (pt.x () == 0 || pt.x () != ref.x ()) {
stream << pt.x ();
out << tl::to_string (pt.x ());
} else {
stream << "*";
out << "*";
}
if (pt.y () == 0 || pt.y () != ref.y ()) {
stream << pt.y ();
out << tl::to_string (pt.y ());
} else {
stream << "*";
out << "*";
}
}
@ -310,25 +387,20 @@ void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref,
}
template <class T, class Tr>
void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr, db::Point &ref, bool relative)
static void write_points (TokenizedOutput &out, const T &poly, const Tr &tr, db::Point &ref, bool relative)
{
for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) {
typename T::point_type pt = tr * *c;
stream << " ";
write_point (stream, pt, ref, relative);
write_point (out, tr * *c, ref, relative);
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string &indent, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Circuit &circuit, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
if (circuit.boundary ().vertices () > 0) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Circuit boundary" << endl;
stream << endl << "# Circuit boundary" << endl;
}
reset_geometry_ref ();
@ -337,22 +409,20 @@ void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string
if (poly.is_box ()) {
db::Box box = poly.box ();
*mp_stream << indent << indent1 << Keys::rect_key << "(";
write_point (*mp_stream, box.p1 (), m_ref, true);
*mp_stream << " ";
write_point (*mp_stream, box.p2 (), m_ref, true);
*mp_stream << ")" << endl;
TokenizedOutput out (stream, Keys::rect_key);
write_point (out, box.p1 (), m_ref, true);
write_point (out, box.p2 (), m_ref, true);
} else {
*mp_stream << indent << indent1 << Keys::polygon_key << "(";
TokenizedOutput out (stream, Keys::polygon_key);
if (poly.holes () > 0) {
db::SimplePolygon sp = db::polygon_to_simple_polygon (poly);
write_points (*mp_stream, sp, db::UnitTrans (), m_ref, true);
write_points (out, sp, db::UnitTrans (), m_ref, true);
} else {
write_points (*mp_stream, poly, db::UnitTrans (), m_ref, true);
write_points (out, poly, db::UnitTrans (), m_ref, true);
}
*mp_stream << ")" << endl;
}
@ -360,9 +430,9 @@ void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string
for (db::NetlistObject::property_iterator p = circuit.begin_properties (); p != circuit.end_properties (); ++p) {
if (p == circuit.begin_properties() && ! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Properties" << endl;
stream << endl << "# Properties" << endl;
}
*mp_stream << indent << indent1 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (stream, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
std::map<const db::Net *, unsigned int> net2id_local;
@ -379,60 +449,56 @@ void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string
if (circuit.begin_nets () != circuit.end_nets ()) {
if (! Keys::is_short ()) {
if (mp_l2n) {
*mp_stream << endl << indent << indent1 << "# Nets with their geometries" << endl;
stream << endl << "# Nets with their geometries" << endl;
} else {
*mp_stream << endl << indent << indent1 << "# Nets" << endl;
stream << endl << "# Nets" << endl;
}
}
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
write (*n, (*net2id) [n.operator-> ()], indent);
write (stream, *n, (*net2id) [n.operator-> ()]);
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_pins () != circuit.end_pins ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Outgoing pins and their connections to nets" << endl;
stream << endl << "# Outgoing pins and their connections to nets" << endl;
}
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
*mp_stream << indent << indent1 << Keys::pin_key << "(";
TokenizedOutput out (stream, Keys::pin_key);
const db::Net *net = circuit.net_for_pin (p->id ());
if (net) {
*mp_stream << (*net2id) [net];
out << tl::to_string ((*net2id) [net]);
}
if (! p->name ().empty ()) {
if (net) {
*mp_stream << " ";
}
*mp_stream << Keys::name_key << "(" << tl::to_word_or_quoted_string (p->name ()) << ")";
TokenizedOutput (out, Keys::name_key, true) << tl::to_word_or_quoted_string (p->name ());
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_devices () != circuit.end_devices ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Devices and their connections" << endl;
stream << endl << "# Devices and their connections" << endl;
}
for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) {
write (*d, *net2id, indent);
write (stream, *d, *net2id);
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Subcircuits and their connections" << endl;
stream << endl << "# Subcircuits and their connections" << endl;
}
for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) {
write (*x, *net2id, indent);
write (stream, *x, *net2id);
m_progress.set (mp_stream->pos ());
}
}
if (! Keys::is_short ()) {
*mp_stream << endl;
stream << endl;
}
}
@ -443,7 +509,7 @@ void std_writer_impl<Keys>::reset_geometry_ref ()
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative)
{
if (s->type () == db::NetShape::Polygon) {
@ -454,39 +520,36 @@ void std_writer_impl<Keys>::write (const db::NetShape *s, const db::ICplxTrans &
if (poly.is_box ()) {
db::Box box = t * poly.box ();
*mp_stream << Keys::rect_key << "(" << lname;
*mp_stream << " ";
write_point (*mp_stream, box.p1 (), m_ref, relative);
*mp_stream << " ";
write_point (*mp_stream, box.p2 (), m_ref, relative);
*mp_stream << ")";
TokenizedOutput out (stream, Keys::rect_key);
out << lname;
write_point (out, box.p1 (), m_ref, relative);
write_point (out, box.p2 (), m_ref, relative);
} else {
*mp_stream << Keys::polygon_key << "(" << lname;
TokenizedOutput out (stream, Keys::polygon_key);
out << lname;
if (poly.holes () > 0) {
db::SimplePolygon sp = db::polygon_to_simple_polygon (poly);
write_points (*mp_stream, sp, t, m_ref, relative);
write_points (out, sp, t, m_ref, relative);
} else {
write_points (*mp_stream, poly, t, m_ref, relative);
write_points (out, poly, t, m_ref, relative);
}
*mp_stream << ")";
}
} else if (s->type () == db::NetShape::Text) {
*mp_stream << Keys::text_key << "(" << lname;
TokenizedOutput out (stream, Keys::text_key);
out << lname;
db::TextRef txtr = s->text_ref ();
db::ICplxTrans t = tr * db::ICplxTrans (txtr.trans ());
*mp_stream << " " << tl::to_word_or_quoted_string (txtr.obj ().string ()) << " ";
out << tl::to_word_or_quoted_string (txtr.obj ().string ());
db::Point pt = t * (db::Point () + txtr.obj ().trans ().disp ());
write_point (*mp_stream, pt, m_ref, relative);
*mp_stream << ")";
write_point (out, pt, m_ref, relative);
}
}
@ -498,13 +561,13 @@ bool std_writer_impl<Keys>::new_cell (cell_index_type ci) const
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Net &net, unsigned int id, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Net &net, unsigned int id)
{
const db::hier_clusters<db::NetShape> &clusters = mp_l2n->net_clusters ();
const db::Circuit *circuit = net.circuit ();
const db::Connectivity &conn = mp_l2n->connectivity ();
bool any = false;
std::unique_ptr<TokenizedOutput> outp;
if (mp_l2n) {
@ -527,25 +590,24 @@ void std_writer_impl<Keys>::write (const db::Net &net, unsigned int id, const st
} else {
if (! any) {
if (! outp) {
*mp_stream << indent << indent1 << Keys::net_key << "(" << id;
outp.reset (new TokenizedOutput (stream, Keys::net_key));
*outp << tl::to_string (id);
if (! net.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ());
}
*mp_stream << endl;
*outp << endl;
for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (*outp, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
any = true;
}
*mp_stream << indent << indent2;
write (si.operator-> (), si.trans (), name_for_layer (mp_l2n, *l), true);
*mp_stream << endl;
write (*outp, si.operator-> (), si.trans (), name_for_layer (mp_l2n, *l), true);
m_progress.set (mp_stream->pos ());
prev_ci = ci;
@ -560,80 +622,66 @@ void std_writer_impl<Keys>::write (const db::Net &net, unsigned int id, const st
}
if (any) {
*mp_stream << indent << indent1 << ")" << endl;
} else {
if (! outp) {
outp.reset (new TokenizedOutput (stream, Keys::net_key));
*outp << tl::to_string (id);
*mp_stream << indent << indent1 << Keys::net_key << "(" << id;
if (! net.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ());
}
if (net.begin_properties () != net.end_properties ()) {
*mp_stream << endl;
*outp << endl;
for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (*outp, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
*mp_stream << indent << ")" << endl;
} else {
*mp_stream << ")" << endl;
}
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::SubCircuit &subcircuit, std::map<const db::Net *, unsigned int> &net2id, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::SubCircuit &subcircuit, std::map<const db::Net *, unsigned int> &net2id)
{
*mp_stream << indent << indent1 << Keys::circuit_key << "(" << tl::to_string (subcircuit.id ());
*mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ());
TokenizedOutput out (stream, Keys::circuit_key);
out << tl::to_string (subcircuit.id ());
out << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ());
if (! subcircuit.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()) << ")";
TokenizedOutput (out, Keys::name_key, true) << tl::to_word_or_quoted_string (subcircuit.name ());
}
if (mp_l2n) {
*mp_stream << " ";
write (subcircuit.trans ());
write (out, subcircuit.trans ());
}
// each pin in one line for more than a few pins
bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1) || subcircuit.begin_properties () != subcircuit.end_properties ();
if (separate_lines) {
*mp_stream << endl;
out << endl;
}
for (db::NetlistObject::property_iterator p = subcircuit.begin_properties (); p != subcircuit.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (out, Keys::property_key, ! separate_lines) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
unsigned int pin_id = 0;
for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p, ++pin_id) {
const db::Net *net = subcircuit.net_for_pin (p->id ());
if (net) {
if (separate_lines) {
*mp_stream << indent << indent2;
} else {
*mp_stream << " ";
}
*mp_stream << Keys::pin_key << "(" << tl::to_string (pin_id) << " " << net2id [net] << ")";
if (separate_lines) {
*mp_stream << endl;
}
TokenizedOutput (out, Keys::pin_key, ! separate_lines) << tl::to_string (pin_id) << tl::to_string (net2id [net]);
m_progress.set (mp_stream->pos ());
}
}
if (separate_lines) {
*mp_stream << indent << indent1;
}
*mp_stream << ")" << endl;
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::DeviceAbstract &device_abstract, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::DeviceAbstract &device_abstract)
{
tl_assert (mp_l2n);
const std::vector<db::DeviceTerminalDefinition> &td = device_abstract.device_class ()->terminal_definitions ();
const db::hier_clusters<db::NetShape> &clusters = mp_l2n->net_clusters ();
@ -641,10 +689,13 @@ void std_writer_impl<Keys>::write (const db::DeviceAbstract &device_abstract, co
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
*mp_stream << indent << indent1 << Keys::terminal_key << "(" << t->name () << endl;
TokenizedOutput out (stream, Keys::terminal_key);
out << t->name ();
reset_geometry_ref ();
bool any = false;
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
size_t cid = device_abstract.cluster_id_for_terminal (t->id ());
@ -656,72 +707,62 @@ void std_writer_impl<Keys>::write (const db::DeviceAbstract &device_abstract, co
const db::local_cluster<db::NetShape> &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (cid);
for (db::local_cluster<db::NetShape>::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) {
*mp_stream << indent << indent2;
write (s.operator-> (), db::ICplxTrans (), name_for_layer (mp_l2n, *l), true);
*mp_stream << endl;
if (! any) {
out << endl;
}
write (out, s.operator-> (), db::ICplxTrans (), name_for_layer (mp_l2n, *l), true);
m_progress.set (mp_stream->pos ());
any = true;
}
}
*mp_stream << indent << indent1 << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::DCplxTrans &tr)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::DCplxTrans &tr)
{
bool first = true;
if (tr.is_mag ()) {
*mp_stream << Keys::scale_key << "(" << tr.mag () << ")";
first = false;
TokenizedOutput (stream, Keys::scale_key, true) << tl::to_string (tr.mag ());
}
if (tr.is_mirror ()) {
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::mirror_key;
first = false;
stream << Keys::mirror_key;
}
if (fabs (tr.angle ()) > 1e-6) {
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::rotation_key << "(" << tr.angle () << ")";
first = false;
TokenizedOutput (stream, Keys::rotation_key, true) << tl::to_string (tr.angle ());
}
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::location_key << "(" << floor (0.5 + tr.disp ().x () / m_dbu) << " " << floor (0.5 + tr.disp ().y () / m_dbu) << ")";
TokenizedOutput (stream, Keys::location_key, true) << tl::to_string (floor (0.5 + tr.disp ().x () / m_dbu)) << tl::to_string (floor (0.5 + tr.disp ().y () / m_dbu));
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Device &device, std::map<const Net *, unsigned int> &net2id, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Device &device, std::map<const Net *, unsigned int> &net2id)
{
tl_assert (device.device_class () != 0);
const std::vector<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
const std::vector<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
*mp_stream << indent << indent1 << Keys::device_key << "(" << tl::to_string (device.id ());
TokenizedOutput out (stream, Keys::device_key);
out << tl::to_string (device.id ());
if (device.device_abstract ()) {
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
out << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
const std::vector<db::DeviceAbstractRef> &other_abstracts = device.other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) {
*mp_stream << indent << indent2 << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " ";
write (a->trans);
*mp_stream << ")" << endl;
TokenizedOutput o (out, Keys::device_key);
o << tl::to_word_or_quoted_string (a->device_abstract->name ());
write (o, a->trans);
}
@ -729,41 +770,38 @@ void std_writer_impl<Keys>::write (const db::Device &device, std::map<const Net
for (std::map<unsigned int, std::vector<db::DeviceReconnectedTerminal> >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) {
for (std::vector<db::DeviceReconnectedTerminal>::const_iterator c = t->second.begin (); c != t->second.end (); ++c) {
*mp_stream << indent << indent2 << Keys::connect_key << "(" << c->device_index << " " << tl::to_word_or_quoted_string (td [t->first].name ()) << " " << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()) << ")" << endl;
TokenizedOutput (out, Keys::connect_key) << tl::to_string (c->device_index) << tl::to_word_or_quoted_string (td [t->first].name ()) << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ());
}
}
*mp_stream << indent << indent2;
write (device.trans ());
*mp_stream << endl;
write (out, device.trans ());
out << endl;
} else {
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl;
out << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl;
}
if (! device.name ().empty ()) {
*mp_stream << indent << indent2 << Keys::name_key << "(" << tl::to_word_or_quoted_string (device.name ()) << ")" << endl;
TokenizedOutput (out, Keys::name_key) << tl::to_word_or_quoted_string (device.name ());
}
for (db::NetlistObject::property_iterator p = device.begin_properties (); p != device.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (out, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
*mp_stream << indent << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::sprintf ("%.12g", device.parameter_value (i->id ())) << ")" << endl;
TokenizedOutput (out, Keys::param_key) << tl::to_word_or_quoted_string (i->name ()) << tl::sprintf ("%.12g", device.parameter_value (i->id ()));
}
for (std::vector<DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
const db::Net *net = device.net_for_terminal (i->id ());
TokenizedOutput o (out, Keys::terminal_key);
o << tl::to_word_or_quoted_string (i->name ());
if (net) {
*mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl;
} else {
*mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << ")" << endl;
o << tl::to_string (net2id [net]);
}
}
*mp_stream << indent << indent1 << ")" << endl;
}
// explicit instantiation

View File

@ -44,6 +44,35 @@ class Netlist;
class LayoutToNetlist;
class NetShape;
/**
* @brief A helper class to produce token/list lines
* Such lines are like:
* token(a b c)
* This class takes care of properly handling separation blanks
*/
class TokenizedOutput
{
public:
TokenizedOutput (tl::OutputStream &stream);
TokenizedOutput (tl::OutputStream &stream, const std::string &token);
TokenizedOutput (tl::OutputStream &stream, int indent, const std::string &token);
TokenizedOutput (TokenizedOutput &output, const std::string &token, bool inl = false);
~TokenizedOutput ();
TokenizedOutput &operator<< (const std::string &s);
tl::OutputStream &stream () { return *mp_stream; }
private:
tl::OutputStream *mp_stream;
TokenizedOutput *mp_parent;
bool m_first, m_inline, m_newline;
int m_indent;
void emit_sep ();
int indent () const { return m_indent; }
};
namespace l2n_std_format
{
@ -55,7 +84,7 @@ public:
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
void write (const db::LayoutToNetlist *l2n);
void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (TokenizedOutput &stream, bool nested, const db::Netlist *netlist, const db::LayoutToNetlist *l2n, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
protected:
tl::OutputStream &stream ()
@ -71,15 +100,15 @@ private:
const db::LayoutToNetlist *mp_l2n;
tl::AbsoluteProgress m_progress;
void write (bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (const db::Circuit &circuit, const std::string &indent, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (const db::Net &net, unsigned int id, const std::string &indent);
void write (const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id, const std::string &indent);
void write (const db::Device &device, std::map<const Net *, unsigned int> &net2id, const std::string &indent);
void write (const db::DeviceAbstract &device_abstract, const std::string &indent);
void write (const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative);
void write (const db::DCplxTrans &trans);
void write_device_class (const std::string &indent, const db::DeviceClass *cls, const std::string &name, const db::DeviceClass *temp_class);
void write (bool nested, TokenizedOutput &stream, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (TokenizedOutput &stream, const db::Circuit &circuit, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (TokenizedOutput &stream, const db::Net &net, unsigned int id);
void write (TokenizedOutput &stream, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id);
void write (TokenizedOutput &stream, const db::Device &device, std::map<const Net *, unsigned int> &net2id);
void write (TokenizedOutput &stream, const db::DeviceAbstract &device_abstract);
void write (TokenizedOutput &stream, const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative);
void write (TokenizedOutput &stream, const db::DCplxTrans &trans);
void write_device_class (TokenizedOutput &stream, const db::DeviceClass *cls, const std::string &name, const db::DeviceClass *temp_class);
void reset_geometry_ref ();
// implementation of CircuitCallback

View File

@ -34,6 +34,8 @@ namespace lvs_std_format
DB_PUBLIC std::string LongKeys::reference_key ("reference");
DB_PUBLIC std::string LongKeys::layout_key ("layout");
DB_PUBLIC std::string LongKeys::xref_key ("xref");
DB_PUBLIC std::string LongKeys::log_key ("log");
DB_PUBLIC std::string LongKeys::log_entry_key ("entry");
DB_PUBLIC std::string LongKeys::mismatch_key ("mismatch");
DB_PUBLIC std::string LongKeys::match_key ("match");
@ -41,15 +43,27 @@ namespace lvs_std_format
DB_PUBLIC std::string LongKeys::warning_key ("warning");
DB_PUBLIC std::string LongKeys::skipped_key ("skipped");
DB_PUBLIC std::string LongKeys::info_severity_key ("info");
DB_PUBLIC std::string LongKeys::warning_severity_key ("warning");
DB_PUBLIC std::string LongKeys::error_severity_key ("error");
// E, H, I, J, L, M, S, W, X, Z, 0, 1
DB_PUBLIC std::string ShortKeys::reference_key ("H");
DB_PUBLIC std::string ShortKeys::layout_key ("J");
DB_PUBLIC std::string ShortKeys::xref_key ("Z");
DB_PUBLIC std::string ShortKeys::log_key ("L");
DB_PUBLIC std::string ShortKeys::log_entry_key ("M");
DB_PUBLIC std::string ShortKeys::mismatch_key ("0");
DB_PUBLIC std::string ShortKeys::match_key ("1");
DB_PUBLIC std::string ShortKeys::nomatch_key ("X");
DB_PUBLIC std::string ShortKeys::warning_key ("W");
DB_PUBLIC std::string ShortKeys::skipped_key ("S");
DB_PUBLIC std::string ShortKeys::info_severity_key ("I");
DB_PUBLIC std::string ShortKeys::warning_severity_key ("W");
DB_PUBLIC std::string ShortKeys::error_severity_key ("E");
}
}

View File

@ -47,80 +47,85 @@ namespace db
* The file follows the declaration-before-use principle
* (circuits before subcircuits, nets before use ...)
*
* Global statements:
*
* Main body:
* #%lvsdb-klayout - header line identifies format
* [version|description|layout-netlist|reference-netlist|xrefs|any]*
*
* [version]:
* version(<number>) - file format version [short key: V]
*
* [description]:
* description(<text>) - an arbitrary description text [short key: B]
* layout([layout]) - layout part [short key: J]
* reference([reference-def]*) - reference netlist part [short key: H]
* xref([xref-def]*) - cross-reference part [short key: Z]
*
* [layout]:
* [layout-netlist]:
* layout(...) - layout netlist part [short key: J]
* Content is the LayoutToNetlist dump without version and description
*
* ... - the LayoutToNetlist dump without version and description
* [reference-netlist]:
* reference(...)
* - reference netlist part [short key: H]
* Content is the Netlist dump (reduced version of LayoutToNetlist)
*
* [reference-def]:
* [xrefs]:
* xref([xref|any]*) - cross-reference part [short key: Z]
*
* circuit(<name> [netlist-circuit-def]*)
* - circuit [short key: X]
* [netlist-circuit-def]:
*
* net(<id> [net-name]?) - a net declaration [short key: N]
* pin(<name> <net-id>) - outgoing pin connection [short key: P]
* device(<name> [device-def]*) - device with connections [short key: D]
* circuit(<name> [subcircuit-def]*)
* - subcircuit with connections [short key: X]
*
* [net-name]:
*
* name(<net-name>) - specify net name [short key: I]
*
* [device-def]:
*
* terminal(<terminal-name> <net-id>)
* - specifies connection of the terminal with
* a net [short key: T]
*
* [subcircuit-def]:
*
* pin(<pin-name> <net-id>) - specifies connection of the pin with a net [short key: P]
*
* [xref-def]:
*
* circuit([non] [non] [status]? [message]? [circuit-xrefs])
* [xref]:
* circuit([non] [non] [status|message|log|circuit-xrefs|any]*)
* - circuit pair [short key: X]
*
* [circuit-xrefs]:
*
* xref([pair]*) - circuit cross-reference part [short key: Z]
*
* [pair]
*
* pin([ion] [ion] [status]? [message]?) - a pin pair [short key: P]
* device([ion] [ion] [status]? [message]?) - a device pair [short key: D]
* circuit([ion] [ion] [status]? [message]?) - a subcircuit pair [short key: X]
* net([ion] [ion] [status]? [message]?) - a net pair [short key: N]
*
* [non]
*
* <name> | ()
*
* [ion]
* [log]:
* log([log-entry]*) - log entries [short key: L]
*
* [log-entry]:
* entry([severity] [message|any]*) - log entry [short key: M]
*
* [severity]:
* info | - [short key: I]
* warning | - [short key: W]
* error - [short key: E]
*
* [circuit-xrefs]:
* xref([xref-pin|xref-device|xref-circuit|xref-net|any]*)
* - circuit cross-reference part [short key: Z]
*
* [xref-pin]:
* pin([ion] [ion] [status]? [message]? [any]*)
* - a pin pair [short key: P]
*
* [xref-device]:
* device([ion] [ion] [status]? [message]? [any]*)
* - a device pair [short key: D]
*
* [xref-circuit]:
* circuit([ion] [ion] [status]? [message]? [any]*)
* - a subcircuit pair [short key: X]
*
* [xref-net]:
* net([ion] [ion] [status]? [message]? [any]*)
* - a net pair [short key: N]
*
* [ion]:
* <id> | ()
*
* [message]
*
* [message]:
* description(<name>) - error description [short key: B]
*
* [status]
*
* [status]:
* mismatch | - [short key: 0]
* match | - [short key: 1]
* nomatch | - [short key: X]
* warning | - [short key: W]
* skipped - [short key: S]
*
* [any]:
* * |
* <token> |
* <token> ( [any]* ) |
* <float> |
* <quoted-string>
*/
namespace lvs_std_format
@ -132,12 +137,18 @@ namespace lvs_std_format
static std::string reference_key;
static std::string layout_key;
static std::string xref_key;
static std::string log_key;
static std::string log_entry_key;
static std::string mismatch_key;
static std::string match_key;
static std::string nomatch_key;
static std::string warning_key;
static std::string skipped_key;
static std::string info_severity_key;
static std::string warning_severity_key;
static std::string error_severity_key;
};
struct DB_PUBLIC LongKeys
@ -147,12 +158,18 @@ namespace lvs_std_format
static std::string reference_key;
static std::string layout_key;
static std::string xref_key;
static std::string log_key;
static std::string log_entry_key;
static std::string mismatch_key;
static std::string match_key;
static std::string nomatch_key;
static std::string warning_key;
static std::string skipped_key;
static std::string info_severity_key;
static std::string warning_severity_key;
static std::string error_severity_key;
};
template <bool Short> struct DB_PUBLIC keys;

View File

@ -38,10 +38,10 @@ LayoutVsSchematicStandardReader::LayoutVsSchematicStandardReader (tl::InputStrea
// .. nothing yet ..
}
void LayoutVsSchematicStandardReader::do_read_lvs (db::LayoutVsSchematic *l2n)
void LayoutVsSchematicStandardReader::do_read_lvs (db::LayoutVsSchematic *lvs)
{
try {
read_netlist (l2n);
read_netlist (lvs);
} catch (tl::Exception &ex) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("%s in line: %d of %s")), ex.msg (), stream ().line_number (), path ()));
}
@ -81,14 +81,14 @@ void LayoutVsSchematicStandardReader::read_netlist (db::LayoutVsSchematic *lvs)
} else if (test (skeys::layout_key) || test (lkeys::layout_key)) {
Brace br (this);
LayoutToNetlistStandardReader::read_netlist (0, lvs, true /*nested*/, &m_map_per_circuit_a);
LayoutToNetlistStandardReader::read_netlist (0, lvs, &br, &m_map_per_circuit_a);
br.done ();
} else if (test (skeys::reference_key) || test (lkeys::reference_key)) {
Brace br (this);
std::unique_ptr<db::Netlist> netlist (new db::Netlist ());
LayoutToNetlistStandardReader::read_netlist (netlist.get (), 0, true /*nested*/, &m_map_per_circuit_b);
LayoutToNetlistStandardReader::read_netlist (netlist.get (), 0, &br, &m_map_per_circuit_b);
lvs->set_reference_netlist (netlist.release ());
br.done ();
@ -106,10 +106,14 @@ void LayoutVsSchematicStandardReader::read_netlist (db::LayoutVsSchematic *lvs)
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword")));
skip_element ();
}
}
if (version > 1) {
throw tl::Exception (tl::to_string (tr ("This program version only supports version 1 of the LVS DB format. File version is: ")) + tl::to_string (version));
}
}
bool LayoutVsSchematicStandardReader::read_message (std::string &msg)
@ -146,6 +150,59 @@ bool LayoutVsSchematicStandardReader::read_status (db::NetlistCrossReference::St
}
}
bool LayoutVsSchematicStandardReader::read_severity (db::NetlistCrossReference::Severity &severity)
{
if (test (skeys::info_severity_key) || test (lkeys::info_severity_key)) {
severity = db::NetlistCrossReference::Info;
return true;
} else if (test (skeys::warning_severity_key) || test (lkeys::warning_severity_key)) {
severity = db::NetlistCrossReference::Warning;
return true;
} else if (test (skeys::error_severity_key) || test (lkeys::error_severity_key)) {
severity = db::NetlistCrossReference::Error;
return true;
} else {
return false;
}
}
void LayoutVsSchematicStandardReader::read_log_entry (db::NetlistCrossReference *xref)
{
db::NetlistCrossReference::Severity severity = db::NetlistCrossReference::NoSeverity;
std::string msg;
Brace br (this);
while (br) {
if (read_severity (severity)) {
// continue
} else if (read_message (msg)) {
// continue
} else {
skip_element ();
}
}
br.done ();
xref->log_entry (severity, msg);
}
void LayoutVsSchematicStandardReader::read_logs_for_circuits (db::NetlistCrossReference *xref)
{
Brace br (this);
while (br) {
if (test (skeys::log_entry_key) || test (lkeys::log_entry_key)) {
read_log_entry (xref);
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (net, pin, device or circuit expected)")));
} else {
skip_element ();
}
}
br.done ();
}
void LayoutVsSchematicStandardReader::read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b)
{
Brace br (this);
@ -162,7 +219,7 @@ void LayoutVsSchematicStandardReader::read_xrefs_for_circuits (db::NetlistCrossR
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (net, pin, device or circuit expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)")));
skip_element ();
}
}
@ -211,10 +268,12 @@ void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref
// continue
} else if (test (skeys::xref_key) || test (lkeys::xref_key)) {
read_xrefs_for_circuits (xref, circuit_a, circuit_b);
} else if (test (skeys::log_key) || test (lkeys::log_key)) {
read_logs_for_circuits (xref);
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (status keyword of xrefs expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (status keyword of xrefs expected)")));
skip_element ();
}
}
@ -223,6 +282,8 @@ void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref
br.done ();
} else {
skip_element ();
}
}
@ -344,6 +405,10 @@ void LayoutVsSchematicStandardReader::read_net_pair (db::NetlistCrossReference *
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_nets (net_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), net_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status, msg);
@ -362,6 +427,10 @@ void LayoutVsSchematicStandardReader::read_pin_pair (db::NetlistCrossReference *
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_pins (pin_by_numerical_id (circuit_a, ion_a), pin_by_numerical_id (circuit_b, ion_b), status, msg);
@ -380,6 +449,10 @@ void LayoutVsSchematicStandardReader::read_device_pair (db::NetlistCrossReferenc
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_devices (device_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), device_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status, msg);
@ -398,6 +471,10 @@ void LayoutVsSchematicStandardReader::read_subcircuit_pair (db::NetlistCrossRefe
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_subcircuits (subcircuit_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), subcircuit_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status, msg);

View File

@ -79,6 +79,9 @@ private:
bool read_status (db::NetlistCrossReference::Status &status);
bool read_message (std::string &msg);
void read_log_entry (db::NetlistCrossReference *xref);
void read_logs_for_circuits (db::NetlistCrossReference *xref);
bool read_severity (db::NetlistCrossReference::Severity &severity);
void read_xref (db::NetlistCrossReference *xref);
void read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);
void read_net_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);

View File

@ -28,6 +28,8 @@
namespace db
{
static const std::string endl ("\n");
// -------------------------------------------------------------------------------------------
// LayoutVsSchematicWriterBase implementation
@ -61,25 +63,22 @@ class std_writer_impl
public:
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
void write (const db::LayoutVsSchematic *l2n);
void write (const db::LayoutVsSchematic *lvs);
private:
tl::OutputStream &stream ()
tl::OutputStream &ostream ()
{
return l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::stream ();
}
std::string status_to_s (const db::NetlistCrossReference::Status status);
std::string severity_to_s (const db::NetlistCrossReference::Severity severity);
std::string message_to_s (const std::string &msg);
void write (const db::NetlistCrossReference *xref);
void write (TokenizedOutput &stream, const db::NetlistCrossReference *xref);
std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > m_net2id_per_circuit_a, m_net2id_per_circuit_b;
};
static const std::string endl ("\n");
static const std::string indent1 (" ");
static const std::string indent2 (" ");
template <class Keys>
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
: l2n_std_format::std_writer_impl<typename Keys::l2n_keys> (stream, dbu, progress_description.empty () ? tl::to_string (tr ("Writing LVS database")) : progress_description)
@ -90,39 +89,40 @@ std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, co
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutVsSchematic *lvs)
{
TokenizedOutput out (ostream ());
out << Keys::lvs_magic_string << endl;
const int version = 0;
stream () << Keys::lvs_magic_string << endl;
if (version > 0) {
stream () << Keys::version_key << "(" << version << ")" << endl;
TokenizedOutput (out, Keys::version_key) << tl::to_string (version);
}
if (lvs->netlist ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Layout" << endl;
out << endl << "# Layout" << endl;
}
stream () << Keys::layout_key << "(" << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (lvs->netlist (), lvs, true, &m_net2id_per_circuit_a);
stream () << ")" << endl;
TokenizedOutput o (out, Keys::layout_key);
o << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (o, true, lvs->netlist (), lvs, &m_net2id_per_circuit_a);
}
if (lvs->reference_netlist ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Reference netlist" << endl;
out << endl << "# Reference netlist" << endl;
}
stream () << Keys::reference_key << "(" << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (lvs->reference_netlist (), 0, true, &m_net2id_per_circuit_b);
stream () << ")" << endl;
TokenizedOutput o (out, Keys::reference_key);
o << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (o, true, lvs->reference_netlist (), 0, &m_net2id_per_circuit_b);
}
if (lvs->cross_ref ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Cross reference" << endl;
out << endl << "# Cross reference" << endl;
}
stream () << Keys::xref_key << "(" << endl;
write (lvs->cross_ref ());
stream () << ")" << endl;
TokenizedOutput o (out, Keys::xref_key);
o << endl;
write (o, lvs->cross_ref ());
}
}
@ -184,7 +184,7 @@ std::string std_writer_impl<Keys>::message_to_s (const std::string &msg)
if (msg.empty ()) {
return std::string ();
} else {
return " " + Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")";
return Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")";
}
}
@ -192,54 +192,83 @@ template <class Keys>
std::string std_writer_impl<Keys>::status_to_s (const db::NetlistCrossReference::Status status)
{
if (status == db::NetlistCrossReference::Match) {
return " " + Keys::match_key;
return Keys::match_key;
} else if (status == db::NetlistCrossReference::NoMatch) {
return " " + Keys::nomatch_key;
return Keys::nomatch_key;
} else if (status == db::NetlistCrossReference::Mismatch) {
return " " + Keys::mismatch_key;
return Keys::mismatch_key;
} else if (status == db::NetlistCrossReference::MatchWithWarning) {
return " " + Keys::warning_key;
return Keys::warning_key;
} else if (status == db::NetlistCrossReference::Skipped) {
return " " + Keys::skipped_key;
return Keys::skipped_key;
} else {
return std::string ();
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::NetlistCrossReference *xref)
std::string std_writer_impl<Keys>::severity_to_s (const db::NetlistCrossReference::Severity severity)
{
if (severity == db::NetlistCrossReference::Info) {
return Keys::info_severity_key;
} else if (severity == db::NetlistCrossReference::Warning) {
return Keys::warning_severity_key;
} else if (severity == db::NetlistCrossReference::Error) {
return Keys::error_severity_key;
} else {
return std::string ();
}
}
template <class Keys>
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::NetlistCrossReference *xref)
{
for (db::NetlistCrossReference::circuits_iterator c = xref->begin_circuits (); c != xref->end_circuits (); ++c) {
const db::NetlistCrossReference::PerCircuitData *pcd = xref->per_circuit_data_for (*c);
tl_assert (pcd != 0);
stream () << indent1 << Keys::circuit_key << "(" << name_to_s (c->first) << " " << name_to_s (c->second) << status_to_s (pcd->status) << message_to_s (pcd->msg) << endl;
stream () << indent2 << Keys::xref_key << "(" << endl;
TokenizedOutput out (stream, Keys::circuit_key);
out << name_to_s (c->first) << name_to_s (c->second) << status_to_s (pcd->status) << message_to_s (pcd->msg);
out << endl;
if (! pcd->log_entries.empty ()) {
TokenizedOutput o (out, Keys::log_key);
o << endl;
for (db::NetlistCrossReference::PerCircuitData::log_entries_const_iterator l = pcd->log_entries.begin (); l != pcd->log_entries.end (); ++l) {
TokenizedOutput (o, Keys::log_entry_key, true) << severity_to_s (l->severity) << message_to_s (l->msg);
o << endl;
}
for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) {
stream () << indent1 << indent2 << Keys::net_key << "(" << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << " " << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
}
std::map<const db::Pin *, unsigned int> pin2index_a, pin2index_b;
build_pin_index_map (c->first, pin2index_a);
build_pin_index_map (c->second, pin2index_b);
{
TokenizedOutput o (out, Keys::xref_key);
o << endl;
for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) {
stream () << indent1 << indent2 << Keys::pin_key << "(" << pin_id_to_s (n->pair.first, pin2index_a) << " " << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) {
TokenizedOutput (o, Keys::net_key) << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << message_to_s (n->msg);
}
std::map<const db::Pin *, unsigned int> pin2index_a, pin2index_b;
build_pin_index_map (c->first, pin2index_a);
build_pin_index_map (c->second, pin2index_b);
for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) {
TokenizedOutput (o, Keys::pin_key) << pin_id_to_s (n->pair.first, pin2index_a) << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << message_to_s (n->msg);
}
for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) {
TokenizedOutput (o, Keys::device_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg);
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) {
TokenizedOutput (o, Keys::circuit_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg);
}
}
for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) {
stream () << indent1 << indent2 << Keys::device_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) {
stream () << indent1 << indent2 << Keys::circuit_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
}
stream () << indent2 << ")" << endl;
stream () << indent1 << ")" << endl;
}
}

View File

@ -30,6 +30,7 @@
namespace db
{
LoadLayoutOptions::LoadLayoutOptions ()
: m_warn_level (1)
{
// .. nothing yet ..
}
@ -44,6 +45,8 @@ namespace db
{
if (&d != this) {
m_warn_level = d.m_warn_level;
release ();
for (std::map <std::string, FormatSpecificReaderOptions *>::const_iterator o = d.m_options.begin (); o != d.m_options.end (); ++o) {
m_options.insert (std::make_pair (o->first, o->second->clone ()));

View File

@ -79,6 +79,26 @@ public:
*/
~LoadLayoutOptions ();
/**
* @brief Gets the warning level
*
* The warning level is a reader-specific setting which enables or disables warnings
* on specific levels. Level 0 is always "warnings off". The default level is 1
* which means "reasonable warnings emitted".
*/
int warn_level () const
{
return m_warn_level;
}
/**
* @brief Sets the warning level
*/
void set_warn_level (int w)
{
m_warn_level = w;
}
/**
* @brief Sets specific options for the given format
*
@ -217,6 +237,7 @@ public:
private:
std::map <std::string, FormatSpecificReaderOptions *> m_options;
int m_warn_level;
void release ();
};

View File

@ -20,12 +20,11 @@
*/
#include "dbLocalOperation.h"
#include "dbHierProcessor.h"
#include "dbBoxScanner.h"
#include "dbRecursiveShapeIterator.h"
#include "dbBoxConvert.h"
#include "dbEdgeProcessor.h"
#include "dbPolygonGenerators.h"
#include "dbPolygonTools.h"
#include "dbLocalOperationUtils.h"
@ -366,10 +365,16 @@ EdgeBoolAndOrNotLocalOperation::description () const
void
EdgeBoolAndOrNotLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{
tl_assert (results.size () == 1);
tl_assert (results.size () == size_t (m_op == EdgeAndNot ? 2 : 1));
std::unordered_set<db::Edge> &result = results.front ();
EdgeBooleanClusterCollector<std::unordered_set<db::Edge> > cluster_collector (&result, m_op);
std::unordered_set<db::Edge> *result2 = 0;
if (results.size () > 1) {
result2 = &results[1];
}
EdgeBooleanClusterCollector<std::unordered_set<db::Edge> > cluster_collector (&result, m_op, result2);
db::box_scanner<db::Edge, size_t> scanner;
@ -381,7 +386,7 @@ EdgeBoolAndOrNotLocalOperation::do_compute_local (db::Layout * /*layout*/, const
}
bool any_subject = false;
bool is_and = (m_op == EdgeAnd || m_op == EdgeIntersections);
bool is_and = (m_op == EdgeAnd || m_op == EdgeAndNot || m_op == EdgeIntersections);
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
@ -416,8 +421,8 @@ EdgeBoolAndOrNotLocalOperation::do_compute_local (db::Layout * /*layout*/, const
// ---------------------------------------------------------------------------------------------
// EdgeToPolygonLocalOperation implementation
EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (bool outside, bool include_borders)
: m_outside (outside), m_include_borders (include_borders)
EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders)
: m_op (op), m_include_borders (include_borders)
{
// .. nothing yet ..
}
@ -425,21 +430,33 @@ EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (bool outside, bool inc
OnEmptyIntruderHint
EdgeToPolygonLocalOperation::on_empty_intruder_hint () const
{
return m_outside ? Copy : Drop;
return m_op == EdgePolygonOp::Inside ? Drop : (m_op == EdgePolygonOp::Outside ? Copy : CopyToSecond);
}
std::string
EdgeToPolygonLocalOperation::description () const
{
return tl::to_string (m_outside ? tr ("Edge to polygon AND/INSIDE") : tr ("Edge to polygons NOT/OUTSIDE"));
if (m_op == EdgePolygonOp::Inside) {
return tl::to_string (tr ("Edge to polygon AND/INSIDE"));
} else if (m_op == EdgePolygonOp::Outside) {
return tl::to_string (tr ("Edge to polygon NOT/OUTSIDE"));
} else {
return tl::to_string (tr ("Edge to polygon ANDNOT/INOUTSIDE"));
}
}
void
EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::Edge, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{
tl_assert (results.size () == 1);
tl_assert (results.size () == size_t (m_op == EdgePolygonOp::Both ? 2 : 1));
std::unordered_set<db::Edge> &result = results.front ();
std::unordered_set<db::Edge> *result2 = 0;
if (results.size () > 1) {
result2 = &results[1];
}
db::EdgeProcessor ep;
std::set<db::PolygonRef> others;
@ -456,8 +473,10 @@ EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, const sh
const db::Edge &subject = interactions.subject_shape (i->first);
if (i->second.empty ()) {
// shortcut (outside: keep, otherwise: drop)
if (m_outside) {
if (m_op == db::EdgePolygonOp::Outside) {
result.insert (subject);
} else if (m_op == db::EdgePolygonOp::Both) {
result2->insert (subject);
}
} else {
ep.insert (subject, 1);
@ -474,8 +493,13 @@ EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, const sh
}
}
db::EdgeToEdgeSetGenerator cc (result);
db::EdgePolygonOp op (m_outside, m_include_borders);
std::unique_ptr<db::EdgeToEdgeSetGenerator> cc_second;
if (result2) {
cc_second.reset (new db::EdgeToEdgeSetGenerator (*result2, 2 /*second tag*/));
}
db::EdgeToEdgeSetGenerator cc (result, 1 /*first tag*/, cc_second.get ());
db::EdgePolygonOp op (m_op, m_include_borders);
ep.process (cc, op);
}

View File

@ -29,6 +29,7 @@
#include "dbLayout.h"
#include "dbEdgeBoolean.h"
#include "dbEdgeProcessor.h"
#include <unordered_map>
#include <unordered_set>
@ -187,7 +188,7 @@ class DB_PUBLIC EdgeBoolAndOrNotLocalOperation
: public local_operation<db::Edge, db::Edge, db::Edge>
{
public:
EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op);
EdgeBoolAndOrNotLocalOperation (db::EdgeBoolOp op);
virtual void do_compute_local (db::Layout *layout, const shape_interactions<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &result, size_t max_vertex_count, double area_ratio) const;
virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
@ -197,7 +198,7 @@ public:
virtual db::Coord dist () const { return 1; }
private:
EdgeBoolOp m_op;
db::EdgeBoolOp m_op;
};
/**
@ -210,7 +211,7 @@ class DB_PUBLIC EdgeToPolygonLocalOperation
: public local_operation<db::Edge, db::PolygonRef, db::Edge>
{
public:
EdgeToPolygonLocalOperation (bool outside, bool include_borders);
EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders);
virtual void do_compute_local (db::Layout *layout, const shape_interactions<db::Edge, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &result, size_t max_vertex_count, double area_ratio) const;
virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
@ -220,7 +221,7 @@ public:
virtual db::Coord dist () const { return m_include_borders ? 1 : 0; }
private:
bool m_outside;
db::EdgePolygonOp::mode_t m_op;
bool m_include_borders;
};

View File

@ -30,8 +30,8 @@ namespace db
// -----------------------------------------------------------------------------------------------
// class EdgeToEdgeSetGenerator
EdgeToEdgeSetGenerator::EdgeToEdgeSetGenerator (std::unordered_set<db::Edge> &edges)
: mp_edges (&edges)
EdgeToEdgeSetGenerator::EdgeToEdgeSetGenerator (std::unordered_set<db::Edge> &edges, int tag, EdgeToEdgeSetGenerator *chained)
: mp_edges (&edges), m_tag (tag), mp_chained (chained)
{
// .. nothing yet ..
}
@ -39,6 +39,19 @@ EdgeToEdgeSetGenerator::EdgeToEdgeSetGenerator (std::unordered_set<db::Edge> &ed
void EdgeToEdgeSetGenerator::put (const db::Edge &edge)
{
mp_edges->insert (edge);
if (mp_chained) {
mp_chained->put (edge);
}
}
void EdgeToEdgeSetGenerator::put (const db::Edge &edge, int tag)
{
if (m_tag == 0 || m_tag == tag) {
mp_edges->insert (edge);
}
if (mp_chained) {
mp_chained->put (edge, tag);
}
}
// -----------------------------------------------------------------------------------------------

View File

@ -130,15 +130,22 @@ public:
/**
* @brief Constructor
*/
EdgeToEdgeSetGenerator (std::unordered_set<db::Edge> &edges);
EdgeToEdgeSetGenerator (std::unordered_set<db::Edge> &edges, int tag = 0, EdgeToEdgeSetGenerator *chained = 0);
/**
* @brief Implementation of the PolygonSink interface
*/
virtual void put (const db::Edge &edge);
/**
* @brief Implementation of the PolygonSink interface
*/
virtual void put (const db::Edge &edge, int tag);
private:
std::unordered_set<db::Edge> *mp_edges;
int m_tag;
EdgeToEdgeSetGenerator *mp_chained;
};
class DB_PUBLIC PolygonRefToShapesGenerator

View File

@ -62,6 +62,8 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger)
m_dont_consider_net_names = false;
m_case_sensitive = false;
m_with_log = true;
}
NetlistComparer::~NetlistComparer ()
@ -379,8 +381,16 @@ NetlistComparer::compare_impl (const db::Netlist *a, const db::Netlist *b) const
} else {
if (mp_logger) {
mp_logger->circuit_skipped (ca, cb, generate_subcircuits_not_verified_warning (ca, verified_circuits_a, cb, verified_circuits_b));
std::string msg = generate_subcircuits_not_verified_warning (ca, verified_circuits_a, cb, verified_circuits_b);
if (m_with_log) {
mp_logger->log_entry (db::NetlistCompareLogger::Error, msg);
}
mp_logger->circuit_skipped (ca, cb, msg);
good = false;
}
}
@ -442,7 +452,6 @@ NetlistComparer::all_subcircuits_verified (const db::Circuit *c, const std::set<
return false;
}
}
return true;
}
@ -889,6 +898,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
// in must_match mode, check if the nets are identical
if (mp_logger) {
if (p->second && ! exact_match) {
if (m_with_log) {
mp_logger->log_entry (db::NetlistCompareLogger::Error,
tl::sprintf (tl::to_string (tr ("Nets %s are paired explicitly, but are not identical topologically")), nets2string (p->first)));
}
mp_logger->net_mismatch (p->first.first, p->first.second);
} else {
mp_logger->match_nets (p->first.first, p->first.second);
@ -944,7 +957,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
// Three passes: one without ambiguities, the second one with ambiguities and names (optional) and a third with ambiguities with topology
for (int pass = 0; pass < 3 && ! good; ++pass) {
int num_passes = 3;
for (int pass = 0; pass < num_passes && ! good; ++pass) {
if (pass == 1 && m_dont_consider_net_names) {
// skip the named pass in "don't consider net names" mode
@ -971,8 +985,11 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
compare.subcircuit_equivalence = &subcircuit_equivalence;
compare.device_equivalence = &device_equivalence;
compare.logger = mp_logger;
compare.with_log = m_with_log;
compare.progress = &progress;
std::vector<NodeEdgePair> nodes, other_nodes;
good = true;
while (true) {
@ -1015,7 +1032,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
// derive new identities through topology: first collect all nets with the same topological signature
std::vector<NodeEdgePair> nodes, other_nodes;
nodes.clear ();
other_nodes.clear ();
std::vector<NetGraphNode::edge_type> no_edges;
no_edges.push_back (NetGraphNode::edge_type ());
@ -1070,6 +1088,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
}
if (pass + 1 == num_passes && ! good && mp_logger && m_with_log) {
compare.analyze_failed_matches ();
}
}
// Report missing net assignment
@ -1119,6 +1141,33 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
return good;
}
static void
analyze_pin_mismatch (const db::Pin *pin1, const db::Circuit *c1, const db::Pin *pin2, const db::Circuit * /*c2*/, db::NetlistCompareLogger *logger)
{
if (! pin1) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from reference netlist found in netlist.\nThis is an indication that a physical connection is not made to the subcircuit.")), pin2->expanded_name ()));
}
if (! pin2) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from netlist found in reference netlist.\nThis is an indication that additional physical connections are made to the subcircuit cell.")), pin1->expanded_name ()));
// attempt to identify pins which are creating invalid connections
for (auto p = c1->begin_parents (); p != c1->end_parents (); ++p) {
for (auto c = p->begin_subcircuits (); c != p->end_subcircuits (); ++c) {
const db::SubCircuit &sc = *c;
if (sc.circuit_ref () == c1) {
const db::Net *net = sc.net_for_pin (pin1->id ());
if (net && (net->subcircuit_pin_count () > 1 || net->terminal_count () > 0 || net->pin_count () > 0)) {
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (tl::to_string (tr ("Potential invalid connection in circuit %s, subcircuit cell reference at %s")), p->name (), sc.trans ().to_string ()));
}
}
}
}
}
}
bool
NetlistComparer::handle_pin_mismatch (const db::NetGraph &g1, const db::Circuit *c1, const db::Pin *pin1, const db::NetGraph &g2, const db::Circuit *c2, const db::Pin *pin2) const
{
@ -1163,7 +1212,11 @@ NetlistComparer::handle_pin_mismatch (const db::NetGraph &g1, const db::Circuit
}
return true;
} else {
if (mp_logger) {
if (m_with_log) {
analyze_pin_mismatch (pin1, c1, pin2, c2, mp_logger);
}
mp_logger->pin_mismatch (pin1, pin2);
}
return false;

View File

@ -50,6 +50,16 @@ public:
NetlistCompareLogger () { }
virtual ~NetlistCompareLogger () { }
/**
* @brief An enum describing the severity for the log_entry function
*/
enum Severity {
NoSeverity = 0, // unspecific
Info = 1, // information only
Warning = 2, // a warning
Error = 3 // an error
};
/**
* @brief Begin logging for netlist a and b
*/
@ -88,6 +98,11 @@ public:
*/
virtual void circuit_mismatch (const db::Circuit * /*a*/, const db::Circuit * /*b*/, const std::string & /*msg*/ = std::string ()) { }
/**
* @brief Receives log entries for the current circuit pair
*/
virtual void log_entry (Severity /*level*/, const std::string & /*msg*/) { }
/**
* @brief Nets a and b match exactly
*/
@ -257,6 +272,24 @@ public:
return m_max_depth;
}
/**
* @brief Sets a value indicating that log messages are generated
* Log messages may be expensive to compute, hence they can be turned off.
* By default, log messages are generated.
*/
void set_with_log (bool f)
{
m_with_log = f;
}
/**
* @brief Gets a value indicating that log messages are generated
*/
bool with_log () const
{
return m_with_log;
}
/**
* @brief Sets a value indicating whether not to consider net names
* This feature is mainly intended for testing.
@ -365,6 +398,7 @@ protected:
bool handle_pin_mismatch (const NetGraph &g1, const db::Circuit *c1, const db::Pin *pin1, const NetGraph &g2, const db::Circuit *c2, const db::Pin *p2) const;
mutable NetlistCompareLogger *mp_logger;
bool m_with_log;
std::map<std::pair<const db::Circuit *, const db::Circuit *>, std::vector<std::pair<std::pair<const Net *, const Net *>, bool> > > m_same_nets;
std::unique_ptr<CircuitPinCategorizer> mp_circuit_pin_categorizer;
std::unique_ptr<DeviceCategorizer> mp_device_categorizer;

View File

@ -31,6 +31,7 @@
#include "tlAssert.h"
#include "tlLog.h"
#include "tlInternational.h"
namespace db
{
@ -402,6 +403,7 @@ NetlistCompareCore::NetlistCompareCore (NetGraph *graph, NetGraph *other_graph)
dont_consider_net_names (false),
with_ambiguous (false),
logger (0),
with_log (true),
circuit_pin_mapper (0),
subcircuit_equivalence (0),
device_equivalence (0),
@ -1049,6 +1051,10 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange
if (ambiguous) {
if (logger) {
if (with_log) {
logger->log_entry (db::NetlistCompareLogger::Warning,
tl::sprintf (tl::to_string (tr ("Matching nets %s from an ambiguous group of nets")), nets2string (p->first->net (), p->second->net ())));
}
logger->match_ambiguous_nets (p->first->net (), p->second->net ());
}
for (db::Net::const_pin_iterator i = p->first->net ()->begin_pins (); i != p->first->net ()->end_pins (); ++i) {
@ -1070,9 +1076,11 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange
std::vector<std::pair<const NetGraphNode *, const NetGraphNode *> >::const_iterator p = pairs.begin ();
for (std::list<TentativeNodeMapping>::iterator tn_of_pair = tn_for_pairs.begin (); tn_of_pair != tn_for_pairs.end (); ++tn_of_pair, ++p) {
bool was_ambiguous = equivalent_other_nodes.has_attribute (p->second);
// Note: this would propagate ambiguities to all *derived* mappings. But this probably goes too far:
// bool ambiguous = equivalent_other_nodes.has_attribute (p->second);
// Instead we ignore propagated ambiguities for now:
// bool ambiguous = was_ambiguous;
// Instead we ignore propagated ambiguitied for now:
bool ambiguous = false;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
@ -1100,13 +1108,18 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange
NetGraphNode *n_other = & mp_other_graph->node (other_net_index);
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
if (ambiguous) {
tl::info << indent_s << "deduced ambiguous match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
if (was_ambiguous) {
tl::info << indent_s << "deduced from ambiguous match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
} else {
tl::info << indent_s << "deduced match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
}
if (logger && with_log && was_ambiguous) {
logger->log_entry (db::NetlistCompareLogger::Info,
tl::sprintf (tl::to_string (tr ("Matching nets %s following an ambiguous match")), nets2string (n->net (), n_other->net ())));
}
if (ambiguous) {
if (logger) {
logger->match_ambiguous_nets (n->net (), n_other->net ());
@ -1168,7 +1181,7 @@ NetlistCompareCore::derive_node_identities_from_singular_match (const NetGraphNo
}
return tentative ? failed_match : 0;
} else if ((! n->has_any_other () && ! n_other->has_any_other ()) || (n->has_unknown_other () && n_other->has_unknown_other ())) { // @@@
} else if ((! n->has_any_other () && ! n_other->has_any_other ()) || (n->has_unknown_other () && n_other->has_unknown_other ())) {
// in tentative mode, reject this choice if both nets are named and
// their names differ -> this favors net matching by name
@ -1244,6 +1257,263 @@ NetlistCompareCore::derive_node_identities_from_singular_match (const NetGraphNo
}
}
static size_t distance (const NetGraphNode &a, const NetGraphNode &b)
{
auto i = a.begin ();
auto j = b.begin ();
size_t fuzz = 0;
while (i != a.end () || j != b.end ()) {
if (j == b.end ()) {
++fuzz;
++i;
continue;
}
if (i == a.end ()) {
++fuzz;
++j;
continue;
}
if (i->first < j->first) {
++fuzz;
++i;
continue;
} else if (j->first < i->first) {
++fuzz;
++j;
continue;
}
++i;
++j;
}
return fuzz;
}
static size_t distance3 (const NetGraphNode &a, const NetGraphNode &b1, const NetGraphNode &b2, const NetGraph &gb)
{
bool needs_join = false;
for (auto e = b1.begin (); e != b1.end () && ! needs_join; ++e) {
needs_join = (e->second.second == b1.net () || e->second.second == b2.net ());
}
for (auto e = b2.begin (); e != b2.end () && ! needs_join; ++e) {
needs_join = (e->second.second == b1.net () || e->second.second == b2.net ());
}
if (needs_join) {
return distance (a, gb.joined (b1, b2));
}
auto i = a.begin ();
auto j1 = b1.begin ();
auto j2 = b2.begin ();
size_t fuzz = 0;
while (i != a.end () || j1 != b1.end () || j2 != b2.end ()) {
if (j1 == b1.end () && j2 == b2.end ()) {
++fuzz;
++i;
continue;
}
bool use_j1 = j2 == b2.end () || (j1 != b1.end () && *j1 < *j2);
auto &j = use_j1 ? j1 : j2;
if (i == a.end ()) {
++fuzz;
++j;
continue;
}
if (i->first < j->first) {
++fuzz;
++i;
continue;
} else if (j->first < i->first) {
++fuzz;
++j;
continue;
}
++i;
++j;
}
return fuzz;
}
static void
analyze_nodes_for_close_matches (const std::multimap<size_t, const NetGraphNode *> &nodes_by_edges1, const std::multimap<size_t, const NetGraphNode *> &nodes_by_edges2, bool layout2ref, db::NetlistCompareLogger *logger, const db::NetGraph &g2)
{
size_t max_search = 100;
double max_fuzz_factor = 0.25;
size_t max_fuzz_count = 3;
size_t max_edges_split = 3; // by how many edges joining will reduce the edge count at max
size_t min_edges = 2;
std::string msg;
if (layout2ref) {
msg = tl::to_string (tr ("Net %s may be shorting nets %s and %s from reference netlist (fuzziness %d nodes)"));
} else {
msg = tl::to_string (tr ("Connecting nets %s and %s is making a better match to net %s from reference netlist (fuzziness %d nodes)"));
}
for (auto i = nodes_by_edges1.begin (); i != nodes_by_edges1.end (); ++i) {
if (i->first < min_edges) {
continue;
}
std::set<const db::NetGraphNode *> seen;
for (auto j = nodes_by_edges2.begin (); j != nodes_by_edges2.end (); ++j) {
seen.insert (j->second);
if (j->first > i->first + max_fuzz_count - 1) {
break;
}
size_t ne = i->first > j->first ? i->first - j->first : 0;
if (ne > max_fuzz_count) {
ne -= max_fuzz_count;
}
if (ne == 0 && layout2ref) {
// analyze nets for similarities (only layout -> ref as the other case is symmetric)
size_t fuzz = distance (*i->second, *j->second);
double fuzz_factor = double (fuzz) / ne;
if (fuzz_factor < max_fuzz_factor) {
std::string msg = tl::to_string (tr ("Net %s from netlist approximately matches net %s from reference netlist (fuzziness %d nodes)"));
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg,
i->second->net ()->expanded_name (),
j->second->net ()->expanded_name (),
int (fuzz)));
}
}
auto k = nodes_by_edges2.lower_bound (ne);
size_t tries = max_search;
for ( ; k != nodes_by_edges2.end () && j->first + k->first < i->first + max_fuzz_count + max_edges_split && tries > 0; ++k) {
if (seen.find (k->second) != seen.end ()) {
continue;
}
size_t fuzz = distance3 (*i->second, *j->second, *k->second, g2);
double fuzz_factor = double (fuzz) / i->first;
if (fuzz_factor < max_fuzz_factor) {
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg,
(layout2ref ? i : j)->second->net ()->expanded_name (),
(layout2ref ? j : k)->second->net ()->expanded_name (),
(layout2ref ? k : i)->second->net ()->expanded_name (),
int (fuzz)));
}
--tries;
}
}
}
}
void
NetlistCompareCore::analyze_failed_matches () const
{
// Determine the range of nodes with same identity
std::vector<NetGraphNode::edge_type> no_edges;
no_edges.push_back (NetGraphNode::edge_type ());
std::vector<NodeEdgePair> nodes, other_nodes;
nodes.reserve (mp_graph->end () - mp_graph->begin ());
for (db::NetGraph::node_iterator i1 = mp_graph->begin (); i1 != mp_graph->end (); ++i1) {
if (i1->net ()) {
nodes.push_back (NodeEdgePair (i1.operator-> (), no_edges.begin ()));
}
}
other_nodes.reserve (mp_other_graph->end () - mp_other_graph->begin ());
for (db::NetGraph::node_iterator i2 = mp_other_graph->begin (); i2 != mp_other_graph->end (); ++i2) {
if (i2->net ()) {
other_nodes.push_back (NodeEdgePair (i2.operator-> (), no_edges.begin ()));
}
}
std::sort (nodes.begin (), nodes.end (), CompareNodeEdgePair ());
std::sort (other_nodes.begin (), other_nodes.end (), CompareNodeEdgePair ());
auto n1 = nodes.begin ();
auto n2 = other_nodes.begin ();
std::vector<const db::NetGraphNode *> singular1, singular2;
while (n1 != nodes.end () || n2 != other_nodes.end ()) {
if (n2 == other_nodes.end ()) {
singular1.push_back (n1->node);
++n1;
continue;
} else if (n1 == nodes.end ()) {
singular2.push_back (n2->node);
++n2;
continue;
}
if (*n1->node < *n2->node) {
singular1.push_back (n1->node);
++n1;
continue;
} else if (*n2->node < *n1->node) {
singular2.push_back (n2->node);
++n2;
continue;
}
++n1;
++n2;
}
for (auto i = singular1.begin (); i != singular1.end (); ++i) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("Net %s is not matching any net from reference netlist")), (*i)->net ()->expanded_name ()));
}
// attempt some analysis for close matches (including shorts / opens)
std::multimap<size_t, const NetGraphNode *> nodes_by_edges1, nodes_by_edges2;
for (auto i = singular1.begin (); i != singular1.end (); ++i) {
const NetGraphNode *n = *i;
nodes_by_edges1.insert (std::make_pair (n->end () - n->begin (), n));
}
for (auto i = singular2.begin (); i != singular2.end (); ++i) {
const NetGraphNode *n = *i;
nodes_by_edges2.insert (std::make_pair (n->end () - n->begin (), n));
}
analyze_nodes_for_close_matches (nodes_by_edges1, nodes_by_edges2, true, logger, *mp_other_graph);
analyze_nodes_for_close_matches (nodes_by_edges2, nodes_by_edges1, false, logger, *mp_graph);
}
size_t
NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePair> &nodes, std::vector<NodeEdgePair> &other_nodes) const
{
@ -1281,6 +1551,9 @@ NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePa
}
if (max_depth != std::numeric_limits<size_t>::max() && depth > max_depth) {
if (with_log) {
logger->log_entry (db::NetlistCompareLogger::Warning, tl::sprintf (tl::to_string (tr ("Maximum depth exhausted (max depth is %d)")), int (max_depth)));
}
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "max. depth exhausted (" << depth << ">" << max_depth << ")";
}
@ -1396,6 +1669,9 @@ NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePa
} else if (max_n_branch != std::numeric_limits<size_t>::max () && double (std::max (nr->num1, nr->num2)) * double (n_branch) > double (max_n_branch)) {
if (with_log) {
logger->log_entry (db::NetlistCompareLogger::Warning, tl::sprintf (tl::to_string (tr ("Maximum complexity exhausted (max complexity is %s, needs at least %s)")), tl::to_string (max_n_branch), tl::to_string (std::max (nr->num1, nr->num2) * n_branch)));
}
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "max. complexity exhausted (" << std::max (nr->num1, nr->num2) << "*" << n_branch << ">" << max_n_branch << ") - mismatch.";
}

View File

@ -85,12 +85,18 @@ public:
*/
size_t derive_node_identities_from_node_set (std::vector<NodeEdgePair> &nodes, std::vector<NodeEdgePair> &other_nodes) const;
/**
* @brief Analyzes the non-matched remaining nodes and produces log output
*/
void analyze_failed_matches () const;
size_t max_depth;
size_t max_n_branch;
bool depth_first;
bool dont_consider_net_names;
bool with_ambiguous;
NetlistCompareLogger *logger;
bool with_log;
CircuitPinCategorizer *circuit_pin_mapper;
SubCircuitEquivalenceTracker *subcircuit_equivalence;
DeviceEquivalenceTracker *device_equivalence;

View File

@ -624,4 +624,48 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci
}
}
NetGraphNode
NetGraph::joined (const NetGraphNode &a, const NetGraphNode &b) const
{
NetGraphNode nj = a;
nj.edges ().clear ();
nj.edges ().reserve ((a.end () - a.begin ()) + (b.end () - b.begin ()));
std::map<const db::Net *, NetGraphNode::edge_type> joined;
for (int m = 0; m < 2; ++m) {
const NetGraphNode &n = (m == 0 ? a : b);
for (auto i = n.begin (); i != n.end (); ++i) {
if (i->second.second) {
const db::Net *net = i->second.second == b.net () ? a.net () : i->second.second;
auto j = joined.find (net);
if (j != joined.end ()) {
j->second.first.insert (j->second.first.end (), i->first.begin (), i->first.end ());
} else {
j = joined.insert (std::make_pair (net, *i)).first;
j->second.second.second = net;
}
} else {
nj.edges ().push_back (*i);
}
}
}
for (auto i = joined.begin (); i != joined.end (); ++i) {
nj.edges ().push_back (i->second);
}
nj.apply_net_index (m_net_index);
return nj;
}
}

View File

@ -272,6 +272,11 @@ public:
}
}
std::vector<edge_type> &edges ()
{
return m_edges;
}
private:
const db::Net *mp_net;
size_t m_other_net_index;
@ -414,6 +419,11 @@ public:
return const_cast<db::NetGraphNode &> (((const NetGraph *) this)->virtual_node (sc));
}
/**
* @brief Creates a new node representing two joined nodes
*/
NetGraphNode joined (const NetGraphNode &a, const NetGraphNode &b) const;
/**
* @brief Gets the net for a given node index
*/

View File

@ -61,7 +61,8 @@ NetlistCompareGlobalOptions::options ()
// --------------------------------------------------------------------------------------------------------------------
// Some utilities
std::string nl_compare_debug_indent (size_t depth)
std::string
nl_compare_debug_indent (size_t depth)
{
std::string s;
for (size_t d = 0; d < depth; ++d) {
@ -70,6 +71,38 @@ std::string nl_compare_debug_indent (size_t depth)
return s;
}
const std::string var_sep = tl::to_string (tr (" vs. "));
static std::string
expanded_name (const db::Net *a)
{
if (a == 0) {
return tl::to_string (tr ("(not connected)"));
} else {
return a->expanded_name ();
}
}
std::string
nets2string (const db::Net *a, const db::Net *b)
{
std::string na = expanded_name (a);
std::string nb = expanded_name (b);
if (na != nb) {
return na + var_sep + nb;
} else {
return nb;
}
}
std::string
nets2string (const std::pair<const db::Net *, const db::Net *> &np)
{
return nets2string (np.first, np.second);
}
// --------------------------------------------------------------------------------------------------------------------
// Some functions

View File

@ -83,6 +83,8 @@ const size_t unknown_id = std::numeric_limits<size_t>::max () - 1;
// Some utilities
std::string nl_compare_debug_indent (size_t depth);
std::string nets2string (const db::Net *a, const db::Net *b);
std::string nets2string (const std::pair<const db::Net *, const db::Net *> &np);
// --------------------------------------------------------------------------------------------------------------------
// Net name compare

View File

@ -416,6 +416,16 @@ NetlistCrossReference::gen_end_circuit (const db::Circuit *, const db::Circuit *
mp_per_circuit_data = 0;
}
void
NetlistCrossReference::gen_log_entry (Severity severity, const std::string &msg)
{
if (mp_per_circuit_data) {
mp_per_circuit_data->log_entries.push_back (LogEntryData (severity, msg));
} else {
m_other_log_entries.push_back (LogEntryData (severity, msg));
}
}
void
NetlistCrossReference::gen_nets (const db::Net *a, const db::Net *b, Status status, const std::string &msg)
{

View File

@ -105,6 +105,15 @@ public:
std::string msg;
};
struct LogEntryData
{
LogEntryData (Severity s, const std::string &m) : severity (s), msg (m) { }
LogEntryData () : severity (NoSeverity) { }
Severity severity;
std::string msg;
};
struct PerCircuitData
{
PerCircuitData () : status (None) { }
@ -117,6 +126,8 @@ public:
typedef pin_pairs_type::const_iterator pin_pairs_const_iterator;
typedef std::vector<SubCircuitPairData> subcircuit_pairs_type;
typedef subcircuit_pairs_type::const_iterator subcircuit_pairs_const_iterator;
typedef std::vector<LogEntryData> log_entries_type;
typedef log_entries_type::const_iterator log_entries_const_iterator;
Status status;
std::string msg;
@ -124,6 +135,7 @@ public:
device_pairs_type devices;
pin_pairs_type pins;
subcircuit_pairs_type subcircuits;
log_entries_type log_entries;
};
struct PerNetData
@ -145,6 +157,7 @@ public:
void gen_end_netlist (const db::Netlist *a, const db::Netlist *b);
void gen_begin_circuit (const db::Circuit *a, const db::Circuit *b);
void gen_end_circuit (const db::Circuit *a, const db::Circuit *b, Status status, const std::string &msg);
void gen_log_entry (Severity severity, const std::string &msg);
void gen_nets (const db::Net *a, const db::Net *b, Status status, const std::string &msg);
void gen_devices (const db::Device *a, const db::Device *b, Status status, const std::string &msg);
void gen_pins (const db::Pin *a, const db::Pin *b, Status status, const std::string &msg);
@ -185,6 +198,11 @@ public:
gen_end_circuit (a, b, Mismatch, msg);
}
virtual void log_entry (Severity severity, const std::string &msg)
{
gen_log_entry (severity, msg);
}
virtual void match_nets (const db::Net *a, const db::Net *b)
{
gen_nets (a, b, Match, std::string ());
@ -261,6 +279,11 @@ public:
return m_circuits.end ();
}
const PerCircuitData::log_entries_type &other_log_entries () const
{
return m_other_log_entries;
}
const db::Pin *other_pin_for (const db::Pin *pin) const;
const db::Device *other_device_for (const db::Device *device) const;
const db::SubCircuit *other_subcircuit_for (const db::SubCircuit *subcircuit) const;
@ -295,6 +318,7 @@ private:
std::map<const db::SubCircuit *, const db::SubCircuit *> m_other_subcircuit;
std::pair<const db::Circuit *, const db::Circuit *> m_current_circuits;
PerCircuitData *mp_per_circuit_data;
PerCircuitData::log_entries_type m_other_log_entries;
void establish_pair (const db::Circuit *a, const db::Circuit *b);
void establish_pair (const db::Net *a, const db::Net *b, Status status, const std::string &msg);

View File

@ -220,14 +220,6 @@ public:
*/
~NetlistDeviceExtractor ();
/**
* @brief Gets the name of the extractor and the device class
*/
const std::string &name ()
{
return m_name;
}
/**
* @brief Gets the property name for the device terminal annotation
* This name is used to attach the terminal ID to terminal shapes.
@ -321,6 +313,14 @@ public:
m_name = name;
}
/**
* @brief Sets the name of the device class and the device extractor
*/
const std::string &name () const
{
return m_name;
}
/**
* @brief Sets up the extractor
*

View File

@ -61,7 +61,7 @@ join_layer_names (std::string &s, const std::string &n)
// ReaderBase implementation
ReaderBase::ReaderBase ()
: m_warnings_as_errors (false)
: m_warnings_as_errors (false), m_warn_level (1)
{
}
@ -75,6 +75,12 @@ ReaderBase::set_warnings_as_errors (bool f)
m_warnings_as_errors = f;
}
void
ReaderBase::init (const db::LoadLayoutOptions &options)
{
m_warn_level = options.warn_level ();
}
// ---------------------------------------------------------------
// Reader implementation
@ -91,7 +97,13 @@ Reader::Reader (tl::InputStream &stream)
}
if (! mp_actual_reader) {
throw db::ReaderException (tl::to_string (tr ("Stream has unknown format: ")) + stream.source ());
m_stream.reset ();
std::string head = m_stream.read_all (4000);
bool has_more (m_stream.get (1) != 0);
throw db::ReaderUnknownFormatException (tl::to_string (tr ("Stream has unknown format: ")) + stream.source (), head, has_more);
}
}

View File

@ -54,6 +54,35 @@ public:
{ }
};
/**
* @brief A class representing the "unknown format" reader error
*
* The purpose of this class is supply the header bytes of the
* data stream for analysis.
*/
class DB_PUBLIC ReaderUnknownFormatException
: public ReaderException
{
public:
ReaderUnknownFormatException (const std::string &msg, const std::string &data, bool has_more)
: ReaderException (msg), m_data (data), m_has_more (has_more)
{ }
const std::string &data () const
{
return m_data;
}
bool has_more () const
{
return m_has_more;
}
private:
std::string m_data;
bool m_has_more;
};
/**
* @brief Joins layer names into a single, combined layer
* @param s The first layer name and output
@ -89,8 +118,20 @@ public:
return m_warnings_as_errors;
}
/**
* @brief Gets the warning level
*/
int warn_level () const
{
return m_warn_level;
}
protected:
virtual void init (const db::LoadLayoutOptions &options);
private:
bool m_warnings_as_errors;
int m_warn_level;
};
/**

View File

@ -105,8 +105,8 @@ public:
*
* @param clear_shapes If true, the shapes container is cleared on the start event.
*/
EdgeShapeGenerator (db::Shapes &shapes, bool clear_shapes = false)
: EdgeSink (), mp_shapes (&shapes), m_clear_shapes (clear_shapes)
EdgeShapeGenerator (db::Shapes &shapes, bool clear_shapes = false, int tag = 0, EdgeShapeGenerator *chained = 0)
: EdgeSink (), mp_shapes (&shapes), m_clear_shapes (clear_shapes), m_tag (tag), mp_chained (chained)
{ }
/**
@ -115,6 +115,22 @@ public:
virtual void put (const db::Edge &edge)
{
mp_shapes->insert (edge);
if (mp_chained) {
mp_chained->put (edge);
}
}
/**
* @brief Implementation of the EdgeSink interface
*/
virtual void put (const db::Edge &edge, int tag)
{
if (m_tag == 0 || m_tag == tag) {
mp_shapes->insert (edge);
}
if (mp_chained) {
mp_chained->put (edge, tag);
}
}
/**
@ -127,11 +143,16 @@ public:
// The single-shot scheme is a easy way to overcome problems with multiple start/flush brackets (i.e. on size filter)
m_clear_shapes = false;
}
if (mp_chained) {
mp_chained->start ();
}
}
private:
db::Shapes *mp_shapes;
bool m_clear_shapes;
int m_tag;
EdgeShapeGenerator *mp_chained;
};
/**

View File

@ -998,6 +998,8 @@ Shapes::clear ()
{
if (!m_layers.empty ()) {
invalidate_state (); // HINT: must come before the change is done!
for (tl::vector<LayerBase *>::const_iterator l = m_layers.end (); l != m_layers.begin (); ) {
// because the undo stack will do a push, we need to remove layers from the back (this is the last undo
// element to be executed)
@ -1010,7 +1012,6 @@ Shapes::clear ()
}
}
invalidate_state (); // HINT: must come before the change is done!
m_layers.clear ();
}

View File

@ -38,9 +38,11 @@
#include "dbCellMapping.h"
#include "dbPCellDeclaration.h"
#include "dbSaveLayoutOptions.h"
#include "dbLoadLayoutOptions.h"
#include "dbRecursiveShapeIterator.h"
#include "dbRecursiveInstanceIterator.h"
#include "dbWriter.h"
#include "dbReader.h"
#include "dbHash.h"
#include "tlStream.h"
@ -141,6 +143,55 @@ struct cell_inst_array_defs
}
}
// Cell-based constructors
static C *
new_cell_inst_vector2 (const db::Cell *cell, const vector_type &v)
{
tl_assert (cell != 0);
return new_cell_inst_vector (cell->cell_index (), v);
}
static C *
new_cell_inst2 (const db::Cell *cell, const trans_type &t)
{
tl_assert (cell != 0);
return new_cell_inst (cell->cell_index (), t);
}
static C *
new_cell_inst_cplx2 (const db::Cell *cell, const complex_trans_type &t)
{
tl_assert (cell != 0);
return new_cell_inst_cplx (cell->cell_index (), t);
}
static C *
new_cell_inst_array_vector2 (const db::Cell *cell, const vector_type &v,
const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb)
{
tl_assert (cell != 0);
return new_cell_inst_array_vector (cell->cell_index (), v, a, b, na, nb);
}
static C *
new_cell_inst_array2 (const db::Cell *cell, const trans_type &t,
const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb)
{
tl_assert (cell != 0);
return new_cell_inst_array (cell->cell_index (), t, a, b, na, nb);
}
static C *
new_cell_inst_array_cplx2 (const db::Cell *cell, const complex_trans_type &t,
const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb)
{
tl_assert (cell != 0);
return new_cell_inst_array_cplx (cell->cell_index (), t, a, b, na, nb);
}
// Methods
static db::cell_index_type cell_index (const C *a)
{
return a->object ().cell_index ();
@ -151,6 +202,12 @@ struct cell_inst_array_defs
a->object ().cell_index (cell_index);
}
static void set_cell (C *a, db::Cell *cell)
{
tl_assert (cell != 0);
a->object ().cell_index (cell->cell_index ());
}
static C transformed_simple (const C *arr, const coord_trans_type &t)
{
return arr->transformed (t);
@ -421,17 +478,41 @@ struct cell_inst_array_defs
"@param cell_index The cell to instantiate\n"
"@param trans The transformation by which to instantiate the cell\n"
) +
gsi::constructor ("new", &new_cell_inst2, gsi::arg ("cell"), gsi::arg ("trans"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param trans The transformation by which to instantiate the cell\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_vector, gsi::arg ("cell_index"), gsi::arg ("disp"),
"@brief Creates a single cell instance\n"
"@param cell_index The cell to instantiate\n"
"@param disp The displacement\n"
"This convenience initializer has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_vector2, gsi::arg ("cell"), gsi::arg ("disp"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param disp The displacement\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_cplx, gsi::arg ("cell_index"), gsi::arg ("trans"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell_index The cell to instantiate\n"
"@param trans The complex transformation by which to instantiate the cell\n"
) +
gsi::constructor ("new", &new_cell_inst_cplx2, gsi::arg ("cell"), gsi::arg ("trans"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell The cell to instantiate\n"
"@param trans The complex transformation by which to instantiate the cell\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array, gsi::arg ("cell_index"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell_index The cell to instantiate\n"
@ -445,6 +526,18 @@ struct cell_inst_array_defs
"Starting with version 0.25 the displacements are of vector type."
)
) +
gsi::constructor ("new", &new_cell_inst_array2, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param trans The transformation by which to instantiate the cell\n"
"@param a The displacement vector of the array in the 'a' axis\n"
"@param b The displacement vector of the array in the 'b' axis\n"
"@param na The number of placements in the 'a' axis\n"
"@param nb The number of placements in the 'b' axis\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array_vector, gsi::arg ("cell_index"), gsi::arg ("disp"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell_index The cell to instantiate\n"
@ -456,6 +549,18 @@ struct cell_inst_array_defs
"\n"
"This convenience initializer has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array_vector2, gsi::arg ("cell"), gsi::arg ("disp"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param disp The basic displacement of the first instance\n"
"@param a The displacement vector of the array in the 'a' axis\n"
"@param b The displacement vector of the array in the 'b' axis\n"
"@param na The number of placements in the 'a' axis\n"
"@param nb The number of placements in the 'b' axis\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array_cplx, gsi::arg ("cell_index"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell_index The cell to instantiate\n"
@ -469,6 +574,18 @@ struct cell_inst_array_defs
"Starting with version 0.25 the displacements are of vector type."
)
) +
gsi::constructor ("new", &new_cell_inst_array_cplx2, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell The cell to instantiate\n"
"@param trans The complex transformation by which to instantiate the cell\n"
"@param a The displacement vector of the array in the 'a' axis\n"
"@param b The displacement vector of the array in the 'b' axis\n"
"@param na The number of placements in the 'a' axis\n"
"@param nb The number of placements in the 'b' axis\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::iterator ("each_trans", (typename C::iterator (C::*) () const) &C::begin,
"@brief Gets the simple transformations represented by this instance\n"
"For a single instance, this iterator will deliver the single, simple transformation. "
@ -500,10 +617,18 @@ struct cell_inst_array_defs
) +
gsi::method_ext ("cell_index", &cell_index,
"@brief Gets the cell index of the cell instantiated \n"
"Use \\Layout#cell to get the \\Cell object from the cell index."
) +
method_ext ("cell_index=", &set_cell_index, gsi::arg ("index"),
"@brief Sets the index of the cell this instance refers to\n"
) +
method_ext ("cell=", &set_cell, gsi::arg ("cell"),
"@brief Sets the cell this instance refers to\n"
"This is a convenience method and equivalent to 'cell_index = cell.cell_index()'. There is no getter for "
"the cell pointer because the \\CellInstArray object only knows about cell indexes.\n"
"\n"
"This convenience method has been introduced in version 0.28.\n"
) +
gsi::method ("cplx_trans", (complex_trans_type (C::*) () const) &C::complex_trans,
"@brief Gets the complex transformation of the first instance in the array\n"
"This method is always applicable, compared to \\trans, since simple transformations can be expressed as complex transformations as well."
@ -1560,6 +1685,39 @@ static const char *cell_name (const db::Cell *cell)
}
}
static std::vector<db::cell_index_type>
read_options (db::Cell *cell, const std::string &path, const db::LoadLayoutOptions &options)
{
if (! cell->layout ()) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside in a layout - cannot read such cells")));
}
db::Layout tmp (cell->layout ()->dbu ());
{
tl::InputStream stream (path);
db::Reader reader (stream);
reader.read (tmp, options);
}
if (tmp.end_top_cells () - tmp.begin_top_down () != 1) {
throw tl::Exception (tl::to_string (tr ("Imported layout does not have a single top cell - cannot read such layouts into a cell")));
}
db::CellMapping cm;
std::vector<db::cell_index_type> new_cells = cm.create_single_mapping_full (*cell->layout (), cell->cell_index (), tmp, *tmp.begin_top_down ());
cell->move_tree_shapes (tmp.cell (*tmp.begin_top_down ()), cm);
return new_cells;
}
static std::vector<db::cell_index_type>
read_simple (db::Cell *cell, const std::string &path)
{
return read_options (cell, path, db::LoadLayoutOptions ());
}
static db::Point default_origin;
Class<db::Cell> decl_Cell ("db", "Cell",
@ -1638,6 +1796,40 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"This method has been introduced in version 0.23.\n"
) +
gsi::method_ext ("read", &read_options, gsi::arg ("file_name"), gsi::arg ("options"),
"@brief Reads a layout file into this cell\n"
"\n"
"@param file_name The path of the file to read\n"
"@param options The reader options to use\n"
"@return The indexes of the cells created during the reading (new child cells)\n"
"\n"
"The format of the file will be determined from the file name. "
"The layout will be read into the cell, potentially creating new layers and "
"a subhierarchy of cells below this cell.\n"
"\n"
"This feature is equivalent to the following code:\n"
"\n"
"@code\n"
"def Cell.read(file_name, options)\n"
" layout = RBA::Layout::new\n"
" layout.read(file_name, options)\n"
" cm = RBA::CellMapping::new\n"
" cm.for_single_cell_full(self, layout.top_cell)\n"
" self.move_tree_shapes(layout.top_cell)\n"
"end\n"
"@/code\n"
"\n"
"See \\move_tree_shapes and \\CellMapping for more details and how to "
"implement more elaborate schemes.\n"
"\n"
"This method has been introduced in version 0.28.\n"
) +
gsi::method_ext ("read", &read_simple, gsi::arg ("file_name"),
"@brief Reads a layout file into this cell\n"
"This version uses the default options for reading the file.\n"
"\n"
"This method has been introduced in version 0.28.\n"
) +
gsi::method_ext ("dup", &dup_cell,
"@brief Creates a copy of the cell\n"
"\n"
@ -2526,14 +2718,16 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"@return The bounding box of the cell\n"
"\n"
"The bounding box is computed over all layers. To compute the bounding box over single layers, "
"use \\bbox_per_layer.\n"
"use \\bbox with a layer index argument.\n"
) +
gsi::method ("bbox_per_layer", (const db::Cell::box_type &(db::Cell::*) (unsigned int) const) &db::Cell::bbox, gsi::arg ("layer_index"),
gsi::method ("bbox|#bbox_per_layer", (const db::Cell::box_type &(db::Cell::*) (unsigned int) const) &db::Cell::bbox, gsi::arg ("layer_index"),
"@brief Gets the per-layer bounding box of the cell\n"
"\n"
"@return The bounding box of the cell considering only the given layer\n"
"\n"
"The bounding box is the box enclosing all shapes on the given layer.\n"
"\n"
"'bbox' is the preferred synonym since version 0.28.\n"
) +
gsi::method_ext ("dbbox", &cell_dbbox,
"@brief Gets the bounding box of the cell in micrometer units\n"
@ -2541,18 +2735,19 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"@return The bounding box of the cell\n"
"\n"
"The bounding box is computed over all layers. To compute the bounding box over single layers, "
"use \\dbbox_per_layer.\n"
"use \\dbbox with a layer index argument.\n"
"\n"
"This method has been introduced in version 0.25."
) +
gsi::method_ext ("dbbox_per_layer", &cell_dbbox_per_layer, gsi::arg ("layer_index"),
gsi::method_ext ("dbbox|#dbbox_per_layer", &cell_dbbox_per_layer, gsi::arg ("layer_index"),
"@brief Gets the per-layer bounding box of the cell in micrometer units\n"
"\n"
"@return The bounding box of the cell considering only the given layer\n"
"\n"
"The bounding box is the box enclosing all shapes on the given layer.\n"
"\n"
"This method has been introduced in version 0.25."
"This method has been introduced in version 0.25. "
"'dbbox' is the preferred synonym since version 0.28.\n"
) +
gsi::iterator_ext ("each_overlapping_inst", &begin_overlapping_inst, gsi::arg ("b"),
"@brief Gets the instances overlapping the given rectangle\n"
@ -3721,22 +3916,22 @@ Class<db::Instance> decl_Instance ("db", "Instance",
"\n"
"This method has been introduced in version 0.25."
) +
gsi::method_ext ("bbox_per_layer", &inst_bbox_per_layer, gsi::arg ("layer_index"),
gsi::method_ext ("bbox|#bbox_per_layer", &inst_bbox_per_layer, gsi::arg ("layer_index"),
"@brief Gets the bounding box of the instance for a given layer\n"
"@param layer_index The index of the layer the bounding box will be computed for.\n"
"The bounding box incorporates all instances that the array represents. "
"It gives the overall extension of the child cell as seen in the calling cell (or all array members if the instance forms an array) "
"for the given layer. If the layer is empty in this cell and all it's children', an empty bounding box will be returned. "
"\n"
"This method has been introduced in version 0.25."
"This method has been introduced in version 0.25. 'bbox' is the preferred synonym for it since version 0.28."
) +
gsi::method_ext ("dbbox_per_layer", &inst_dbbox_per_layer, gsi::arg ("layer_index"),
gsi::method_ext ("dbbox|#dbbox_per_layer", &inst_dbbox_per_layer, gsi::arg ("layer_index"),
"@brief Gets the bounding box of the instance in micron units\n"
"@param layer_index The index of the layer the bounding box will be computed for.\n"
"Gets the bounding box (see \\bbox_per_layer) of the instance, but will compute the micrometer unit box by "
"multiplying \\bbox_per_layer with the database unit.\n"
"Gets the bounding box (see \\bbox) of the instance, but will compute the micrometer unit box by "
"multiplying \\bbox with the database unit.\n"
"\n"
"This method has been introduced in version 0.25."
"This method has been introduced in version 0.25. 'dbbox' is the preferred synonym for it since version 0.28."
) +
gsi::method_ext ("parent_cell", &parent_cell_ptr,
"@brief Gets the cell this instance is contained in\n"
@ -4220,10 +4415,12 @@ static db::CellInstArray::box_type cell_inst_array_bbox_per_layer (const db::Cel
Class<db::CellInstArray> decl_CellInstArray ("db", "CellInstArray",
cell_inst_array_defs<db::CellInstArray>::methods (false /*old version*/) +
gsi::method_ext ("bbox_per_layer", &cell_inst_array_bbox_per_layer, gsi::arg ("layout"), gsi::arg ("layer_index"),
gsi::method_ext ("bbox|#bbox_per_layer", &cell_inst_array_bbox_per_layer, gsi::arg ("layout"), gsi::arg ("layer_index"),
"@brief Gets the bounding box of the array with respect to one layer\n"
"The bounding box incorporates all instances that the array represents. It needs the layout object to access the "
"actual cell from the cell index."
"actual cell from the cell index.\n"
"\n"
"'bbox' is the preferred synonym since version 0.28.\n"
) +
gsi::method_ext ("bbox", &cell_inst_array_bbox, gsi::arg ("layout"),
"@brief Gets the bounding box of the array\n"
@ -4282,10 +4479,12 @@ static db::DBox cell_dinst_array_bbox_per_layer (const db::DCellInstArray *a, co
Class<db::DCellInstArray> decl_DCellInstArray ("db", "DCellInstArray",
cell_inst_array_defs<db::DCellInstArray>::methods (true /*new version*/) +
gsi::method_ext ("bbox_per_layer", &cell_dinst_array_bbox_per_layer, gsi::arg ("layout"), gsi::arg ("layer_index"),
gsi::method_ext ("bbox|#bbox_per_layer", &cell_dinst_array_bbox_per_layer, gsi::arg ("layout"), gsi::arg ("layer_index"),
"@brief Gets the bounding box of the array with respect to one layer\n"
"The bounding box incorporates all instances that the array represents. It needs the layout object to access the "
"actual cell from the cell index."
"actual cell from the cell index.\n"
"\n"
"'bbox' is the preferred synonym since version 0.28.\n"
) +
gsi::method_ext ("bbox", &cell_dinst_array_bbox, gsi::arg ("layout"),
"@brief Gets the bounding box of the array\n"

View File

@ -36,9 +36,98 @@ static db::cell_index_type drop_cell_const ()
return db::DropCell;
}
static void create_single_mapping (db::CellMapping *cm, db::Cell &a, const db::Cell &b)
{
tl_assert (a.layout () != 0);
tl_assert (b.layout () != 0);
cm->create_single_mapping (*a.layout (), a.cell_index (), *b.layout(), b.cell_index ());
}
static std::vector<db::cell_index_type> create_single_mapping_full (db::CellMapping *cm, db::Cell &a, const db::Cell &b)
{
tl_assert (a.layout () != 0);
tl_assert (b.layout () != 0);
return cm->create_single_mapping_full (*a.layout (), a.cell_index (), *b.layout(), b.cell_index ());
}
static std::vector<db::cell_index_type> create_multi_mapping_gen (db::CellMapping *cm, const std::vector<db::Cell *> &a, const std::vector<const db::Cell *> &b, bool full)
{
db::Layout *lya = 0;
const db::Layout *lyb = 0;
std::vector<db::cell_index_type> cia, cib;
for (auto i = a.begin (); i != a.end (); ++i) {
tl_assert (*i != 0);
tl_assert ((*i)->layout () != 0);
cia.push_back ((*i)->cell_index ());
if (lya == 0) {
lya = (*i)->layout ();
} else if (lya != (*i)->layout ()) {
throw tl::Exception (tl::to_string (tr ("First cell array contains cells from different layouts")));
}
}
for (auto i = b.begin (); i != b.end (); ++i) {
tl_assert (*i != 0);
tl_assert ((*i)->layout () != 0);
cib.push_back ((*i)->cell_index ());
if (lyb == 0) {
lyb = (*i)->layout ();
} else if (lyb != (*i)->layout ()) {
throw tl::Exception (tl::to_string (tr ("Second cell array contains cells from different layouts")));
}
}
if (full) {
return cm->create_multi_mapping_full (*lya, cia, *lyb, cib);
} else {
cm->create_multi_mapping (*lya, cia, *lyb, cib);
return std::vector<db::cell_index_type> ();
}
}
static std::vector<db::cell_index_type> create_multi_mapping_full (db::CellMapping *cm, const std::vector<db::Cell *> &a, const std::vector<const db::Cell *> &b)
{
return create_multi_mapping_gen (cm, a, b, true);
}
static void create_multi_mapping (db::CellMapping *cm, const std::vector<db::Cell *> &a, const std::vector<const db::Cell *> &b)
{
create_multi_mapping_gen (cm, a, b, false);
}
static void create_from_geometry (db::CellMapping *cm, db::Cell &a, const db::Cell &b)
{
tl_assert (a.layout () != 0);
tl_assert (b.layout () != 0);
return cm->create_from_geometry (*a.layout (), a.cell_index (), *b.layout(), b.cell_index ());
}
static std::vector<db::cell_index_type> create_from_geometry_full (db::CellMapping *cm, db::Cell &a, const db::Cell &b)
{
tl_assert (a.layout () != 0);
tl_assert (b.layout () != 0);
return cm->create_from_geometry_full (*a.layout (), a.cell_index (), *b.layout(), b.cell_index ());
}
static void create_from_names (db::CellMapping *cm, db::Cell &a, const db::Cell &b)
{
tl_assert (a.layout () != 0);
tl_assert (b.layout () != 0);
return cm->create_from_names (*a.layout (), a.cell_index (), *b.layout(), b.cell_index ());
}
static std::vector<db::cell_index_type> create_from_names_full (db::CellMapping *cm, db::Cell &a, const db::Cell &b)
{
tl_assert (a.layout () != 0);
tl_assert (b.layout () != 0);
return cm->create_from_names_full (*a.layout (), a.cell_index (), *b.layout(), b.cell_index ());
}
Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
gsi::method ("DropCell", &drop_cell_const,
"@brief A constant indicating the reques to drop a cell\n"
"@brief A constant indicating the request to drop a cell\n"
"\n"
"If used as a pseudo-target for the cell mapping, this index indicates "
"that the cell shall be dropped rather than created on the target side "
@ -65,9 +154,19 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method ("for_single_cell_full", &db::CellMapping::create_single_mapping_full, gsi::arg ("layout_a"), gsi::arg ("cell_index_a"), gsi::arg ("layout_b"), gsi::arg ("cell_index_b"),
gsi::method_ext ("for_single_cell", &create_single_mapping, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping for top-level identity\n"
"\n"
"@param cell_a The target cell.\n"
"@param cell_b The source cell.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("for_single_cell_full", &db::CellMapping::create_single_mapping_full, gsi::arg ("layout_a"), gsi::arg ("cell_index_a"), gsi::arg ("layout_b"), gsi::arg ("cell_index_b"),
"@brief Initializes the cell mapping for top-level identity in full mapping mode\n"
"\n"
"@param layout_a The target layout.\n"
"@param cell_index_a The index of the target cell.\n"
"@param layout_b The source layout.\n"
@ -80,6 +179,16 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method_ext ("for_single_cell_full", &create_single_mapping_full, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping for top-level identity in full mapping mode\n"
"\n"
"@param cell_a The target cell.\n"
"@param cell_b The source cell.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("for_multi_cells", &db::CellMapping::create_multi_mapping, gsi::arg ("layout_a"), gsi::arg ("cell_indexes_a"), gsi::arg ("layout_b"), gsi::arg ("cell_indexes_b"),
"@brief Initializes the cell mapping for top-level identity\n"
"\n"
@ -97,9 +206,19 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.27."
) +
gsi::method ("for_multi_cells_full", &db::CellMapping::create_multi_mapping_full, gsi::arg ("layout_a"), gsi::arg ("cell_indexes_a"), gsi::arg ("layout_b"), gsi::arg ("cell_indexes_b"),
gsi::method_ext ("for_multi_cells", &create_multi_mapping, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping for top-level identity\n"
"\n"
"@param cell_a A list of target cells.\n"
"@param cell_b A list of source cells.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("for_multi_cells_full", &db::CellMapping::create_multi_mapping_full, gsi::arg ("layout_a"), gsi::arg ("cell_indexes_a"), gsi::arg ("layout_b"), gsi::arg ("cell_indexes_b"),
"@brief Initializes the cell mapping for top-level identity in full mapping mode\n"
"\n"
"@param layout_a The target layout.\n"
"@param cell_indexes_a A list of cell indexes for the target cells.\n"
"@param layout_b The source layout.\n"
@ -112,6 +231,16 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.27."
) +
gsi::method_ext ("for_multi_cells_full", &create_multi_mapping_full, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping for top-level identity in full mapping mode\n"
"\n"
"@param cell_a A list of target cells.\n"
"@param cell_b A list of source cells.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("from_geometry_full", &db::CellMapping::create_from_geometry_full, gsi::arg ("layout_a"), gsi::arg ("cell_index_a"), gsi::arg ("layout_b"), gsi::arg ("cell_index_b"),
"@brief Initializes the cell mapping using the geometrical identity in full mapping mode\n"
"\n"
@ -132,6 +261,16 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method_ext ("from_geometry_full", &create_from_geometry_full, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping using the geometrical identity in full mapping mode\n"
"\n"
"@param cell_a The target cell.\n"
"@param cell_b The source cell.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("from_geometry", &db::CellMapping::create_from_geometry, gsi::arg ("layout_a"), gsi::arg ("cell_index_a"), gsi::arg ("layout_b"), gsi::arg ("cell_index_b"),
"@brief Initializes the cell mapping using the geometrical identity\n"
"\n"
@ -147,6 +286,16 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method_ext ("from_geometry", &create_from_geometry, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping using the geometrical identity\n"
"\n"
"@param cell_a The target cell.\n"
"@param cell_b The source cell.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("from_names", &db::CellMapping::create_from_names, gsi::arg ("layout_a"), gsi::arg ("cell_index_a"), gsi::arg ("layout_b"), gsi::arg ("cell_index_b"),
"@brief Initializes the cell mapping using the name identity\n"
"\n"
@ -161,6 +310,16 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method_ext ("from_names", &create_from_names, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping using the name identity\n"
"\n"
"@param cell_a The target cell.\n"
"@param cell_b The source cell.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("from_names_full", &db::CellMapping::create_from_names_full, gsi::arg ("layout_a"), gsi::arg ("cell_index_a"), gsi::arg ("layout_b"), gsi::arg ("cell_index_b"),
"@brief Initializes the cell mapping using the name identity in full mapping mode\n"
"\n"
@ -180,7 +339,17 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method ("clear", &db::CellMapping::clear,
gsi::method_ext ("from_names_full", &create_from_names_full, gsi::arg ("cell_a"), gsi::arg ("cell_b"),
"@brief Initializes the cell mapping using the name identity in full mapping mode\n"
"\n"
"@param cell_a The target cell.\n"
"@param cell_b The source cell.\n"
"@return A list of indexes of cells created.\n"
"\n"
"This is a convenience version which uses cell references instead of layout/cell index combinations. "
"It has been introduced in version 0.28."
) +
gsi::method ("clear", &db::CellMapping::clear,
"@brief Clears the mapping.\n"
"\n"
"This method has been introduced in version 0.23."
@ -208,7 +377,6 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
gsi::method ("has_mapping?", &db::CellMapping::has_mapping, gsi::arg ("cell_index_b"),
"@brief Returns as value indicating whether a cell of layout_b has a mapping to a layout_a cell.\n"
"\n"
"\n"
"@param cell_index_b The index of the cell in layout_b whose mapping is requested.\n"
"@return true, if the cell has a mapping\n"
"\n"
@ -231,15 +399,27 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"the mapping of cells of a source layout B to a target layout A. The cell mapping object "
"is basically a table associating a cell in layout B with a cell in layout A.\n"
"\n"
"The cell mapping is of particular interest for providing the cell mapping recipe in "
"\\Cell#copy_tree_shapes or \\Cell#move_tree_shapes.\n"
"\n"
"The mapping object is used to create and hold that table. There are three basic modes in which "
"a table can be generated:\n"
"\n"
"@ul\n"
" @li Top-level identity @/li\n"
" @li Geometrical identity @/li\n"
" @li Name identity @/li\n"
" @li Top-level identity (\\for_single_cell and \\for_single_cell_full) @/li\n"
" @li Top-level identify for multiple cells (\\for_multi_cells_full and \\for_multi_cells_full) @/li\n"
" @li Geometrical identity (\\from_geometry and \\from_geometry_full)@/li\n"
" @li Name identity (\\from_names and \\from_names_full) @/li\n"
"@/ul\n"
"\n"
"'full' refers to the way cells are treated which are not mentioned. In the 'full' versions, "
"cells for which no mapping is established explicitly - specifically all child cells in top-level identity modes - "
"are created in the target layout and instantiated according to their source layout hierarchy. Then, these new "
"cells become targets of the respective source cells. "
"In the plain version (without 'full' cells), no additional cells are created. For the case of \\Layout#copy_tree_shapes "
"cells not explicitly mapped are flattened. Hence for example, \\for_single_cell will flatten all children of the source cell during "
"\\Layout#copy_tree_shapes or \\Layout#move_tree_shapes.\n"
"\n"
"Top-level identity means that only one cell (the top cell) is regarded identical. All child cells are "
"not considered identical. In full mode (see below), this will create a new, identical cell tree "
"below the top cell in layout A.\n"
@ -264,10 +444,42 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\\from_names_full or \\from_geometry_full. These versions will create new cells and their corresponding "
"instances in the target layout if no suitable target cell is found.\n"
"\n"
"CellMapping objects play a role mainly in the hierarchical copy or move operations of \\Layout. "
"However, use is not restricted to these applications.\n"
"This is a simple example for a cell mapping preserving the hierarchy of the source cell and creating "
"a hierarchy copy in the top cell of the target layout ('hierarchical merge'):\n"
"\n"
"Here is one example for using \\CellMapping. It extracts cells 'A', 'B' and 'C' from one layout "
"@code\n"
"cell_names = [ \"A\", \"B\", \"C\" ]\n"
"\n"
"source = RBA::Layout::new\n"
"source.read(\"input.gds\")\n"
"\n"
"target = RBA::Layout::new\n"
"target_top = target.create_cell(\"IMPORTED\")\n"
"\n"
"cm = RBA::CellMapping::new\n"
"# Copies the source layout hierarchy into the target top cell:\n"
"cm.for_single_cell_full(target_top, source.top_cell)\n"
"target.copy_tree_shapes(source, cm)\n"
"@/code\n"
"\n"
"Without 'full', the effect is move-with-flattening (note we're using 'move' in this example):\n"
"\n"
"@code\n"
"cell_names = [ \"A\", \"B\", \"C\" ]\n"
"\n"
"source = RBA::Layout::new\n"
"source.read(\"input.gds\")\n"
"\n"
"target = RBA::Layout::new\n"
"target_top = target.create_cell(\"IMPORTED\")\n"
"\n"
"cm = RBA::CellMapping::new\n"
"# Flattens the source layout hierarchy into the target top cell:\n"
"cm.for_single_cell(target_top, source.top_cell)\n"
"target.move_tree_shapes(source, cm)\n"
"@/code\n"
"\n"
"This is another example for using \\CellMapping in multiple top cell identity mode. It extracts cells 'A', 'B' and 'C' from one layout "
"and copies them to another. It will also copy all shapes and all child cells. Child cells which are "
"shared between the three initial cells will be shared in the target layout too.\n"
"\n"
@ -279,11 +491,11 @@ Class<db::CellMapping> decl_CellMapping ("db", "CellMapping",
"\n"
"target = RBA::Layout::new\n"
"\n"
"source_cells = cell_names.collect { |n| source.cell_by_name(n).cell_index }\n"
"target_cells = cell_names.collect { |n| target.create_cell(n).cell_index }\n"
"source_cells = cell_names.collect { |n| source.cell_by_name(n) }\n"
"target_cells = cell_names.collect { |n| target.create_cell(n) }\n"
"\n"
"cm = RBA::CellMapping::new\n"
"cm.for_multi_cells_full(source, source_cells, target, target_cells)\n"
"cm.for_multi_cells_full(target_cells, source_cells)\n"
"target.copy_tree_shapes(source, cm)\n"
"@/code\n"
);

View File

@ -711,7 +711,7 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
"This node renders the input if the specified bounding box parameter of the input shape is between pmin and pmax (exclusively). If 'inverse' is set to true, the "
"input shape is returned if the parameter is less than pmin (exclusively) or larger than pmax (inclusively)."
) +
gsi::constructor ("new_ratio_filter", &new_ratio_filter, gsi::arg ("input"), gsi::arg ("parameter"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0.0), gsi::arg ("pmin_included"), gsi::arg ("pmax", std::numeric_limits<double>::max (), "max"), gsi::arg ("pmax_included", true),
gsi::constructor ("new_ratio_filter", &new_ratio_filter, gsi::arg ("input"), gsi::arg ("parameter"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0.0), gsi::arg ("pmin_included", true), gsi::arg ("pmax", std::numeric_limits<double>::max (), "max"), gsi::arg ("pmax_included", true),
"@brief Creates a node filtering the input by ratio parameters.\n"
"This node renders the input if the specified ratio parameter of the input shape is between pmin and pmax. If 'pmin_included' is true, the range will include pmin. Same for 'pmax_included' and pmax. "
"If 'inverse' is set to true, the input shape is returned if the parameter is not within the specified range."
@ -732,7 +732,7 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_edge_length_sum_filter", &new_edge_length_sum_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits<db::Edge::distance_type>::max (), "max"),
"@brief Creates a node filtering edges by their length sum (over the local set).\n"
) +
gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"),
gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse"), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"),
"@brief Creates a node filtering edges by their orientation.\n"
) +
gsi::constructor ("new_polygons", &new_polygons, gsi::arg ("input"), gsi::arg ("e", 0),
@ -923,4 +923,3 @@ gsi::EnumIn<db::CompoundRegionOperationNode, db::RegionRatioFilter::parameter_ty
);
}

View File

@ -49,7 +49,7 @@ struct edge_pair_defs
return c.release ();
}
static C *new_v ()
static C *new_v ()
{
return new C ();
}
@ -67,29 +67,29 @@ struct edge_pair_defs
static gsi::Methods methods ()
{
return
constructor ("new", &new_v,
constructor ("new", &new_v,
"@brief Default constructor\n"
"\n"
"This constructor creates an default edge pair.\n"
) +
) +
constructor ("new", &new_ee, gsi::arg ("first"), gsi::arg ("second"), gsi::arg ("symmetric", false),
"@brief Constructor from two edges\n"
"\n"
"This constructor creates an edge pair from the two edges given.\n"
"See \\symmetric? for a description of this attribute."
) +
method ("first", (const edge_type &(C::*) () const) &C::first,
) +
method ("first", (const edge_type &(C::*) () const) &C::first,
"@brief Gets the first edge\n"
) +
) +
method ("first=", &C::set_first, gsi::arg ("edge"),
"@brief Sets the first edge\n"
) +
method ("second", (const edge_type &(C::*) () const) &C::second,
) +
method ("second", (const edge_type &(C::*) () const) &C::second,
"@brief Gets the second edge\n"
) +
) +
method ("second=", &C::set_second, gsi::arg ("edge"),
"@brief Sets the second edge\n"
) +
) +
method ("symmetric?", &C::is_symmetric,
"@brief Returns a value indicating whether the edge pair is symmetric\n"
"For symmetric edge pairs, the edges are commutable. Specifically, a symmetric edge pair with (e1,e2) is identical to (e2,e1). "
@ -125,8 +125,8 @@ struct edge_pair_defs
"Normalization is a first step recommended before converting an edge pair to a polygon, "
"because that way the polygons won't be self-overlapping and the enlargement parameter "
"is applied properly."
) +
method ("polygon", &C::to_polygon, gsi::arg ("e The enlargement (set to zero for exact representation)"),
) +
method ("polygon", &C::to_polygon, gsi::arg ("e"),
"@brief Convert an edge pair to a polygon\n"
"The polygon is formed by connecting the end and start points of the edges. It is recommended to "
"use \\normalized before converting the edge pair to a polygon.\n"
@ -137,8 +137,10 @@ struct edge_pair_defs
"edge pairs consisting of two point-like edges.\n"
"\n"
"Another version for converting edge pairs to simple polygons is \\simple_polygon which renders a \\SimplePolygon object."
) +
method ("simple_polygon", &C::to_simple_polygon, gsi::arg ("e The enlargement (set to zero for exact representation)"),
"\n"
"@param e The enlargement (set to zero for exact representation)"
) +
method ("simple_polygon", &C::to_simple_polygon, gsi::arg ("e"),
"@brief Convert an edge pair to a simple polygon\n"
"The polygon is formed by connecting the end and start points of the edges. It is recommended to "
"use \\normalized before converting the edge pair to a polygon.\n"
@ -149,7 +151,9 @@ struct edge_pair_defs
"edge pairs consisting of two point-like edges.\n"
"\n"
"Another version for converting edge pairs to polygons is \\polygon which renders a \\Polygon object."
) +
"\n"
"@param e The enlargement (set to zero for exact representation)"
) +
constructor ("from_s", &from_string, gsi::arg ("s"),
"@brief Creates an object from a string\n"
"Creates the object from a string representation (as returned by \\to_s)\n"
@ -162,7 +166,7 @@ struct edge_pair_defs
"\n"
"The DBU argument has been added in version 0.27.6.\n"
) +
method ("bbox", &C::bbox,
method ("bbox", &C::bbox,
"@brief Gets the bounding box of the edge pair\n"
) +
method ("<", &C::less, gsi::arg ("box"),

View File

@ -371,6 +371,61 @@ static size_t id (const db::Edges *e)
return tl::id_of (e->delegate ());
}
static inline std::vector<db::Edges> as_2edges_vector (const std::pair<db::Edges, db::Edges> &rp)
{
std::vector<db::Edges> res;
res.reserve (2);
res.push_back (db::Edges (const_cast<db::Edges &> (rp.first).take_delegate ()));
res.push_back (db::Edges (const_cast<db::Edges &> (rp.second).take_delegate ()));
return res;
}
static std::vector<db::Edges> andnot_with_edges (const db::Edges *r, const db::Edges &other)
{
return as_2edges_vector (r->andnot (other));
}
static std::vector<db::Edges> andnot_with_region (const db::Edges *r, const db::Region &other)
{
return as_2edges_vector (r->andnot (other));
}
static std::vector<db::Edges> inside_outside_part (const db::Edges *r, const db::Region &other)
{
return as_2edges_vector (r->inside_outside_part (other));
}
static std::vector<db::Edges> split_inside_with_edges (const db::Edges *r, const db::Edges &other)
{
return as_2edges_vector (r->selected_inside_differential (other));
}
static std::vector<db::Edges> split_inside_with_region (const db::Edges *r, const db::Region &other)
{
return as_2edges_vector (r->selected_inside_differential (other));
}
static std::vector<db::Edges> split_outside_with_edges (const db::Edges *r, const db::Edges &other)
{
return as_2edges_vector (r->selected_outside_differential (other));
}
static std::vector<db::Edges> split_outside_with_region (const db::Edges *r, const db::Region &other)
{
return as_2edges_vector (r->selected_outside_differential (other));
}
static std::vector<db::Edges> split_interacting_with_edges (const db::Edges *r, const db::Edges &other)
{
return as_2edges_vector (r->selected_interacting_differential (other));
}
static std::vector<db::Edges> split_interacting_with_region (const db::Edges *r, const db::Region &other)
{
return as_2edges_vector (r->selected_interacting_differential (other));
}
extern Class<db::ShapeCollection> decl_dbShapeCollection;
// NOTE: the Metrics constants are injected into Edges in gsiDeclDbRegion.cc because this is where these constants are instantiated.
@ -774,6 +829,26 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"\n"
"This method has been introduced in version 0.24."
) +
method_ext ("andnot", &andnot_with_edges, gsi::arg ("other"),
"@brief Returns the boolean AND and NOT between self and the other edge set\n"
"\n"
"@return A two-element array of edge collections with the first one being the AND result and the second one being the NOT result\n"
"\n"
"This method will compute the boolean AND and NOT between two edge sets simultaneously. "
"Because this requires a single sweep only, using this method is faster than doing AND and NOT separately.\n"
"\n"
"This method has been added in version 0.28.\n"
) +
method_ext ("andnot", &andnot_with_region, gsi::arg ("other"),
"@brief Returns the boolean AND and NOT between self and the region\n"
"\n"
"@return A two-element array of edge collections with the first one being the AND result and the second one being the NOT result\n"
"\n"
"This method will compute the boolean AND and NOT simultaneously. "
"Because this requires a single sweep only, using this method is faster than doing AND and NOT separately.\n"
"\n"
"This method has been added in version 0.28.\n"
) +
method ("^", &db::Edges::operator^, gsi::arg ("other"),
"@brief Returns the boolean XOR between self and the other edge collection\n"
"\n"
@ -828,66 +903,202 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"@brief Returns the edges of this edge collection which overlap or touch edges from the other edge collection\n"
"\n"
"@return A new edge collection containing the edges overlapping or touching edges from the other edge collection\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method ("not_interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"),
"@brief Returns the edges of this edge collection which do not overlap or touch edges from the other edge collection\n"
"\n"
"@return A new edge collection containing the edges not overlapping or touching edges from the other edge collection\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_interacting, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which overlap or touch edges from the other edge collection\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_not_interacting, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which do not overlap or touch edges from the other edge collection\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method_ext ("split_interacting", &split_interacting_with_edges, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which do and do not interact with edges from the other collection\n"
"\n"
"@return A two-element list of edge collections (first: interacting, second: non-interacting)\n"
"\n"
"This method provides a faster way to compute both interacting and non-interacting edges compared to using separate methods. "
"It has been introduced in version 0.28."
) +
method ("interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_interacting, gsi::arg ("other"),
"@brief Returns the edges from this edge collection which overlap or touch polygons from the region\n"
"\n"
"@return A new edge collection containing the edges overlapping or touching polygons from the region\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method ("not_interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"),
"@brief Returns the edges from this edge collection which do not overlap or touch polygons from the region\n"
"\n"
"@return A new edge collection containing the edges not overlapping or touching polygons from the region\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_interacting, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which overlap or touch polygons from the region\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_not_interacting, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which do not overlap or touch polygons from the region\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method does not merge the edges before they are selected. If you want to select coherent "
"edges, make sure the edge collection is merged before this method is used.\n"
) +
method_ext ("split_interacting", &split_interacting_with_region, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which do and do not interact with polygons from the other region\n"
"\n"
"@return A two-element list of edge collections (first: interacting, second: non-interacting)\n"
"\n"
"This method provides a faster way to compute both interacting and non-interacting edges compared to using separate methods. "
"It has been introduced in version 0.28."
) +
method ("inside", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_inside, gsi::arg ("other"),
"@brief Returns the edges of this edge collection which are inside (completely covered by) edges from the other edge collection\n"
"\n"
"@return A new edge collection containing the edges overlapping or touching edges from the other edge collection\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("not_inside", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_not_inside, gsi::arg ("other"),
"@brief Returns the edges of this edge collection which are not inside (completely covered by) edges from the other edge collection\n"
"\n"
"@return A new edge collection containing the edges not overlapping or touching edges from the other edge collection\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_inside", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_inside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are inside (completely covered by) edges from the other edge collection\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_not_inside", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_not_inside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are not inside (completely covered by) edges from the other edge collection\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method_ext ("split_inside", &split_inside_with_edges, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are and are not inside (completely covered by) edges from the other collection\n"
"\n"
"@return A two-element list of edge collections (first: inside, second: non-inside)\n"
"\n"
"This method provides a faster way to compute both inside and non-inside edges compared to using separate methods. "
"It has been introduced in version 0.28."
) +
method ("inside", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_inside, gsi::arg ("other"),
"@brief Returns the edges from this edge collection which are inside (completely covered by) polygons from the region\n"
"\n"
"@return A new edge collection containing the edges overlapping or touching polygons from the region\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("not_inside", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_not_inside, gsi::arg ("other"),
"@brief Returns the edges from this edge collection which are not inside (completely covered by) polygons from the region\n"
"\n"
"@return A new edge collection containing the edges not overlapping or touching polygons from the region\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_inside", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_inside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are inside (completely covered by) polygons from the region\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_not_inside", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_not_inside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are not inside (completely covered by) polygons from the region\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method_ext ("split_inside", &split_inside_with_region, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are and are not inside (completely covered by) polygons from the other region\n"
"\n"
"@return A two-element list of edge collections (first: inside, second: non-inside)\n"
"\n"
"This method provides a faster way to compute both inside and non-inside edges compared to using separate methods. "
"It has been introduced in version 0.28."
) +
method ("outside", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_outside, gsi::arg ("other"),
"@brief Returns the edges of this edge collection which are outside (completely covered by) edges from the other edge collection\n"
"\n"
"@return A new edge collection containing the edges overlapping or touching edges from the other edge collection\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("not_outside", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_not_outside, gsi::arg ("other"),
"@brief Returns the edges of this edge collection which are not outside (completely covered by) edges from the other edge collection\n"
"\n"
"@return A new edge collection containing the edges not overlapping or touching edges from the other edge collection\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_outside", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_outside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are outside (completely covered by) edges from the other edge collection\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_not_outside", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_not_outside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are not outside (completely covered by) edges from the other edge collection\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method_ext ("split_outside", &split_outside_with_edges, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are and are not outside (completely covered by) edges from the other collection\n"
"\n"
"@return A two-element list of edge collections (first: outside, second: non-outside)\n"
"\n"
"This method provides a faster way to compute both outside and non-outside edges compared to using separate methods. "
"It has been introduced in version 0.28."
) +
method ("outside", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_outside, gsi::arg ("other"),
"@brief Returns the edges from this edge collection which are outside (completely covered by) polygons from the region\n"
"\n"
"@return A new edge collection containing the edges overlapping or touching polygons from the region\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("not_outside", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_not_outside, gsi::arg ("other"),
"@brief Returns the edges from this edge collection which are not outside (completely covered by) polygons from the region\n"
"\n"
"@return A new edge collection containing the edges not overlapping or touching polygons from the region\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_outside", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_outside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are outside (completely covered by) polygons from the region\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method ("select_not_outside", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_not_outside, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are not outside (completely covered by) polygons from the region\n"
"\n"
"@return The edge collection after the edges have been selected (self)\n"
"\n"
"This method has been introduced in version 0.28."
) +
method_ext ("split_outside", &split_outside_with_region, gsi::arg ("other"),
"@brief Selects the edges from this edge collection which are and are not outside (completely covered by) polygons from the other region\n"
"\n"
"@return A two-element list of edge collections (first: outside, second: non-outside)\n"
"\n"
"This method provides a faster way to compute both outside and non-outside edges compared to using separate methods. "
"It has been introduced in version 0.28."
) +
method_ext ("pull_interacting", &pull_interacting, gsi::arg ("other"),
"@brief Returns all polygons of \"other\" which are interacting with (overlapping, touching) edges of this edge set\n"
"The \"pull_...\" methods are similar to \"select_...\" but work the opposite way: they "
@ -966,6 +1177,16 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"\n"
"This method has been introduced in version 0.24."
) +
method_ext ("inside_outside_part", &inside_outside_part, gsi::arg ("other"),
"@brief Returns the partial edges inside and outside the given region\n"
"\n"
"@return A two-element array of edge collections with the first one being the \\inside_part result and the second one being the \\outside_part result\n"
"\n"
"This method will compute the results simultaneously. "
"Because this requires a single sweep only, using this method is faster than doing \\inside_part and \\outside_part separately.\n"
"\n"
"This method has been added in version 0.28.\n"
) +
method ("clear", &db::Edges::clear,
"@brief Clears the edge collection\n"
) +

View File

@ -98,7 +98,7 @@ static std::vector<std::string> l2n_layer_names (const db::LayoutToNetlist *l2n)
return ln;
}
static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_area_factor, double poly_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes)
static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_area_factor, double poly_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes, db::Texts *texts)
{
std::vector<std::pair<const db::Region *, double> > diode_pairs;
@ -127,17 +127,17 @@ static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &po
}
return l2n->antenna_check (poly, poly_area_factor, poly_perimeter_factor, metal, metal_area_factor, metal_perimeter_factor, ratio, diode_pairs);
return l2n->antenna_check (poly, poly_area_factor, poly_perimeter_factor, metal, metal_area_factor, metal_perimeter_factor, ratio, diode_pairs, texts);
}
static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes)
static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes, db::Texts *texts)
{
return antenna_check3 (l2n, poly, 1, poly_perimeter_factor, metal, 1, metal_perimeter_factor, ratio, diodes);
return antenna_check3 (l2n, poly, 1, poly_perimeter_factor, metal, 1, metal_perimeter_factor, ratio, diodes, texts);
}
static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector<tl::Variant> &diodes)
static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector<tl::Variant> &diodes, db::Texts *texts)
{
return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes);
return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes, texts);
}
static void join_net_names (db::LayoutToNetlist *l2n, const std::string &s)
@ -701,7 +701,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"@brief Reads the extracted netlist from the file.\n"
"This method employs the native format of KLayout.\n"
) +
gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"),
gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"),
"@brief Runs an antenna check on the extracted clusters\n"
"\n"
"The antenna check will traverse all clusters and run an antenna check\n"
@ -741,8 +741,13 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"# diode_layer1 increases the ratio by 50 per square micrometer area:\n"
"errors = l2n.antenna(poly, metal, 10.0 [ [ diode_layer, 50.0 ] ])\n"
"@/code\n"
"\n"
"If 'texts' is non-nil, this text collection will receive labels explaining the error in "
"terms of area values and relevant ratio.\n"
"\n"
"The 'texts' parameter has been added in version 0.27.11."
) +
gsi::method_ext ("antenna_check", &antenna_check2, gsi::arg ("gate"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"),
gsi::method_ext ("antenna_check", &antenna_check2, gsi::arg ("gate"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"),
"@brief Runs an antenna check on the extracted clusters taking the perimeter into account\n"
"\n"
"This version of the \\antenna_check method allows taking the perimeter of gate or metal into account. "
@ -759,7 +764,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"This variant has been introduced in version 0.26.6.\n"
) +
gsi::method_ext ("antenna_check", &antenna_check3, gsi::arg ("gate"), gsi::arg ("gate_area_factor"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_area_factor"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"),
gsi::method_ext ("antenna_check", &antenna_check3, gsi::arg ("gate"), gsi::arg ("gate_area_factor"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_area_factor"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"),
"@brief Runs an antenna check on the extracted clusters taking the perimeter into account and providing an area factor\n"
"\n"
"This (most generic) version of the \\antenna_check method allows taking the perimeter of gate or metal into account and also "

View File

@ -21,6 +21,7 @@
*/
#include "gsiDecl.h"
#include "gsiEnums.h"
#include "dbNetlistCompare.h"
namespace {
@ -127,7 +128,7 @@ public:
if (cb_circuit_mismatch.can_issue ()) {
cb_circuit_mismatch.issue<GenericNetlistCompareLogger, const db::Circuit *, const db::Circuit *, const std::string &> (&GenericNetlistCompareLogger::circuit_mismatch_fb, a, b, msg);
} else {
db::NetlistCompareLogger::circuit_mismatch (a, b);
db::NetlistCompareLogger::circuit_mismatch (a, b, msg);
}
}
@ -136,6 +137,20 @@ public:
db::NetlistCompareLogger::circuit_mismatch (a, b, msg);
}
virtual void log_entry (db::NetlistCompareLogger::Severity severity, const std::string &msg)
{
if (cb_log_entry.can_issue ()) {
cb_log_entry.issue<GenericNetlistCompareLogger, db::NetlistCompareLogger::Severity, const std::string &> (&GenericNetlistCompareLogger::log_entry, severity, msg);
} else {
db::NetlistCompareLogger::log_entry (severity, msg);
}
}
void log_entry_fb (db::NetlistCompareLogger::Severity severity, const std::string &msg)
{
db::NetlistCompareLogger::log_entry (severity, msg);
}
virtual void match_nets (const db::Net *a, const db::Net *b)
{
if (cb_match_nets.can_issue ()) {
@ -297,8 +312,9 @@ public:
gsi::Callback cb_end_circuit;
gsi::Callback cb_circuit_skipped;
gsi::Callback cb_match_nets;
gsi::Callback cb_net_mismatch;
gsi::Callback cb_circuit_mismatch;
gsi::Callback cb_log_entry;
gsi::Callback cb_net_mismatch;
gsi::Callback cb_match_ambiguous_nets;
gsi::Callback cb_match_devices;
gsi::Callback cb_match_devices_with_different_parameters;
@ -368,6 +384,13 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger (decl_dbNetl
"\n"
"This method is called instead of \\begin_circuit and \\end_circuit."
) +
gsi::callback ("log_entry", &GenericNetlistCompareLogger::log_entry, &GenericNetlistCompareLogger::cb_log_entry, gsi::arg ("level"), gsi::arg ("msg"),
"@brief Issues an entry for the compare log.\n"
"This method delivers a log message generated during the compare of two circuits.\n"
"It is called between of \\begin_circuit and \\end_circuit.\n"
"\n"
"This method has been added in version 0.28."
) +
gsi::callback ("match_nets", &GenericNetlistCompareLogger::match_nets, &GenericNetlistCompareLogger::cb_match_nets, gsi::arg ("a"), gsi::arg ("b"),
"@brief This function is called when two nets are identified.\n"
"If two nets are identified as a corresponding pair, this method will be called with both nets.\n"
@ -471,6 +494,19 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"The logger is a delegate or event receiver which the comparer will send compare events to. "
"See the class description for more details."
) +
gsi::method ("with_log=", &db::NetlistComparer::set_with_log, gsi::arg ("flag"),
"@brief Sets a value indicating that log messages are generated.\n"
"Log messages may be expensive to compute, hence they can be turned off.\n"
"By default, log messages are generated.\n"
"\n"
"This attribute have been introduced in version 0.28.\n"
) +
gsi::method ("with_log", &db::NetlistComparer::with_log,
"@brief Gets a value indicating that log messages are generated.\n"
"See \\with_log= for details about this flag.\n"
"\n"
"This attribute have been introduced in version 0.28.\n"
) +
gsi::method ("same_nets", (void (db::NetlistComparer::*) (const db::Net *, const db::Net *, bool)) &db::NetlistComparer::same_nets, gsi::arg ("net_a"), gsi::arg ("net_b"), gsi::arg ("must_match", false),
"@brief Marks two nets as identical.\n"
"This makes a net net_a in netlist a identical to the corresponding\n"
@ -621,4 +657,21 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"This class has been introduced in version 0.26."
);
gsi::EnumIn<GenericNetlistCompareLogger, db::NetlistCompareLogger::Severity> decl_CompareLoggerSeverity ("db", "Severity",
gsi::enum_const ("NoSeverity", db::NetlistCompareLogger::NoSeverity,
"@brief Unspecific severity\n"
) +
gsi::enum_const ("Info", db::NetlistCompareLogger::Info,
"@brief Information only\n"
) +
gsi::enum_const ("Warning", db::NetlistCompareLogger::Warning,
"@brief A warning\n"
) +
gsi::enum_const ("Error", db::NetlistCompareLogger::Error,
"@brief An error\n"
),
"@brief This class represents the log severity level for \\GenericNetlistCompareLogger#log_entry.\n"
"This enum has been introduced in version 0.28."
);
}

View File

@ -266,6 +266,9 @@ Class<db::NetlistDeviceExtractor> decl_dbNetlistDeviceExtractor ("db", "DeviceEx
gsi::method ("name", &db::NetlistDeviceExtractor::name,
"@brief Gets the name of the device extractor and the device class."
) +
gsi::method ("name=", &db::NetlistDeviceExtractor::set_name, gsi::arg ("name"),
"@brief Sets the name of the device extractor and the device class."
) +
gsi::method ("device_class", &db::NetlistDeviceExtractor::device_class,
"@brief Gets the device class used during extraction\n"
"The attribute will hold the actual device class used in the device extraction. It "
@ -293,9 +296,6 @@ Class<db::NetlistDeviceExtractor> decl_dbNetlistDeviceExtractor ("db", "DeviceEx
);
Class<GenericDeviceExtractor> decl_GenericDeviceExtractor (decl_dbNetlistDeviceExtractor, "db", "GenericDeviceExtractor",
gsi::method ("name=", &GenericDeviceExtractor::set_name,
"@brief Sets the name of the device extractor and the device class."
) +
gsi::callback ("setup", &GenericDeviceExtractor::setup, &GenericDeviceExtractor::cb_setup,
"@brief Sets up the extractor.\n"
"This method is supposed to set up the device extractor. This involves three basic steps:\n"

View File

@ -83,9 +83,13 @@ namespace gsi
}
static void
lm_map_string (db::LayerMap *layer_map, std::string &s, unsigned int l)
lm_map_string (db::LayerMap *layer_map, std::string &s, int l)
{
layer_map->map_expr (s, l);
if (l < 0) {
layer_map->map_expr (s, layer_map->next_index ());
} else {
layer_map->map_expr (s, (unsigned int) l);
}
}
static void
@ -113,9 +117,13 @@ namespace gsi
}
static void
lm_mmap_string (db::LayerMap *layer_map, std::string &s, unsigned int l)
lm_mmap_string (db::LayerMap *layer_map, std::string &s, int l)
{
layer_map->mmap_expr (s, l);
if (l < 0) {
layer_map->mmap_expr (s, layer_map->next_index ());
} else {
layer_map->mmap_expr (s, (unsigned int) l);
}
}
static void
@ -215,7 +223,7 @@ namespace gsi
"\n"
"This method has been added in version 0.20.\n"
) +
gsi::method_ext ("map", &lm_map_string, gsi::arg ("map_expr"), gsi::arg ("log_layer"),
gsi::method_ext ("map", &lm_map_string, gsi::arg ("map_expr"), gsi::arg ("log_layer", -1),
"@brief Maps a physical layer given by a string to a logical one\n"
"@param map_expr The string describing the physical layer to map.\n"
"@param log_layer The logical layer to which the physical layers are mapped.\n"
@ -242,7 +250,9 @@ namespace gsi
"For example, \"1-10/0: *+1/0\" will add 1 to the original layer number.\n"
"\"1-10/0-50: * / *\" will use the original layers.\n"
"\n"
"Target mapping has been added in version 0.20.\n"
"If the logical layer is negative or omitted, the method will select the next available one.\n"
"\n"
"Target mapping has been added in version 0.20. The logical layer is optional since version 0.28.\n"
) +
gsi::method_ext ("mmap", &lm_mmap, gsi::arg ("phys_layer"), gsi::arg ("log_layer"),
"@brief Maps a physical layer to a logical one and adds to existing mappings\n"
@ -280,14 +290,16 @@ namespace gsi
"\n"
"Multi-mapping has been added in version 0.27.\n"
) +
gsi::method_ext ("mmap", &lm_mmap_string, gsi::arg ("map_expr"), gsi::arg ("log_layer"),
gsi::method_ext ("mmap", &lm_mmap_string, gsi::arg ("map_expr"), gsi::arg ("log_layer", -1),
"@brief Maps a physical layer given by an expression to a logical one and adds to existing mappings\n"
"\n"
"This method acts like the corresponding 'map' method, but adds the logical layer to the receivers of the "
"given physical one. Hence this method implements 1:n mapping capabilities.\n"
"For backward compatibility, 'map' still substitutes mapping.\n"
"\n"
"Multi-mapping has been added in version 0.27.\n"
"If the logical layer is negative or omitted, the method will select the next available one.\n"
"\n"
"Multi-mapping has been added in version 0.27. The logical layer is optional since version 0.28.\n"
) +
gsi::method_ext ("unmap", &lm_unmap, gsi::arg ("phys_layer"),
"@brief Unmaps the given layer\n"
@ -347,9 +359,9 @@ namespace gsi
"\n"
"@code"
"lm = RBA::LayerMap::new\n"
"lm.map(\"1/0-255 : ONE (1/0)\", 0)\n"
"lm.map(\"2/0-255 : TWO (2/0)\", 1)\n"
"lm.map(\"3/0-255 : THREE (3/0)\", 2)\n"
"lm.map(\"1/0-255 : ONE (1/0)\")\n"
"lm.map(\"2/0-255 : TWO (2/0)\")\n"
"lm.map(\"3/0-255 : THREE (3/0)\")\n"
"\n"
"# read the layout using the layer map\n"
"lo = RBA::LoadLayoutOptions::new\n"
@ -384,7 +396,20 @@ namespace gsi
// NOTE: the contribution comes from format specific extensions.
Class<db::LoadLayoutOptions> decl_LoadLayoutOptions ("db", "LoadLayoutOptions",
gsi::Methods (),
gsi::method ("warn_level=", &db::LoadLayoutOptions::set_warn_level, gsi::arg ("level"),
"@brief Sets the warning level.\n"
"The warning level is a reader-specific setting which enables or disables warnings\n"
"on specific levels. Level 0 is always \"warnings off\". The default level is 1\n"
"which means \"reasonable warnings emitted\".\n"
"\n"
"This attribute has been added in version 0.28."
) +
gsi::method ("warn_level", &db::LoadLayoutOptions::warn_level,
"@brief Sets the warning level.\n"
"See \\warn_level= for details about this attribute.\n"
"\n"
"This attribute has been added in version 0.28."
),
"@brief Layout reader options\n"
"\n"
"This object describes various layer reader options used for loading layouts.\n"

View File

@ -437,6 +437,15 @@ static void insert_texts_with_dtrans (db::Shapes *sh, const db::Texts &r, const
}
}
static db::Layout *layout (db::Shapes *sh)
{
if (sh->cell ()) {
return sh->cell ()->layout ();
} else {
return 0;
}
}
static unsigned int s_all () { return db::ShapeIterator::All; }
static unsigned int s_all_with_properties () { return db::ShapeIterator::AllWithProperties; }
static unsigned int s_properties () { return db::ShapeIterator::Properties; }
@ -1231,6 +1240,18 @@ Class<db::Shapes> decl_Shapes ("db", "Shapes",
"This method was introduced in version 0.16\n"
"@return The number of shapes in this container\n"
) +
gsi::method ("cell", &db::Shapes::cell,
"@brief Gets the cell the shape container belongs to\n"
"This method returns nil if the shape container does not belong to a cell.\n"
"\n"
"This method has been added in version 0.28."
) +
gsi::method_ext ("layout", &layout,
"@brief Gets the layout object the shape container belongs to\n"
"This method returns nil if the shape container does not belong to a layout.\n"
"\n"
"This method has been added in version 0.28."
) +
gsi::method ("replace_prop_id", (db::Shape (db::Shapes::*) (const db::Shape &, db::properties_id_type)) &db::Shapes::replace_prop_id, gsi::arg ("shape"), gsi::arg ("property_id"),
"@brief Replaces (or install) the properties of a shape\n"
"@return A \\Shape object representing the new shape\n"

View File

@ -590,3 +590,595 @@ TEST(10_PullInteracting)
db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au10.gds");
}
TEST(11_SelectedInsideWithRegion)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Region r;
r.insert (db::Box (0, -1000, 2000, 0));
r.insert (db::Box (1000, 1000, 2000, 1500));
r.insert (db::Box (1000, 1500, 2000, 2000));
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
r.insert_into (&ly, top_cell.cell_index (), l2);
db::Region rflat = r;
r = db::Region (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e.selected_inside (db::Region ()), ""), true);
EXPECT_EQ (db::compare (e.selected_not_inside (db::Region ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Region ()).first, ""), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Region ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_inside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (r).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (r).second, ""), true);
EXPECT_EQ (db::compare (e.selected_inside (r), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside (rflat), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_inside (r), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_inside (r), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_inside (rflat), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_not_inside (r), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (r).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (rflat).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_inside_differential (r).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (r).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (rflat).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_inside_differential (r).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
TEST(12_SelectedInsideWithEdges)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Edges ee;
for (int i = 0; i <= 2000; i += 100) {
ee.insert (db::Edge (i, -1000, i, 0));
}
for (int i = 1000; i <= 2000; i += 100) {
ee.insert (db::Edge (i, 1000, i, 1500));
ee.insert (db::Edge (i, 1500, i, 2000));
}
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
ee.insert_into (&ly, top_cell.cell_index (), l2);
db::Edges eeflat = ee;
ee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e.selected_inside (db::Edges ()), ""), true);
EXPECT_EQ (db::compare (e.selected_not_inside (db::Edges ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Edges ()).first, ""), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Edges ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_inside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (ee).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (ee).second, ""), true);
EXPECT_EQ (db::compare (e.selected_inside (ee), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside (eeflat), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_inside (ee), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_inside (ee), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_inside (eeflat), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_not_inside (ee), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (ee).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (eeflat).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_inside_differential (ee).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (ee).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (eeflat).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_inside_differential (ee).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
TEST(13_SelectedOutsideWithRegion)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Region r;
r.insert (db::Box (0, -1000, 2000, 0));
r.insert (db::Box (1000, 1000, 2000, 1500));
r.insert (db::Box (1000, 1500, 2000, 2000));
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
r.insert_into (&ly, top_cell.cell_index (), l2);
db::Region rflat = r;
r = db::Region (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e.selected_outside (db::Region ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (db::Region ()), ""), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Region ()).first, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Region ()).second, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_outside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (r).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (r).second, ""), true);
EXPECT_EQ (db::compare (e.selected_outside (r), "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside (rflat), "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_outside (r), "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (r), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (rflat), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_not_outside (r), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (r).first, "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (rflat).first, "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_outside_differential (r).first, "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (r).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (rflat).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_outside_differential (r).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
}
TEST(14_SelectedOutsideWithEdges)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Edges ee;
for (int i = 0; i <= 2000; i += 100) {
ee.insert (db::Edge (i, -1000, i, 0));
}
for (int i = 1000; i <= 2000; i += 100) {
ee.insert (db::Edge (i, 1000, i, 1500));
ee.insert (db::Edge (i, 1500, i, 2000));
}
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
ee.insert_into (&ly, top_cell.cell_index (), l2);
db::Edges eeflat = ee;
ee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e.selected_outside (db::Edges ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (db::Edges ()), ""), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Edges ()).first, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Edges ()).second, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_outside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (ee).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (ee).second, ""), true);
EXPECT_EQ (db::compare (e.selected_outside (ee), "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside (eeflat), "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_outside (ee), "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (ee), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (eeflat), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_not_outside (ee), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (ee).first, "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (eeflat).first, "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_outside_differential (ee).first, "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (ee).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (eeflat).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_outside_differential (ee).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
}
TEST(15_SelectedInteractingWithRegion)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Region r;
r.insert (db::Box (0, -1000, 2000, 0));
r.insert (db::Box (1000, 1000, 2000, 1500));
r.insert (db::Box (1000, 1500, 2000, 2000));
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
r.insert_into (&ly, top_cell.cell_index (), l2);
db::Region rflat = r;
r = db::Region (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e.selected_interacting (db::Region ()), ""), true);
EXPECT_EQ (db::compare (e.selected_not_interacting (db::Region ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (db::Region ()).first, ""), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (db::Region ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges ().selected_interacting (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_interacting (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_interacting_differential (r).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_interacting_differential (r).second, ""), true);
EXPECT_EQ (db::compare (e.selected_interacting (r), "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting (rflat), "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_interacting (r), "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_interacting (r), "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_interacting (rflat), "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_not_interacting (r), "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (r).first, "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (rflat).first, "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_interacting_differential (r).first, "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (r).second, "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (rflat).second, "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_interacting_differential (r).second, "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
TEST(16_SelectedInteractingWithEdges)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Edges ee;
for (int i = 0; i <= 2000; i += 100) {
ee.insert (db::Edge (i, -1000, i, 0));
}
for (int i = 1000; i <= 2000; i += 100) {
ee.insert (db::Edge (i, 1000, i, 1500));
ee.insert (db::Edge (i, 1500, i, 2000));
}
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
ee.insert_into (&ly, top_cell.cell_index (), l2);
db::Edges eeflat = ee;
ee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e.selected_interacting (db::Edges ()), ""), true);
EXPECT_EQ (db::compare (e.selected_not_interacting (db::Edges ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (db::Edges ()).first, ""), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (db::Edges ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges ().selected_interacting (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_interacting (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_interacting_differential (ee).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_interacting_differential (ee).second, ""), true);
EXPECT_EQ (db::compare (e.selected_interacting (ee), "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting (eeflat), "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_interacting (ee), "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_interacting (ee), "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_interacting (eeflat), "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_not_interacting (ee), "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (ee).first, "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (eeflat).first, "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (eflat.selected_interacting_differential (ee).first, "(0,0;0,1000);(1100,-1000;1100,2000);(1300,-800;1300,-200);(1200,-1000;1200,0);(1400,1000;1400,1100);(1600,-800;1600,-200);(100,0;100,3000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (ee).second, "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (eeflat).second, "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.selected_interacting_differential (ee).second, "(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
TEST(17_InsideOutside)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Region r;
r.insert (db::Box (0, -1000, 2000, 0));
r.insert (db::Box (1000, 1000, 2000, 1500));
r.insert (db::Box (1000, 1500, 2000, 2000));
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
r.insert_into (&ly, top_cell.cell_index (), l2);
db::Region rflat = r;
r = db::Region (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e.inside_part (db::Region ()), ""), true);
EXPECT_EQ (db::compare (e.outside_part (db::Region ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.inside_outside_part (db::Region ()).first, ""), true);
EXPECT_EQ (db::compare (e.inside_outside_part (db::Region ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges ().inside_part (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().outside_part (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().inside_outside_part (r).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().inside_outside_part (r).second, ""), true);
EXPECT_EQ (db::compare (e.inside_part (r), "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.inside_part (rflat), "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (eflat.inside_part (r), "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.outside_part (r), "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.outside_part (rflat), "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.outside_part (r), "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.inside_outside_part (r).first, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.inside_outside_part (rflat).first, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (eflat.inside_outside_part (r).first, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.inside_outside_part (r).second, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.inside_outside_part (rflat).second, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.inside_outside_part (r).second, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
TEST(18_AndNotWithRegion)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Region r;
r.insert (db::Box (0, -1000, 2000, 0));
r.insert (db::Box (1000, 1000, 2000, 1500));
r.insert (db::Box (1000, 1500, 2000, 2000));
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
r.insert_into (&ly, top_cell.cell_index (), l2);
db::Region rflat = r;
r = db::Region (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e & db::Region (), ""), true);
EXPECT_EQ (db::compare (e - db::Region (), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.andnot (db::Region ()).first, ""), true);
EXPECT_EQ (db::compare (e.andnot (db::Region ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges () & r, ""), true);
EXPECT_EQ (db::compare (db::Edges () - r, ""), true);
EXPECT_EQ (db::compare (db::Edges ().andnot (r).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().andnot (r).second, ""), true);
EXPECT_EQ (db::compare (e & r, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e & rflat, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (eflat & r, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e - r, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e - rflat, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat - r, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.andnot (r).first, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.andnot (rflat).first, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (eflat.andnot (r).first, "(1500,1500;1500,2000);(1100,1500;1100,2000);(1900,1000;1900,1500);(1900,1500;1900,2000);(1600,-800;1600,-400);(1500,1000;1500,1500);(1100,1000;1100,1500);(1600,-400;1600,-200);(1300,-800;1300,-200);(1700,1500;1650,2000);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.andnot (r).second, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.andnot (rflat).second, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (eflat.andnot (r).second, "(1650,2000;1600,2500);(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
TEST(19_AndNotWithEdges)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Edges ee;
for (int i = 0; i <= 2000; i += 100) {
ee.insert (db::Edge (i, -1000, i, 0));
}
for (int i = 1000; i <= 2000; i += 100) {
ee.insert (db::Edge (i, 1000, i, 1500));
ee.insert (db::Edge (i, 1500, i, 2000));
}
// make deep
db::DeepShapeStore dss;
db::Layout ly;
ly.add_cell ("TOP");
unsigned int l1 = ly.insert_layer ();
unsigned int l2 = ly.insert_layer ();
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
e.insert_into (&ly, top_cell.cell_index (), l1);
db::Edges eflat = e;
e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
ee.insert_into (&ly, top_cell.cell_index (), l2);
db::Edges eeflat = ee;
ee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
EXPECT_EQ (db::compare (e & db::Edges (), ""), true);
EXPECT_EQ (db::compare (e - db::Edges (), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.andnot (db::Edges ()).first, ""), true);
EXPECT_EQ (db::compare (e.andnot (db::Edges ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges () & ee, ""), true);
EXPECT_EQ (db::compare (db::Edges () - ee, ""), true);
EXPECT_EQ (db::compare (db::Edges ().andnot (ee).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().andnot (ee).second, ""), true);
EXPECT_EQ (db::compare (e & ee, "(1500,1000;1500,2000);(1900,1000;1900,2000);(1600,-800;1600,-200);(1100,1000;1100,2000);(1300,-800;1300,-200);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e & eeflat, "(1500,1000;1500,2000);(1900,1000;1900,2000);(1600,-800;1600,-200);(1100,1000;1100,2000);(1300,-800;1300,-200);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (eflat & ee, "(1500,1000;1500,2000);(1900,1000;1900,2000);(1600,-800;1600,-200);(1100,1000;1100,2000);(1300,-800;1300,-200);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e - ee, "(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1700,1500;1600,2500)"), true);
EXPECT_EQ (db::compare (e - eeflat, "(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1700,1500;1600,2500)"), true);
EXPECT_EQ (db::compare (eflat - ee, "(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1700,1500;1600,2500)"), true);
EXPECT_EQ (db::compare (e.andnot (ee).first, "(1500,1000;1500,2000);(1900,1000;1900,2000);(1600,-800;1600,-200);(1100,1000;1100,2000);(1300,-800;1300,-200);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.andnot (eeflat).first, "(1500,1000;1500,2000);(1900,1000;1900,2000);(1600,-800;1600,-200);(1100,1000;1100,2000);(1300,-800;1300,-200);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (eflat.andnot (ee).first, "(1500,1000;1500,2000);(1900,1000;1900,2000);(1600,-800;1600,-200);(1100,1000;1100,2000);(1300,-800;1300,-200);(1100,-1000;1100,0);(1200,-1000;1200,0);(1400,1000;1400,1100)"), true);
EXPECT_EQ (db::compare (e.andnot (ee).second, "(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1700,1500;1600,2500)"), true);
EXPECT_EQ (db::compare (e.andnot (eeflat).second, "(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1700,1500;1600,2500)"), true);
EXPECT_EQ (db::compare (eflat.andnot (ee).second, "(1500,2000;1500,2100);(1100,0;1100,1000);(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000);(1700,1500;1600,2500)"), true);
}

View File

@ -1992,13 +1992,7 @@ TEST(31)
ep.process (ec, op);
std::string s;
for (size_t i = 0; i < out.size (); ++i) {
if (i > 0) {
s += ";";
}
s += out[i].to_string ();
}
std::string s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(0,0;0,400);(0,0;500,0);(500,0;500,300);(0,400;0,417);(400,400;0,400);(394,406;0,417);(0,417;0,500)");
ep.clear ();
@ -2015,14 +2009,7 @@ TEST(31)
ep.process (ec, op);
s.clear ();
for (size_t i = 0; i < out.size (); ++i) {
if (i > 0) {
s += ";";
}
s += out[i].to_string ();
}
s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(400,0;400,400);(0,500;300,500)");
}
@ -2051,17 +2038,11 @@ TEST(32)
std::vector<db::Edge> out;
db::EdgeContainer ec (out);
db::EdgePolygonOp op (false, false /*not including touch*/);
db::EdgePolygonOp op (db::EdgePolygonOp::Inside, false /*not including touch*/);
ep.process (ec, op);
std::string s;
for (size_t i = 0; i < out.size (); ++i) {
if (i > 0) {
s += ";";
}
s += out[i].to_string ();
}
std::string s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(400,400;0,400);(394,406;0,417)");
ep.clear ();
@ -2078,14 +2059,7 @@ TEST(32)
ep.process (ec, op);
s.clear ();
for (size_t i = 0; i < out.size (); ++i) {
if (i > 0) {
s += ";";
}
s += out[i].to_string ();
}
s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(400,0;400,400)");
}
@ -2114,17 +2088,11 @@ TEST(33)
std::vector<db::Edge> out;
db::EdgeContainer ec (out);
db::EdgePolygonOp op (true, true /*including touch*/);
db::EdgePolygonOp op (db::EdgePolygonOp::Outside, true /*including touch*/);
ep.process (ec, op);
std::string s;
for (size_t i = 0; i < out.size (); ++i) {
if (i > 0) {
s += ";";
}
s += out[i].to_string ();
}
std::string s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(0,-100;0,0);(500,-100;500,0);(-100,0;0,0);(500,0;600,0);(500,300;500,400);"
"(0,400;-100,400);(500,400;400,400);(500,400;500,403);(600,400;500,400);"
"(600,400;500,403);(500,403;394,406);(500,403;500,600);(0,417;-100,420);"
@ -2144,19 +2112,76 @@ TEST(33)
ep.process (ec, op);
s.clear ();
for (size_t i = 0; i < out.size (); ++i) {
if (i > 0) {
s += ";";
}
s += out[i].to_string ();
}
s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(-100,-100;-100,500);(-100,-100;400,-100);(400,-100;400,0);(400,-100;600,-100);"
"(600,-100;600,500);(400,400;400,500);(-100,500;-100,600);(-100,500;0,500);"
"(300,500;400,500);(400,500;400,600);(400,500;600,500);(600,500;600,600)");
}
TEST(34)
{
std::vector<db::Polygon> a;
db::Point a1[] = {
db::Point (0, 0),
db::Point (0, 500),
db::Point (300, 500),
db::Point (500, 300),
db::Point (500, 0)
};
a.push_back (db::Polygon ());
a.back ().assign_hull (a1 + 0, a1 + sizeof (a1) / sizeof (a1[0]));
db::EdgeProcessor ep;
for (std::vector<db::Polygon>::const_iterator i = a.begin (); i != a.end (); ++i) {
ep.insert (*i, 0);
}
ep.insert (db::Edge (600, 400, -100, 420), 1);
ep.insert (db::Edge (600, 400, -100, 400), 1);
ep.insert (db::Edge (-100, 0, 600, 0), 1);
ep.insert (db::Edge (0, -100, 0, 600), 1);
ep.insert (db::Edge (500, -100, 500, 600), 1);
std::vector<db::Edge> out2;
db::EdgeContainer ec2 (out2, false, 2);
std::vector<db::Edge> out;
db::EdgeContainer ec (out, false, 1, &ec2);
db::EdgePolygonOp op (db::EdgePolygonOp::Both, true /*not including touch*/);
ep.process (ec, op);
std::string s = tl::join (out2.begin (), out2.end (), ";");
EXPECT_EQ (s, "(0,-100;0,0);(500,-100;500,0);(-100,0;0,0);(500,0;600,0);(500,300;500,400);"
"(0,400;-100,400);(500,400;400,400);(500,400;500,403);(600,400;500,400);"
"(600,400;500,403);(500,403;394,406);(500,403;500,600);(0,417;-100,420);"
"(0,500;0,600)");
s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(0,0;0,400);(0,0;500,0);(500,0;500,300);(0,400;0,417);(400,400;0,400);(394,406;0,417);(0,417;0,500)");
ep.clear ();
out.clear ();
out2.clear ();
for (std::vector<db::Polygon>::const_iterator i = a.begin (); i != a.end (); ++i) {
ep.insert (*i, 0);
}
ep.insert (db::Edge (-100, 500, 600, 500), 1);
ep.insert (db::Edge (400, -100, 400, 600), 1);
ep.insert (db::Edge (-100, -100, -100, 600), 1);
ep.insert (db::Edge (600, -100, 600, 600), 1);
ep.insert (db::Edge (-100, -100, 600, -100), 1);
ep.process (ec, op);
s = tl::join (out2.begin (), out2.end (), ";");
EXPECT_EQ (s, "(-100,-100;-100,500);(-100,-100;400,-100);(400,-100;400,0);(400,-100;600,-100);"
"(600,-100;600,500);(400,400;400,500);(-100,500;-100,600);(-100,500;0,500);"
"(300,500;400,500);(400,500;400,600);(400,500;600,500);(600,500;600,600)");
s = tl::join (out.begin (), out.end (), ";");
EXPECT_EQ (s, "(400,0;400,400);(0,500;300,500)");
}
// TrapezoidGenerator
// Basic

View File

@ -120,13 +120,16 @@ TEST(2)
r2.insert (db::Box (db::Point (0, 10), db::Point (100, 210)));
EXPECT_EQ (db::compare ((r & r1), "(10,200;100,200);(100,0;10,0)"), true);
EXPECT_EQ (db::compare (r.andnot(r1).first, "(10,200;100,200);(100,0;10,0)"), true);
EXPECT_EQ (db::compare ((r & r2), "(0,10;0,200);(100,200;100,10)"), true);
EXPECT_EQ (db::compare (r.andnot(r2).first, "(0,10;0,200);(100,200;100,10)"), true);
db::Edges o1 = r;
o1 &= r1;
EXPECT_EQ (o1.is_merged (), true);
EXPECT_EQ (db::compare (o1, "(10,200;100,200);(100,0;10,0)"), true);
EXPECT_EQ (db::compare ((r - r1), "(0,0;0,200);(100,200;100,0);(0,200;10,200);(10,0;0,0)"), true);
EXPECT_EQ (db::compare (r.andnot(r1).second, "(0,0;0,200);(100,200;100,0);(0,200;10,200);(10,0;0,0)"), true);
db::Edges o2 = r;
o2 -= r1;
EXPECT_EQ (o2.is_merged (), true);
@ -392,31 +395,41 @@ TEST(8)
e2.insert (db::Edge (db::Point (0, 100), db::Point (100, 100)));
EXPECT_EQ (e.selected_interacting (e2).to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_interacting_differential (e2).first.to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_not_interacting (e2).to_string (), "(250,200;300,0)");
EXPECT_EQ (e.selected_interacting_differential (e2).second.to_string (), "(250,200;300,0)");
e2.clear ();
e2.insert (db::Edge (db::Point (0, 100), db::Point (0, 100)));
EXPECT_EQ (e.selected_interacting (e2).to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_interacting_differential (e2).first.to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_not_interacting (e2).to_string (), "(250,200;300,0)");
EXPECT_EQ (e.selected_interacting_differential (e2).second.to_string (), "(250,200;300,0)");
e2.clear ();
e2.insert (db::Edge (db::Point (100, 0), db::Point (0, 0)));
EXPECT_EQ (e.selected_interacting (e2).to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_interacting_differential (e2).first.to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_not_interacting (e2).to_string (), "(250,200;300,0)");
EXPECT_EQ (e.selected_interacting_differential (e2).second.to_string (), "(250,200;300,0)");
e2.clear ();
e2.insert (db::Edge (db::Point (-100, -1), db::Point (100, -1)));
EXPECT_EQ (e.selected_interacting (e2).to_string (), "");
EXPECT_EQ (e.selected_interacting_differential (e2).first.to_string (), "");
EXPECT_EQ (db::compare (e.selected_not_interacting (e2), "(0,0;0,200);(250,200;300,0)"), true);
EXPECT_EQ (db::compare (e.selected_interacting_differential (e2).second, "(0,0;0,200);(250,200;300,0)"), true);
e2.clear ();
e2.insert (db::Edge (db::Point (-100, 0), db::Point (100, 0)));
EXPECT_EQ (e.selected_interacting (e2).to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_interacting_differential (e2).first.to_string (), "(0,0;0,200)");
EXPECT_EQ (e.selected_not_interacting (e2).to_string (), "(250,200;300,0)");
EXPECT_EQ (e.selected_interacting_differential (e2).second.to_string (), "(250,200;300,0)");
db::Edges ee = e;
e.select_interacting (e2);
@ -785,7 +798,9 @@ TEST(20)
EXPECT_EQ (r2.merged ().to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,70;140,40);(140,40;80,40)");
EXPECT_EQ (rr1.to_string (100), "(0,0;0,30;30,30;30,0);(50,0;50,30;80,30;80,0);(50,40;50,70;80,70;80,40)");
EXPECT_EQ (r2.selected_interacting (rr1).to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)");
EXPECT_EQ (r2.selected_interacting_differential (rr1).first.to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)");
EXPECT_EQ (r2.selected_not_interacting (rr1).to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)");
EXPECT_EQ (r2.selected_interacting_differential (rr1).second.to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)");
db::Edges r2dup = r2;
r2.select_interacting (rr1);
@ -809,7 +824,9 @@ TEST(21)
db::Edges e, ee;
e.insert (db::Edge (-100, 100, 200, 100));
EXPECT_EQ ((e & r).to_string (), "(0,100;100,100)");
EXPECT_EQ (e.andnot(r).first.to_string (), "(0,100;100,100)");
EXPECT_EQ (e.inside_part (r).to_string (), "(0,100;100,100)");
EXPECT_EQ (e.inside_outside_part (r).first.to_string (), "(0,100;100,100)");
ee = e;
ee &= r;
@ -820,7 +837,9 @@ TEST(21)
EXPECT_EQ (ee.to_string (), "(0,100;100,100)");
EXPECT_EQ (db::compare ((e - r), "(-100,100;0,100);(100,100;200,100)"), true);
EXPECT_EQ (db::compare (e.andnot(r).second, "(-100,100;0,100);(100,100;200,100)"), true);
EXPECT_EQ (db::compare (e.outside_part (r), "(-100,100;0,100);(100,100;200,100)"), true);
EXPECT_EQ (db::compare (e.inside_outside_part (r).second, "(-100,100;0,100);(100,100;200,100)"), true);
ee = e;
ee -= r;
@ -833,7 +852,9 @@ TEST(21)
e.clear ();
e.insert (db::Edge (-100, 0, 200, 0));
EXPECT_EQ ((e & r).to_string (), "(0,0;100,0)");
EXPECT_EQ (e.andnot(r).first.to_string (), "(0,0;100,0)");
EXPECT_EQ (e.inside_part (r).to_string (), "");
EXPECT_EQ (e.inside_outside_part (r).first.to_string (), "");
ee = e;
ee &= r;
@ -844,6 +865,7 @@ TEST(21)
EXPECT_EQ (ee.to_string (), "");
EXPECT_EQ (db::compare ((e - r), "(-100,0;0,0);(100,0;200,0)"), true);
EXPECT_EQ (db::compare (e.andnot(r).second, "(-100,0;0,0);(100,0;200,0)"), true);
EXPECT_EQ (db::compare (e.outside_part (r), "(-100,0;0,0);(0,0;100,0);(100,0;200,0)"), true);
ee = e;
@ -874,6 +896,7 @@ TEST(22)
ee.insert (db::Edge (4000,-2000,-2000,-2000));
EXPECT_EQ (db::compare ((e & ee), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true);
EXPECT_EQ (db::compare (e.andnot(ee).first, "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true);
EXPECT_EQ (db::compare (e.intersections (ee), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true);
// Edge/edge intersections
@ -883,6 +906,7 @@ TEST(22)
ee.insert (db::Edge (-50, 50, 50, 50));
ee.insert (db::Edge (-50, 100, 50, 100));
EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points
EXPECT_EQ (e.andnot(ee).first.to_string (), ""); // AND does not report intersection points
EXPECT_EQ (db::compare (e.intersections (ee), "(0,50;0,50);(0,100;0,100)"), true);
// Edge is intersected by pair with connection point on this line
@ -894,6 +918,7 @@ TEST(22)
ee.insert (db::Edge (-50, 100, 0, 100));
ee.insert (db::Edge (0, 100, 50, 100));
EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points
EXPECT_EQ (e.andnot(ee).first.to_string (), ""); // AND does not report intersection points
EXPECT_EQ (db::compare (e.intersections (ee), "(0,50;0,50);(0,60;0,60);(0,100;0,100)"), true);
// Coincident edges are crossed by another one
@ -904,6 +929,7 @@ TEST(22)
ee.insert (db::Edge (-50, 100, 50, 100));
ee.insert (db::Edge (-50, 200, 50, 200));
EXPECT_EQ ((e & ee).to_string (), "(0,0;0,150)");
EXPECT_EQ (e.andnot(ee).first.to_string (), "(0,0;0,150)");
EXPECT_EQ (db::compare (e.intersections (ee), "(0,0;0,150);(0,200;0,200)"), true);
}
@ -939,6 +965,162 @@ TEST(23)
EXPECT_EQ (e2.pull_interacting (e).to_string (), "(0,0;0,200)");
}
// Edges::selected_inside(region)
TEST(24)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Region r;
r.insert (db::Box (0, -1000, 2000, 0));
r.insert (db::Box (1000, 1000, 2000, 1500));
r.insert (db::Box (1000, 1500, 2000, 2000));
EXPECT_EQ (db::compare (e.selected_inside (db::Region ()), ""), true);
EXPECT_EQ (db::compare (e.selected_not_inside (db::Region ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Region ()).first, ""), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Region ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_inside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (r).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (r).second, ""), true);
EXPECT_EQ (db::compare (e.selected_inside (r), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_inside (r), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (r).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (r).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
// Edges::selected_inside(edges)
TEST(25)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Edges ee;
for (int i = 0; i <= 2000; i += 100) {
ee.insert (db::Edge (i, -1000, i, 0));
}
for (int i = 1000; i <= 2000; i += 100) {
ee.insert (db::Edge (i, 1000, i, 1500));
ee.insert (db::Edge (i, 1500, i, 2000));
}
EXPECT_EQ (db::compare (e.selected_inside (db::Edges ()), ""), true);
EXPECT_EQ (db::compare (e.selected_not_inside (db::Edges ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Edges ()).first, ""), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (db::Edges ()).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_inside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (ee).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_inside_differential (ee).second, ""), true);
EXPECT_EQ (db::compare (e.selected_inside (ee), "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_not_inside (ee), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (ee).first, "(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_inside_differential (ee).second, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1500,1000;1500,2100);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
}
// Edges::selected_outside(region)
TEST(26)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Region r;
r.insert (db::Box (0, -1000, 2000, 0));
r.insert (db::Box (1000, 1000, 2000, 1500));
r.insert (db::Box (1000, 1500, 2000, 2000));
EXPECT_EQ (db::compare (e.selected_outside (db::Region ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (db::Region ()), ""), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Region ()).first, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Region ()).second, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_outside (r), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (r).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (r).second, ""), true);
EXPECT_EQ (db::compare (e.selected_outside (r), "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (r), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (r).first, "(0,0;0,1000);(100,0;100,3000);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (r).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1700,1500;1600,2500);(1900,1000;1900,2000)"), true);
}
// Edges::selected_outside(edges)
TEST(27)
{
db::Edges e;
e.insert (db::Edge (0, 0, 0, 1000));
e.insert (db::Edge (100, 0, 100, 3000));
e.insert (db::Edge (1100, -1000, 1100, 2000));
e.insert (db::Edge (1200, -1000, 1200, 0));
e.insert (db::Edge (1300, -800, 1300, -200));
e.insert (db::Edge (1400, 1000, 1400, 1100));
e.insert (db::Edge (1500, 1000, 1500, 2100));
e.insert (db::Edge (1600, -800, 1600, -400));
e.insert (db::Edge (1600, -400, 1600, -200));
e.insert (db::Edge (1700, 1500, 1600, 2500));
e.insert (db::Edge (1800, 2500, 1800, 3500));
e.insert (db::Edge (1900, 1000, 1900, 2000));
e.insert (db::Edge (-1500, 0, -1500, 1000));
db::Edges ee;
for (int i = 0; i <= 2000; i += 100) {
ee.insert (db::Edge (i, -1000, i, 0));
}
for (int i = 1000; i <= 2000; i += 100) {
ee.insert (db::Edge (i, 1000, i, 1500));
ee.insert (db::Edge (i, 1500, i, 2000));
}
EXPECT_EQ (db::compare (e.selected_outside (db::Edges ()), "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (db::Edges ()), ""), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Edges ()).first, "(0,0;0,1000);(100,0;100,3000);(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-400);(1600,-400;1600,-200);(1700,1500;1600,2500);(1800,2500;1800,3500);(1900,1000;1900,2000);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (db::Edges ()).second, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_not_outside (ee), ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (ee).first, ""), true);
EXPECT_EQ (db::compare (db::Edges ().selected_outside_differential (ee).second, ""), true);
EXPECT_EQ (db::compare (e.selected_outside (ee), "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_not_outside (ee), "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (ee).first, "(0,0;0,1000);(100,0;100,3000);(1700,1500;1600,2500);(1800,2500;1800,3500);(-1500,0;-1500,1000)"), true);
EXPECT_EQ (db::compare (e.selected_outside_differential (ee).second, "(1100,-1000;1100,2000);(1200,-1000;1200,0);(1300,-800;1300,-200);(1400,1000;1400,1100);(1500,1000;1500,2100);(1600,-800;1600,-200);(1900,1000;1900,2000)"), true);
}
// GitHub issue #72 (Edges/Region NOT issue)
TEST(100)
{

View File

@ -0,0 +1,87 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlUnitTest.h"
#include "dbEdges.h"
#include "dbEdgesUtils.h"
TEST(1)
{
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 10))), true);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (10, 0), db::Point (20, 00)), db::Edge (db::Point (0, 0), db::Point (10, 0))), true);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (11, 0), db::Point (20, 00)), db::Edge (db::Point (0, 0), db::Point (10, 0))), false);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 1), db::Point (10, 10))), true);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (1, 0), db::Point (1, 10))), true);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 1), db::Point (0, 10))), false);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 10), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (10, 20), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (10, 20), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 10))), true);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (-10, -10), db::Point (10, 20))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (5, 5), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (20, 20))), true);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 2), db::Point (20, 22))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (0, 20))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (1, 1), db::Point (20, 20))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (30, 30), db::Point (20, 20))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon ()), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (15, 15)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 10), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 5), db::Point (10, 5)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (-5, 5), db::Point (15, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 10))), false);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (10, 10), db::Point (20, 10))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (-10, -10), db::Point (10, 20))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (5, 5), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (20, 20))), false);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 2), db::Point (20, 22))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (0, 20))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (1, 1), db::Point (20, 20))), false);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (30, 30), db::Point (20, 20))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (1700, 1500), db::Point (1600, 2500)), db::Edge (db::Point (1700, 1000), db::Point (1700, 2000))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon ()), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (15, 15)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (0, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 10), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), true);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 5), db::Point (10, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false);
EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-5, 5), db::Point (15, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false);
}

View File

@ -473,3 +473,27 @@ TEST(4_ReaderCombinedDevices)
}
}
TEST(5_ReaderFuture)
{
db::LayoutToNetlist l2n;
std::string in_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_5.l2n");
tl::InputStream is_in (in_path);
db::LayoutToNetlistStandardReader reader (is_in);
reader.read (&l2n);
// verify against the input
std::string path = tmp_file ("tmp.txt");
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, false);
writer.write (&l2n);
}
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_au_5.l2n");
compare_text_files (path, au_path);
}

View File

@ -443,3 +443,19 @@ TEST(2_FlowWithErrors)
compare_lvsdbs (_this, path2, au_path2);
}
TEST(3_ReaderFuture)
{
db::LayoutVsSchematic lvs;
std::string in_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "lvs_test3.lvsdb");
lvs.load (in_path);
// verify against the input
std::string path = tmp_file ("tmp.txt");
lvs.save (path, false);
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "lvs_test3_au.lvsdb");
compare_text_files (path, au_path);
}

View File

@ -71,6 +71,7 @@ SOURCES = \
dbEdgePairRelationsTests.cc \
dbEdgePairTests.cc \
dbEdgeTests.cc \
dbEdgesUtilsTests.cc \
dbClipTests.cc \
dbCellMappingTests.cc \
dbCellHullGeneratorTests.cc \

View File

@ -1873,7 +1873,9 @@ CODE
# This method returns a two-element array containing one layer for the
# AND result and one for the NOT result.
#
# This method is available for polygon layers.
# This method is available for polygon and edge layers.
# For polygon layers, the other input must be a polygon layer too.
# For edge layers, the other input can be polygon or edge.
#
# It can be used to initialize two variables with the AND and NOT results:
#
@ -1889,8 +1891,12 @@ CODE
@engine._context("andnot") do
check_is_layer(other)
requires_region
other.requires_region
requires_edges_or_region
if self.data.is_a?(RBA::Edges)
other.requires_edges_or_region
elsif self.data.is_a?(RBA::Region)
other.requires_region
end
res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :andnot, other.data)
@ -2140,54 +2146,94 @@ CODE
# %DRC%
# @name inside
# @brief Selects shapes or regions of self which are inside the other region
# @brief Selects edges or polygons of self which are inside edges or polygons from the other layer
# @synopsis layer.inside(other)
# This method selects all shapes or regions from self which are inside the other region.
# completely (completely covered by polygons from the other region). If self is
# in raw mode, this method will select individual shapes. Otherwise, this method
# will select coherent regions and no part of these regions may be outside the
# other region.
# It returns a new layer containing the selected shapes. A version which modifies self
# is \select_inside.
#
# This method is available for polygon layers.
# If layer is a polygon layer, the other layer needs to be a polygon layer too.
# In this case, this method selects all polygons which are completely inside
# polygons from the other layer.
#
# The following image shows the effect of the "inside" method (input1: red, input2: blue):
# If layer is an edge layer, the other layer can be polygon or edge layer. In the
# first case, all edges completely inside the polygons from the other layer are
# selected. If the other layer is an edge layer, all edges completely contained
# in edges from the other layer are selected.
#
# Merged semantics applies - i.e. edges or polygons are joined before the
# result is computed, unless the layers are in \raw mode.
#
# This method returns a new layer containing the selected shapes. A version which modifies self
# is \select_inside. \not_inside is a function computing the inverse of \inside.
# \split_inside is a function computing both results in a single call. \outside
# is a similar function selecting edges or polygons outside other edges or polygons.
#
# The following image shows the effect of the "inside" method for polygons (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_inside.png) @/td
# @/tr
# @/table
#
# The following images show the effect of the "inside" method for edge layers and edge or polygon layers
# the second input. Note that the edges are computed from the polygons in this example
# (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_inside_ee.png) @/td
# @/tr
# @/table
#
# @table
# @tr
# @td @img(/images/drc_inside_ep.png) @/td
# @/tr
# @/table
# %DRC%
# @name not_inside
# @brief Selects shapes or regions of self which are not inside the other region
# @brief Selects edges or polygons of self which are not inside edges or polygons from the other layer
# @synopsis layer.not_inside(other)
# This method selects all shapes or regions from self which are not inside the other region.
# completely (completely covered by polygons from the other region). If self is
# in raw mode, this method will select individual shapes. Otherwise, this method
# will select coherent regions and no part of these regions may be outside the
# other region.
# It returns a new layer containing the selected shapes. A version which modifies self
#
# This method computes the inverse of \inside - i.e. edges or polygons from the layer
# not being inside polygons or edges from the other layer.
#
# This method returns a new layer containing the selected shapes. A version which modifies self
# is \select_not_inside.
# \split_inside is a function computing both results of \inside and \not_inside in a single call. \outside
# is a similar function selecting edges or polygons outside other edges or polygons. Note
# that "outside" is not the same than "not inside".
#
# This method is available for polygon layers.
#
# The following image shows the effect of the "not_inside" method (input1: red, input2: blue):
# The following image shows the effect of the "not_inside" method for polygon layers (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_not_inside.png) @/td
# @/tr
# @/table
#
# The following images show the effect of the "not_inside" method for edge layers and edge or polygon layers
# the second input. Note that the edges are computed from the polygons in this example
# (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_not_inside_ee.png) @/td
# @/tr
# @/table
#
# @table
# @tr
# @td @img(/images/drc_not_inside_ep.png) @/td
# @/tr
# @/table
# %DRC%
# @name split_inside
# @brief Returns the results of \inside and \not_inside at the same time
# @synopsis (a, b) = layer.split_inside(other)
#
# This method returns the polygons inside of polygons from the other layer in
# This method returns the polygons or edges inside of polygons or edges from the other layer in
# one layer and all others in a second layer. This method is equivalent to calling
# \inside and \not_inside, but is faster than doing this in separate steps:
#
@ -2197,82 +2243,110 @@ CODE
# %DRC%
# @name select_inside
# @brief Selects shapes or regions of self which are inside the other region
# @brief Selects edges or polygons of self which are inside edges or polygons from the other layer
# @synopsis layer.select_inside(other)
# This method selects all shapes or regions from self which are inside the other region.
# completely (completely covered by polygons from the other region). If self is
# in raw mode, this method will select individual shapes. Otherwise, this method
# will select coherent regions and no part of these regions may be outside the
# other region.
# It modifies self to contain the selected shapes. A version which does not modify self
# is \inside.
#
# This method is available for polygon layers.
# This method is the in-place version of \inside - i.e. it modifies the layer instead
# of returning a new layer and leaving the original layer untouched.
# %DRC%
# @name select_not_inside
# @brief Selects shapes or regions of self which are not inside the other region
# @brief Selects edges or polygons of self which are not inside edges or polygons from the other layer
# @synopsis layer.select_not_inside(other)
# This method selects all shapes or regions from self which are not inside the other region.
# completely (completely covered by polygons from the other region). If self is
# in raw mode, this method will select individual shapes. Otherwise, this method
# will select coherent regions and no part of these regions may be outside the
# other region.
# It modifies self to contain the selected shapes. A version which does not modify self
# is \not_inside.
#
# This method is available for polygon layers.
# This method is the in-place version of \inside - i.e. it modifies the layer instead
# of returning a new layer and leaving the original layer untouched.
# %DRC%
# @name outside
# @brief Selects shapes or regions of self which are outside the other region
# @brief Selects edges or polygons of self which are outside edges or polygons from the other layer
# @synopsis layer.outside(other)
# This method selects all shapes or regions from self which are completely outside
# the other region (no part of these shapes or regions may be covered by shapes from
# the other region). If self is in raw mode, this method will select individual
# shapes. Otherwise, this method will select coherent regions and no part of these
# regions may overlap with shapes from the other region.
# It returns a new layer containing the selected shapes. A version which modifies self
# is \select_outside.
#
# This method is available for polygon layers.
# If layer is a polygon layer, the other layer needs to be a polygon layer too.
# In this case, this method selects all polygons which are entirely outside
# polygons from the other layer.
#
# The following image shows the effect of the "outside" method (input1: red, input2: blue):
# If layer is an edge layer, the other layer can be polygon or edge layer. In the
# first case, all edges entirely outside the polygons from the other layer are
# selected. If the other layer is an edge layer, all edges entirely outside
# of edges from the other layer are selected.
#
# Merged semantics applies - i.e. edges or polygons are joined before the
# result is computed, unless the layers are in \raw mode.
#
# This method returns a new layer containing the selected shapes. A version which modifies self
# is \select_outside. \not_outside is a function computing the inverse of \outside.
# \split_outside is a function computing both results in a single call. \outside
# is a similar function selecting edges or polygons outside other edges or polygons.
#
# The following image shows the effect of the "outside" method for polygons (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_outside.png) @/td
# @/tr
# @/table
#
# The following images show the effect of the "outside" method for edge layers and edge or polygon layers
# the second input. Note that the edges are computed from the polygons in this example
# (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_outside_ee.png) @/td
# @/tr
# @/table
#
# @table
# @tr
# @td @img(/images/drc_outside_ep.png) @/td
# @/tr
# @/table
# %DRC%
# @name not_outside
# @brief Selects shapes or regions of self which are not outside the other region
# @brief Selects edges or polygons of self which are not outside edges or polygons from the other layer
# @synopsis layer.not_outside(other)
# This method selects all shapes or regions from self which are not completely outside
# the other region (part of these shapes or regions may be covered by shapes from
# the other region). If self is in raw mode, this method will select individual
# shapes. Otherwise, this method will select coherent regions and no part of these
# regions may overlap with shapes from the other region.
# It returns a new layer containing the selected shapes. A version which modifies self
#
# This method computes the inverse of \outside - i.e. edges or polygons from the layer
# not being outside polygons or edges from the other layer.
#
# This method returns a new layer containing the selected shapes. A version which modifies self
# is \select_not_outside.
# \split_outside is a function computing both results of \outside and \not_outside in a single call. \outside
# is a similar function selecting edges or polygons outside other edges or polygons. Note
# that "outside" is not the same than "not outside".
#
# This method is available for polygon layers.
#
# The following image shows the effect of the "not_outside" method (input1: red, input2: blue):
# The following image shows the effect of the "not_outside" method for polygon layers (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_not_outside.png) @/td
# @/tr
# @/table
#
# The following images show the effect of the "not_outside" method for edge layers and edge or polygon layers
# the second input. Note that the edges are computed from the polygons in this example
# (input1: red, input2: blue):
#
# @table
# @tr
# @td @img(/images/drc_not_outside_ee.png) @/td
# @/tr
# @/table
#
# @table
# @tr
# @td @img(/images/drc_not_outside_ep.png) @/td
# @/tr
# @/table
# %DRC%
# @name split_outside
# @brief Returns the results of \outside and \not_outside at the same time
# @synopsis (a, b) = layer.split_outside(other)
#
# This method returns the polygons outside of polygons from the other layer in
# This method returns the polygons or edges outside of polygons or edges from the other layer in
# one layer and all others in a second layer. This method is equivalent to calling
# \outside and \not_outside, but is faster than doing this in separate steps:
#
@ -2282,31 +2356,19 @@ CODE
# %DRC%
# @name select_outside
# @brief Selects shapes or regions of self which are outside the other region
# @brief Selects edges or polygons of self which are outside edges or polygons from the other layer
# @synopsis layer.select_outside(other)
# This method selects all shapes or regions from self which are completely outside
# the other region (no part of these shapes or regions may be covered by shapes from
# the other region). If self is in raw mode, this method will select individual
# shapes. Otherwise, this method will select coherent regions and no part of these
# regions may overlap with shapes from the other region.
# It modifies self to contain the selected shapes. A version which does not modify self
# is \outside.
#
# This method is available for polygon layers.
# This method is the in-place version of \outside - i.e. it modifies the layer instead
# of returning a new layer and leaving the original layer untouched.
# %DRC%
# @name select_not_outside
# @brief Selects shapes or regions of self which are not outside the other region
# @brief Selects edges or polygons of self which are not outside edges or polygons from the other layer
# @synopsis layer.select_not_outside(other)
# This method selects all shapes or regions from self which are not completely outside
# the other region (part of these shapes or regions may be covered by shapes from
# the other region). If self is in raw mode, this method will select individual
# shapes. Otherwise, this method will select coherent regions and no part of these
# regions may overlap with shapes from the other region.
# It modifies self to contain the selected shapes. A version which does not modify self
# is \not_outside.
#
# This method is available for polygon layers.
# This method is the in-place version of \outside - i.e. it modifies the layer instead
# of returning a new layer and leaving the original layer untouched.
# %DRC%
# @name in
@ -2439,7 +2501,7 @@ CODE
# @brief Returns the results of \interacting and \not_interacting at the same time
# @synopsis (a, b) = layer.split_interacting(other [, options ])
#
# This method returns the polygons interacting with objects from the other container in
# This method returns the polygons or edges interacting with objects from the other container in
# one layer and all others in a second layer. This method is equivalent to calling
# \interacting and \not_interacting, but is faster than doing this in separate steps:
#
@ -2513,6 +2575,9 @@ CODE
#
# This method is available for edge layers. The argument must be a polygon layer.
#
# \outside_part is a method computing the opposite part. \inside_outside_part is a
# method computing both inside and outside part in a single call.
#
# @table
# @tr
# @td @img(/images/drc_inside_part.png) @/td
@ -2529,12 +2594,26 @@ CODE
#
# This method is available for edge layers. The argument must be a polygon layer.
#
# \inside_part is a method computing the opposite part. \inside_outside_part is a
# method computing both inside and outside part in a single call.
#
# @table
# @tr
# @td @img(/images/drc_outside_part.png) @/td
# @/tr
# @/table
# %DRC%
# @name inside_outside_part
# @brief Returns the parts of the edges inside and outside the given region
# @synopsis (inside_part, outside_part) = layer.inside_outside_part(region)
# The method is available for edge layers. The argument must be a polygon layer.
#
# This method returns two layers: the first with the edge parts inside the given region
# and the second with the parts outside the given region. It is equivalent
# to calling \inside_part and \outside_part, but more efficient if both parts
# need to be computed.
# %DRC%
# @name pull_interacting
# @brief Selects shapes or edges of other which touch or overlap shapes from the this region
@ -2608,7 +2687,28 @@ CODE
CODE
end
%w(| ^ inside not_inside outside not_outside in not_in).each do |f|
%w(inside not_inside outside not_outside).each do |f|
eval <<"CODE"
def #{f}(other)
@engine._context("#{f}") do
requires_edges_or_region
if self.data.is_a?(RBA::Edges)
other.requires_edges_or_region
else
other.requires_region
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
end
CODE
end
%w(| ^ in not_in).each do |f|
eval <<"CODE"
def #{f}(other)
@ -2723,8 +2823,31 @@ CODE
@engine._context("#{f}") do
check_is_layer(other)
requires_region
other.requires_edges_texts_or_region
requires_edges_or_region
if self.data.is_a?(RBA::Edges)
other.requires_edges_or_region
elsif self.data.is_a?(RBA::Region)
other.requires_edges_texts_or_region
end
res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :#{f}, other.data, *minmax_count(*args))
[ DRCLayer::new(@engine, res[0]), DRCLayer::new(@engine, res[1]) ]
end
end
CODE
end
%w(inside_outside_part).each do |f|
eval <<"CODE"
def #{f}(other, *args)
@engine._context("#{f}") do
check_is_layer(other)
requires_edges
other.requires_region
res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :#{f}, other.data, *minmax_count(*args))
[ DRCLayer::new(@engine, res[0]), DRCLayer::new(@engine, res[1]) ]
@ -2803,8 +2926,12 @@ CODE
@engine._context("#{f}") do
requires_region
requires_same_type(other)
requires_edges_or_region
if self.data.is_a?(RBA::Edges)
other.requires_edges_or_region
else
other.requires_region
end
if @engine.is_tiled?
self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data)
@ -2821,15 +2948,19 @@ CODE
%w(split_inside split_outside).each do |f|
eval <<"CODE"
def #{f}(other)
def #{f}(other, *args)
@engine._context("#{f}") do
check_is_layer(other)
requires_region
other.requires_region
requires_edges_or_region
if self.data.is_a?(RBA::Edges)
other.requires_edges_or_region
elsif self.data.is_a?(RBA::Region)
other.requires_region
end
res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :#{f}, other.data)
res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :#{f}, other.data, *minmax_count(*args))
[ DRCLayer::new(@engine, res[0]), DRCLayer::new(@engine, res[1]) ]
end

View File

@ -351,7 +351,7 @@ module DRC
# %DRC%
# @brief Performs an antenna check
# @name antenna_check
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ])
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ] [, texts ])
#
# The antenna check is used to avoid plasma induced damage. Physically,
# the damage happes if during the manufacturing of a metal layer with
@ -486,8 +486,13 @@ module DRC
# The error shapes produced by the antenna check are copies
# of the metal shapes on the metal layers of each network
# violating the antenna rule.
#
# You can specify a text layer (use "labels" to create one). It will receive
# error labels describing the measured values and computation parameters for debugging
# the layout. This option has been introduced in version 0.27.11.
#
def antenna_check(agate, ametal, ratio, *diodes)
def antenna_check(agate, ametal, ratio, *args)
@engine._context("antenna_check") do
@ -529,18 +534,31 @@ module DRC
raise("Ratio argument is not a number")
end
dl = diodes.collect do |d|
if d.is_a?(Array)
d.size == 2 || raise("Diode specification pair expects two elements")
d[0].requires_region
[ d[0].data, d[1].to_f ]
else
d.requires_region
[ d.data, 0.0 ]
dl = []
texts = nil
n = 3
args.each do |a|
if a.is_a?(Array)
a.size == 2 || raise("Diode specification pair expects two elements for argument #{n + 1}")
if ! a[0].is_a?(DRC::DRCLayer)
raise("Diode specification pair needs a layer for the first argument of argument #{n + 1}")
end
a[0].requires_region
dl << [ a[0].data, a[1].to_f ]
elsif ! a.is_a?(DRC::DRCLayer)
raise("Argument #{n + 1} has to be a layer")
else
a.requires_texts_or_region
if a.data.is_a?(RBA::Region)
dl << [ a.data, 0.0 ]
else
texts = a.data
end
end
n += 1
end
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl))
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl, texts))
end

View File

@ -367,6 +367,7 @@ CODE
# %DRC%
# @name input
# @brief Specifies input from a source
# @synopsis source.input
# @synopsis source.input(layer)
# @synopsis source.input(layer, datatype)
# @synopsis source.input(layer_into)
@ -405,6 +406,8 @@ CODE
# True text layers should be preferred over mixed polygon/text layers if text object processing
# is required.
#
# "input" without any arguments will create a new, empty original layer.
#
# Use the global version of "input" without a source object to address the default source.
def input(*args)
@ -417,6 +420,7 @@ CODE
# %DRC%
# @name labels
# @brief Gets the labels (texts) from an input layer
# @synopsis source.labels
# @synopsis source.labels(layer)
# @synopsis source.labels(layer, datatype)
# @synopsis source.labels(layer_into)
@ -429,6 +433,8 @@ CODE
# to provide text support but a layer type which is provided for carrying text objects
# explicitly.
#
# "labels" without any arguments will create a new, empty original layer.
#
# Use the global version of "labels" without a source object to address the default source.
def labels(*args)
@ -441,6 +447,7 @@ CODE
# %DRC%
# @name polygons
# @brief Gets the polygon shapes (or shapes that can be converted polygons) from an input layer
# @synopsis source.polygons
# @synopsis source.polygons(layer)
# @synopsis source.polygons(layer, datatype)
# @synopsis source.polygons(layer_into)
@ -452,6 +459,8 @@ CODE
#
# This method is identical to \input with respect to the options supported.
#
# "polygons" without any arguments will create a new, empty original layer.
#
# Use the global version of "polygons" without a source object to address the default source.
def polygons(*args)
@ -464,6 +473,7 @@ CODE
# %DRC%
# @name edges
# @brief Gets the edge shapes (or shapes that can be converted edges) from an input layer
# @synopsis source.edges
# @synopsis source.edges(layer)
# @synopsis source.edges(layer, datatype)
# @synopsis source.edges(layer_into)
@ -478,6 +488,8 @@ CODE
#
# Use the global version of "edges" without a source object to address the default source.
#
# "edges" without any arguments will create a new, empty original layer.
#
# This method has been introduced in version 0.27.
def edges(*args)
@ -490,6 +502,7 @@ CODE
# %DRC%
# @name edge_pairs
# @brief Gets the edge pairs from an input layer
# @synopsis source.edge_pairs
# @synopsis source.edge_pairs(layer)
# @synopsis source.edge_pairs(layer, datatype)
# @synopsis source.edge_pairs(layer_into)
@ -504,6 +517,8 @@ CODE
#
# Use the global version of "edge_pairs" without a source object to address the default source.
#
# "edge_pairs" without any arguments will create a new, empty original layer.
#
# This method has been introduced in version 0.27.
def edge_pairs(*args)
@ -517,7 +532,8 @@ CODE
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchy of the layout
# @synopsis make_layer
# This method delivers a new empty original layer.
# This method delivers a new empty original layer. It is provided to keep old code working.
# Use "input" without arguments instead.
def make_layer
layers = []

View File

@ -1117,18 +1117,18 @@ TEST(24_enclosing)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
static void run_test (tl::TestBase *_this, const std::string &number, bool deep)
static void run_test (tl::TestBase *_this, const std::string &number, bool deep, bool oasis = false)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_" + number + ".drc";
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_" + number + ".gds";
input += "/drc/drcSimpleTests_" + number + "." + (oasis ? "oas" : "gds");
std::string au = tl::testdata ();
au += "/drc/drcSimpleTests_au" + number + std::string (deep ? "d" : "") + ".gds";
au += "/drc/drcSimpleTests_au" + number + std::string (deep ? "d" : "") + "." + (oasis ? "oas" : "gds");
std::string output = _this->tmp_file ("tmp.gds");
std::string output = _this->tmp_file (oasis ? "tmp.oas" : "tmp.gds");
{
// Set some variables
@ -1295,7 +1295,7 @@ TEST(49d_epAngle)
TEST(50_issue826)
{
run_test (_this, "50", false);
run_test (_this, "50", false, true /*OASIS*/);
}
TEST(51_epInternalAngle)

View File

@ -26,7 +26,7 @@
#include "dbLibrary.h"
#include "dbLibraryManager.h"
#include "tlExceptions.h"
#include "layLayoutViewBase.h"
#include "layLayoutView.h"
#include "laySelector.h"
#include "layFinder.h"
#include "layLayerProperties.h"
@ -100,7 +100,7 @@ edt::RoundCornerOptionsDialog *
MainService::round_corners_dialog ()
{
if (! mp_round_corners_dialog) {
mp_round_corners_dialog = new edt::RoundCornerOptionsDialog (view ()->widget ());
mp_round_corners_dialog = new edt::RoundCornerOptionsDialog (lay::widget_from_view (view ()));
}
return mp_round_corners_dialog;
}
@ -109,7 +109,7 @@ edt::AlignOptionsDialog *
MainService::align_options_dialog ()
{
if (! mp_align_options_dialog) {
mp_align_options_dialog = new edt::AlignOptionsDialog (view ()->widget ());
mp_align_options_dialog = new edt::AlignOptionsDialog (lay::widget_from_view (view ()));
}
return mp_align_options_dialog;
}
@ -118,7 +118,7 @@ edt::DistributeOptionsDialog *
MainService::distribute_options_dialog ()
{
if (! mp_distribute_options_dialog) {
mp_distribute_options_dialog = new edt::DistributeOptionsDialog (view ()->widget ());
mp_distribute_options_dialog = new edt::DistributeOptionsDialog (lay::widget_from_view (view ()));
}
return mp_distribute_options_dialog;
}
@ -127,7 +127,7 @@ lay::FlattenInstOptionsDialog *
MainService::flatten_inst_options_dialog ()
{
if (! mp_flatten_inst_options_dialog) {
mp_flatten_inst_options_dialog = new lay::FlattenInstOptionsDialog (view ()->widget (), false /*don't allow pruning*/);
mp_flatten_inst_options_dialog = new lay::FlattenInstOptionsDialog (lay::widget_from_view (view ()), false /*don't allow pruning*/);
}
return mp_flatten_inst_options_dialog;
}
@ -136,7 +136,7 @@ edt::MakeCellOptionsDialog *
MainService::make_cell_options_dialog ()
{
if (! mp_make_cell_options_dialog) {
mp_make_cell_options_dialog = new edt::MakeCellOptionsDialog (view ()->widget ());
mp_make_cell_options_dialog = new edt::MakeCellOptionsDialog (lay::widget_from_view (view ()));
}
return mp_make_cell_options_dialog;
}
@ -145,7 +145,7 @@ edt::MakeArrayOptionsDialog *
MainService::make_array_options_dialog ()
{
if (! mp_make_array_options_dialog) {
mp_make_array_options_dialog = new edt::MakeArrayOptionsDialog (view ()->widget ());
mp_make_array_options_dialog = new edt::MakeArrayOptionsDialog (lay::widget_from_view (view ()));
}
return mp_make_array_options_dialog;
}
@ -1228,7 +1228,7 @@ MainService::cm_convert_to_pcell ()
}
bool ok = false;
QString item = QInputDialog::getItem (view ()->widget (),
QString item = QInputDialog::getItem (lay::widget_from_view (view ()),
tr ("Select Target PCell"),
tr ("Select the PCell the shape should be converted into"),
items, 0, false, &ok);
@ -1333,7 +1333,7 @@ MainService::cm_convert_to_pcell ()
if (any_non_converted) {
tl::warn << tl::to_string (tr ("Some of the shapes could not be converted to the desired PCell"));
#if defined(HAVE_QT)
QMessageBox::warning (view ()->widget (), tr ("Warning"), tr ("Some of the shapes could not be converted to the desired PCell"));
QMessageBox::warning (lay::widget_from_view (view ()), tr ("Warning"), tr ("Some of the shapes could not be converted to the desired PCell"));
#endif
}
@ -1565,7 +1565,7 @@ MainService::cm_size ()
#if defined(HAVE_QT)
// TODO: keep the value persistent so we can set it externally in the Qt-less case
bool ok = false;
QString s = QInputDialog::getText (view ()->widget (),
QString s = QInputDialog::getText (lay::widget_from_view (view ()),
tr ("Sizing"),
tr ("Sizing (in micron, positive or negative). Two values (dx, dy) for anisotropic sizing."),
QLineEdit::Normal, QString::fromUtf8 ("0.0"),
@ -2159,7 +2159,8 @@ MainService::cm_tap ()
#endif
#if defined(HAVE_QT)
if (! view ()->canvas ()->widget ()) {
QWidget *view_widget = lay::widget_from_view (view ());
if (! view_widget) {
return;
}
#endif
@ -2202,7 +2203,8 @@ MainService::cm_tap ()
#if defined(HAVE_QT)
// TODO: what to do here in Qt-less case? Store results in configuration so they can be retrieved externally?
std::unique_ptr<QMenu> menu (new QMenu (view ()->widget ()));
std::unique_ptr<QMenu> menu (new QMenu (view_widget));
menu->show ();
int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize);

View File

@ -182,7 +182,7 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std
case RecentConfigurationPage::Layer:
{
int icon_size = view ()->widget ()->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
int icon_size = item->treeWidget ()->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
lay::LayerPropertiesConstIterator l;
try {
l = lp_iter_from_string (view (), values [column]);
@ -348,7 +348,7 @@ RecentConfigurationPage::update_list (const std::list<std::vector<std::string> >
}
static bool
set_or_request_current_layer (lay::LayoutViewBase *view, unsigned int cv_index, const db::LayerProperties &lp)
set_or_request_current_layer (QWidget *parent, lay::LayoutViewBase *view, unsigned int cv_index, const db::LayerProperties &lp)
{
bool ok = view->set_current_layer (cv_index, lp);
if (ok) {
@ -364,7 +364,7 @@ set_or_request_current_layer (lay::LayoutViewBase *view, unsigned int cv_index,
return false;
}
if (QMessageBox::question (view->widget (), tr ("Create Layer"), tr ("Layer %1 does not exist yet. Create it now?").arg (tl::to_qstring (lp.to_string ()))) == QMessageBox::Yes) {
if (QMessageBox::question (parent, tr ("Create Layer"), tr ("Layer %1 does not exist yet. Create it now?").arg (tl::to_qstring (lp.to_string ()))) == QMessageBox::Yes) {
lay::LayerPropertiesNode lpn;
lpn.set_source (lay::ParsedLayerSource (lp, cv_index));
@ -400,7 +400,7 @@ RecentConfigurationPage::item_clicked (QTreeWidgetItem *item)
ex.read (cv_index);
}
set_or_request_current_layer (view (), cv_index, lp);
set_or_request_current_layer (item->treeWidget (), view (), cv_index, lp);
} else {
dispatcher ()->config_set (c->cfg_name, v);

View File

@ -31,7 +31,7 @@
# include "edtDialogs.h"
#endif
#include "layFinder.h"
#include "layLayoutViewBase.h"
#include "layLayoutView.h"
#include "laySnap.h"
#include "tlProgress.h"
#include "tlTimer.h"
@ -315,7 +315,7 @@ void
Service::copy_selected ()
{
#if defined(HAVE_QT)
edt::CopyModeDialog mode_dialog (view ()->widget ());
edt::CopyModeDialog mode_dialog (lay::widget_from_view (view ()));
bool need_to_ask_for_copy_mode = false;
unsigned int inst_mode = 0;

View File

@ -54,7 +54,7 @@ static int t_object () { return T_object; }
static int t_vector () { return T_vector; }
static int t_map () { return T_map; }
static int type (const ArgType *t)
static int type (const ArgType *t)
{
return t->type ();
}
@ -105,9 +105,13 @@ Class<ArgType> decl_ArgType ("tl", "ArgType",
"@brief Return the basic type (see t_.. constants)\n"
) +
gsi::method ("inner", &ArgType::inner,
"@brief Returns the inner ArgType object (i.e. value of a vector)\n"
"@brief Returns the inner ArgType object (i.e. value of a vector/map)\n"
"Starting with version 0.22, this method replaces the is_vector method.\n"
) +
gsi::method ("inner_k", &ArgType::inner_k,
"@brief Returns the inner ArgType object (i.e. key of a map)\n"
"This method has been introduced in version 0.27."
) +
gsi::method ("pass_obj?", &ArgType::pass_obj,
"@brief True, if the ownership over an object represented by this type is passed to the receiver\n"
"In case of the return type, a value of true indicates, that the object is a freshly created one and "
@ -283,6 +287,12 @@ Class<ClassBase> decl_Class ("tl", "Class",
gsi::iterator ("each_method", &ClassBase::begin_methods, &ClassBase::end_methods,
"@brief Iterate over all methods of this class\n"
) +
gsi::iterator ("each_child_class", &ClassBase::begin_child_classes, &ClassBase::end_child_classes,
"@brief Iterate over all child classes defined within this class\n"
) +
gsi::method ("parent", &ClassBase::parent,
"@brief The parent of the class\n"
) +
gsi::method ("name", &ClassBase::name,
"@brief The name of the class\n"
) +
@ -309,5 +319,3 @@ Class<ClassBase> decl_Class ("tl", "Class",
);
}

View File

@ -85,7 +85,7 @@ void MethodBase::parse_name (const std::string &name)
{
const char *n = name.c_str ();
if (*n == '*' && n[1]) {
if (*n == '*' && n[1] && n[1] != '*' && n[1] != '=') {
m_protected = true;
++n;
}

View File

@ -51,27 +51,26 @@ Navigator::Navigator (QWidget *parent)
img::Object *
Navigator::setup (lay::Dispatcher *root, img::Object *img)
{
mp_view = new lay::LayoutView (0, false, root, this, "img_navigator_view", lay::LayoutView::LV_Naked + lay::LayoutView::LV_NoZoom + lay::LayoutView::LV_NoServices + lay::LayoutView::LV_NoGrid);
tl_assert (mp_view->widget ());
mp_view->widget ()->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
mp_view->widget ()->setMinimumWidth (100);
mp_view->widget ()->setMinimumHeight (100);
mp_view = new lay::LayoutViewWidget (0, false, root, this, lay::LayoutView::LV_Naked + lay::LayoutView::LV_NoZoom + lay::LayoutView::LV_NoServices + lay::LayoutView::LV_NoGrid);
mp_view->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
mp_view->setMinimumWidth (100);
mp_view->setMinimumHeight (100);
QVBoxLayout *layout = new QVBoxLayout (this);
layout->addWidget (mp_view->widget ());
layout->setStretchFactor (mp_view->widget (), 1);
layout->addWidget (mp_view);
layout->setStretchFactor (mp_view, 1);
layout->setContentsMargins (0, 0, 0, 0);
layout->setSpacing (0);
setLayout (layout);
mp_zoom_service = new lay::ZoomService (mp_view);
mp_zoom_service = new lay::ZoomService (view ());
img::Service *img_target = mp_view->get_plugin<img::Service> ();
img::Service *img_target = view ()->get_plugin<img::Service> ();
if (img_target) {
img_target->clear_images ();
img::Object *img_object = img_target->insert_image (*img);
img_object->set_matrix (db::Matrix3d (1.0));
mp_view->zoom_fit ();
view ()->zoom_fit ();
return img_object;
} else {
return 0;
@ -91,10 +90,15 @@ Navigator::~Navigator ()
}
}
lay::LayoutView *Navigator::view ()
{
return mp_view->view ();
}
void
Navigator::activate_service (lay::ViewService *service)
{
mp_view->canvas ()->activate (service);
view ()->canvas ()->activate (service);
}
void

View File

@ -30,6 +30,7 @@
namespace lay
{
class Dispatcher;
class LayoutViewWidget;
class LayoutView;
class ZoomService;
class ViewService;
@ -55,15 +56,12 @@ public:
void background_color (QColor c);
img::Object *setup (lay::Dispatcher *root, img::Object *img);
lay::LayoutView *view ()
{
return mp_view;
}
lay::LayoutView *view ();
void activate_service (lay::ViewService *service);
private:
lay::LayoutView *mp_view;
lay::LayoutViewWidget *mp_view;
lay::ZoomService *mp_zoom_service;
};

View File

@ -0,0 +1,278 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReaderErrorForm</class>
<widget class="QDialog" name="ReaderErrorForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>496</width>
<height>297</height>
</rect>
</property>
<property name="windowTitle">
<string>Script Error</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="2" column="3">
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>411</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>411</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3">
<widget class="QLabel" name="msg_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="2" rowspan="3">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>71</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" rowspan="3">
<widget class="QLabel" name="icon_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string>X</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>71</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="details_frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextBrowser" name="details_text">
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Liberation Mono';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="details_pb">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string> Details &gt;&gt; </string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>341</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ok_button">
<property name="text">
<string>OK</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections>
<connection>
<sender>ok_button</sender>
<signal>clicked()</signal>
<receiver>ReaderErrorForm</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>457</x>
<y>330</y>
</hint>
<hint type="destinationlabel">
<x>271</x>
<y>177</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -103,7 +103,9 @@ polygons will be written to the output (labels: red, input2: blue):
This method returns a two-element array containing one layer for the
AND result and one for the NOT result.
</p><p>
This method is available for polygon layers.
This method is available for polygon and edge layers.
For polygon layers, the other input must be a polygon layer too.
For edge layers, the other input can be polygon or edge.
</p><p>
It can be used to initialize two variables with the AND and NOT results:
</p><p>
@ -1343,30 +1345,67 @@ pl = polygon_layer
pl.insert(box(0.0, 0.0, 100.0, 200.0)
</pre>
</p>
<a name="inside"/><h2>"inside" - Selects shapes or regions of self which are inside the other region</h2>
<a name="inside"/><h2>"inside" - Selects edges or polygons of self which are inside edges or polygons from the other layer</h2>
<keyword name="inside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.inside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are inside the other region.
completely (completely covered by polygons from the other region). If self is
in raw mode, this method will select individual shapes. Otherwise, this method
will select coherent regions and no part of these regions may be outside the
other region.
It returns a new layer containing the selected shapes. A version which modifies self
is <a href="#select_inside">select_inside</a>.
If layer is a polygon layer, the other layer needs to be a polygon layer too.
In this case, this method selects all polygons which are completely inside
polygons from the other layer.
</p><p>
This method is available for polygon layers.
If layer is an edge layer, the other layer can be polygon or edge layer. In the
first case, all edges completely inside the polygons from the other layer are
selected. If the other layer is an edge layer, all edges completely contained
in edges from the other layer are selected.
</p><p>
The following image shows the effect of the "inside" method (input1: red, input2: blue):
Merged semantics applies - i.e. edges or polygons are joined before the
result is computed, unless the layers are in <a href="#raw">raw</a> mode.
</p><p>
This method returns a new layer containing the selected shapes. A version which modifies self
is <a href="#select_inside">select_inside</a>. <a href="#not_inside">not_inside</a> is a function computing the inverse of <a href="#inside">inside</a>.
<a href="#split_inside">split_inside</a> is a function computing both results in a single call. <a href="#outside">outside</a>
is a similar function selecting edges or polygons outside other edges or polygons.
</p><p>
The following image shows the effect of the "inside" method for polygons (input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_inside.png"/></td>
</tr>
</table>
</p><p>
The following images show the effect of the "inside" method for edge layers and edge or polygon layers
the second input. Note that the edges are computed from the polygons in this example
(input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_inside_ee.png"/></td>
</tr>
</table>
</p><p>
<table>
<tr>
<td><img src="/images/drc_inside_ep.png"/></td>
</tr>
</table>
</p>
<a name="inside_outside_part"/><h2>"inside_outside_part" - Returns the parts of the edges inside and outside the given region</h2>
<keyword name="inside_outside_part"/>
<p>Usage:</p>
<ul>
<li><tt>(inside_part, outside_part) = layer.inside_outside_part(region)</tt></li>
</ul>
<p>
The method is available for edge layers. The argument must be a polygon layer.
</p><p>
This method returns two layers: the first with the edge parts inside the given region
and the second with the parts outside the given region. It is equivalent
to calling <a href="#inside_part">inside_part</a> and <a href="#outside_part">outside_part</a>, but more efficient if both parts
need to be computed.
</p>
<a name="inside_part"/><h2>"inside_part" - Returns the parts of the edges inside the given region</h2>
<keyword name="inside_part"/>
@ -1381,6 +1420,9 @@ of the polygons of the region.
</p><p>
This method is available for edge layers. The argument must be a polygon layer.
</p><p>
<a href="#outside_part">outside_part</a> is a method computing the opposite part. <a href="#inside_outside_part">inside_outside_part</a> is a
method computing both inside and outside part in a single call.
</p><p>
<table>
<tr>
<td><img src="/images/drc_inside_part.png"/></td>
@ -1798,30 +1840,45 @@ The following image shows the effect of the "not_in" method (input1: red, input2
</tr>
</table>
</p>
<a name="not_inside"/><h2>"not_inside" - Selects shapes or regions of self which are not inside the other region</h2>
<a name="not_inside"/><h2>"not_inside" - Selects edges or polygons of self which are not inside edges or polygons from the other layer</h2>
<keyword name="not_inside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.not_inside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are not inside the other region.
completely (completely covered by polygons from the other region). If self is
in raw mode, this method will select individual shapes. Otherwise, this method
will select coherent regions and no part of these regions may be outside the
other region.
It returns a new layer containing the selected shapes. A version which modifies self
This method computes the inverse of <a href="#inside">inside</a> - i.e. edges or polygons from the layer
not being inside polygons or edges from the other layer.
</p><p>
This method returns a new layer containing the selected shapes. A version which modifies self
is <a href="#select_not_inside">select_not_inside</a>.
<a href="#split_inside">split_inside</a> is a function computing both results of <a href="#inside">inside</a> and <a href="#not_inside">not_inside</a> in a single call. <a href="#outside">outside</a>
is a similar function selecting edges or polygons outside other edges or polygons. Note
that "outside" is not the same than "not inside".
</p><p>
This method is available for polygon layers.
</p><p>
The following image shows the effect of the "not_inside" method (input1: red, input2: blue):
The following image shows the effect of the "not_inside" method for polygon layers (input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_not_inside.png"/></td>
</tr>
</table>
</p><p>
The following images show the effect of the "not_inside" method for edge layers and edge or polygon layers
the second input. Note that the edges are computed from the polygons in this example
(input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_not_inside_ee.png"/></td>
</tr>
</table>
</p><p>
<table>
<tr>
<td><img src="/images/drc_not_inside_ep.png"/></td>
</tr>
</table>
</p>
<a name="not_interacting"/><h2>"not_interacting" - Selects shapes or regions of self which do not touch or overlap shapes from the other region</h2>
<keyword name="not_interacting"/>
@ -1867,30 +1924,45 @@ from the other layer. Two polygons overlapping or touching at two locations are
</tr>
</table>
</p>
<a name="not_outside"/><h2>"not_outside" - Selects shapes or regions of self which are not outside the other region</h2>
<a name="not_outside"/><h2>"not_outside" - Selects edges or polygons of self which are not outside edges or polygons from the other layer</h2>
<keyword name="not_outside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.not_outside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are not completely outside
the other region (part of these shapes or regions may be covered by shapes from
the other region). If self is in raw mode, this method will select individual
shapes. Otherwise, this method will select coherent regions and no part of these
regions may overlap with shapes from the other region.
It returns a new layer containing the selected shapes. A version which modifies self
This method computes the inverse of <a href="#outside">outside</a> - i.e. edges or polygons from the layer
not being outside polygons or edges from the other layer.
</p><p>
This method returns a new layer containing the selected shapes. A version which modifies self
is <a href="#select_not_outside">select_not_outside</a>.
<a href="#split_outside">split_outside</a> is a function computing both results of <a href="#outside">outside</a> and <a href="#not_outside">not_outside</a> in a single call. <a href="#outside">outside</a>
is a similar function selecting edges or polygons outside other edges or polygons. Note
that "outside" is not the same than "not outside".
</p><p>
This method is available for polygon layers.
</p><p>
The following image shows the effect of the "not_outside" method (input1: red, input2: blue):
The following image shows the effect of the "not_outside" method for polygon layers (input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_not_outside.png"/></td>
</tr>
</table>
</p><p>
The following images show the effect of the "not_outside" method for edge layers and edge or polygon layers
the second input. Note that the edges are computed from the polygons in this example
(input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_not_outside_ee.png"/></td>
</tr>
</table>
</p><p>
<table>
<tr>
<td><img src="/images/drc_not_outside_ep.png"/></td>
</tr>
</table>
</p>
<a name="not_overlapping"/><h2>"not_overlapping" - Selects shapes or regions of self which do not overlap shapes from the other region</h2>
<keyword name="not_overlapping"/>
@ -2009,30 +2081,53 @@ a single <class_doc href="LayerInfo">LayerInfo</class_doc> object.
See <a href="/about/drc_ref_global.xml#report">report</a> and <a href="/about/drc_ref_global.xml#target">target</a> on how to configure output to a target layout
or report database.
</p>
<a name="outside"/><h2>"outside" - Selects shapes or regions of self which are outside the other region</h2>
<a name="outside"/><h2>"outside" - Selects edges or polygons of self which are outside edges or polygons from the other layer</h2>
<keyword name="outside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.outside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are completely outside
the other region (no part of these shapes or regions may be covered by shapes from
the other region). If self is in raw mode, this method will select individual
shapes. Otherwise, this method will select coherent regions and no part of these
regions may overlap with shapes from the other region.
It returns a new layer containing the selected shapes. A version which modifies self
is <a href="#select_outside">select_outside</a>.
If layer is a polygon layer, the other layer needs to be a polygon layer too.
In this case, this method selects all polygons which are entirely outside
polygons from the other layer.
</p><p>
This method is available for polygon layers.
If layer is an edge layer, the other layer can be polygon or edge layer. In the
first case, all edges entirely outside the polygons from the other layer are
selected. If the other layer is an edge layer, all edges entirely outside
of edges from the other layer are selected.
</p><p>
The following image shows the effect of the "outside" method (input1: red, input2: blue):
Merged semantics applies - i.e. edges or polygons are joined before the
result is computed, unless the layers are in <a href="#raw">raw</a> mode.
</p><p>
This method returns a new layer containing the selected shapes. A version which modifies self
is <a href="#select_outside">select_outside</a>. <a href="#not_outside">not_outside</a> is a function computing the inverse of <a href="#outside">outside</a>.
<a href="#split_outside">split_outside</a> is a function computing both results in a single call. <a href="#outside">outside</a>
is a similar function selecting edges or polygons outside other edges or polygons.
</p><p>
The following image shows the effect of the "outside" method for polygons (input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_outside.png"/></td>
</tr>
</table>
</p><p>
The following images show the effect of the "outside" method for edge layers and edge or polygon layers
the second input. Note that the edges are computed from the polygons in this example
(input1: red, input2: blue):
</p><p>
<table>
<tr>
<td><img src="/images/drc_outside_ee.png"/></td>
</tr>
</table>
</p><p>
<table>
<tr>
<td><img src="/images/drc_outside_ep.png"/></td>
</tr>
</table>
</p>
<a name="outside_part"/><h2>"outside_part" - Returns the parts of the edges outside the given region</h2>
<keyword name="outside_part"/>
@ -2047,6 +2142,9 @@ of the polygons of the region.
</p><p>
This method is available for edge layers. The argument must be a polygon layer.
</p><p>
<a href="#inside_part">inside_part</a> is a method computing the opposite part. <a href="#inside_outside_part">inside_outside_part</a> is a
method computing both inside and outside part in a single call.
</p><p>
<table>
<tr>
<td><img src="/images/drc_outside_part.png"/></td>
@ -2401,22 +2499,15 @@ is <a href="#covering">covering</a>.
</p><p>
This method is available for polygons only.
</p>
<a name="select_inside"/><h2>"select_inside" - Selects shapes or regions of self which are inside the other region</h2>
<a name="select_inside"/><h2>"select_inside" - Selects edges or polygons of self which are inside edges or polygons from the other layer</h2>
<keyword name="select_inside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.select_inside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are inside the other region.
completely (completely covered by polygons from the other region). If self is
in raw mode, this method will select individual shapes. Otherwise, this method
will select coherent regions and no part of these regions may be outside the
other region.
It modifies self to contain the selected shapes. A version which does not modify self
is <a href="#inside">inside</a>.
</p><p>
This method is available for polygon layers.
This method is the in-place version of <a href="#inside">inside</a> - i.e. it modifies the layer instead
of returning a new layer and leaving the original layer untouched.
</p>
<a name="select_interacting"/><h2>"select_interacting" - Selects shapes or regions of self which touch or overlap shapes from the other region</h2>
<keyword name="select_interacting"/>
@ -2461,22 +2552,15 @@ is <a href="#not_covering">not_covering</a>.
</p><p>
This method is available for polygons only.
</p>
<a name="select_not_inside"/><h2>"select_not_inside" - Selects shapes or regions of self which are not inside the other region</h2>
<a name="select_not_inside"/><h2>"select_not_inside" - Selects edges or polygons of self which are not inside edges or polygons from the other layer</h2>
<keyword name="select_not_inside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.select_not_inside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are not inside the other region.
completely (completely covered by polygons from the other region). If self is
in raw mode, this method will select individual shapes. Otherwise, this method
will select coherent regions and no part of these regions may be outside the
other region.
It modifies self to contain the selected shapes. A version which does not modify self
is <a href="#not_inside">not_inside</a>.
</p><p>
This method is available for polygon layers.
This method is the in-place version of <a href="#inside">inside</a> - i.e. it modifies the layer instead
of returning a new layer and leaving the original layer untouched.
</p>
<a name="select_not_interacting"/><h2>"select_not_interacting" - Selects shapes or regions of self which do not touch or overlap shapes from the other region</h2>
<keyword name="select_not_interacting"/>
@ -2503,22 +2587,15 @@ number of (different) shapes from the other layer. If a min and max count is giv
self are selected only if they interact with less than min_count or more than max_count different shapes
from the other layer. Two polygons overlapping or touching at two locations are counted as single interactions.
</p>
<a name="select_not_outside"/><h2>"select_not_outside" - Selects shapes or regions of self which are not outside the other region</h2>
<a name="select_not_outside"/><h2>"select_not_outside" - Selects edges or polygons of self which are not outside edges or polygons from the other layer</h2>
<keyword name="select_not_outside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.select_not_outside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are not completely outside
the other region (part of these shapes or regions may be covered by shapes from
the other region). If self is in raw mode, this method will select individual
shapes. Otherwise, this method will select coherent regions and no part of these
regions may overlap with shapes from the other region.
It modifies self to contain the selected shapes. A version which does not modify self
is <a href="#not_outside">not_outside</a>.
</p><p>
This method is available for polygon layers.
This method is the in-place version of <a href="#outside">outside</a> - i.e. it modifies the layer instead
of returning a new layer and leaving the original layer untouched.
</p>
<a name="select_not_overlapping"/><h2>"select_not_overlapping" - Selects shapes or regions of self which do not overlap shapes from the other region</h2>
<keyword name="select_not_overlapping"/>
@ -2538,22 +2615,15 @@ is <a href="#not_overlapping">not_overlapping</a>.
</p><p>
This method is available for polygons only.
</p>
<a name="select_outside"/><h2>"select_outside" - Selects shapes or regions of self which are outside the other region</h2>
<a name="select_outside"/><h2>"select_outside" - Selects edges or polygons of self which are outside edges or polygons from the other layer</h2>
<keyword name="select_outside"/>
<p>Usage:</p>
<ul>
<li><tt>layer.select_outside(other)</tt></li>
</ul>
<p>
This method selects all shapes or regions from self which are completely outside
the other region (no part of these shapes or regions may be covered by shapes from
the other region). If self is in raw mode, this method will select individual
shapes. Otherwise, this method will select coherent regions and no part of these
regions may overlap with shapes from the other region.
It modifies self to contain the selected shapes. A version which does not modify self
is <a href="#outside">outside</a>.
</p><p>
This method is available for polygon layers.
This method is the in-place version of <a href="#outside">outside</a> - i.e. it modifies the layer instead
of returning a new layer and leaving the original layer untouched.
</p>
<a name="select_overlapping"/><h2>"select_overlapping" - Selects shapes or regions of self which overlap shapes from the other region</h2>
<keyword name="select_overlapping"/>
@ -2866,7 +2936,7 @@ The options of this method are the same than <a href="#covering">covering</a>.
<li><tt>(a, b) = layer.split_inside(other)</tt></li>
</ul>
<p>
This method returns the polygons inside of polygons from the other layer in
This method returns the polygons or edges inside of polygons or edges from the other layer in
one layer and all others in a second layer. This method is equivalent to calling
<a href="#inside">inside</a> and <a href="#not_inside">not_inside</a>, but is faster than doing this in separate steps:
</p><p>
@ -2881,7 +2951,7 @@ one layer and all others in a second layer. This method is equivalent to calling
<li><tt>(a, b) = layer.split_interacting(other [, options ])</tt></li>
</ul>
<p>
This method returns the polygons interacting with objects from the other container in
This method returns the polygons or edges interacting with objects from the other container in
one layer and all others in a second layer. This method is equivalent to calling
<a href="#interacting">interacting</a> and <a href="#not_interacting">not_interacting</a>, but is faster than doing this in separate steps:
</p><p>
@ -2898,7 +2968,7 @@ The options of this method are the same than <a href="#interacting">interacting<
<li><tt>(a, b) = layer.split_outside(other)</tt></li>
</ul>
<p>
This method returns the polygons outside of polygons from the other layer in
This method returns the polygons or edges outside of polygons or edges from the other layer in
one layer and all others in a second layer. This method is equivalent to calling
<a href="#outside">outside</a> and <a href="#not_outside">not_outside</a>, but is faster than doing this in separate steps:
</p><p>
@ -3037,6 +3107,12 @@ The effect of the operation is shown in these examples:
</tr>
</table>
</p>
<a name="texts?"/><h2>"texts?" - Returns true, if the layer is a text collection</h2>
<keyword name="texts?"/>
<p>Usage:</p>
<ul>
<li><tt>layer.texts?</tt></li>
</ul>
<a name="texts_not"/><h2>"texts_not" - Selects texts from an original layer not matching a specific selection</h2>
<keyword name="texts_not"/>
<p>Usage:</p>

View File

@ -65,7 +65,7 @@ More methods will be added in the future to support network-related features.
<keyword name="antenna_check"/>
<p>Usage:</p>
<ul>
<li><tt>antenna_check(gate, metal, ratio, [ diode_specs ... ])</tt></li>
<li><tt>antenna_check(gate, metal, ratio, [ diode_specs ... ] [, texts ])</tt></li>
</ul>
<p>
The antenna check is used to avoid plasma induced damage. Physically,
@ -201,6 +201,10 @@ errors = antenna_check(perimeter_only(gate, 0.5), ...)
The error shapes produced by the antenna check are copies
of the metal shapes on the metal layers of each network
violating the antenna rule.
</p><p>
You can specify a text layer (use "labels" to create one). It will receive
error labels describing the measured values and computation parameters for debugging
the layout. This option has been introduced in version 0.27.11.
</p>
<a name="clear_connections"/><h2>"clear_connections" - Clears all connections stored so far</h2>
<keyword name="clear_connections"/>

View File

@ -58,6 +58,7 @@ same but without clipping is <a href="#touching">touching</a> or <a href="#overl
<keyword name="edge_pairs"/>
<p>Usage:</p>
<ul>
<li><tt>source.edge_pairs</tt></li>
<li><tt>source.edge_pairs(layer)</tt></li>
<li><tt>source.edge_pairs(layer, datatype)</tt></li>
<li><tt>source.edge_pairs(layer_into)</tt></li>
@ -73,12 +74,15 @@ This method is identical to <a href="#input">input</a> with respect to the optio
</p><p>
Use the global version of "edge_pairs" without a source object to address the default source.
</p><p>
"edge_pairs" without any arguments will create a new, empty original layer.
</p><p>
This method has been introduced in version 0.27.
</p>
<a name="edges"/><h2>"edges" - Gets the edge shapes (or shapes that can be converted edges) from an input layer</h2>
<keyword name="edges"/>
<p>Usage:</p>
<ul>
<li><tt>source.edges</tt></li>
<li><tt>source.edges(layer)</tt></li>
<li><tt>source.edges(layer, datatype)</tt></li>
<li><tt>source.edges(layer_into)</tt></li>
@ -94,6 +98,8 @@ This method is identical to <a href="#input">input</a> with respect to the optio
</p><p>
Use the global version of "edges" without a source object to address the default source.
</p><p>
"edges" without any arguments will create a new, empty original layer.
</p><p>
This method has been introduced in version 0.27.
</p>
<a name="extent"/><h2>"extent" - Returns a layer with the bounding box of the selected layout or cells</h2>
@ -160,6 +166,7 @@ source.global_transform(shift(0, 100.um), rotate(90.0))
<keyword name="input"/>
<p>Usage:</p>
<ul>
<li><tt>source.input</tt></li>
<li><tt>source.input(layer)</tt></li>
<li><tt>source.input(layer, datatype)</tt></li>
<li><tt>source.input(layer_into)</tt></li>
@ -200,12 +207,15 @@ operations is available for these objects, such as boolean "and" and "not" with
True text layers should be preferred over mixed polygon/text layers if text object processing
is required.
</p><p>
"input" without any arguments will create a new, empty original layer.
</p><p>
Use the global version of "input" without a source object to address the default source.
</p>
<a name="labels"/><h2>"labels" - Gets the labels (texts) from an input layer</h2>
<keyword name="labels"/>
<p>Usage:</p>
<ul>
<li><tt>source.labels</tt></li>
<li><tt>source.labels(layer)</tt></li>
<li><tt>source.labels(layer, datatype)</tt></li>
<li><tt>source.labels(layer_into)</tt></li>
@ -219,6 +229,8 @@ layer. Starting with version 0.27, the result is no longer a polygon layer that
to provide text support but a layer type which is provided for carrying text objects
explicitly.
</p><p>
"labels" without any arguments will create a new, empty original layer.
</p><p>
Use the global version of "labels" without a source object to address the default source.
</p>
<a name="layers"/><h2>"layers" - Gets the layers the source contains</h2>
@ -258,7 +270,8 @@ layers.each { |l| (input(l) &amp; clip_box).output(l) }
<li><tt>make_layer</tt></li>
</ul>
<p>
This method delivers a new empty original layer.
This method delivers a new empty original layer. It is provided to keep old code working.
Use "input" without arguments instead.
</p>
<a name="overlapping"/><h2>"overlapping" - Specifies input selected from a region in overlapping mode</h2>
<keyword name="overlapping"/>
@ -286,6 +299,7 @@ the search region with their bounding box (without the requirement to overlap)
<keyword name="polygons"/>
<p>Usage:</p>
<ul>
<li><tt>source.polygons</tt></li>
<li><tt>source.polygons(layer)</tt></li>
<li><tt>source.polygons(layer, datatype)</tt></li>
<li><tt>source.polygons(layer_into)</tt></li>
@ -298,6 +312,8 @@ Those are boxes, paths and real polygons.
</p><p>
This method is identical to <a href="#input">input</a> with respect to the options supported.
</p><p>
"polygons" without any arguments will create a new, empty original layer.
</p><p>
Use the global version of "polygons" without a source object to address the default source.
</p>
<a name="select"/><h2>"select" - Adds cell name expressions to the cell filters</h2>

View File

@ -37,7 +37,7 @@ See <a href="/about/lvs_ref_netter.xml#align">Netter#align</a> for a description
<p>
See <a href="/about/lvs_ref_netter.xml#blank_circuit">Netter#blank_circuit</a> for a description of that function.
</p>
<a name="compare"/><h2>"compare" - Compares the extracted netlist vs. the schematic</h2>
<a name="compare"/><h2>"compare" - Compares the extracted netlist vs. the schematic netlist</h2>
<keyword name="compare"/>
<p>Usage:</p>
<ul>
@ -154,6 +154,15 @@ See <a href="/about/lvs_ref_netter.xml#min_caps">Netter#min_caps</a> for a descr
<p>
See <a href="/about/lvs_ref_netter.xml">Netter</a> for more details
</p>
<a name="no_lvs_hints"/><h2>"no_lvs_hints" - Disables LVS hints</h2>
<keyword name="no_lvs_hints"/>
<p>Usage:</p>
<ul>
<li><tt>no_lvs_hints</tt></li>
</ul>
<p>
See <a href="/about/lvs_ref_netter.xml#no_lvs_hints">Netter#no_lvs_hints</a> for a description of that feature.
</p>
<a name="report_lvs"/><h2>"report_lvs" - Specifies an LVS report for output</h2>
<keyword name="report_lvs"/>
<p>Usage:</p>

View File

@ -302,6 +302,16 @@ with a resistance value above the given threshold (in Farad).
After using this method, the netlist compare will ignore capacitance devices
with a capacitance values below the given threshold (in Farad).
</p>
<a name="no_lvs_hints"/><h2>"no_lvs_hints" - Disables LVS hints</h2>
<keyword name="no_lvs_hints"/>
<p>Usage:</p>
<ul>
<li><tt>no_lvs_hints</tt></li>
</ul>
<p>
LVS hints may be expensive to compute. Use this function to disable
generation of LVS hints
</p>
<a name="same_circuits"/><h2>"same_circuits" - Establishes an equivalence between the circuits</h2>
<keyword name="same_circuits"/>
<p>Usage:</p>

View File

@ -4,6 +4,7 @@
<doc>
<title>Transformations in KLayout</title>
<keyword name="transformation"/>
<p>
KLayout supports a subset of affine transformations with the following contributions:
@ -37,7 +38,9 @@
given displacement vector.
</p>
<p>
<img src="/about/transformation_overview.png"/>
</p>
<p>
The notation shown here is used in many places within KLayout. It is basically composed of the following parts
@ -75,7 +78,9 @@
multiples of 90 degree:
</p>
<p>
<img src="/about/transformation_basic.png"/>
</p>
<p>
KLayout is not restricted to these basic operations. Arbitrary angles are supported (i.e. "r45" or "m22.5"). Usually however,
@ -83,5 +88,160 @@
simple transformations involving only rotations by multiples of 90 degree and do not use scaling.
</p>
<h2>Coding transformations</h2>
<keyword name="transformation objects"/>
<p>
Note that mirroring at an axis with a given angle "a" is equivalent to mirroring at the x axis followed by a rotation
by twice the angle "a". For example:
</p>
<pre>m45 == m0 followed by r90</pre>
<p>
When coding transformations, two parameters are used to represent the rotation/mirror part:
a rotation angle and a flag indicating mirroring at the x axis. The mirroring is applied before
the rotation. In terms of these parameters, the basic transformations are:
</p>
<table>
<tr><th>Rotation angle<br/>(degree)</th><th>Mirror flag <br/>= False</th><th>Mirror flag <br/>= True</th></tr>
<tr><td>0</td><td>r0</td><td>m0</td></tr>
<tr><td>90</td><td>r90</td><td>m45</td></tr>
<tr><td>180</td><td>r180</td><td>m90</td></tr>
<tr><td>270</td><td>r270</td><td>m135</td></tr>
</table>
<h3>Transformation objects</h3>
<p>
Transformation objects are convenient objects to operate with. They represent a
transformation (technically a matrix) consisting of an angle/mirror and a displacement
part. They support some basic operations:
</p>
<ul>
<li>Concatenation: <tt>T = T1 * T2</tt><br/><tt>T</tt> is transformation <tt>T2</tt> applied, then <tt>T1</tt> (note this order)</li>
<li>Inversion: <tt>TI = T.inverted()</tt><br/><tt>TI</tt> is the inverse of <tt>T</tt>, i.e. <tt>TI * T = T * TI = 1</tt> where <tt>1</tt> is the neutral transformation which does not modify coordinates</li>
<li>Application to geometrical objects: <tt>q = T * p</tt><br/>where <tt>p</tt> is a box, polygon, path, text, point, vector etc. and <tt>q</tt> is the transformed object</li>
</ul>
<h3>Vectors and Points</h3>
<keyword name="vector"/>
<keyword name="point"/>
<p>
In KLayout there are two two-dimensional coordinate objects: the vector and the point.
Basically, the vector is the difference between two points:
</p>
<pre>v = p2 - p1</pre>
<p>Here <tt>v</tt> is a vector object while <tt>p1</tt> and <tt>p2</tt> are points.</p>
<p>
Regarding transformations, vectors and points behave differently. While for a point, the
displacement is applied, it is not for vectors. So
</p>
<pre>p' = T * p = M * p + d
v' = T * v = M * v</pre>
<p>
Here <tt>M</tt> is the 2x2 rotation/mirror matrix part of the transformation and <tt>d</tt> is the
displacement vector
</p>
<p>
The reason why the displacement is not applied to a vector is seen here:
</p>
<pre>
v' = T * v
= T * (p2 - p1)
= T * p2 - T * p1
= (M * p2 + d) - (M * p1 + d)
= M * p2 + d - M * p1 - d
= M * p2 - M * p1
= M * (p2 - p1)</pre>
<p>where the latter simply is:</p>
<pre>v' = M * v</pre>
<h3>Simple transformations</h3>
<keyword name="simple transformation"/>
<keyword name="Trans"/>
<keyword name="DTrans"/>
<p>
Simple transformations are represented by <class_doc href="DTrans"/> or <class_doc href="Trans"/> objects.
The first operates with floating-point displacements in units of micrometers while the second one with integer displacements
in database units. "DTrans" objects act on "D" type floating-point coordinate shapes (e.g. <class_doc href="DBox"/>) while "Trans" objects act
on the integer coordinate shapes (e.g. <class_doc href="Box"/>).
</p>
<p>
The basic construction parameters of "DTrans" and "Trans" are:
</p>
<pre>Trans(angle, mirror, displacement)
DTrans(angle, mirror, displacement)</pre>
<p>"displacement" is a <class_doc href="DVector"/> (for DTrans) or a <class_doc href="Vector"/> (for Trans).</p>
<p>"angle" is the rotation angle in units of 90 degree and "mirror" is the mirror flag:</p>
<table>
<tr><th>angle</th><th>mirror <br/>= False</th><th>mirror <br/>= True</th></tr>
<tr><td>0</td><td>r0</td><td>m0</td></tr>
<tr><td>1</td><td>r90</td><td>m45</td></tr>
<tr><td>2</td><td>r180</td><td>m90</td></tr>
<tr><td>3</td><td>r270</td><td>m135</td></tr>
</table>
<h3>Complex transformations</h3>
<keyword name="complex transformation"/>
<keyword name="CplxTrans"/>
<keyword name="ICplxTrans"/>
<keyword name="DCplxTrans"/>
<keyword name="VCplxTrans"/>
<p>
Complex transformations in addition to the simple transformations feature
scaling (magnification) and arbitrary rotation angles. Other than simple transformations
they do not necessarily preserve a grid and rounding is implied. Furthermore they imply a shift is physical scale
which renders them difficult to use in physical frameworks (e.g. DRC). Hence their
use is discouraged for certain applications.
</p>
<p>
The basic classes are <class_doc href="DCplxTrans"/> for the micrometer-unit (floating-point) version
and <class_doc href="ICplxTrans"/> for the database-unit (integer) version. The
construction parameters are:
</p>
<pre>ICplxTrans(angle, mirror, magnification, displacement)
DCplxTrans(angle, mirror, magnification, displacement)</pre>
<p>
Here, "angle" is the rotation angle in degree (note the difference to "Trans" and "DTrans" where the
rotation angle is in units for 90 degree. "magnification" is a factor (1.0 for "no change in scale").
</p>
<p>
There are two other variants useful for transforming coordinate systems: <class_doc href="CplxTrans"/> takes
integer-unit objects and converts them to floating-point unit objects. It can be used to convert
from database units to micrometer units when configured with a magnification equal to the database unit value:
</p>
<pre>T = CplxTrans(magnification: dbu)
q = T * p</pre>
<p>
The other variant is <class_doc href="VCplxTrans"/> which converts floating-point unit objects
to integer-unit ones. These objects are generated when inverting "CplxTrans" objects.
</p>
</doc>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Some files were not shown because too many files have changed in this diff Show More