Merge remote-tracking branch 'origin/master' into master-mac-qt6 discarding changes in the macbuild/ dir.
|
|
@ -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
|
||||
|
|
|
|||
10
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
2
setup.py
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ®ion, 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 *
|
||||
|
|
|
|||
|
|
@ -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 ®ion) const;
|
||||
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, bool inverse) const;
|
||||
virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, 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 ®ion, EdgeInteractionMode mode, bool inverse) const;
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair_generic (const Region ®ion, 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> ();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ®ion) const;
|
||||
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, bool invert) const;
|
||||
virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, 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 ®ion, EdgeInteractionMode mode, bool inverse) const;
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const;
|
||||
DeepEdges *apply_filter (const EdgeFilterBase &filter) const;
|
||||
|
||||
template <class Result, class OutputContainer> OutputContainer *processed_impl (const edge_processor<Result> &filter) const;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 (); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
|||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ SOURCES = \
|
|||
dbEdgePairRelationsTests.cc \
|
||||
dbEdgePairTests.cc \
|
||||
dbEdgeTests.cc \
|
||||
dbEdgesUtilsTests.cc \
|
||||
dbClipTests.cc \
|
||||
dbCellMappingTests.cc \
|
||||
dbCellHullGeneratorTests.cc \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
|||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||
<p style="-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';"><br /></p></body></html></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 >> </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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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) & 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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.6 KiB |