Merge pull request #813 from KLayout/wip

Wip
This commit is contained in:
Matthias Köfferlein 2021-05-29 23:59:44 +02:00 committed by GitHub
commit 63f4d727e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
120 changed files with 2358 additions and 378 deletions

View File

@ -269,6 +269,7 @@ gen = Gen::new
run_demo gen, "input1.separation(input2, 1.2, euclidian)", "drc_separation1.png" run_demo gen, "input1.separation(input2, 1.2, euclidian)", "drc_separation1.png"
run_demo gen, "input1.drc(separation(input2, euclidian) < 1.2)", "drc_separation1u.png" run_demo gen, "input1.drc(separation(input2, euclidian) < 1.2)", "drc_separation1u.png"
run_demo gen, "input1.drc((separation(input2) >= 1.2).first_edges)", "drc_separation1un.png"
class Gen class Gen
def produce(s1, s2) def produce(s1, s2)

View File

@ -38,6 +38,7 @@ GenericWriterOptions::GenericWriterOptions ()
m_gds2_max_vertex_count (8000), m_gds2_max_vertex_count (8000),
m_gds2_no_zero_length_paths (false), m_gds2_no_zero_length_paths (false),
m_gds2_multi_xy_records (false), m_gds2_multi_xy_records (false),
m_gds2_resolve_skew_arrays (false),
m_gds2_max_cellname_length (32000), m_gds2_max_cellname_length (32000),
m_gds2_libname ("LIB"), m_gds2_libname ("LIB"),
m_gds2_user_units (1.0), m_gds2_user_units (1.0),
@ -150,6 +151,12 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin
"If this option is given, multiple XY records will be written to accommodate an unlimited number " "If this option is given, multiple XY records will be written to accommodate an unlimited number "
"of points per polygon or path. However, such files may not be compatible with some consumers." "of points per polygon or path. However, such files may not be compatible with some consumers."
) )
<< tl::arg (group +
"-ow|--resolve-skew-arrays", &m_gds2_resolve_skew_arrays, "Resolve skew (non-orthogonal) arrays",
"If this option is given, skew arrays are resolved into single instances. Skew arrays "
"are ones where the row or column vectors are not horizontal or vertical. Such arrays can cause problems "
"in legacy software. This option will eliminate them at the expense of bigger files and loss of the array instance property."
)
<< tl::arg (group + << tl::arg (group +
"#--no-zero-length-paths", &m_gds2_no_zero_length_paths, "Converts zero-length paths to polygons", "#--no-zero-length-paths", &m_gds2_no_zero_length_paths, "Converts zero-length paths to polygons",
"If this option is given, zero-length paths (such with one point) are not written as paths " "If this option is given, zero-length paths (such with one point) are not written as paths "
@ -349,6 +356,7 @@ GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db::
save_options.set_option_by_name ("gds2_max_vertex_count", m_gds2_max_vertex_count); save_options.set_option_by_name ("gds2_max_vertex_count", m_gds2_max_vertex_count);
save_options.set_option_by_name ("gds2_no_zero_length_paths", m_gds2_no_zero_length_paths); save_options.set_option_by_name ("gds2_no_zero_length_paths", m_gds2_no_zero_length_paths);
save_options.set_option_by_name ("gds2_multi_xy_records", m_gds2_multi_xy_records); save_options.set_option_by_name ("gds2_multi_xy_records", m_gds2_multi_xy_records);
save_options.set_option_by_name ("gds2_resolve_skew_arrays", m_gds2_resolve_skew_arrays);
save_options.set_option_by_name ("gds2_max_cellname_length", m_gds2_max_cellname_length); save_options.set_option_by_name ("gds2_max_cellname_length", m_gds2_max_cellname_length);
save_options.set_option_by_name ("gds2_libname", m_gds2_libname); save_options.set_option_by_name ("gds2_libname", m_gds2_libname);
save_options.set_option_by_name ("gds2_user_units", m_gds2_user_units); save_options.set_option_by_name ("gds2_user_units", m_gds2_user_units);

View File

@ -116,6 +116,7 @@ private:
unsigned int m_gds2_max_vertex_count; unsigned int m_gds2_max_vertex_count;
bool m_gds2_no_zero_length_paths; bool m_gds2_no_zero_length_paths;
bool m_gds2_multi_xy_records; bool m_gds2_multi_xy_records;
bool m_gds2_resolve_skew_arrays;
unsigned int m_gds2_max_cellname_length; unsigned int m_gds2_max_cellname_length;
std::string m_gds2_libname; std::string m_gds2_libname;
double m_gds2_user_units; double m_gds2_user_units;

View File

@ -24,6 +24,7 @@ SOURCES = \
dbCompoundOperation.cc \ dbCompoundOperation.cc \
dbEdge.cc \ dbEdge.cc \
dbEdgePair.cc \ dbEdgePair.cc \
dbEdgePairFilters.cc \
dbEdgePairRelations.cc \ dbEdgePairRelations.cc \
dbEdgePairs.cc \ dbEdgePairs.cc \
dbEdgeProcessor.cc \ dbEdgeProcessor.cc \
@ -233,6 +234,7 @@ HEADERS = \
dbCompoundOperation.h \ dbCompoundOperation.h \
dbEdge.h \ dbEdge.h \
dbEdgePair.h \ dbEdgePair.h \
dbEdgePairFilters.h \
dbEdgePairRelations.h \ dbEdgePairRelations.h \
dbEdgePairs.h \ dbEdgePairs.h \
dbEdgeProcessor.h \ dbEdgeProcessor.h \

View File

@ -46,7 +46,17 @@ AsIfFlatEdgePairs::AsIfFlatEdgePairs ()
AsIfFlatEdgePairs::AsIfFlatEdgePairs (const AsIfFlatEdgePairs &other) AsIfFlatEdgePairs::AsIfFlatEdgePairs (const AsIfFlatEdgePairs &other)
: EdgePairsDelegate (other), m_bbox_valid (other.m_bbox_valid), m_bbox (other.m_bbox) : EdgePairsDelegate (other), m_bbox_valid (other.m_bbox_valid), m_bbox (other.m_bbox)
{ {
// .. nothing yet .. operator= (other);
}
AsIfFlatEdgePairs &
AsIfFlatEdgePairs::operator= (const AsIfFlatEdgePairs &other)
{
if (this != &other) {
m_bbox_valid = other.m_bbox_valid;
m_bbox = other.m_bbox;
}
return *this;
} }
AsIfFlatEdgePairs::~AsIfFlatEdgePairs () AsIfFlatEdgePairs::~AsIfFlatEdgePairs ()

View File

@ -81,6 +81,8 @@ protected:
void invalidate_bbox (); void invalidate_bbox ();
private: private:
friend class DeepEdgePairs;
AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other); AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other);
mutable bool m_bbox_valid; mutable bool m_bbox_valid;

View File

@ -167,22 +167,6 @@ AsIfFlatRegion::in (const Region &other, bool invert) const
return new_region.release (); return new_region.release ();
} }
size_t
AsIfFlatRegion::count () const
{
size_t n = 0;
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
++n;
}
return n;
}
size_t
AsIfFlatRegion::hier_count () const
{
return count ();
}
bool bool
AsIfFlatRegion::is_box () const AsIfFlatRegion::is_box () const
{ {
@ -1077,8 +1061,14 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
others.push_back (begin_merged ()); others.push_back (begin_merged ());
} else { } else {
foreign.push_back (false); foreign.push_back (false);
others.push_back (other->begin ()); if (options.whole_edges) {
other_is_merged = other->is_merged (); // NOTE: whole edges needs both inputs merged
others.push_back (other->begin_merged ());
other_is_merged = true;
} else {
others.push_back (other->begin ());
other_is_merged = other->is_merged ();
}
has_other = true; has_other = true;
} }

View File

@ -47,8 +47,6 @@ public:
virtual ~AsIfFlatRegion (); virtual ~AsIfFlatRegion ();
virtual bool is_box () const; virtual bool is_box () const;
virtual size_t count () const;
virtual size_t hier_count () const;
virtual area_type area (const db::Box &box) const; virtual area_type area (const db::Box &box) const;
virtual perimeter_type perimeter (const db::Box &box) const; virtual perimeter_type perimeter (const db::Box &box) const;

View File

@ -346,16 +346,87 @@ EdgePairsDelegate *DeepEdgePairs::add (const EdgePairs &other) const
} }
} }
EdgePairsDelegate *DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter) EdgePairsDelegate *
DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
{ {
// TODO: implement // TODO: implement to be really in-place
return AsIfFlatEdgePairs::filter_in_place (filter); *this = *apply_filter (filter);
return this;
} }
EdgePairsDelegate *DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const EdgePairsDelegate *
DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
{ {
// TODO: implement return apply_filter (filter);
return AsIfFlatEdgePairs::filtered (filter); }
DeepEdgePairs *
DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const
{
const db::DeepLayer &edge_pairs = deep_layer ();
std::unique_ptr<VariantsCollectorBase> vars;
if (filter.vars ()) {
vars.reset (new db::VariantsCollectorBase (filter.vars ()));
vars->collect (edge_pairs.layout (), edge_pairs.initial_cell ());
if (filter.wants_variants ()) {
const_cast<db::DeepLayer &> (edge_pairs).separate_variants (*vars);
}
}
db::Layout &layout = const_cast<db::Layout &> (edge_pairs.layout ());
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > to_commit;
std::unique_ptr<db::DeepEdgePairs> res (new db::DeepEdgePairs (edge_pairs.derived ()));
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
const db::Shapes &s = c->shapes (edge_pairs.layer ());
if (vars.get ()) {
const std::map<db::ICplxTrans, size_t> &vv = vars->variants (c->cell_index ());
for (std::map<db::ICplxTrans, size_t>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
db::Shapes *st;
if (vv.size () == 1) {
st = & c->shapes (res->deep_layer ().layer ());
} else {
st = & to_commit [c->cell_index ()] [v->first];
}
const db::ICplxTrans &tr = v->first;
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) {
if (filter.selected (si->edge_pair ().transformed (tr))) {
st->insert (*si);
}
}
}
} else {
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) {
if (filter.selected (si->edge_pair ())) {
st.insert (*si);
}
}
}
}
if (! to_commit.empty () && vars.get ()) {
res->deep_layer ().commit_shapes (*vars, to_commit);
}
return res.release ();
} }
RegionDelegate * RegionDelegate *
@ -461,4 +532,14 @@ void DeepEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type
deep_layer ().insert_into_as_polygons (layout, into_cell, into_layer, enl); deep_layer ().insert_into_as_polygons (layout, into_cell, into_layer, enl);
} }
DeepEdgePairs &DeepEdgePairs::operator= (const DeepEdgePairs &other)
{
if (this != &other) {
AsIfFlatEdgePairs::operator= (other);
DeepShapeCollectionDelegateBase::operator= (other);
}
return *this;
}
} }

View File

@ -104,6 +104,7 @@ private:
void init (); void init ();
EdgesDelegate *generic_edges (bool first, bool second) const; EdgesDelegate *generic_edges (bool first, bool second) const;
DeepEdgePairs *apply_filter (const EdgePairFilterBase &filter) const;
}; };
} }

View File

@ -273,24 +273,33 @@ void DeepEdges::reserve (size_t)
// Not implemented for deep regions // Not implemented for deep regions
} }
void DeepEdges::flatten () static
void flatten_layer (db::DeepLayer &deep_layer)
{ {
db::Layout &layout = deep_layer ().layout (); db::Layout &layout = deep_layer.layout ();
if (layout.begin_top_down () != layout.end_top_down ()) { if (layout.begin_top_down () != layout.end_top_down ()) {
db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
db::Shapes flat_shapes (layout.is_editable ()); db::Shapes flat_shapes (layout.is_editable ());
for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) {
flat_shapes.insert (iter->edge ().transformed (iter.trans ())); flat_shapes.insert (iter->edge ().transformed (iter.trans ()));
} }
layout.clear_layer (deep_layer ().layer ()); layout.clear_layer (deep_layer.layer ());
top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); top_cell.shapes (deep_layer.layer ()).swap (flat_shapes);
} }
} }
void DeepEdges::flatten ()
{
flatten_layer (deep_layer ());
if (m_merged_edges_valid) {
flatten_layer (const_cast<db::DeepLayer &> (merged_deep_layer ()));
}
}
EdgesIteratorDelegate * EdgesIteratorDelegate *
DeepEdges::begin () const DeepEdges::begin () const
{ {
@ -518,7 +527,7 @@ DeepEdges::ensure_merged_edges_valid () const
m_merged_edges = deep_layer ().derived (); m_merged_edges = deep_layer ().derived ();
tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged polygons"); tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged edges");
db::Layout &layout = const_cast<db::Layout &> (deep_layer ().layout ()); db::Layout &layout = const_cast<db::Layout &> (deep_layer ().layout ());

View File

@ -284,26 +284,37 @@ void DeepRegion::reserve (size_t)
// Not implemented for deep regions // Not implemented for deep regions
} }
void DeepRegion::flatten () static void
flatten_layer (db::DeepLayer &deep_layer)
{ {
db::Layout &layout = deep_layer ().layout (); db::Layout &layout = deep_layer.layout ();
if (layout.begin_top_down () != layout.end_top_down ()) { if (layout.begin_top_down () != layout.end_top_down ()) {
db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
db::Shapes flat_shapes (layout.is_editable ()); db::Shapes flat_shapes (layout.is_editable ());
for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) {
db::Polygon poly; if (iter->is_polygon ()) {
iter->polygon (poly); db::Polygon poly;
flat_shapes.insert (poly.transformed (iter.trans ())); iter->polygon (poly);
flat_shapes.insert (db::PolygonRef (poly.transformed (iter.trans ()), layout.shape_repository ()));
}
} }
layout.clear_layer (deep_layer ().layer ()); layout.clear_layer (deep_layer.layer ());
top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); top_cell.shapes (deep_layer.layer ()).swap (flat_shapes);
} }
} }
void DeepRegion::flatten ()
{
flatten_layer (deep_layer ());
if (m_merged_polygons_valid) {
flatten_layer (const_cast<db::DeepLayer &> (merged_deep_layer ()));
}
}
RegionIteratorDelegate * RegionIteratorDelegate *
DeepRegion::begin () const DeepRegion::begin () const
{ {
@ -651,6 +662,13 @@ DeepRegion::not_with (const Region &other) const
} }
} }
RegionDelegate *
DeepRegion::or_with (const Region &other) const
{
// NOTE: this is somewhat different from the as if flat case because it does not merge
return add (other);
}
std::pair<RegionDelegate *, RegionDelegate *> std::pair<RegionDelegate *, RegionDelegate *>
DeepRegion::andnot_with (const Region &other) const DeepRegion::andnot_with (const Region &other) const
{ {
@ -1090,6 +1108,7 @@ public:
virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{ {
db::EdgeProcessor ep; db::EdgeProcessor ep;
ep.set_base_verbosity (50);
for (shape_interactions<db::PolygonRef, db::PolygonRef>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { for (shape_interactions<db::PolygonRef, db::PolygonRef>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
ep.insert (s->second); ep.insert (s->second);
@ -1637,8 +1656,14 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
if (! other_deep) { if (! other_deep) {
return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options); return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options);
} }
other_layer = other_deep->deep_layer ().layer (); if (options.whole_edges) {
other_is_merged = other->is_merged (); // NOTE: whole edges needs both inputs merged
other_layer = other_deep->merged_deep_layer ().layer ();
other_is_merged = true;
} else {
other_layer = other_deep->deep_layer ().layer ();
other_is_merged = other->is_merged ();
}
} }
const db::DeepLayer &polygons = merged_deep_layer (); const db::DeepLayer &polygons = merged_deep_layer ();

View File

@ -99,6 +99,7 @@ public:
virtual RegionDelegate *and_with (const Region &other) const; virtual RegionDelegate *and_with (const Region &other) const;
virtual RegionDelegate *not_with (const Region &other) const; virtual RegionDelegate *not_with (const Region &other) const;
virtual RegionDelegate *xor_with (const Region &other) const; virtual RegionDelegate *xor_with (const Region &other) const;
virtual RegionDelegate *or_with (const Region &other) const;
virtual std::pair<RegionDelegate *, RegionDelegate *> andnot_with (const Region &) const; virtual std::pair<RegionDelegate *, RegionDelegate *> andnot_with (const Region &) const;
virtual RegionDelegate *add_in_place (const Region &other); virtual RegionDelegate *add_in_place (const Region &other);

View File

@ -403,9 +403,15 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
{ {
unsigned int layer_index = layout.insert_layer (); unsigned int layer_index = layout.insert_layer ();
if (si.layout () && si.layer () < si.layout ()->layers ()) { if (si.layout ()) {
// try to preserve the layer properties // try to preserve the layer properties
layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ())); if (! si.multiple_layers ()) {
if (si.layer () < si.layout ()->layers ()) {
layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ()));
}
} else if (! si.layers ().empty ()) {
layout.set_properties (layer_index, si.layout ()->get_properties (si.layers ().front ()));
}
} }
return layer_index; return layer_index;

View File

@ -79,18 +79,6 @@ bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) cons
return false; return false;
} }
bool EqualDeviceParameters::equal (const db::Device &a, const db::Device &b) const
{
for (std::vector<std::pair<size_t, std::pair<double, double> > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) {
int cmp = compare_parameters (a.parameter_value (c->first), b.parameter_value (c->first), c->second.first, c->second.second);
if (cmp != 0) {
return false;
}
}
return true;
}
EqualDeviceParameters &EqualDeviceParameters::operator+= (const EqualDeviceParameters &other) EqualDeviceParameters &EqualDeviceParameters::operator+= (const EqualDeviceParameters &other)
{ {
for (std::vector<std::pair<size_t, std::pair<double, double> > >::const_iterator c = other.m_compare_set.begin (); c != other.m_compare_set.end (); ++c) { for (std::vector<std::pair<size_t, std::pair<double, double> > >::const_iterator c = other.m_compare_set.begin (); c != other.m_compare_set.end (); ++c) {
@ -121,19 +109,6 @@ bool AllDeviceParametersAreEqual::less (const db::Device &a, const db::Device &b
return false; return false;
} }
bool AllDeviceParametersAreEqual::equal (const db::Device &a, const db::Device &b) const
{
const std::vector<db::DeviceParameterDefinition> &parameters = a.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator c = parameters.begin (); c != parameters.end (); ++c) {
int cmp = compare_parameters (a.parameter_value (c->id ()), b.parameter_value (c->id ()), 0.0, m_relative);
if (cmp != 0) {
return false;
}
}
return true;
}
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// DeviceClass class implementation // DeviceClass class implementation
@ -293,7 +268,7 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b)
} }
if (pcd != 0) { if (pcd != 0) {
return pcd->equal (a, b); return ! pcd->less (a, b) && ! pcd->less (b, a);
} else { } else {
const std::vector<db::DeviceParameterDefinition> &pd = a.device_class ()->parameter_definitions (); const std::vector<db::DeviceParameterDefinition> &pd = a.device_class ()->parameter_definitions ();

View File

@ -297,7 +297,6 @@ public:
virtual DeviceParameterCompareDelegate *clone () const = 0; virtual DeviceParameterCompareDelegate *clone () const = 0;
virtual bool less (const db::Device &a, const db::Device &b) const = 0; virtual bool less (const db::Device &a, const db::Device &b) const = 0;
virtual bool equal (const db::Device &a, const db::Device &b) const = 0;
}; };
/** /**
@ -315,7 +314,6 @@ public:
EqualDeviceParameters (size_t parameter_id, double relative, double absolute); EqualDeviceParameters (size_t parameter_id, double relative, double absolute);
virtual bool less (const db::Device &a, const db::Device &b) const; virtual bool less (const db::Device &a, const db::Device &b) const;
virtual bool equal (const db::Device &a, const db::Device &b) const;
virtual DeviceParameterCompareDelegate *clone () const virtual DeviceParameterCompareDelegate *clone () const
{ {
@ -345,7 +343,6 @@ public:
AllDeviceParametersAreEqual (double relative); AllDeviceParametersAreEqual (double relative);
virtual bool less (const db::Device &a, const db::Device &b) const; virtual bool less (const db::Device &a, const db::Device &b) const;
virtual bool equal (const db::Device &a, const db::Device &b) const;
virtual DeviceParameterCompareDelegate *clone () const virtual DeviceParameterCompareDelegate *clone () const
{ {

View File

@ -856,6 +856,9 @@ public:
* line through the edge. If the edge is degenerated, the distance * line through the edge. If the edge is degenerated, the distance
* is not defined. * is not defined.
* *
* The distance is through as a distance of the point from the line
* through the edge.
*
* @param p The point to test. * @param p The point to test.
* *
* @return The distance * @return The distance
@ -877,6 +880,27 @@ public:
} }
} }
/**
* @brief Gets the distance of the point from the edge.
*
* The distance is computed as the minimum distance of the point to any of the edge's
* points.
*
* @param p The point whose distance is to be computed
*
* @return The distance
*/
distance_type euclidian_distance (const db::point<C> &p)
{
if (db::sprod_sign (p - p1 (), d ()) < 0) {
return p1 ().distance (p);
} else if (db::sprod_sign (p - p2 (), d ()) > 0) {
return p2 ().distance (p);
} else {
return std::abs (distance (p));
}
}
/** /**
* @brief Side of the point * @brief Side of the point
* *

View File

@ -204,6 +204,24 @@ public:
return !equal (b); return !equal (b);
} }
/**
* @brief Computes the distance of the edges in the edge pair
*
* The distance is the minimum distance of any of the points from
* each edge.
*/
distance_type distance () const
{
db::edge<C> e1 = first (), e2 = second ();
if (! e1.intersect (e2)) {
distance_type d12 = std::min (e2.euclidian_distance (e1.p1 ()), e2.euclidian_distance (e1.p2 ()));
distance_type d21 = std::min (e1.euclidian_distance (e2.p1 ()), e1.euclidian_distance (e2.p2 ()));
return std::min (d12, d21);
} else {
return 0;
}
}
/** /**
* @brief A method binding of operator* (mainly for automation purposes) * @brief A method binding of operator* (mainly for automation purposes)
*/ */

View File

@ -0,0 +1,81 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 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 "dbCommon.h"
#include "dbEdgePairFilters.h"
#include "dbEdges.h"
namespace db
{
// ---------------------------------------------------------------------------------------------------
// EdgeFilterBasedEdgePairFilter implementation
EdgeFilterBasedEdgePairFilter::EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match)
: mp_edge_filter (edge_filter), m_one_must_match (one_must_match)
{
// .. nothing yet ..
}
EdgeFilterBasedEdgePairFilter::~EdgeFilterBasedEdgePairFilter ()
{
// .. nothing yet ..
}
bool EdgeFilterBasedEdgePairFilter::selected (const db::EdgePair &edge_pair) const
{
if (m_one_must_match) {
return mp_edge_filter->selected (edge_pair.first ()) || mp_edge_filter->selected (edge_pair.second ());
} else {
return mp_edge_filter->selected (edge_pair.first ()) && mp_edge_filter->selected (edge_pair.second ());
}
}
const TransformationReducer *EdgeFilterBasedEdgePairFilter::vars () const
{
return mp_edge_filter->vars ();
}
bool EdgeFilterBasedEdgePairFilter::wants_variants () const
{
return mp_edge_filter->wants_variants ();
}
// ---------------------------------------------------------------------------------------------------
// EdgePairFilterByDistance implementation
EdgePairFilterByDistance::EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted)
: m_min_distance (min_distance), m_max_distance (max_distance), m_inverted (inverted)
{
// .. nothing yet ..
}
bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair) const
{
distance_type dist = edge_pair.distance ();
bool sel = (dist >= m_min_distance && dist < m_max_distance);
return m_inverted ? !sel : sel;
}
}

View File

@ -0,0 +1,82 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 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
*/
#ifndef HDR_dbEdgePairFilters
#define HDR_dbEdgePairFilters
#include "dbEdgePairs.h"
namespace db
{
class EdgeFilterBase;
/**
* @brief A base class for edge pair filters based on edge filters
*
* If "one_must_match" is true, it is sufficient for one edge to be selected.
* If it is false, both edges need to be selected to make the edge pair selected.
*
* NOTE: the edge filter is not owned by the edge pair filter object.
*/
class DB_PUBLIC EdgeFilterBasedEdgePairFilter
: public EdgePairFilterBase
{
public:
EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match);
virtual ~EdgeFilterBasedEdgePairFilter ();
virtual bool selected (const db::EdgePair &edge_pair) const;
virtual const TransformationReducer *vars () const;
virtual bool wants_variants () const;
private:
EdgeFilterBase *mp_edge_filter;
bool m_one_must_match;
};
/**
* @brief Filters edge pairs based on the distance of the edges.
*
* The distance is measured as the smallest distance between each of the points of the two edges.
*/
class DB_PUBLIC EdgePairFilterByDistance
: public EdgePairFilterBase
{
public:
typedef db::coord_traits<db::Coord>::distance_type distance_type;
EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted);
virtual bool selected (const db::EdgePair &edge_pair) const;
virtual const TransformationReducer *vars () const { return 0; }
virtual bool wants_variants () const { return false; }
private:
distance_type m_min_distance, m_max_distance;
bool m_inverted;
};
}
#endif

View File

@ -38,6 +38,9 @@
namespace db namespace db
{ {
// ---------------------------------------------------------------------------------------------------
// EdgePairs implementation
EdgePairs::EdgePairs () EdgePairs::EdgePairs ()
: mp_delegate (new EmptyEdgePairs ()) : mp_delegate (new EmptyEdgePairs ())
{ {

View File

@ -42,17 +42,13 @@ class Edges;
class Region; class Region;
class DeepShapeStore; class DeepShapeStore;
class TransformationReducer; class TransformationReducer;
class EdgeFilterBase;
/** /**
* @brief An edge pair set iterator * @brief An edge pair set iterator
* *
* The iterator delivers the edge pairs of the edge pair set * The iterator delivers the edge pairs of the edge pair set
*/ */
/**
* @brief An edge set iterator
*
* The iterator delivers the edges of the edge set
*/
class DB_PUBLIC EdgePairsIterator class DB_PUBLIC EdgePairsIterator
: public generic_shape_iterator<db::EdgePair> : public generic_shape_iterator<db::EdgePair>
{ {
@ -117,7 +113,7 @@ public:
EdgePairFilterBase () { } EdgePairFilterBase () { }
virtual ~EdgePairFilterBase () { } virtual ~EdgePairFilterBase () { }
virtual bool selected (const db::EdgePair &edge) const = 0; virtual bool selected (const db::EdgePair &edge_pair) const = 0;
virtual const TransformationReducer *vars () const = 0; virtual const TransformationReducer *vars () const = 0;
virtual bool wants_variants () const = 0; virtual bool wants_variants () const = 0;
}; };

View File

@ -32,6 +32,7 @@
#include "dbShapeCollection.h" #include "dbShapeCollection.h"
#include "dbShapeCollectionUtils.h" #include "dbShapeCollectionUtils.h"
#include "dbGenericShapeIterator.h" #include "dbGenericShapeIterator.h"
#include "dbHash.h"
#include <list> #include <list>
#include <unordered_set> #include <unordered_set>

View File

@ -32,6 +32,7 @@
#include "dbLayoutVsSchematic.h" #include "dbLayoutVsSchematic.h"
#include "dbLayoutToNetlistFormatDefs.h" #include "dbLayoutToNetlistFormatDefs.h"
#include "dbLayoutVsSchematicFormatDefs.h" #include "dbLayoutVsSchematicFormatDefs.h"
#include "dbShapeProcessor.h"
#include "tlGlobPattern.h" #include "tlGlobPattern.h"
namespace db namespace db
@ -246,11 +247,21 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co
extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters, m_device_scaling); extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters, m_device_scaling);
} }
void LayoutToNetlist::connect (const db::Region &l) void LayoutToNetlist::reset_extracted ()
{ {
if (m_netlist_extracted) { if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
m_net_clusters.clear ();
mp_netlist.reset (0);
m_netlist_extracted = false;
} }
}
void LayoutToNetlist::connect (const db::Region &l)
{
reset_extracted ();
if (! is_persisted (l)) { if (! is_persisted (l)) {
register_layer (l, make_new_name ()); register_layer (l, make_new_name ());
@ -265,9 +276,8 @@ void LayoutToNetlist::connect (const db::Region &l)
void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b) void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b)
{ {
if (m_netlist_extracted) { reset_extracted ();
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! is_persisted (a)) { if (! is_persisted (a)) {
register_layer (a, make_new_name ()); register_layer (a, make_new_name ());
} }
@ -286,9 +296,8 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap
size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn) size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn)
{ {
if (m_netlist_extracted) { reset_extracted ();
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! is_persisted (l)) { if (! is_persisted (l)) {
register_layer (l, make_new_name ()); register_layer (l, make_new_name ());
} }
@ -1293,6 +1302,93 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
} }
} }
namespace
{
class PolygonAreaAndPerimeterCollector
: public db::PolygonSink
{
public:
typedef db::Polygon polygon_type;
typedef polygon_type::perimeter_type perimeter_type;
typedef polygon_type::area_type area_type;
PolygonAreaAndPerimeterCollector ()
: m_area (0), m_perimeter (0)
{ }
area_type area () const
{
return m_area;
}
perimeter_type perimeter () const
{
return m_perimeter;
}
virtual void put (const db::Polygon &poly)
{
m_area += poly.area ();
m_perimeter += poly.perimeter ();
}
public:
area_type m_area;
perimeter_type m_perimeter;
};
}
static void
compute_area_and_perimeter_of_net_shapes (const db::hier_clusters<db::NetShape> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Polygon::area_type &area, db::Polygon::perimeter_type &perimeter)
{
db::EdgeProcessor ep;
// count vertices and reserve space
size_t n = 0;
for (db::recursive_cluster_shape_iterator<db::NetShape> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
n += rci->polygon_ref ().vertices ();
}
ep.reserve (n);
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);
}
PolygonAreaAndPerimeterCollector ap_collector;
db::PolygonGenerator pg (ap_collector, false);
db::SimpleMerge op;
ep.process (pg, op);
area = ap_collector.area ();
perimeter = ap_collector.perimeter ();
}
static void
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::EdgeProcessor ep;
// count vertices and reserve space
size_t n = 0;
for (db::recursive_cluster_shape_iterator<db::NetShape> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
n += rci->polygon_ref ().vertices ();
}
ep.reserve (n);
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);
}
db::ShapeGenerator sg (shapes);
db::PolygonGenerator pg (sg, false);
db::SimpleMerge op;
ep.process (pg, op);
}
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::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)
{ {
// TODO: that's basically too much .. we only need the clusters // TODO: that's basically too much .. we only need the clusters
@ -1318,56 +1414,65 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
continue; continue;
} }
db::Region rgate, rmetal;
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0);
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0);
double agate = 0.0;
if (fabs (gate_area_factor) > 1e-6) {
agate += rgate.area () * dbu * dbu * gate_area_factor;
}
if (fabs (gate_perimeter_factor) > 1e-6) {
agate += rgate.perimeter () * dbu * gate_perimeter_factor;
}
double ametal = 0.0;
if (fabs (metal_area_factor) > 1e-6) {
ametal += rmetal.area () * dbu * dbu * metal_area_factor;
}
if (fabs (metal_perimeter_factor) > 1e-6) {
ametal += rmetal.perimeter () * dbu * metal_perimeter_factor;
}
double r = ratio; double r = ratio;
bool skip = false; bool skip = false;
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) { for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
db::Region rdiode; db::Polygon::area_type adiode_int = 0;
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode, 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);
if (fabs (d->second) < db::epsilon) { if (fabs (d->second) < db::epsilon) {
if (rdiode.area () > 0) { if (adiode_int > 0) {
skip = true; skip = true;
} }
} else { } else {
r += rdiode.area () * dbu * dbu * d->second; r += adiode_int * dbu * dbu * d->second;
} }
} }
if (! skip) { if (! skip) {
if (tl::verbosity () >= 50) { db::Polygon::area_type agate_int = 0;
tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r); db::Polygon::perimeter_type pgate_int = 0;
compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (gate), agate_int, pgate_int);
double agate = 0.0;
if (fabs (gate_area_factor) > 1e-6) {
agate += agate_int * dbu * dbu * gate_area_factor;
}
if (fabs (gate_perimeter_factor) > 1e-6) {
agate += pgate_int * dbu * gate_perimeter_factor;
} }
if (agate > dbu * dbu && ametal / agate > r + db::epsilon) { if (agate > dbu * dbu) {
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) { db::Polygon::area_type ametal_int = 0;
shapes.insert (*r); db::Polygon::perimeter_type pmetal_int = 0;
compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (metal), ametal_int, pmetal_int);
double ametal = 0.0;
if (fabs (metal_area_factor) > 1e-6) {
ametal += ametal_int * dbu * dbu * metal_area_factor;
} }
if (fabs (metal_perimeter_factor) > 1e-6) {
ametal += pmetal_int * dbu * metal_perimeter_factor;
}
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);
}
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);
}
} }
} }

View File

@ -359,6 +359,14 @@ public:
*/ */
void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::ShapeCollection *> &layers); void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::ShapeCollection *> &layers);
/**
* @brief Resets the extracted netlist
*
* This method will invalidate the netlist and extraction. It is called automatically when
* cone of the connect methods is called.
*/
void reset_extracted ();
/** /**
* @brief Defines an intra-layer connection for the given layer. * @brief Defines an intra-layer connection for the given layer.
* The layer is either an original layer created with "make_layer" and it's variants or * The layer is either an original layer created with "make_layer" and it's variants or
@ -532,6 +540,14 @@ public:
*/ */
void set_netlist_extracted (); void set_netlist_extracted ();
/**
* @brief Gets a value indicating whether the netlist has been extracted
*/
bool is_netlist_extracted () const
{
return m_netlist_extracted;
}
/** /**
* @brief Gets the internal DeepShapeStore object * @brief Gets the internal DeepShapeStore object
* *

View File

@ -56,7 +56,6 @@ void local_operation<TS, TI, TR>::compute_local (db::Layout *layout, const shape
} }
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) { for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
const TS &subject_shape = interactions.subject_shape (i->first); const TS &subject_shape = interactions.subject_shape (i->first);
shape_interactions<TS, TI> single_interactions; shape_interactions<TS, TI> single_interactions;

View File

@ -3052,7 +3052,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const
db::DeviceClass *db = const_cast<db::DeviceClass *> (i->second.second); db::DeviceClass *db = const_cast<db::DeviceClass *> (i->second.second);
const db::DeviceParameterCompareDelegate *cmp = da->parameter_compare_delegate (); const db::DeviceParameterCompareDelegate *cmp = da->parameter_compare_delegate ();
db->set_parameter_compare_delegate (cmp ? cmp->clone () : 0); db->set_parameter_compare_delegate (const_cast<db::DeviceParameterCompareDelegate *> (cmp));
} }

View File

@ -130,7 +130,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
os << format_name (dev.device_class ()->name ()); os << format_name (dev.device_class ()->name ());
} }
} else if (res) { } else if (res || res3) {
os << "R"; os << "R";
os << format_name (dev.expanded_name ()); os << format_name (dev.expanded_name ());

View File

@ -29,6 +29,7 @@
#include "dbDeepEdges.h" #include "dbDeepEdges.h"
#include "dbDeepRegion.h" #include "dbDeepRegion.h"
#include "dbDeepShapeStore.h" #include "dbDeepShapeStore.h"
#include "dbCellGraphUtils.h"
#include "tlGlobPattern.h" #include "tlGlobPattern.h"
namespace db namespace db
@ -186,6 +187,96 @@ OriginalLayerRegion::min_coherence_changed ()
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
} }
size_t
OriginalLayerRegion::count () const
{
// NOTE: we should to make sure the iterator isn't validated as this would spoil the usability or OriginalLayerRegion upon
// layout changes
db::RecursiveShapeIterator iter = m_iter;
if (iter.has_complex_region () || iter.region () != db::Box::world () || ! iter.enables ().empty () || ! iter.disables ().empty ()) {
// complex case with a search region - use the iterator to determine the count (expensive)
size_t n = 0;
for (db::RecursiveShapeIterator i = iter; ! i.at_end (); ++i) {
++n;
}
return n;
} else {
// otherwise we can utilize the CellCounter
size_t n = 0;
const db::Layout &layout = *iter.layout ();
std::set<db::cell_index_type> cells;
iter.top_cell ()->collect_called_cells (cells);
cells.insert (iter.top_cell ()->cell_index ());
db::CellCounter cc (&layout);
for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
if (cells.find (*c) == cells.end ()) {
continue;
}
size_t nn = 0;
if (iter.multiple_layers ()) {
for (std::vector<unsigned int>::const_iterator l = iter.layers ().begin (); l != iter.layers ().end (); ++l) {
nn += layout.cell (*c).shapes (*l).size (iter.shape_flags () & db::ShapeIterator::Regions);
}
} else if (iter.layer () < layout.layers ()) {
nn += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & db::ShapeIterator::Regions);
}
n += cc.weight (*c) * nn;
}
return n;
}
}
size_t
OriginalLayerRegion::hier_count () const
{
// NOTE: we should to make sure the iterator isn't validated as this would spoil the usability or OriginalLayerRegion upon
// layout changes
db::RecursiveShapeIterator iter = m_iter;
if (iter.has_complex_region () || iter.region () != db::Box::world ()) {
// TODO: how to establish a "hierarchical" interpretation in this case?
return count ();
} else {
size_t n = 0;
const db::Layout &layout = *iter.layout ();
std::set<db::cell_index_type> cells;
iter.top_cell ()->collect_called_cells (cells);
cells.insert (iter.top_cell ()->cell_index ());
for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
if (cells.find (*c) == cells.end ()) {
continue;
}
if (iter.multiple_layers ()) {
for (std::vector<unsigned int>::const_iterator l = iter.layers ().begin (); l != iter.layers ().end (); ++l) {
n += layout.cell (*c).shapes (*l).size (iter.shape_flags () & db::ShapeIterator::Regions);
}
} else if (iter.layer () < layout.layers ()) {
n += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & db::ShapeIterator::Regions);
}
}
return n;
}
}
RegionIteratorDelegate * RegionIteratorDelegate *
OriginalLayerRegion::begin () const OriginalLayerRegion::begin () const
{ {

View File

@ -58,6 +58,8 @@ public:
virtual bool empty () const; virtual bool empty () const;
virtual bool is_merged () const; virtual bool is_merged () const;
virtual size_t count () const;
virtual size_t hier_count () const;
virtual const db::Polygon *nth (size_t n) const; virtual const db::Polygon *nth (size_t n) const;
virtual bool has_valid_polygons () const; virtual bool has_valid_polygons () const;

View File

@ -550,11 +550,13 @@ public:
} }
/** /**
* @brief Get the layer of the current shape * @brief Gets the layer of the current shape
*/ */
unsigned int layer () const unsigned int layer () const
{ {
validate (0); if (m_has_layers) {
validate (0);
}
return m_layer; return m_layer;
} }
@ -562,7 +564,7 @@ public:
* @brief Gets the layers from which the shapes are taken from * @brief Gets the layers from which the shapes are taken from
* *
* The returned layers are useful only if \multiple_layers is * The returned layers are useful only if \multiple_layers is
* true. * true. Otherwise use \layer to get the iterated layer.
*/ */
const std::vector<unsigned int> &layers () const const std::vector<unsigned int> &layers () const
{ {

View File

@ -34,8 +34,10 @@
#include "dbShapeCollection.h" #include "dbShapeCollection.h"
#include "dbGenericShapeIterator.h" #include "dbGenericShapeIterator.h"
#include "dbRegionLocalOperations.h" #include "dbRegionLocalOperations.h"
#include "dbHash.h"
#include <list> #include <list>
#include <unordered_set>
namespace db { namespace db {

View File

@ -240,6 +240,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
// the processor. Reason: the search range is limited, hence not all necessary components may have been // the processor. Reason: the search range is limited, hence not all necessary components may have been
// captured. // captured.
db::EdgeProcessor ep; db::EdgeProcessor ep;
ep.set_base_verbosity (50);
ep.clear (); ep.clear ();
size_t i = 0; size_t i = 0;
@ -561,6 +562,7 @@ void interacting_local_operation<TS, TI, TR>::do_compute_local (db::Layout * /*l
} }
db::EdgeProcessor ep; db::EdgeProcessor ep;
ep.set_base_verbosity (50);
std::set<TI> others; std::set<TI> others;
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) { for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
@ -597,6 +599,7 @@ void interacting_local_operation<TS, TI, TR>::do_compute_local (db::Layout * /*l
// in counted mode we need to merge the shapes because they might overlap // in counted mode we need to merge the shapes because they might overlap
db::EdgeProcessor ep_merge; db::EdgeProcessor ep_merge;
ep_merge.set_base_verbosity (50);
size_t i = 0; size_t i = 0;
for (typename std::set<TI>::const_iterator o = others.begin (); o != others.end (); ++o) { for (typename std::set<TI>::const_iterator o = others.begin (); o != others.end (); ++o) {
@ -730,6 +733,7 @@ void pull_local_operation<TS, TI, TR>::do_compute_local (db::Layout * /*layout*/
std::unordered_set<TR> &result = results.front (); std::unordered_set<TR> &result = results.front ();
db::EdgeProcessor ep; db::EdgeProcessor ep;
ep.set_base_verbosity (50);
std::set<TI> others; std::set<TI> others;
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) { for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {

View File

@ -52,7 +52,7 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo
db::Point pn = ctr [j]; db::Point pn = ctr [j];
if (m_checker (pt - pp, pn - pt)) { if (m_checker (pt - pp, pn - pt)) {
delivery.make_point (pt); delivery.make_point (pt, db::Edge (pp, pt), db::Edge (pt, pn));
} }
pp = pt; pp = pt;

View File

@ -41,7 +41,7 @@ namespace db
class DB_PUBLIC CornerPointDelivery class DB_PUBLIC CornerPointDelivery
{ {
public: public:
virtual void make_point (const db::Point &pt) const = 0; virtual void make_point (const db::Point &pt, const db::Edge &e1, const db::Edge &e2) const = 0;
}; };
/** /**
@ -55,7 +55,7 @@ public:
: m_d (dim, dim), mp_result (&result) : m_d (dim, dim), mp_result (&result)
{ } { }
virtual void make_point (const db::Point &pt) const virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
{ {
mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d))); mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d)));
} }
@ -76,7 +76,7 @@ public:
: mp_result (&result) : mp_result (&result)
{ } { }
virtual void make_point (const db::Point &pt) const virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
{ {
mp_result->push_back (db::Edge (pt, pt)); mp_result->push_back (db::Edge (pt, pt));
} }
@ -86,6 +86,26 @@ private:
std::vector<db::Edge> *mp_result; std::vector<db::Edge> *mp_result;
}; };
/**
* @brief An interface to accept corners and turns them into edge pairs
*/
class DB_PUBLIC CornerEdgePairDelivery
: public CornerPointDelivery
{
public:
CornerEdgePairDelivery (std::vector<db::EdgePair> &result)
: mp_result (&result)
{ }
virtual void make_point (const db::Point &, const db::Edge &e1, const db::Edge &e2) const
{
mp_result->push_back (db::EdgePair (e1, e2));
}
private:
std::vector<db::EdgePair> *mp_result;
};
/** /**
* @brief A helper class implementing the core corner detection algorithm * @brief A helper class implementing the core corner detection algorithm
*/ */
@ -155,6 +175,31 @@ public:
virtual bool wants_variants () const { return false; } virtual bool wants_variants () const { return false; }
}; };
/**
* @brief A corner detector delivering edge pairs for the corners
*/
class DB_PUBLIC CornersAsEdgePairs
: public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore
{
public:
CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
{
// .. nothing yet ..
}
void process (const db::Polygon &poly, std::vector<db::EdgePair> &result) const
{
detect_corners (poly, CornerEdgePairDelivery (result));
}
virtual const TransformationReducer *vars () const { return 0; }
virtual bool result_is_merged () const { return false; }
virtual bool result_must_not_be_merged () const { return true; } // to preserve dots
virtual bool requires_raw_input () const { return false; }
virtual bool wants_variants () const { return false; }
};
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Extents // Extents

View File

@ -154,13 +154,16 @@ Edge2EdgeCheckBase::finish (const Edge *o, const size_t &p)
} }
} }
void bool
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner) Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
{ {
if (m_pass == 1) { if (m_pass == 1) {
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) { for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
scanner.insert (&e->first, e->second); scanner.insert (&e->first, e->second);
} }
return ! m_pseudo_edges.empty ();
} else {
return false;
} }
} }
@ -442,11 +445,33 @@ poly2poly_check<PolygonType>::add (const PolygonType *o1, size_t p1, const Polyg
enter (*o1, p1, *o2, p2); enter (*o1, p1, *o2, p2);
} }
static bool interact (const db::Box &box, const db::Edge &e)
{
if (! e.bbox ().touches (box)) {
return false;
} else if (e.is_ortho ()) {
return true;
} else {
return e.clipped (box).first;
}
}
template <class PolygonType> template <class PolygonType>
void void
poly2poly_check<PolygonType>::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2) poly2poly_check<PolygonType>::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2)
{ {
if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) {
bool take_all = mp_output->has_negative_edge_output ();
db::Box common_box;
if (! take_all) {
db::Vector e (mp_output->distance (), mp_output->distance ());
common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e);
if (common_box.empty ()) {
return;
}
}
m_scanner.clear (); m_scanner.clear ();
m_scanner.reserve (vertices (o1) + vertices (o2)); m_scanner.reserve (vertices (o1) + vertices (o2));
@ -454,20 +479,30 @@ poly2poly_check<PolygonType>::enter (const PolygonType &o1, size_t p1, const Pol
m_edges.clear (); m_edges.clear ();
m_edges.reserve (vertices (o1) + vertices (o2)); m_edges.reserve (vertices (o1) + vertices (o2));
bool any_o1 = false, any_o2 = false;
for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) { for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) {
m_edges.push_back (*e); if (take_all || interact (common_box, *e)) {
m_scanner.insert (& m_edges.back (), p1); m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p1);
any_o1 = true;
}
} }
for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) { for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) {
m_edges.push_back (*e); if (take_all || interact (common_box, *e)) {
m_scanner.insert (& m_edges.back (), p2); m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p2);
any_o2 = true;
}
}
if (! take_all && (! any_o1 || ! any_o2)) {
return;
} }
mp_output->feed_pseudo_edges (m_scanner); mp_output->feed_pseudo_edges (m_scanner);
tl_assert (m_edges.size () == vertices (o1) + vertices (o2));
// temporarily disable intra-polygon check in that step .. we do that later in finish() // temporarily disable intra-polygon check in that step .. we do that later in finish()
// if required (#650). // if required (#650).
bool no_intra = mp_output->different_polygons (); bool no_intra = mp_output->different_polygons ();

View File

@ -601,7 +601,7 @@ public:
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner * @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
* (required for negative edge output - cancellation of perpendicular edges) * (required for negative edge output - cancellation of perpendicular edges)
*/ */
void feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner); bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
/** /**
* @brief Reimplementation of the box_scanner_receiver interface * @brief Reimplementation of the box_scanner_receiver interface
@ -641,6 +641,14 @@ public:
m_has_negative_edge_output = f; m_has_negative_edge_output = f;
} }
/**
* @brief Gets a flag indicating that this class wants negative edge output
*/
bool has_negative_edge_output () const
{
return m_has_negative_edge_output;
}
/** /**
* @brief Sets a flag indicating that this class wants normal edge pair output * @brief Sets a flag indicating that this class wants normal edge pair output
*/ */
@ -649,6 +657,14 @@ public:
m_has_edge_pair_output = f; m_has_edge_pair_output = f;
} }
/**
* @brief Gets a flag indicating that this class wants normal edge pair output
*/
bool has_edge_pair_output () const
{
return m_has_edge_pair_output;
}
/** /**
* @brief Gets the distance value * @brief Gets the distance value
*/ */
@ -828,9 +844,13 @@ protected:
if (layer == 0) { if (layer == 0) {
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false); edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
} }
#if 0
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
// the outer edges to not represent the actual contour.
if (layer == 1) { if (layer == 1) {
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false); edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
} }
#endif
} }
}; };

View File

@ -1250,6 +1250,20 @@ public:
return n; return n;
} }
/**
* @brief Report the number of shapes stored for a given type mask
*/
size_t size (unsigned int flags) const
{
size_t n = 0;
for (tl::vector<LayerBase *>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) {
if ((flags & (*l)->type_mask ()) != 0) {
n += (*l)->size ();
}
}
return n;
}
/** /**
* @brief Report the shape count for a certain type * @brief Report the shape count for a certain type
*/ */

View File

@ -226,6 +226,12 @@ static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionO
return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/); return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
} }
static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
{
check_non_null (input, "input");
return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
}
static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e) static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e)
{ {
check_non_null (input, "input"); check_non_null (input, "input");
@ -603,6 +609,12 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
"@brief Creates a node turning corners into dots (single-point edges).\n" "@brief Creates a node turning corners into dots (single-point edges).\n"
) + ) +
gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
"@brief Creates a node turning corners into edge pairs containing the two edges adjacent to the corner.\n"
"The first edge will be the incoming edge and the second one the outgoing edge.\n"
"\n"
"This feature has been introduced in version 0.27.1.\n"
) +
gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0), gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0),
"@brief Creates a node returning the extents of the objects.\n" "@brief Creates a node returning the extents of the objects.\n"
"The 'e' parameter provides a generic enlargement which is applied to the boxes. This is helpful to cover dot-like edges or edge pairs in the input." "The 'e' parameter provides a generic enlargement which is applied to the boxes. This is helpful to cover dot-like edges or edge pairs in the input."

View File

@ -27,6 +27,8 @@
#include "dbEdges.h" #include "dbEdges.h"
#include "dbRegion.h" #include "dbRegion.h"
#include "dbDeepEdgePairs.h" #include "dbDeepEdgePairs.h"
#include "dbEdgesUtils.h"
#include "dbEdgePairFilters.h"
namespace gsi namespace gsi
{ {
@ -176,6 +178,74 @@ static size_t id (const db::EdgePairs *ep)
return tl::id_of (ep->delegate ()); return tl::id_of (ep->delegate ());
} }
static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
{
db::EdgePairFilterByDistance ef (length, length + 1, inverse);
return r->filtered (ef);
}
static db::EdgePairs with_distance2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
{
db::EdgePairFilterByDistance ef (min.is_nil () ? db::Edges::distance_type (0) : min.to<db::Edges::distance_type> (), max.is_nil () ? std::numeric_limits <db::Edges::distance_type>::max () : max.to<db::Edges::distance_type> (), inverse);
return r->filtered (ef);
}
static db::EdgePairs with_length1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
{
db::EdgeLengthFilter f (length, length + 1, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_length2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
{
db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to<db::Edges::distance_type> (), max.is_nil () ? std::numeric_limits <db::Edges::distance_type>::max () : max.to<db::Edges::distance_type> (), inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_length_both1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
{
db::EdgeLengthFilter f (length, length + 1, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
{
db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to<db::Edges::distance_type> (), max.is_nil () ? std::numeric_limits <db::Edges::distance_type>::max () : max.to<db::Edges::distance_type> (), inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
extern Class<db::ShapeCollection> decl_dbShapeCollection; extern Class<db::ShapeCollection> decl_dbShapeCollection;
Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
@ -502,6 +572,112 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"The boxes will not be merged, so it is possible to determine overlaps " "The boxes will not be merged, so it is possible to determine overlaps "
"of these boxes for example.\n" "of these boxes for example.\n"
) + ) +
method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of one of their edges\n"
"Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only "
"edge pairs with at least one edge having the given length are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_length", with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of one of their edges\n"
"Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only "
"edge pairs with at least one edge having a length between min_length and max_length (excluding max_length itself) are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"If you don't want to specify a lower or upper limit, pass nil to that parameter.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_length_both", with_length_both1, gsi::arg ("length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of both of their edges\n"
"Filters the edge pairs in the edge pair collection by length of both of their edges. If \"inverse\" is false, only "
"edge pairs where both edges have the given length are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_length_both", with_length_both2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of both of their edges\n"
"Filters the edge pairs in the edge pair collection by length of both of their edges. If \"inverse\" is false, only "
"edge pairs with both edges having a length between min_length and max_length (excluding max_length itself) are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"If you don't want to specify a lower or upper limit, pass nil to that parameter.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_distance", with_distance1, gsi::arg ("distance"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by the distance of the edges\n"
"Filters the edge pairs in the edge pair collection by distance of the edges. If \"inverse\" is false, only "
"edge pairs where both edges have the given distance are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"Distance is measured as the shortest distance between any of the points on the edges.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_distance", with_distance2, gsi::arg ("min_distance"), gsi::arg ("max_distance"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by the distance of the edges\n"
"Filters the edge pairs in the edge pair collection by distance of the edges. If \"inverse\" is false, only "
"edge pairs where both edges have a distance between min_distance and max_distance (max_distance itself is excluded). If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"Distance is measured as the shortest distance between any of the points on the edges.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with at least one edge having the given angle to the x-axis are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This will filter edge pairs with at least one horizontal edge:\n"
"\n"
"@code\n"
"horizontal = edge_pairs.with_orientation(0, false)\n"
"@/code\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),
"@brief Filter the edge pairs by orientation of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with at least one edge having an angle between min_angle and max_angle are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the "
"minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of both of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with both edges having the given angle to the x-axis are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This will filter edge pairs with at least one horizontal edge:\n"
"\n"
"@code\n"
"horizontal = edge_pairs.with_orientation(0, false)\n"
"@/code\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle_both", with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),
"@brief Filter the edge pairs by orientation of both of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with both edges having an angle between min_angle and max_angle are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the "
"minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("polygons", &polygons1, method_ext ("polygons", &polygons1,
"@brief Converts the edge pairs to polygons\n" "@brief Converts the edge pairs to polygons\n"
"This method creates polygons from the edge pairs. Each polygon will be a triangle or quadrangle " "This method creates polygons from the edge pairs. Each polygon will be a triangle or quadrangle "

View File

@ -575,10 +575,10 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"edges which have the given angle to the x-axis are returned. If \"inverse\" is true, " "edges which have the given angle to the x-axis are returned. If \"inverse\" is true, "
"edges not having the given angle are returned.\n" "edges not having the given angle are returned.\n"
"\n" "\n"
"This will filter horizontal edges:\n" "This will select horizontal edges:\n"
"\n" "\n"
"@code\n" "@code\n"
"horizontal = edges.with_orientation(0, true)\n" "horizontal = edges.with_orientation(0, false)\n"
"@/code\n" "@/code\n"
) + ) +
method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),

View File

@ -394,6 +394,18 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n" "\n"
"If errors occur, the device extractor will contain theses errors.\n" "If errors occur, the device extractor will contain theses errors.\n"
) + ) +
gsi::method ("reset_extracted", &db::LayoutToNetlist::reset_extracted,
"@brief Resets the extracted netlist and enables re-extraction\n"
"This method is implicitly called when using \\connect or \\connect_global after a netlist has been extracted.\n"
"This enables incremental connect with re-extraction.\n"
"\n"
"This method has been introduced in version 0.27.1.\n"
) +
gsi::method ("is_extracted?", &db::LayoutToNetlist::is_netlist_extracted,
"@brief Gets a value indicating whether the netlist has been extracted\n"
"\n"
"This method has been introduced in version 0.27.1.\n"
) +
gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"), gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"),
"@brief Defines an intra-layer connection for the given layer.\n" "@brief Defines an intra-layer connection for the given layer.\n"
"The layer is either an original layer created with \\make_includelayer and it's variants or\n" "The layer is either an original layer created with \\make_includelayer and it's variants or\n"

View File

@ -843,15 +843,6 @@ public:
} }
} }
virtual bool equal (const db::Device &a, const db::Device &b) const
{
if (cb_equal.can_issue ()) {
return cb_equal.issue<db::EqualDeviceParameters, bool, const db::Device &, const db::Device &> (&db::EqualDeviceParameters::equal, a, b);
} else {
return db::EqualDeviceParameters::equal (a, b);
}
}
gsi::Callback cb_less, cb_equal; gsi::Callback cb_less, cb_equal;
}; };
@ -903,13 +894,12 @@ Class<db::EqualDeviceParameters> decl_dbEqualDeviceParameters ("db", "EqualDevic
); );
Class<GenericDeviceParameterCompare> decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare", Class<GenericDeviceParameterCompare> decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare",
gsi::callback ("equal", &GenericDeviceParameterCompare::equal, &GenericDeviceParameterCompare::cb_equal, gsi::arg ("device_a"), gsi::arg ("device_b"),
"@brief Compares the parameters of two devices for equality. "
"Returns true, if the parameters of device a and b are considered equal."
) +
gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"), gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"),
"@brief Compares the parameters of two devices for a begin less than b. " "@brief Compares the parameters of two devices for a begin less than b. "
"Returns true, if the parameters of device a are considered less than those of device b." "Returns true, if the parameters of device a are considered less than those of device b."
"The 'less' implementation needs to ensure strict weak ordering. Specifically, less(a,b) == false and less(b,a) implies that a is equal to b and "
"less(a,b) == true implies that less(b,a) is false and vice versa. If not, an internal error "
"will be encountered on netlist compare."
), ),
"@brief A class implementing the comparison of device parameters.\n" "@brief A class implementing the comparison of device parameters.\n"
"Reimplement this class to provide a custom device parameter compare scheme.\n" "Reimplement this class to provide a custom device parameter compare scheme.\n"
@ -919,7 +909,7 @@ Class<GenericDeviceParameterCompare> decl_GenericDeviceParameterCompare (decl_db
"This class is intended for special cases. In most scenarios it is easier to use \\EqualDeviceParameters instead of " "This class is intended for special cases. In most scenarios it is easier to use \\EqualDeviceParameters instead of "
"implementing a custom comparer class.\n" "implementing a custom comparer class.\n"
"\n" "\n"
"This class has been added in version 0.26." "This class has been added in version 0.26. The 'equal' method has been dropped in 0.27.1 as it can be expressed as !less(a,b) && !less(b,a)."
); );
static tl::id_type id_of_device_class (const db::DeviceClass *cls) static tl::id_type id_of_device_class (const db::DeviceClass *cls)

View File

@ -123,6 +123,11 @@ static db::Region corners_to_boxes (const db::Region *r, double angle_start, dou
return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim)); return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim));
} }
static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end)
{
return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end));
}
static db::Region *new_si (const db::RecursiveShapeIterator &si) static db::Region *new_si (const db::RecursiveShapeIterator &si)
{ {
return new db::Region (si); return new db::Region (si);
@ -1373,14 +1378,22 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n" "\n"
"A similar function that reports corners as point-like edges is \\corners_dots.\n" "A similar function that reports corners as point-like edges is \\corners_dots.\n"
"\n" "\n"
"This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
) + ) +
method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n" "\n"
"This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n" "This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n"
"\n" "\n"
"This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
) +
method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n"
"This method is similar to \\corners, but delivers an \\EdgePairs collection with an edge pairs for each corner.\n"
"The first edge is the incoming edge of the corner, the second one the outgoing edge.\n"
"\n"
"This method has been introduced in version 0.27.1.\n"
) + ) +
method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge, method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge,
"@brief Merge the region\n" "@brief Merge the region\n"
@ -2589,7 +2602,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"@param max_projection The upper limit of the projected length of one edge onto another\n" "@param max_projection The upper limit of the projected length of one edge onto another\n"
"@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n"
"@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n"
"@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param negative Negative output from the first input\n"
"\n" "\n"
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
"edges which contribute in the width check.\n" "edges which contribute in the width check.\n"
@ -2617,9 +2630,15 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. "
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n"
"\n" "\n"
"If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the distance is "
"larger or equal to the limit. This is a way to flag the parts of the first input where the distance to the second "
"input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair "
"contains one edge from the original input and the reverse version of the edge as the second edge.\n"
"\n"
"Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n"
"\n" "\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. "
"The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n"
) + ) +
method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false), method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false),
"@brief Performs an overlap check with options\n" "@brief Performs an overlap check with options\n"
@ -2632,7 +2651,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"@param max_projection The upper limit of the projected length of one edge onto another\n" "@param max_projection The upper limit of the projected length of one edge onto another\n"
"@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n"
"@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n"
"@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param negative Negative output from the first input\n"
"\n" "\n"
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
"edges which contribute in the width check.\n" "edges which contribute in the width check.\n"
@ -2660,9 +2679,15 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. "
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n"
"\n" "\n"
"If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the overlap is "
"larger or equal to the limit. This is a way to flag the parts of the first input where the overlap vs. the second "
"input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair "
"contains one edge from the original input and the reverse version of the edge as the second edge.\n"
"\n"
"Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n"
"\n" "\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. "
"The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n"
) + ) +
method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false), method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false),
"@brief Performs an enclosing check with options\n" "@brief Performs an enclosing check with options\n"
@ -2675,7 +2700,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"@param max_projection The upper limit of the projected length of one edge onto another\n" "@param max_projection The upper limit of the projected length of one edge onto another\n"
"@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n"
"@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n"
"@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param negative Negative output from the first input\n"
"\n" "\n"
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
"edges which contribute in the width check.\n" "edges which contribute in the width check.\n"
@ -2703,9 +2728,15 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. "
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n"
"\n" "\n"
"If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the enclosure is "
"larger or equal to the limit. This is a way to flag the parts of the first input where the enclosure vs. the second "
"input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair "
"contains one edge from the original input and the reverse version of the edge as the second edge.\n"
"\n"
"Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n"
"\n" "\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. "
"The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n"
) + ) +
method_ext ("separation_check", &separation2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false), method_ext ("separation_check", &separation2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false),
"@brief Performs a separation check with options\n" "@brief Performs a separation check with options\n"
@ -2718,7 +2749,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"@param max_projection The upper limit of the projected length of one edge onto another\n" "@param max_projection The upper limit of the projected length of one edge onto another\n"
"@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n"
"@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n"
"@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" "@param negative Negative output from the first input\n"
"\n" "\n"
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
"edges which contribute in the width check.\n" "edges which contribute in the width check.\n"
@ -2746,9 +2777,15 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. "
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n"
"\n" "\n"
"If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the separation is "
"larger or equal to the limit. This is a way to flag the parts of the first input where the distance to the second "
"input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair "
"contains one edge from the original input and the reverse version of the edge as the second edge.\n"
"\n"
"Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n"
"\n" "\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. "
"The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n"
) + ) +
method_ext ("area", &area1, method_ext ("area", &area1,
"@brief The area of the region\n" "@brief The area of the region\n"

View File

@ -202,3 +202,17 @@ TEST(3_symmetric)
eh.insert (db::EdgePair (e2, e1, true)); eh.insert (db::EdgePair (e2, e1, true));
EXPECT_EQ (int (eh.size ()), 1); EXPECT_EQ (int (eh.size ()), 1);
} }
TEST(4_distance)
{
db::Edge e1 (db::Point (0, 0), db::Point (0, 100));
db::Edge e2 (db::Point (200, 100), db::Point (200, 0));
db::Edge e3 (db::Point (0, 0), db::Point (100, 0));
db::Edge e4 (db::Point (200, 0), db::Point (300, 0));
db::Edge e5 (db::Point (200, 100), db::Point (300, 100));
EXPECT_EQ (db::EdgePair (e1, e1).distance (), 0);
EXPECT_EQ (db::EdgePair (e1, e2).distance (), 200);
EXPECT_EQ (db::EdgePair (e3, e2).distance (), 100);
EXPECT_EQ (db::EdgePair (e3, e5).distance (), 141);
}

View File

@ -106,6 +106,17 @@ TEST(2)
EXPECT_EQ (db::Edge (10,20,110,222).distance_abs (db::Point (100, 200)), db::Edge::distance_type (1)); EXPECT_EQ (db::Edge (10,20,110,222).distance_abs (db::Point (100, 200)), db::Edge::distance_type (1));
EXPECT_EQ (db::Edge (10,20,110,222).contains (db::Point (0, 0)), false); EXPECT_EQ (db::Edge (10,20,110,222).contains (db::Point (0, 0)), false);
EXPECT_EQ (db::Edge (10,20,110,222).contains (db::Point (100, 200)), false); EXPECT_EQ (db::Edge (10,20,110,222).contains (db::Point (100, 200)), false);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (100, 120)), 100);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (100, -80)), 100);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (-90, 120)), 141);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (-90, -80)), 141);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (210, 120)), 141);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (210, -80)), 141);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (-90, 20)), 100);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (10, 20)), 0);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (50, 20)), 0);
EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (110, 20)), 0);
} }
TEST(3) TEST(3)

View File

@ -787,14 +787,14 @@ CODE
# The "corners" method is available as a plain function or as a method on \DRC# expressions. # The "corners" method is available as a plain function or as a method on \DRC# expressions.
# The plain function is equivalent to "primary.corners". # The plain function is equivalent to "primary.corners".
def corners(as_dots = DRCAsDots::new(false)) def corners(output_mode = DRCOutputMode::new(:dots))
@engine._context("corners") do @engine._context("corners") do
if as_dots.is_a?(DRCAsDots) if output_mode.is_a?(DRCOutputMode)
as_dots = as_dots.value output_mode = output_mode.value
else else
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method") raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
end end
DRCOpNodeCornersFilter::new(@engine, as_dots, self) DRCOpNodeCornersFilter::new(@engine, output_mode, self)
end end
end end
@ -859,8 +859,8 @@ CODE
args.each_with_index do |a,ia| args.each_with_index do |a,ia|
if a.is_a?(1.0.class) && :#{f} != :middle if a.is_a?(1.0.class) && :#{f} != :middle
f << a f << a
elsif a.is_a?(DRCAsDots) elsif a.is_a?(DRCOutputMode)
as_edges = a.value as_edges = (a.value == :edges || a.value == :dots)
elsif @@std_refs[a] && :#{f} != :middle elsif @@std_refs[a] && :#{f} != :middle
f = @@std_refs[a] f = @@std_refs[a]
else else
@ -1996,11 +1996,11 @@ end
class DRCOpNodeCornersFilter < DRCOpNodeWithCompare class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
attr_accessor :input attr_accessor :input
attr_accessor :as_dots attr_accessor :output_mode
def initialize(engine, as_dots, input) def initialize(engine, output_mode, input)
super(engine) super(engine)
self.as_dots = as_dots self.output_mode = output_mode
self.input = input self.input = input
self.description = "corners" self.description = "corners"
end end
@ -2011,8 +2011,10 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
args << (self.gt ? false : true) args << (self.gt ? false : true)
args << (self.lt ? self.lt : (self.le ? self.le : 180.0)) args << (self.lt ? self.lt : (self.le ? self.le : 180.0))
args << (self.lt ? false : true) args << (self.lt ? false : true)
if self.as_dots if self.output_mode == :dots || self.output_mode == :edges
RBA::CompoundRegionOperationNode::new_corners_as_dots(*args) RBA::CompoundRegionOperationNode::new_corners_as_dots(*args)
elsif self.output_mode == :edge_pairs
RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args)
else else
args << 2 # dimension is 2x2 DBU args << 2 # dimension is 2x2 DBU
RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args) RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args)

View File

@ -781,7 +781,7 @@ CODE
# %DRC% # %DRC%
# @name corners # @name corners
# @brief Selects all polygons which are rectilinear # @brief Selects corners of polygons
# @synopsis corners([ options ]) (in condition) # @synopsis corners([ options ]) (in condition)
# @synopsis corners(layer [, options ]) # @synopsis corners(layer [, options ])
# #
@ -791,15 +791,16 @@ CODE
# \DRC# expressions (see \Layer#drc and \DRC#corners for more details). # \DRC# expressions (see \Layer#drc and \DRC#corners for more details).
# #
# Like the layer-based version, the "corners" operator accepts the # Like the layer-based version, the "corners" operator accepts the
# output type option: "as_dots" for dot-like edges and "as_boxes" for # output type option: "as_dots" for dot-like edges, "as_boxes" for
# small (2x2 DBU) box markers. # small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs.
# The default output type is "as_boxes".
# #
# The "corners" operator can be put into a condition which means it's # The "corners" operator can be put into a condition which means it's
# applied to corners meeting a particular angle constraint. # applied to corners meeting a particular angle constraint.
def _cop_corners(as_dots = DRCAsDots::new(false)) def _cop_corners(output_mode = DRCOutputMode::new(:boxes))
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
return primary.corners(as_dots) return primary.corners(output_mode)
end end
# %DRC% # %DRC%
@ -1024,7 +1025,7 @@ CODE
# #
# With a lower and upper limit, the results are edges marking the positions on the # With a lower and upper limit, the results are edges marking the positions on the
# primary shape where the condition is met. # primary shape where the condition is met.
# With a lower limit alone, these markers are formed by two, identical but opposite edges attached to # With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
# the primary shape. Without an upper limit only, the first edge of the marker is attached to the # the primary shape. Without an upper limit only, the first edge of the marker is attached to the
# primary shape while the second edge is attached to the shape of the "other" layer. # primary shape while the second edge is attached to the shape of the "other" layer.
# #
@ -1034,6 +1035,12 @@ CODE
# @td @img(/images/drc_enc2u.png) @/td # @td @img(/images/drc_enc2u.png) @/td
# @/tr # @/tr
# @/table # @/table
#
# When "larger than" constraints are used, this function will produce the edges from the
# first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
# the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
# actual edges from the first input (see \separation for an example).
#
# %DRC% # %DRC%
# @name separation # @name separation
@ -1048,7 +1055,7 @@ CODE
# as metrics, projection and angle constraints etc. This check also features # as metrics, projection and angle constraints etc. This check also features
# opposite and rectangle filtering. See \Layer#separation for details about opposite and # opposite and rectangle filtering. See \Layer#separation for details about opposite and
# rectangle error filtering. # rectangle error filtering.
# #
# @h3 Classic mode @/h3 # @h3 Classic mode @/h3
# #
# Like \enclosing, this function is available as a classic DRC function with a layer as the first # Like \enclosing, this function is available as a classic DRC function with a layer as the first
@ -1081,6 +1088,23 @@ CODE
# @td @img(/images/drc_separation1u.png) @/td # @td @img(/images/drc_separation1u.png) @/td
# @/tr # @/tr
# @/table # @/table
#
# When "larger than" constraints are used, this function will produce the edges from the
# first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
# the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
# actual edges from the first input:
#
# @code
# l1_edges_without_l2 = l1.drc((separation(l2) >= 1.0).first_edges)
# @/code
#
# The following image shows the effect of such a negative-output separation check:
#
# @table
# @tr
# @td @img(/images/drc_separation1un.png) @/td
# @/tr
# @/table
# %DRC% # %DRC%
# @name overlap # @name overlap
@ -1124,6 +1148,12 @@ CODE
# @td @img(/images/drc_overlap2u.png) @/td # @td @img(/images/drc_overlap2u.png) @/td
# @/tr # @/tr
# @/table # @/table
#
# When "larger than" constraints are used, this function will produce the edges from the
# first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
# the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
# actual edges from the first input (see \separation for an example).
#
# %DRC% # %DRC%
# @name width # @name width
@ -1180,7 +1210,7 @@ CODE
# #
# With a lower and upper limit or with the "equal" condition, the results are edges marking the positions on the # With a lower and upper limit or with the "equal" condition, the results are edges marking the positions on the
# primary shape where the condition is met. # primary shape where the condition is met.
# With a lower limit alone, these markers are formed by two, identical but opposite edges attached to # With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
# the primary shape. Without an upper limit only, both edges are attached to different sides of the primary # the primary shape. Without an upper limit only, both edges are attached to different sides of the primary
# shape. # shape.
# #

View File

@ -64,6 +64,10 @@ module DRC
end end
def both
DRCBothEdges::new
end
def shift(x, y) def shift(x, y)
self._context("shift") do self._context("shift") do
RBA::DCplxTrans::new(RBA::DVector::new(_make_value(x) * self.dbu, _make_value(y) * self.dbu)) RBA::DCplxTrans::new(RBA::DVector::new(_make_value(x) * self.dbu, _make_value(y) * self.dbu))
@ -98,6 +102,14 @@ module DRC
DRCJoinFlag::new(true) DRCJoinFlag::new(true)
end end
def padding_zero
DRCDensityPadding::new(:zero)
end
def padding_ignore
DRCDensityPadding::new(:ignore)
end
def diamond_limit def diamond_limit
DRCSizingMode::new(0) DRCSizingMode::new(0)
end end
@ -214,15 +226,19 @@ module DRC
end end
def as_dots def as_dots
DRCAsDots::new(true) DRCOutputMode::new(:dots)
end end
def as_edges def as_edges
DRCAsDots::new(true) DRCOutputMode::new(:edges)
end end
def as_boxes def as_boxes
DRCAsDots::new(false) DRCOutputMode::new(:boxes)
end
def as_edge_pairs
DRCOutputMode::new(:edge_pairs)
end end
def area_only(r) def area_only(r)
@ -1793,6 +1809,7 @@ CODE
with_bbox_min with_bbox_min
with_bbox_width with_bbox_width
with_length with_length
with_distance
without_angle without_angle
without_area without_area
without_area_ratio without_area_ratio

View File

@ -276,16 +276,37 @@ module DRC
end end
# %DRC% # %DRC%
# @name size # @name count
# @brief Returns the number of objects on the layer # @brief Returns the number of objects on the layer
# @synopsis layer.size # @synopsis layer.count
# #
# The number of objects is the number of raw objects, not merged # The count is the number of raw objects, not merged
# regions or edges. It is more efficent to call this method on output layers than # regions or edges. This is the flat count - the number of polygons,
# on input layers. # edges or edge pairs seen from the top cell.
# "count" can be computationally expensive for original layers with
# clip regions or cell tree filters.
#
# See \hier_count for a hierarchical (each cell counts once) count.
def size def count
self.data.size self.data.count
end
# %DRC%
# @name hier_count
# @brief Returns the hierarchical number of objects on the layer
# @synopsis layer.hier_count
#
# The hier_count is the number of raw objects, not merged
# regions or edges, with each cell counting once.
# A high \count to hier_count (flat to hierarchical) ratio is an indication
# of a good hierarchical compression.
# "hier_count" applies only to original layers without clip regions or
# cell filters and to layers in \deep mode. Otherwise, hier_count gives
# the same value than \count.
def hier_count
self.data.hier_count
end end
# %DRC% # %DRC%
@ -678,13 +699,28 @@ CODE
# @synopsis layer.with_length(min .. max) # @synopsis layer.with_length(min .. max)
# @synopsis layer.with_length(value) # @synopsis layer.with_length(value)
# @synopsis layer.with_length(min, max) # @synopsis layer.with_length(min, max)
# The method selects edges by their length. The first version selected # @synopsis edge_pairlayer.with_length(min, max [, both])
# The method selects edges by their length. The first version selects
# edges with a length larger or equal to min and less than max (but not equal). # edges with a length larger or equal to min and less than max (but not equal).
# The second version selects edges with exactly the given length. The third # The second version selects edges with exactly the given length. The third
# version is similar to the first one, but allows specification of nil for min or # version is similar to the first one, but allows specification of nil for min or
# max indicating that there is no lower or upper limit. # max indicating that there is no lower or upper limit.
# #
# This method is available for edge layers only. # This method is available for edge and edge pair layers.
#
# When called on an edge pair layer, this method will select edge pairs if one
# or both of the edges meet the length criterion. Use the additional argument
# and pass "both" (plain word) to specify that both edges need to be within the given interval.
# By default, it's sufficient for one edge to meet the criterion.
#
# Here are examples for "with_length" on edge pair layers:
#
# @code
# # at least one edge needs to have a length of 1.0 <= l < 2.0
# ep1 = edge_pairs.with_length(1.um .. 2.um)
# # both edges need to have a length of exactly 2 um
# ep2 = edge_pairs.with_length(2.um, both)
# @/code
# %DRC% # %DRC%
# @name without_length # @name without_length
@ -692,11 +728,21 @@ CODE
# @synopsis layer.without_length(min .. max) # @synopsis layer.without_length(min .. max)
# @synopsis layer.without_length(value) # @synopsis layer.without_length(value)
# @synopsis layer.without_length(min, max) # @synopsis layer.without_length(min, max)
# @synopsis edge_pairlayer.with_length(min, max [, both])
# The method basically is the inverse of \with_length. It selects all edges # The method basically is the inverse of \with_length. It selects all edges
# of the edge layer which do not have the given length (second form) or are # of the edge layer which do not have the given length (second form) or are
# not inside the given interval (first and third form). # not inside the given interval (first and third form).
# #
# This method is available for edge layers only. # This method is available for edge and edge pair layers.
#
# A note on the "both" modifier (without_length called on edge pairs): "both" means that
# both edges need to be "without_length". For example
#
# @code
# # both edges are not exactly 1 um in length, or:
# # the edge pair is skipped if one edge has a length of exactly 1 um
# ep = edge_pairs.without_length(1.um, both)
# @/code
%w(length).each do |f| %w(length).each do |f|
[true, false].each do |inv| [true, false].each do |inv|
@ -706,18 +752,96 @@ CODE
@engine._context("#{mn}") do @engine._context("#{mn}") do
requires_edges requires_edges_or_edge_pairs
result_class = self.data.class
f = :with_#{f}
args = args.select do |a|
if a.is_a?(DRCBothEdges)
if !self.data.is_a?(RBA::EdgePairs)
raise("'both' keyword only available for edge pair layers")
end
f = :with_#{f}_both
false
else
true
end
end
if args.size == 1 if args.size == 1
a = args[0] a = args[0]
if a.is_a?(Range) if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect})) DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect}))
else else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value(a), #{inv.inspect})) DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value(a), #{inv.inspect}))
end end
elsif args.size == 2 elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect})) DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect}))
else else
raise("Invalid number of arguments (1 or 2 expected)") raise("Invalid number of range arguments (1 or 2 expected)")
end
end
end
CODE
end
end
# %DRC%
# @name with_distance
# @brief Selects edge pairs by the distance of the edges
# @synopsis layer.with_distance(min .. max)
# @synopsis layer.with_distance(value)
# @synopsis layer.with_distance(min, max)
# The method selects edge pairs by the distance of their edges. The first version selects
# edge pairs with a distance larger or equal to min and less than max (but not equal).
# The second version selects edge pairs with exactly the given distance. The third
# version is similar to the first one, but allows specification of nil for min or
# max indicating that there is no lower or upper limit.
#
# The distance of the edges is defined by the minimum distance of all points from the
# edges involved. For edge pairs generated in geometrical checks this is equivalent
# to the actual distance of the original edges.
#
# This method is available for edge pair layers only.
# %DRC%
# @name without_distance
# @brief Selects edge pairs by the distance of the edges
# @synopsis layer.without_distance(min .. max)
# @synopsis layer.without_distance(value)
# @synopsis layer.without_distance(min, max)
# The method basically is the inverse of \with_distance. It selects all edge pairs
# of the edge pair layer which do not have the given distance (second form) or are
# not inside the given interval (first and third form).
#
# This method is available for edge pair layers only.
%w(distance).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
requires_edge_pairs
result_class = RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect}))
else
raise("Invalid number of range arguments (1 or 2 expected)")
end end
end end
@ -733,6 +857,7 @@ CODE
# @synopsis layer.with_angle(min .. max) # @synopsis layer.with_angle(min .. max)
# @synopsis layer.with_angle(value) # @synopsis layer.with_angle(value)
# @synopsis layer.with_angle(min, max) # @synopsis layer.with_angle(min, max)
# @synopsis edge_pairlayer.with_angle(min, max [, both])
# #
# When called on an edge layer, the method selects edges by their angle, # When called on an edge layer, the method selects edges by their angle,
# measured against the horizontal axis in the mathematical sense. # measured against the horizontal axis in the mathematical sense.
@ -750,6 +875,20 @@ CODE
# given angle or is within the given angle interval. The angle is measured between the edges forming the corner. # given angle or is within the given angle interval. The angle is measured between the edges forming the corner.
# For each corner, an edge pair containing the edges forming in the angle is returned. # For each corner, an edge pair containing the edges forming in the angle is returned.
# #
# When called on an edge pair layer, this method selects edge pairs with one or both edges
# meeting the angle criterion. In this case an additional argument is accepted which can be
# either "both" (plain word) to indicate that both edges have to be within the given interval.
# Without this argument, it is sufficient for one edge to meet the criterion.
#
# Here are examples for "with_angle" on edge pair layers:
#
# @code
# # at least one edge needs to be horizontal
# ep1 = edge_pairs.with_angle(0)
# # both edges need to vertical
# ep2 = edge_pairs.with_angle(90, both)
# @/code
#
# A method delivering all objects not matching the angle criterion is \without_angle. # A method delivering all objects not matching the angle criterion is \without_angle.
# #
# The following images demonstrate some use cases of \with_angle and \without_angle: # The following images demonstrate some use cases of \with_angle and \without_angle:
@ -771,37 +910,62 @@ CODE
# @synopsis layer.without_angle(min .. max) # @synopsis layer.without_angle(min .. max)
# @synopsis layer.without_angle(value) # @synopsis layer.without_angle(value)
# @synopsis layer.without_angle(min, max) # @synopsis layer.without_angle(min, max)
# @synopsis edge_pairlayer.without_angle(min, max [, both])
# #
# The method basically is the inverse of \with_angle. It selects all edges # The method basically is the inverse of \with_angle. It selects all edges
# of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle # of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle
# is not inside the given interval (first and third form). # is not inside the given interval (first and third form).
#
# A note on the "both" modifier (without_angle called on edge pairs): "both" means that
# both edges need to be "without_angle". For example
#
# @code
# # both edges are not horizontal or:
# # the edge pair is skipped if one edge is horizontal
# ep = edge_pairs.without_angle(0, both)
# @/code
#
[true, false].each do |inv| %w(angle).each do |f|
mn = (inv ? "without" : "with") + "_angle" [true, false].each do |inv|
eval <<"CODE" mn = (inv ? "without" : "with") + "_" + f
def #{mn}(*args) eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do @engine._context("#{mn}") do
requires_edges_or_region f = :with_#{f}
result_class = self.data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges args = args.select do |a|
if args.size == 1 if a.is_a?(DRCBothEdges)
a = args[0] if !self.data.is_a?(RBA::EdgePairs)
if a.is_a?(Range) raise("'both' keyword only available for edge pair layers")
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a.begin, a.end, #{inv.inspect})) end
else f = :with_#{f}_both
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a, #{inv.inspect})) false
else
true
end
end end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect})) result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
else if args.size == 1
raise("Invalid number of arguments (1 or 2 expected)") a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, args[0], args[1], #{inv.inspect}))
else
raise("Invalid number of range arguments (1 or 2 expected)")
end
end end
end end
end
CODE CODE
end
end end
# %DRC% # %DRC%
@ -954,8 +1118,8 @@ CODE
elsif a.is_a?(DRCPattern) elsif a.is_a?(DRCPattern)
as_pattern = a.as_pattern as_pattern = a.as_pattern
pattern = a.pattern pattern = a.pattern
elsif a.is_a?(DRCAsDots) elsif a.is_a?(DRCOutputMode)
as_dots = a.value as_dots = (a.value == :edges || a.value == :dots)
else else
raise("Invalid argument type #{a.inspect}") raise("Invalid argument type #{a.inspect}")
end end
@ -1003,6 +1167,8 @@ CODE
# @ul # @ul
# @li @b as_boxes @/b: with this option, small boxes will be produced as markers @/li # @li @b as_boxes @/b: with this option, small boxes will be produced as markers @/li
# @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li # @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li
# @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge
# is the incoming edge to the corner, the second edge the outgoing edge. @/li
# @/ul # @/ul
# #
# The following images show the effect of this method: # The following images show the effect of this method:
@ -1021,7 +1187,7 @@ CODE
requires_region requires_region
as_dots = false output_mode = :boxes
amin = -180.0 amin = -180.0
amax = 180.0 amax = 180.0
@ -1035,14 +1201,23 @@ CODE
elsif a.is_a?(1.0.class) || a.is_a?(1.class) elsif a.is_a?(1.0.class) || a.is_a?(1.class)
amin = a.to_f amin = a.to_f
amax = a.to_f amax = a.to_f
elsif a.is_a?(DRCAsDots) elsif a.is_a?(DRCOutputMode)
as_dots = a.value output_mode = a.value
else else
raise("Invalid argument #{a.inspect}") raise("Invalid argument #{a.inspect}")
end end
end end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax)) f = :corners
cls = RBA::Region
if output_mode == :edges || output_mode == :dots
f = :corners_dots
cls = RBA::Edges
elsif output_mode == :edge_pairs
f = :corners_edge_pairs
cls = RBA::EdgePairs
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax))
end end
@ -1211,8 +1386,8 @@ CODE
args.each do |a| args.each do |a|
if a.is_a?(1.0.class) && :#{f} != :middle if a.is_a?(1.0.class) && :#{f} != :middle
f << a f << a
elsif a.is_a?(DRCAsDots) elsif a.is_a?(DRCOutputMode)
as_edges = a.value as_edges = (a.value == :edges || a.value == :dots)
elsif @@std_refs[a] && :#{f} != :middle elsif @@std_refs[a] && :#{f} != :middle
f = @@std_refs[a] f = @@std_refs[a]
else else
@ -1335,7 +1510,7 @@ CODE
# %DRC% # %DRC%
# @name collect_to_region # @name collect_to_region
# @brief Transforms a layer into polygon objects # @brief Transforms a layer into polygon objects
# @synopsis layer.collect { |object| ... } # @synopsis layer.collect_to_region { |object| ... }
# This method is similar to \collect, but creates a polygon layer. It expects the block to # This method is similar to \collect, but creates a polygon layer. It expects the block to
# deliver objects that can be converted into polygons. Such objects are of class RBA::DPolygon, # deliver objects that can be converted into polygons. Such objects are of class RBA::DPolygon,
# RBA::DBox, RBA::DPath, RBA::Polygon, RBA::Path, RBA::Box and RBA::Region. # RBA::DBox, RBA::DPath, RBA::Polygon, RBA::Path, RBA::Box and RBA::Region.
@ -1343,7 +1518,7 @@ CODE
# %DRC% # %DRC%
# @name collect_to_edges # @name collect_to_edges
# @brief Transforms a layer into edge objects # @brief Transforms a layer into edge objects
# @synopsis layer.collect { |object| ... } # @synopsis layer.collect_to_edges { |object| ... }
# This method is similar to \collect, but creates an edge layer. It expects the block to # This method is similar to \collect, but creates an edge layer. It expects the block to
# deliver objects that can be converted into edges. If polygon-like objects are returned, their # deliver objects that can be converted into edges. If polygon-like objects are returned, their
# contours will be turned into edge sequences. # contours will be turned into edge sequences.
@ -1351,7 +1526,7 @@ CODE
# %DRC% # %DRC%
# @name collect_to_edge_pairs # @name collect_to_edge_pairs
# @brief Transforms a layer into edge pair objects # @brief Transforms a layer into edge pair objects
# @synopsis layer.collect { |object| ... } # @synopsis layer.collect_to_edge_pairs { |object| ... }
# This method is similar to \collect, but creates an edge pair layer. It expects the block to # This method is similar to \collect, but creates an edge pair layer. It expects the block to
# deliver RBA::EdgePair, RBA::DEdgePair or RBA::EdgePairs objects. # deliver RBA::EdgePair, RBA::DEdgePair or RBA::EdgePairs objects.
@ -1613,6 +1788,9 @@ CODE
# @td @img(/images/drc_or2.png) @/td # @td @img(/images/drc_or2.png) @/td
# @/tr # @/tr
# @/table # @/table
#
# In deep mode, "or" or "|" does not imply merging. In deep mode,
# "or" is an alias for "+" ("add").
def or(other) def or(other)
@engine._context("or") do @engine._context("or") do
@ -3344,15 +3522,15 @@ CODE
# %DRC% # %DRC%
# @name isolated # @name isolated
# @brief An isolation check # @brief An inter-polygon isolation check
# @synopsis layer.isolated(value [, options]) # @synopsis layer.isolated(value [, options])
# @synopsis layer.iso(value [, options]) # @synopsis layer.iso(value [, options])
# #
# @b Note: @/b "isolated" and "iso" are available as operators for the "universal DRC" function \Layer#drc within # @b Note: @/b "isolated" and "iso" are available as operators for the "universal DRC" function \Layer#drc within
# the \DRC framework. These variants have more options and are more intuitive to use. See \global#isolated for more details. # the \DRC framework. These variants have more options and are more intuitive to use. See \global#isolated for more details.
# #
# See \space for a description of this method. # See \space for a description of this method. "isolated" is the space check variant which checks different polygons only.
# In contrast to \space, this # In contrast to \space, the "isolated"
# method is available for polygon layers only, since only on such layers # method is available for polygon layers only, since only on such layers
# different polygons can be identified. # different polygons can be identified.
# #
@ -3368,14 +3546,15 @@ CODE
# %DRC% # %DRC%
# @name notch # @name notch
# @brief An intra-region spacing check # @brief An intra-polygon spacing check
# @synopsis layer.notch(value [, options]) # @synopsis layer.notch(value [, options])
# #
# @b Note: @/b "notch" is available as an operator for the "universal DRC" function \Layer#drc within # @b Note: @/b "notch" is available as an operator for the "universal DRC" function \Layer#drc within
# the \DRC framework. This variant has more options and is more intuitive to use. See \global#notch for more details. # the \DRC framework. This variant has more options and is more intuitive to use. See \global#notch for more details.
# #
# See \space for a description of this method. # See \space for a description of this method.
# In contrast to \space, this # "notch" is the space check variant which finds space violations within a single polygon, but not against other polygons.
# In contrast to \space, the "notch"
# method is available for polygon layers only, since only on such layers # method is available for polygon layers only, since only on such layers
# different polygons can be identified. Also, opposite and rectangle error # different polygons can be identified. Also, opposite and rectangle error
# filtering is not available for this method. # filtering is not available for this method.
@ -3729,6 +3908,20 @@ CODE
# # (100 and 150 tiles of 20 um each are used in horizontal and vertical direction): # # (100 and 150 tiles of 20 um each are used in horizontal and vertical direction):
# low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150)) # low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150))
# @/code # @/code
#
# The "padding mode" indicates how the area outside the layout's bounding box is considered.
# There are two modes:
#
# @ul
# @li @b padding_zero @/b: the outside area is considered zero density. This is the default mode. @/li
# @li @b padding_ignore @/b: the outside area is ignored for the density computation. @/li
# @/ul
#
# Example:
#
# @code
# low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), padding_ignore)
# @/code
# #
# The complementary version of "with_density" is \without_density. # The complementary version of "with_density" is \without_density.
@ -3752,6 +3945,7 @@ CODE
tile_origin = nil tile_origin = nil
tile_count = nil tile_count = nil
tile_boundary = nil tile_boundary = nil
padding_mode = :zero
n = 1 n = 1
args.each do |a| args.each do |a|
@ -3765,6 +3959,8 @@ CODE
tile_count = a.get tile_count = a.get
elsif a.is_a?(DRCTileBoundary) elsif a.is_a?(DRCTileBoundary)
tile_boundary = a.get tile_boundary = a.get
elsif a.is_a?(DRCDensityPadding)
padding_mode = a.value
elsif a.is_a?(Float) || a.is_a?(1.class) || a == nil elsif a.is_a?(Float) || a.is_a?(1.class) || a == nil
nlimits < 2 || raise("Too many values specified") nlimits < 2 || raise("Too many values specified")
limits[nlimits] = @engine._make_numeric_value_with_nil(a) limits[nlimits] = @engine._make_numeric_value_with_nil(a)
@ -3809,18 +4005,34 @@ CODE
tp.threads = (@engine.threads || 1) tp.threads = (@engine.threads || 1)
if tile_boundary if tile_boundary
tp.input("boundary", tile_boundary.data) tp.input("boundary", tile_boundary.data)
else
tp.input("boundary", RBA::Region::new(self.data.bbox))
end end
tp.var("vmin", limits[0] || 0.0) tp.var("vmin", limits[0] || 0.0)
tp.var("vmax", limits[1] || 1.0) tp.var("vmax", limits[1] || 1.0)
tp.var("inverse", inverse) tp.var("inverse", inverse)
tp.queue(<<"TP_SCRIPT")
_tile && ( if padding_mode == :zero
var bx = _tile.bbox.enlarged(xoverlap, yoverlap); tp.queue(<<"TP_SCRIPT")
var d = to_f(input.area(bx)) / to_f(bx.area); _tile && (
((d > vmin - 1e-10 && d < vmax + 1e-10) != inverse) && _output(res, bx, false) var bx = _tile.bbox.enlarged(xoverlap, yoverlap);
) var d = to_f(input.area(bx)) / to_f(bx.area);
((d > vmin - 1e-10 && d < vmax + 1e-10) != inverse) && _output(res, bx, false)
)
TP_SCRIPT TP_SCRIPT
elsif padding_mode == :ignore
tp.queue(<<"TP_SCRIPT")
_tile && (
var bx = _tile.bbox.enlarged(xoverlap, yoverlap);
var ba = boundary.area(bx);
ba > 0 && (
var d = to_f(input.area(bx)) / to_f(ba);
((d > vmin - 1e-10 && d < vmax + 1e-10) != inverse) && _output(res, bx, false)
)
)
TP_SCRIPT
end
@engine.run_timed("\"#{method}\" in: #{@engine.src_line}", self.data) do @engine.run_timed("\"#{method}\" in: #{@engine.src_line}", self.data) do
tp.execute("Tiled \"#{method}\" in: #{@engine.src_line}") tp.execute("Tiled \"#{method}\" in: #{@engine.src_line}")
@ -4535,13 +4747,17 @@ END
end end
def requires_edge_pairs def requires_edge_pairs
self.data.is_a?(RBA::EdgePairs) || raise("Requires a edge pair layer") self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge pair layer")
end end
def requires_edges def requires_edges
self.data.is_a?(RBA::Edges) || raise("Requires an edge layer") self.data.is_a?(RBA::Edges) || raise("Requires an edge layer")
end end
def requires_edges_or_edge_pairs
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge or edge pair layer")
end
def requires_edges_or_region def requires_edges_or_region
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("Requires an edge or polygon layer") self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("Requires an edge or polygon layer")
end end

View File

@ -64,7 +64,6 @@ module DRC
def initialize(engine) def initialize(engine)
@engine = engine @engine = engine
@netlisted = false
@connect_implicit = [] @connect_implicit = []
@connect_implicit_per_cell = {} @connect_implicit_per_cell = {}
@connect_explicit = [] @connect_explicit = []
@ -240,7 +239,6 @@ module DRC
# See \connect for more details. # See \connect for more details.
def clear_connections def clear_connections
@netlisted = false
@connect_implicit = [] @connect_implicit = []
@connect_implicit_per_cell = {} @connect_implicit_per_cell = {}
@connect_explicit = [] @connect_explicit = []
@ -387,6 +385,25 @@ module DRC
# errors = antenna_check(gate, metal1, 50.0) # errors = antenna_check(gate, metal1, 50.0)
# @/code # @/code
# #
# Usually antenna checks apply to multiple metal layers. In this case,
# the connectivity needs to be extended after the first check to include
# the next metal layers. This can be achieved with incremental connects:
#
# @code
# # provide connections up to metal1
# connect(gate, poly)
# connect(poly, contact)
# connect(contact, metal1)
# metal1_errors = antenna_check(gate, metal1, 50.0)
#
# # now *add* connections up to metal2
# connect(metal1, via1)
# connect(via1, metal2)
# metal2_errors = antenna_check(gate, metal2, 50.0)
#
# ... continue this scheme with further metal layers ...
# @/code
#
# Plasma induced damage can be rectified by including diodes # Plasma induced damage can be rectified by including diodes
# which create a safe current path for discharging the metal # which create a safe current path for discharging the metal
# islands. Such diodes can be identified with a recognition layer # islands. Such diodes can be identified with a recognition layer
@ -544,7 +561,7 @@ module DRC
ensure_data ensure_data
# run extraction in a timed environment # run extraction in a timed environment
if ! @netlisted if ! @l2n.is_extracted?
# configure implicit net connections # configure implicit net connections
@l2n.clear_join_net_names @l2n.clear_join_net_names
@ -569,7 +586,6 @@ module DRC
end end
@engine._cmd(@l2n, :extract_netlist) @engine._cmd(@l2n, :extract_netlist)
@netlisted = true
end end
@ -609,13 +625,13 @@ module DRC
end end
def _l2n_data def _l2n_data
@netlisted && self.l2n_data @l2n && @l2n.is_extracted? && self.l2n_data
end end
private private
def cleanup def cleanup
@netlisted && clear_connections @l2n && @l2n.is_extracted? && clear_connections
end end
def ensure_data def ensure_data
@ -644,14 +660,13 @@ module DRC
def register_layer(data) def register_layer(data)
id = data.data_id id = data.data_id
ensure_data
if @layers && @layers[id] if @layers && @layers[id]
# already registered # already registered
return return
end end
ensure_data
@layers[id] = data @layers[id] = data
@lnum += 1 @lnum += 1

View File

@ -16,6 +16,18 @@ module DRC
end end
end end
# A wrapper for the "both edges" flag for EdgePair#with_length or EdgePair#with_angle
class DRCBothEdges
end
# A wrapper for the padding modes of with_density
class DRCDensityPadding
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the sizing mode value # A wrapper for the sizing mode value
class DRCSizingMode class DRCSizingMode
attr_accessor :value attr_accessor :value
@ -65,7 +77,7 @@ module DRC
# A wrapper for the "as_dots" or "as_boxes" flag for # A wrapper for the "as_dots" or "as_boxes" flag for
# some DRC functions. The purpose of this class # some DRC functions. The purpose of this class
# is to identify the value by the class. # is to identify the value by the class.
class DRCAsDots class DRCOutputMode
attr_accessor :value attr_accessor :value
def initialize(v) def initialize(v)
self.value = v self.value = v

View File

@ -225,6 +225,46 @@ TEST(5_FlatAntenna)
db::compare_layouts (_this, layout, au, db::NoNormalization); db::compare_layouts (_this, layout, au, db::NoNormalization);
} }
TEST(5_FlatAntennaIncremental)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_5i.drc";
std::string input = tl::testdata ();
input += "/drc/antenna_l1.gds";
std::string au = tl::testdata ();
au += "/drc/drcSimpleTests_au5.gds";
std::string output = this->tmp_file ("tmp.gds");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
TEST(6_HierarchicalAntenna) TEST(6_HierarchicalAntenna)
{ {
std::string rs = tl::testdata (); std::string rs = tl::testdata ();
@ -1232,3 +1272,23 @@ TEST(47_fillWithOverlappingBoxesTiled)
{ {
run_test (_this, "47", false); run_test (_this, "47", false);
} }
TEST(48_drcWithFragments)
{
run_test (_this, "48", false);
}
TEST(48d_drcWithFragments)
{
run_test (_this, "48", true);
}
TEST(49_drcWithFragments)
{
run_test (_this, "49", false);
}
TEST(49d_drcWithFragments)
{
run_test (_this, "49", true);
}

View File

@ -342,7 +342,7 @@ See <a href="/about/drc_ref_netter.xml#connect_global">Netter#connect_global</a>
<p> <p>
See <a href="/about/drc_ref_netter.xml#connect_implicit">Netter#connect_implicit</a> for a description of that function. See <a href="/about/drc_ref_netter.xml#connect_implicit">Netter#connect_implicit</a> for a description of that function.
</p> </p>
<a name="corners"/><h2>"corners" - Selects all polygons which are rectilinear</h2> <a name="corners"/><h2>"corners" - Selects corners of polygons</h2>
<keyword name="corners"/> <keyword name="corners"/>
<p>Usage:</p> <p>Usage:</p>
<ul> <ul>
@ -356,8 +356,9 @@ argument, "corners" represents the corner generator/filter in primary shapes for
<a href="/about/drc_ref_drc.xml">DRC</a> expressions (see <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> and <a href="/about/drc_ref_drc.xml#corners">DRC#corners</a> for more details). <a href="/about/drc_ref_drc.xml">DRC</a> expressions (see <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> and <a href="/about/drc_ref_drc.xml#corners">DRC#corners</a> for more details).
</p><p> </p><p>
Like the layer-based version, the "corners" operator accepts the Like the layer-based version, the "corners" operator accepts the
output type option: "as_dots" for dot-like edges and "as_boxes" for output type option: "as_dots" for dot-like edges, "as_boxes" for
small (2x2 DBU) box markers. small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs.
The default output type is "as_boxes".
</p><p> </p><p>
The "corners" operator can be put into a condition which means it's The "corners" operator can be put into a condition which means it's
applied to corners meeting a particular angle constraint. applied to corners meeting a particular angle constraint.
@ -586,7 +587,7 @@ These markers indicate the presence of the specified condition.
</p><p> </p><p>
With a lower and upper limit, the results are edges marking the positions on the With a lower and upper limit, the results are edges marking the positions on the
primary shape where the condition is met. primary shape where the condition is met.
With a lower limit alone, these markers are formed by two, identical but opposite edges attached to With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
the primary shape. Without an upper limit only, the first edge of the marker is attached to the the primary shape. Without an upper limit only, the first edge of the marker is attached to the
primary shape while the second edge is attached to the shape of the "other" layer. primary shape while the second edge is attached to the shape of the "other" layer.
</p><p> </p><p>
@ -596,6 +597,11 @@ primary shape while the second edge is attached to the shape of the "other" laye
<td><img src="/images/drc_enc2u.png"/></td> <td><img src="/images/drc_enc2u.png"/></td>
</tr> </tr>
</table> </table>
</p><p>
When "larger than" constraints are used, this function will produce the edges from the
first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
actual edges from the first input (see <a href="#separation">separation</a> for an example).
</p> </p>
<a name="error"/><h2>"error" - Prints an error</h2> <a name="error"/><h2>"error" - Prints an error</h2>
<keyword name="error"/> <keyword name="error"/>
@ -1216,6 +1222,11 @@ errors = in.drc(overlap(other) &lt; 0.2.um)
<td><img src="/images/drc_overlap2u.png"/></td> <td><img src="/images/drc_overlap2u.png"/></td>
</tr> </tr>
</table> </table>
</p><p>
When "larger than" constraints are used, this function will produce the edges from the
first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
actual edges from the first input (see <a href="#separation">separation</a> for an example).
</p> </p>
<a name="overlapping"/><h2>"overlapping" - Selects shapes overlapping with other shapes</h2> <a name="overlapping"/><h2>"overlapping" - Selects shapes overlapping with other shapes</h2>
<keyword name="overlapping"/> <keyword name="overlapping"/>
@ -1511,6 +1522,23 @@ work in generating error markers.
<td><img src="/images/drc_separation1u.png"/></td> <td><img src="/images/drc_separation1u.png"/></td>
</tr> </tr>
</table> </table>
</p><p>
When "larger than" constraints are used, this function will produce the edges from the
first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
actual edges from the first input:
</p><p>
<pre>
l1_edges_without_l2 = l1.drc((separation(l2) &gt;= 1.0).first_edges)
</pre>
</p><p>
The following image shows the effect of such a negative-output separation check:
</p><p>
<table>
<tr>
<td><img src="/images/drc_separation1un.png"/></td>
</tr>
</table>
</p> </p>
<a name="silent"/><h2>"silent" - Resets verbose mode</h2> <a name="silent"/><h2>"silent" - Resets verbose mode</h2>
<keyword name="silent"/> <keyword name="silent"/>
@ -1863,7 +1891,7 @@ errors = in.drc(0.1.um &lt;= width &lt; 0.2.um)
</p><p> </p><p>
With a lower and upper limit or with the "equal" condition, the results are edges marking the positions on the With a lower and upper limit or with the "equal" condition, the results are edges marking the positions on the
primary shape where the condition is met. primary shape where the condition is met.
With a lower limit alone, these markers are formed by two, identical but opposite edges attached to With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
the primary shape. Without an upper limit only, both edges are attached to different sides of the primary the primary shape. Without an upper limit only, both edges are attached to different sides of the primary
shape. shape.
</p><p> </p><p>

View File

@ -214,7 +214,7 @@ new_layer = layer.collect { |polygon| polygon.transformed(t) }
<keyword name="collect_to_edge_pairs"/> <keyword name="collect_to_edge_pairs"/>
<p>Usage:</p> <p>Usage:</p>
<ul> <ul>
<li><tt>layer.collect { |object| ... }</tt></li> <li><tt>layer.collect_to_edge_pairs { |object| ... }</tt></li>
</ul> </ul>
<p> <p>
This method is similar to <a href="#collect">collect</a>, but creates an edge pair layer. It expects the block to This method is similar to <a href="#collect">collect</a>, but creates an edge pair layer. It expects the block to
@ -224,7 +224,7 @@ deliver <class_doc href="EdgePair">EdgePair</class_doc>, <class_doc href="DEdgeP
<keyword name="collect_to_edges"/> <keyword name="collect_to_edges"/>
<p>Usage:</p> <p>Usage:</p>
<ul> <ul>
<li><tt>layer.collect { |object| ... }</tt></li> <li><tt>layer.collect_to_edges { |object| ... }</tt></li>
</ul> </ul>
<p> <p>
This method is similar to <a href="#collect">collect</a>, but creates an edge layer. It expects the block to This method is similar to <a href="#collect">collect</a>, but creates an edge layer. It expects the block to
@ -235,7 +235,7 @@ contours will be turned into edge sequences.
<keyword name="collect_to_region"/> <keyword name="collect_to_region"/>
<p>Usage:</p> <p>Usage:</p>
<ul> <ul>
<li><tt>layer.collect { |object| ... }</tt></li> <li><tt>layer.collect_to_region { |object| ... }</tt></li>
</ul> </ul>
<p> <p>
This method is similar to <a href="#collect">collect</a>, but creates a polygon layer. It expects the block to This method is similar to <a href="#collect">collect</a>, but creates a polygon layer. It expects the block to
@ -263,6 +263,8 @@ The options available are:
<ul> <ul>
<li><b>as_boxes </b>: with this option, small boxes will be produced as markers </li> <li><b>as_boxes </b>: with this option, small boxes will be produced as markers </li>
<li><b>as_dots </b>: with this option, point-like edges will be produced instead of small boxes </li> <li><b>as_dots </b>: with this option, point-like edges will be produced instead of small boxes </li>
<li><b>as_edge_pairs </b>: with this option, an edge pair is produced for each corner selected. The first edge
is the incoming edge to the corner, the second edge the outgoing edge. </li>
</ul> </ul>
</p><p> </p><p>
The following images show the effect of this method: The following images show the effect of this method:
@ -275,6 +277,21 @@ The following images show the effect of this method:
</tr> </tr>
</table> </table>
</p> </p>
<a name="count"/><h2>"count" - Returns the number of objects on the layer</h2>
<keyword name="count"/>
<p>Usage:</p>
<ul>
<li><tt>layer.count</tt></li>
</ul>
<p>
The count is the number of raw objects, not merged
regions or edges. This is the flat count - the number of polygons,
edges or edge pairs seen from the top cell.
"count" can be computationally expensive for original layers with
clip regions or cell tree filters.
</p><p>
See <a href="#hier_count">hier_count</a> for a hierarchical (each cell counts once) count.
</p>
<a name="covering"/><h2>"covering" - Selects shapes or regions of self which completely cover (enclose) one or more shapes from the other region</h2> <a name="covering"/><h2>"covering" - Selects shapes or regions of self which completely cover (enclose) one or more shapes from the other region</h2>
<keyword name="covering"/> <keyword name="covering"/>
<p>Usage:</p> <p>Usage:</p>
@ -1187,6 +1204,21 @@ l = nil
</p><p> </p><p>
By setting the layer to nil, it is ensured that it can no longer be accessed. By setting the layer to nil, it is ensured that it can no longer be accessed.
</p> </p>
<a name="hier_count"/><h2>"hier_count" - Returns the hierarchical number of objects on the layer</h2>
<keyword name="hier_count"/>
<p>Usage:</p>
<ul>
<li><tt>layer.hier_count</tt></li>
</ul>
<p>
The hier_count is the number of raw objects, not merged
regions or edges, with each cell counting once.
A high <a href="#count">count</a> to hier_count (flat to hierarchical) ratio is an indication
of a good hierarchical compression.
"hier_count" applies only to original layers without clip regions or
cell filters and to layers in <a href="#deep">deep</a> mode. Otherwise, hier_count gives
the same value than <a href="#count">count</a>.
</p>
<a name="holes"/><h2>"holes" - Selects all polygon holes from the input</h2> <a name="holes"/><h2>"holes" - Selects all polygon holes from the input</h2>
<keyword name="holes"/> <keyword name="holes"/>
<p>Usage:</p> <p>Usage:</p>
@ -1438,7 +1470,7 @@ See <a href="#clean">clean</a> for a discussion of the raw state.
<p> <p>
See <a href="#isolated">isolated</a> for a description of that method See <a href="#isolated">isolated</a> for a description of that method
</p> </p>
<a name="isolated"/><h2>"isolated" - An isolation check</h2> <a name="isolated"/><h2>"isolated" - An inter-polygon isolation check</h2>
<keyword name="isolated"/> <keyword name="isolated"/>
<p>Usage:</p> <p>Usage:</p>
<ul> <ul>
@ -1449,8 +1481,8 @@ See <a href="#isolated">isolated</a> for a description of that method
<b>Note: </b>"isolated" and "iso" are available as operators for the "universal DRC" function <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> within <b>Note: </b>"isolated" and "iso" are available as operators for the "universal DRC" function <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> within
the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use. See <a href="/about/drc_ref_global.xml#isolated">isolated</a> for more details. the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use. See <a href="/about/drc_ref_global.xml#isolated">isolated</a> for more details.
</p><p> </p><p>
See <a href="#space">space</a> for a description of this method. See <a href="#space">space</a> for a description of this method. "isolated" is the space check variant which checks different polygons only.
In contrast to <a href="#space">space</a>, this In contrast to <a href="#space">space</a>, the "isolated"
method is available for polygon layers only, since only on such layers method is available for polygon layers only, since only on such layers
different polygons can be identified. different polygons can be identified.
</p><p> </p><p>
@ -1845,7 +1877,7 @@ This method is available for polygons only.
It returns a new layer containing the selected shapes. A version which modifies self It returns a new layer containing the selected shapes. A version which modifies self
is <a href="#select_not_overlapping">select_not_overlapping</a>. is <a href="#select_not_overlapping">select_not_overlapping</a>.
</p> </p>
<a name="notch"/><h2>"notch" - An intra-region spacing check</h2> <a name="notch"/><h2>"notch" - An intra-polygon spacing check</h2>
<keyword name="notch"/> <keyword name="notch"/>
<p>Usage:</p> <p>Usage:</p>
<ul> <ul>
@ -1855,8 +1887,9 @@ is <a href="#select_not_overlapping">select_not_overlapping</a>.
<b>Note: </b>"notch" is available as an operator for the "universal DRC" function <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> within <b>Note: </b>"notch" is available as an operator for the "universal DRC" function <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> within
the <a href="#DRC">DRC</a> framework. This variant has more options and is more intuitive to use. See <a href="/about/drc_ref_global.xml#notch">notch</a> for more details. the <a href="#DRC">DRC</a> framework. This variant has more options and is more intuitive to use. See <a href="/about/drc_ref_global.xml#notch">notch</a> for more details.
</p><p> </p><p>
See <a href="#space">space</a> for a description of this method. See <a href="#space">space</a> for a description of this method.
In contrast to <a href="#space">space</a>, this "notch" is the space check variant which finds space violations within a single polygon, but not against other polygons.
In contrast to <a href="#space">space</a>, the "notch"
method is available for polygon layers only, since only on such layers method is available for polygon layers only, since only on such layers
different polygons can be identified. Also, opposite and rectangle error different polygons can be identified. Also, opposite and rectangle error
filtering is not available for this method. filtering is not available for this method.
@ -1918,6 +1951,9 @@ on polygons and edges (input1: red, input2: blue):
<td><img src="/images/drc_or2.png"/></td> <td><img src="/images/drc_or2.png"/></td>
</tr> </tr>
</table> </table>
</p><p>
In deep mode, "or" or "|" does not imply merging. In deep mode,
"or" is an alias for "+" ("add").
</p> </p>
<a name="output"/><h2>"output" - Outputs the content of the layer</h2> <a name="output"/><h2>"output" - Outputs the content of the layer</h2>
<keyword name="output"/> <keyword name="output"/>
@ -2688,12 +2724,17 @@ The following images show the effect of various forms of the "sized" method:
<p>Usage:</p> <p>Usage:</p>
<ul> <ul>
<li><tt>layer.smoothed(d)</tt></li> <li><tt>layer.smoothed(d)</tt></li>
<li><tt>layer.smoothed(d, hv_keep)</tt></li>
</ul> </ul>
<p> <p>
"Smoothing" returns a simplified version of the polygons. Simplification is "Smoothing" returns a simplified version of the polygons. Simplification is
achieved by removing vertices unless the resulting polygon deviates by more achieved by removing vertices unless the resulting polygon deviates by more
than the given distance d from the original polygon. than the given distance d from the original polygon.
</p><p> </p><p>
"hv_keep" is a boolean parameter which makes the smoothing function maintain
horizontal or vertical edges. The default is false, meaning horizontal or
vertical edges may be changed into tilted ones.
</p><p>
This method return a layer wit the modified polygons. Merged semantics applies for this This method return a layer wit the modified polygons. Merged semantics applies for this
method (see <a href="#raw">raw</a> and <a href="#clean">clean</a>). method (see <a href="#raw">raw</a> and <a href="#clean">clean</a>).
</p> </p>
@ -3119,6 +3160,7 @@ Shielding is enabled by default, but can be switched off with the "transparent"
<li><tt>layer.with_angle(min .. max)</tt></li> <li><tt>layer.with_angle(min .. max)</tt></li>
<li><tt>layer.with_angle(value)</tt></li> <li><tt>layer.with_angle(value)</tt></li>
<li><tt>layer.with_angle(min, max)</tt></li> <li><tt>layer.with_angle(min, max)</tt></li>
<li><tt>edge_pairlayer.with_angle(min, max [, both])</tt></li>
</ul> </ul>
<p> <p>
When called on an edge layer, the method selects edges by their angle, When called on an edge layer, the method selects edges by their angle,
@ -3137,6 +3179,20 @@ When called on a polygon layer, this method selects corners which match the
given angle or is within the given angle interval. The angle is measured between the edges forming the corner. given angle or is within the given angle interval. The angle is measured between the edges forming the corner.
For each corner, an edge pair containing the edges forming in the angle is returned. For each corner, an edge pair containing the edges forming in the angle is returned.
</p><p> </p><p>
When called on an edge pair layer, this method selects edge pairs with one or both edges
meeting the angle criterion. In this case an additional argument is accepted which can be
either "both" (plain word) to indicate that both edges have to be within the given interval.
Without this argument, it is sufficient for one edge to meet the criterion.
</p><p>
Here are examples for "with_angle" on edge pair layers:
</p><p>
<pre>
# at least one edge needs to be horizontal
ep1 = edge_pairs.with_angle(0)
# both edges need to vertical
ep2 = edge_pairs.with_angle(90, both)
</pre>
</p><p>
A method delivering all objects not matching the angle criterion is <a href="#without_angle">without_angle</a>. A method delivering all objects not matching the angle criterion is <a href="#without_angle">without_angle</a>.
</p><p> </p><p>
The following images demonstrate some use cases of <a href="#with_angle">with_angle</a> and <a href="#without_angle:">without_angle:</a> The following images demonstrate some use cases of <a href="#with_angle">with_angle</a> and <a href="#without_angle:">without_angle:</a>
@ -3328,8 +3384,43 @@ direction. With the "tile_origin" option this allows full control over the area
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150)) low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150))
</pre> </pre>
</p><p> </p><p>
The "padding mode" indicates how the area outside the layout's bounding box is considered.
There are two modes:
</p><p>
<ul>
<li><b>padding_zero </b>: the outside area is considered zero density. This is the default mode. </li>
<li><b>padding_ignore </b>: the outside area is ignored for the density computation. </li>
</ul>
</p><p>
Example:
</p><p>
<pre>
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), padding_ignore)
</pre>
</p><p>
The complementary version of "with_density" is <a href="#without_density">without_density</a>. The complementary version of "with_density" is <a href="#without_density">without_density</a>.
</p> </p>
<a name="with_distance"/><h2>"with_distance" - Selects edge pairs by the distance of the edges</h2>
<keyword name="with_distance"/>
<p>Usage:</p>
<ul>
<li><tt>layer.with_distance(min .. max)</tt></li>
<li><tt>layer.with_distance(value)</tt></li>
<li><tt>layer.with_distance(min, max)</tt></li>
</ul>
<p>
The method selects edge pairs by the distance of their edges. The first version selects
edge pairs with a distance larger or equal to min and less than max (but not equal).
The second version selects edge pairs with exactly the given distance. The third
version is similar to the first one, but allows specification of nil for min or
max indicating that there is no lower or upper limit.
</p><p>
The distance of the edges is defined by the minimum distance of all points from the
edges involved. For edge pairs generated in geometrical checks this is equivalent
to the actual distance of the original edges.
</p><p>
This method is available for edge pair layers only.
</p>
<a name="with_holes"/><h2>"with_holes" - Selects all polygons with the specified number of holes</h2> <a name="with_holes"/><h2>"with_holes" - Selects all polygons with the specified number of holes</h2>
<keyword name="with_holes"/> <keyword name="with_holes"/>
<p>Usage:</p> <p>Usage:</p>
@ -3349,15 +3440,30 @@ which have the specified number of holes.
<li><tt>layer.with_length(min .. max)</tt></li> <li><tt>layer.with_length(min .. max)</tt></li>
<li><tt>layer.with_length(value)</tt></li> <li><tt>layer.with_length(value)</tt></li>
<li><tt>layer.with_length(min, max)</tt></li> <li><tt>layer.with_length(min, max)</tt></li>
<li><tt>edge_pairlayer.with_length(min, max [, both])</tt></li>
</ul> </ul>
<p> <p>
The method selects edges by their length. The first version selected The method selects edges by their length. The first version selects
edges with a length larger or equal to min and less than max (but not equal). edges with a length larger or equal to min and less than max (but not equal).
The second version selects edges with exactly the given length. The third The second version selects edges with exactly the given length. The third
version is similar to the first one, but allows specification of nil for min or version is similar to the first one, but allows specification of nil for min or
max indicating that there is no lower or upper limit. max indicating that there is no lower or upper limit.
</p><p> </p><p>
This method is available for edge layers only. This method is available for edge and edge pair layers.
</p><p>
When called on an edge pair layer, this method will select edge pairs if one
or both of the edges meet the length criterion. Use the additional argument
and pass "both" (plain word) to specify that both edges need to be within the given interval.
By default, it's sufficient for one edge to meet the criterion.
</p><p>
Here are examples for "with_length" on edge pair layers:
</p><p>
<pre>
# at least one edge needs to have a length of 1.0 &lt;= l &lt; 2.0
ep1 = edge_pairs.with_length(1.um .. 2.um)
# both edges need to have a length of exactly 2 um
ep2 = edge_pairs.with_length(2.um, both)
</pre>
</p> </p>
<a name="with_perimeter"/><h2>"with_perimeter" - Selects polygons by perimeter</h2> <a name="with_perimeter"/><h2>"with_perimeter" - Selects polygons by perimeter</h2>
<keyword name="with_perimeter"/> <keyword name="with_perimeter"/>
@ -3402,11 +3508,21 @@ This method is available for polygon layers only.
<li><tt>layer.without_angle(min .. max)</tt></li> <li><tt>layer.without_angle(min .. max)</tt></li>
<li><tt>layer.without_angle(value)</tt></li> <li><tt>layer.without_angle(value)</tt></li>
<li><tt>layer.without_angle(min, max)</tt></li> <li><tt>layer.without_angle(min, max)</tt></li>
<li><tt>edge_pairlayer.without_angle(min, max [, both])</tt></li>
</ul> </ul>
<p> <p>
The method basically is the inverse of <a href="#with_angle">with_angle</a>. It selects all edges The method basically is the inverse of <a href="#with_angle">with_angle</a>. It selects all edges
of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle
is not inside the given interval (first and third form). is not inside the given interval (first and third form).
</p><p>
A note on the "both" modifier (without_angle called on edge pairs): "both" means that
both edges need to be "without_angle". For example
</p><p>
<pre>
# both edges are not horizontal or:
# the edge pair is skipped if one edge is horizontal
ep = edge_pairs.without_angle(0, both)
</pre>
</p> </p>
<a name="without_area"/><h2>"without_area" - Selects polygons by area</h2> <a name="without_area"/><h2>"without_area" - Selects polygons by area</h2>
<keyword name="without_area"/> <keyword name="without_area"/>
@ -3507,6 +3623,21 @@ This method is available for polygon layers only.
For details about the operations and the operation see <a href="#with_density">with_density</a>. This version will return the For details about the operations and the operation see <a href="#with_density">with_density</a>. This version will return the
tiles where the density is not within the given range. tiles where the density is not within the given range.
</p> </p>
<a name="without_distance"/><h2>"without_distance" - Selects edge pairs by the distance of the edges</h2>
<keyword name="without_distance"/>
<p>Usage:</p>
<ul>
<li><tt>layer.without_distance(min .. max)</tt></li>
<li><tt>layer.without_distance(value)</tt></li>
<li><tt>layer.without_distance(min, max)</tt></li>
</ul>
<p>
The method basically is the inverse of <a href="#with_distance">with_distance</a>. It selects all edge pairs
of the edge pair layer which do not have the given distance (second form) or are
not inside the given interval (first and third form).
</p><p>
This method is available for edge pair layers only.
</p>
<a name="without_holes"/><h2>"without_holes" - Selects all polygons with the specified number of holes</h2> <a name="without_holes"/><h2>"without_holes" - Selects all polygons with the specified number of holes</h2>
<keyword name="without_holes"/> <keyword name="without_holes"/>
<p>Usage:</p> <p>Usage:</p>
@ -3526,13 +3657,23 @@ which do not have the specified number of holes.
<li><tt>layer.without_length(min .. max)</tt></li> <li><tt>layer.without_length(min .. max)</tt></li>
<li><tt>layer.without_length(value)</tt></li> <li><tt>layer.without_length(value)</tt></li>
<li><tt>layer.without_length(min, max)</tt></li> <li><tt>layer.without_length(min, max)</tt></li>
<li><tt>edge_pairlayer.with_length(min, max [, both])</tt></li>
</ul> </ul>
<p> <p>
The method basically is the inverse of <a href="#with_length">with_length</a>. It selects all edges The method basically is the inverse of <a href="#with_length">with_length</a>. It selects all edges
of the edge layer which do not have the given length (second form) or are of the edge layer which do not have the given length (second form) or are
not inside the given interval (first and third form). not inside the given interval (first and third form).
</p><p> </p><p>
This method is available for edge layers only. This method is available for edge and edge pair layers.
</p><p>
A note on the "both" modifier (without_length called on edge pairs): "both" means that
both edges need to be "without_length". For example
</p><p>
<pre>
# both edges are not exactly 1 um in length, or:
# the edge pair is skipped if one edge has a length of exactly 1 um
ep = edge_pairs.without_length(1.um, both)
</pre>
</p> </p>
<a name="without_perimeter"/><h2>"without_perimeter" - Selects polygons by perimeter</h2> <a name="without_perimeter"/><h2>"without_perimeter" - Selects polygons by perimeter</h2>
<keyword name="without_perimeter"/> <keyword name="without_perimeter"/>

View File

@ -99,6 +99,25 @@ connect(contact, metal1)
errors = antenna_check(gate, metal1, 50.0) errors = antenna_check(gate, metal1, 50.0)
</pre> </pre>
</p><p> </p><p>
Usually antenna checks apply to multiple metal layers. In this case,
the connectivity needs to be extended after the first check to include
the next metal layers. This can be achieved with incremental connects:
</p><p>
<pre>
# provide connections up to metal1
connect(gate, poly)
connect(poly, contact)
connect(contact, metal1)
metal1_errors = antenna_check(gate, metal1, 50.0)
# now *add* connections up to metal2
connect(metal1, via1)
connect(via1, metal2)
metal2_errors = antenna_check(gate, metal2, 50.0)
... continue this scheme with further metal layers ...
</pre>
</p><p>
Plasma induced damage can be rectified by including diodes Plasma induced damage can be rectified by including diodes
which create a safe current path for discharging the metal which create a safe current path for discharging the metal
islands. Such diodes can be identified with a recognition layer islands. Such diodes can be identified with a recognition layer

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -18,6 +18,7 @@
<file alias="drc_space3u.png">doc/images/drc_space3u.png</file> <file alias="drc_space3u.png">doc/images/drc_space3u.png</file>
<file alias="drc_separation1.png">doc/images/drc_separation1.png</file> <file alias="drc_separation1.png">doc/images/drc_separation1.png</file>
<file alias="drc_separation1u.png">doc/images/drc_separation1u.png</file> <file alias="drc_separation1u.png">doc/images/drc_separation1u.png</file>
<file alias="drc_separation1un.png">doc/images/drc_separation1un.png</file>
<file alias="drc_separation2.png">doc/images/drc_separation2.png</file> <file alias="drc_separation2.png">doc/images/drc_separation2.png</file>
<file alias="drc_separation3.png">doc/images/drc_separation3.png</file> <file alias="drc_separation3.png">doc/images/drc_separation3.png</file>
<file alias="drc_separation4.png">doc/images/drc_separation4.png</file> <file alias="drc_separation4.png">doc/images/drc_separation4.png</file>

View File

@ -120,9 +120,8 @@ LogFile::LogFile (size_t max_entries, bool register_global)
{ {
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ())); connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
m_timer.setSingleShot (false); m_timer.setSingleShot (true);
m_timer.setInterval (100); m_timer.setInterval (0);
m_timer.start ();
if (register_global) { if (register_global) {
tl::info.add (&m_info_receiver, false); tl::info.add (&m_info_receiver, false);
@ -253,30 +252,10 @@ LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continu
void void
LogFile::yield () LogFile::yield ()
{ {
#if 0 // will update on next processEvents
// This looked like a good idea, but in fact it introduces a hell lot of instability if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread ()) {
// as it potentially leads to a recursion of events inside innocent functions. Remember m_timer.start ();
// that log output may be generated from every function called in response of an event
// and not every such function may process further events
bool can_yield = false;
{
QMutexLocker locker (&m_lock);
if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread () && (tl::Clock::current () - m_last_yield).seconds () > 0.1) {
m_last_yield = tl::Clock::current ();
can_yield = true;
}
} }
// use this opportunity to process events
// NOTE: as process events may trigger further log output, it's necessary to do process events outside any other
// method (e.g. add) which is subject to locking. Hence we avoid deadlocks.
if (can_yield) {
lay::ApplicationBase::instance ()->process_events (QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers, true /*silent*/);
}
#endif
} }
int int

View File

@ -26,7 +26,6 @@
#include "ui_LogViewerDialog.h" #include "ui_LogViewerDialog.h"
#include "tlLog.h" #include "tlLog.h"
#include "tlTimer.h"
#include "layCommon.h" #include "layCommon.h"
#include <QTimer> #include <QTimer>
@ -233,7 +232,6 @@ private:
size_t m_last_generation_id; size_t m_last_generation_id;
bool m_has_errors, m_has_warnings; bool m_has_errors, m_has_warnings;
bool m_last_attn; bool m_last_attn;
tl::Clock m_last_yield;
/** /**
* @brief Adds an error * @brief Adds an error

View File

@ -206,3 +206,8 @@ TEST(23_issue709)
run_test (_this, "empty_subcells", "empty_subcells.gds"); run_test (_this, "empty_subcells", "empty_subcells.gds");
} }
// empty gds
TEST(24_issue806)
{
run_test (_this, "custom_compare", "custom_compare.gds");
}

View File

@ -152,7 +152,7 @@ TEST(16_private)
TEST(17_private) TEST(17_private)
{ {
test_is_long_runner (); test_is_long_runner ();
run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17.lvsdb"); run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17b.lvsdb");
} }
TEST(18_private) TEST(18_private)
@ -170,6 +170,6 @@ TEST(19_private)
TEST(20_private) TEST(20_private)
{ {
// test_is_long_runner (); // test_is_long_runner ();
run_test (_this, "test_20.lylvs", "test_20.cir.gz", "test_20.gds.gz", true, "test_20.lvsdb"); run_test (_this, "test_20.lylvs", "test_20.cir.gz", "test_20.gds.gz", true, "test_20b.lvsdb");
} }

View File

@ -74,6 +74,7 @@ class GDS2FormatDeclaration
tl::make_member (&db::GDS2WriterOptions::write_file_properties, "write-file-properties") + tl::make_member (&db::GDS2WriterOptions::write_file_properties, "write-file-properties") +
tl::make_member (&db::GDS2WriterOptions::no_zero_length_paths, "no-zero-length-paths") + tl::make_member (&db::GDS2WriterOptions::no_zero_length_paths, "no-zero-length-paths") +
tl::make_member (&db::GDS2WriterOptions::multi_xy_records, "multi-xy-records") + tl::make_member (&db::GDS2WriterOptions::multi_xy_records, "multi-xy-records") +
tl::make_member (&db::GDS2WriterOptions::resolve_skew_arrays, "resolve-skew-arrays") +
tl::make_member (&db::GDS2WriterOptions::max_vertex_count, "max-vertex-count") + tl::make_member (&db::GDS2WriterOptions::max_vertex_count, "max-vertex-count") +
tl::make_member (&db::GDS2WriterOptions::max_cellname_length, "max-cellname-length") + tl::make_member (&db::GDS2WriterOptions::max_cellname_length, "max-cellname-length") +
tl::make_member (&db::GDS2WriterOptions::libname, "libname") tl::make_member (&db::GDS2WriterOptions::libname, "libname")

View File

@ -109,6 +109,7 @@ public:
: max_vertex_count (8000), : max_vertex_count (8000),
no_zero_length_paths (false), no_zero_length_paths (false),
multi_xy_records (false), multi_xy_records (false),
resolve_skew_arrays (false),
max_cellname_length (32000), max_cellname_length (32000),
libname ("LIB"), libname ("LIB"),
user_units (1.0), user_units (1.0),
@ -146,6 +147,13 @@ public:
*/ */
bool multi_xy_records; bool multi_xy_records;
/**
* @brief Resolve skew arrays into single instances
*
* Setting this property to true will resolve skew (non-orthogonal) arrays into single instances.
*/
bool resolve_skew_arrays;
/** /**
* @brief Maximum length of cell names * @brief Maximum length of cell names
* *

View File

@ -130,6 +130,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8); size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8);
size_t max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4); size_t max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4);
bool no_zero_length_paths = gds2_options.no_zero_length_paths; bool no_zero_length_paths = gds2_options.no_zero_length_paths;
bool resolve_skew_arrays = gds2_options.resolve_skew_arrays;
m_cell_name_map = db::WriterCellNameMap (max_cellname_length); m_cell_name_map = db::WriterCellNameMap (max_cellname_length);
m_cell_name_map.replacement ('$'); m_cell_name_map.replacement ('$');
@ -281,7 +282,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) { if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
progress_checkpoint (); progress_checkpoint ();
write_inst (sf, *inst, true /*normalize*/, layout, inst->prop_id ()); write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ());
} }
@ -346,14 +347,24 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
progress_checkpoint (); progress_checkpoint ();
} }
static bool is_orthogonal (const db::Vector &rv, const db::Vector &cv)
{
return (rv.x () == 0 && cv.y () == 0) || (rv.y () == 0 && cv.x () == 0);
}
void void
GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normalize, const db::Layout &layout, db::properties_id_type prop_id) GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normalize, bool resolve_skew_arrays, const db::Layout &layout, db::properties_id_type prop_id)
{ {
db::Vector a, b; db::Vector a, b;
unsigned long amax, bmax; unsigned long amax, bmax;
bool is_reg = instance.is_regular_array (a, b, amax, bmax); bool is_reg = instance.is_regular_array (a, b, amax, bmax);
// skew arrays are resolved if required
if (is_reg && ! is_orthogonal (a, b) != 0 && resolve_skew_arrays) {
is_reg = false;
}
for (db::CellInstArray::iterator ii = instance.begin (); ! ii.at_end (); ++ii) { for (db::CellInstArray::iterator ii = instance.begin (); ! ii.at_end (); ++ii) {
db::Trans t = *ii; db::Trans t = *ii;

View File

@ -122,7 +122,7 @@ protected:
/** /**
* @brief Write an instance * @brief Write an instance
*/ */
void write_inst (double sf, const db::Instance &instance, bool normalize, const db::Layout &layout, db::properties_id_type prop_id); void write_inst (double sf, const db::Instance &instance, bool normalize, bool resolve_skew_arrays, const db::Layout &layout, db::properties_id_type prop_id);
/** /**
* @brief Write a shape as box * @brief Write a shape as box

View File

@ -65,6 +65,16 @@ static bool get_gds2_multi_xy_records (const db::SaveLayoutOptions *options)
return options->get_options<db::GDS2WriterOptions> ().multi_xy_records; return options->get_options<db::GDS2WriterOptions> ().multi_xy_records;
} }
static void set_gds2_resolve_skew_arrays (db::SaveLayoutOptions *options, bool n)
{
options->get_options<db::GDS2WriterOptions> ().resolve_skew_arrays = n;
}
static bool get_gds2_resolve_skew_arrays (const db::SaveLayoutOptions *options)
{
return options->get_options<db::GDS2WriterOptions> ().resolve_skew_arrays;
}
static void set_gds2_write_file_properties (db::SaveLayoutOptions *options, bool n) static void set_gds2_write_file_properties (db::SaveLayoutOptions *options, bool n)
{ {
options->get_options<db::GDS2WriterOptions> ().write_file_properties = n; options->get_options<db::GDS2WriterOptions> ().write_file_properties = n;
@ -129,7 +139,7 @@ static double get_gds2_user_units (const db::SaveLayoutOptions *options)
static static
gsi::ClassExt<db::SaveLayoutOptions> gds2_writer_options ( gsi::ClassExt<db::SaveLayoutOptions> gds2_writer_options (
gsi::method_ext ("gds2_max_vertex_count=", &set_gds2_max_vertex_count, gsi::arg ("count"), gsi::method_ext ("gds2_max_vertex_count=", &set_gds2_max_vertex_count, gsi::arg ("count"),
"@brief Set the maximum number of vertices for polygons to write\n" "@brief Sets the maximum number of vertices for polygons to write\n"
"This property describes the maximum number of point for polygons in GDS2 files.\n" "This property describes the maximum number of point for polygons in GDS2 files.\n"
"Polygons with more points will be split.\n" "Polygons with more points will be split.\n"
"The minimum value for this property is 4. The maximum allowed value is about 4000 or 8000, depending on the\n" "The minimum value for this property is 4. The maximum allowed value is about 4000 or 8000, depending on the\n"
@ -138,24 +148,37 @@ gsi::ClassExt<db::SaveLayoutOptions> gds2_writer_options (
"\nThis property has been added in version 0.18.\n" "\nThis property has been added in version 0.18.\n"
) + ) +
gsi::method_ext ("gds2_max_vertex_count", &get_gds2_max_vertex_count, gsi::method_ext ("gds2_max_vertex_count", &get_gds2_max_vertex_count,
"@brief Get the maximum number of vertices for polygons to write\n" "@brief Gets the maximum number of vertices for polygons to write\n"
"See \\gds2_max_vertex_count= method for a description of the maximum vertex count." "See \\gds2_max_vertex_count= method for a description of the maximum vertex count."
"\nThis property has been added in version 0.18.\n" "\nThis property has been added in version 0.18.\n"
) + ) +
gsi::method_ext ("gds2_multi_xy_records=", &set_gds2_multi_xy_records, gsi::arg ("flag"), gsi::method_ext ("gds2_multi_xy_records=", &set_gds2_multi_xy_records, gsi::arg ("flag"),
"@brief Use multiple XY records in BOUNDARY elements for unlimited large polygons\n" "@brief Uses multiple XY records in BOUNDARY elements for unlimited large polygons\n"
"\n" "\n"
"Setting this property to true allows producing polygons with an unlimited number of points \n" "Setting this property to true allows producing polygons with an unlimited number of points \n"
"at the cost of incompatible formats. Setting it to true disables the \\gds2_max_vertex_count setting.\n" "at the cost of incompatible formats. Setting it to true disables the \\gds2_max_vertex_count setting.\n"
"\nThis property has been added in version 0.18.\n" "\nThis property has been added in version 0.18.\n"
) + ) +
gsi::method_ext ("gds2_multi_xy_records?", &get_gds2_multi_xy_records, gsi::method_ext ("gds2_multi_xy_records?", &get_gds2_multi_xy_records,
"@brief Get the property enabling multiple XY records for BOUNDARY elements\n" "@brief Gets the property enabling multiple XY records for BOUNDARY elements\n"
"See \\gds2_multi_xy_records= method for a description of this property." "See \\gds2_multi_xy_records= method for a description of this property."
"\nThis property has been added in version 0.18.\n" "\nThis property has been added in version 0.18.\n"
) + ) +
gsi::method_ext ("gds2_resolve_skew_arrays=", &set_gds2_resolve_skew_arrays, gsi::arg ("flag"),
"@brief Resolves skew arrays into single instances\n"
"\n"
"Setting this property to true will make skew (non-orthongonal) arrays being resolved into single instances.\n"
"Skew arrays happen if either the row or column vector isn't paralell to x or y axis. Such arrays can cause problems with "
"some legacy software and can be disabled with this option.\n"
"\nThis property has been added in version 0.27.1.\n"
) +
gsi::method_ext ("gds2_resolve_skew_arrays?", &get_gds2_resolve_skew_arrays,
"@brief Gets a value indicating whether to resolve skew arrays into single instances\n"
"See \\gds2_resolve_skew_arrays= method for a description of this property."
"\nThis property has been added in version 0.27.1.\n"
) +
gsi::method_ext ("gds2_write_timestamps=", &set_gds2_write_timestamps, gsi::arg ("flag"), gsi::method_ext ("gds2_write_timestamps=", &set_gds2_write_timestamps, gsi::arg ("flag"),
"@brief Write the current time into the GDS2 timestamps if set to true\n" "@brief Writes the current time into the GDS2 timestamps if set to true\n"
"\n" "\n"
"If this property is set to false, the time fields will all be zero. This somewhat simplifies compare and diff " "If this property is set to false, the time fields will all be zero. This somewhat simplifies compare and diff "
"applications.\n" "applications.\n"

View File

@ -50,58 +50,13 @@
<property name="spacing"> <property name="spacing">
<number>6</number> <number>6</number>
</property> </property>
<item row="2" column="0"> <item row="7" column="0" colspan="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Max. vertices</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="write_timestamps"> <widget class="QCheckBox" name="write_timestamps">
<property name="text"> <property name="text">
<string>Write current time to time stamps (BGNLIB, BGNSTR)</string> <string>Write current time to time stamps (BGNLIB, BGNSTR)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QLineEdit" name="max_vertex_le"/>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>(&lt;4000 recommended, absolute limit 8191)</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="libname_le"/>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>(keep empty for unspecified limit)</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<widget class="QCheckBox" name="multi_xy_cbx">
<property name="text">
<string>Multi-XY record mode for boundaries
(enables infinitely large polygons/paths at the cost of compatibility)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="cell_name_length_le"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Library name</string>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
@ -109,13 +64,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="3"> <item row="2" column="2">
<widget class="QCheckBox" name="no_zero_length_paths"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Eliminate zero-length paths (convert to BOUNDARY)</string> <string>(&lt;4000 recommended, absolute limit 8191)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<widget class="QLineEdit" name="cell_name_length_le"/>
</item>
<item row="4" column="0" colspan="3"> <item row="4" column="0" colspan="3">
<widget class="QFrame" name="frame"> <widget class="QFrame" name="frame">
<property name="frameShape"> <property name="frameShape">
@ -181,6 +139,55 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QLineEdit" name="max_vertex_le"/>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>(keep empty for unspecified limit)</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<widget class="QCheckBox" name="multi_xy_cbx">
<property name="text">
<string>Multi-XY record mode for boundaries
(enables infinitely large polygons/paths at the cost of compatibility)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Library name</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Max. vertices</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="libname_le"/>
</item>
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="no_zero_length_paths">
<property name="text">
<string>Eliminate zero-length paths (convert to BOUNDARY)</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QCheckBox" name="resolve_skew_arrays_cbx">
<property name="text">
<string>Resolve skew (non-orthogonal) arrays into single instances</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -61,6 +61,7 @@ GDS2WriterOptionPage::setup (const db::FormatSpecificWriterOptions *o, const db:
mp_ui->write_file_properties->setChecked (options->write_file_properties); mp_ui->write_file_properties->setChecked (options->write_file_properties);
mp_ui->no_zero_length_paths->setChecked (options->no_zero_length_paths); mp_ui->no_zero_length_paths->setChecked (options->no_zero_length_paths);
mp_ui->multi_xy_cbx->setChecked (options->multi_xy_records); mp_ui->multi_xy_cbx->setChecked (options->multi_xy_records);
mp_ui->resolve_skew_arrays_cbx->setChecked (options->resolve_skew_arrays);
mp_ui->max_vertex_le->setEnabled (! options->multi_xy_records); mp_ui->max_vertex_le->setEnabled (! options->multi_xy_records);
mp_ui->max_vertex_le->setText (tl::to_qstring (tl::to_string (options->max_vertex_count))); mp_ui->max_vertex_le->setText (tl::to_qstring (tl::to_string (options->max_vertex_count)));
mp_ui->cell_name_length_le->setText (tl::to_qstring (tl::to_string (options->max_cellname_length))); mp_ui->cell_name_length_le->setText (tl::to_qstring (tl::to_string (options->max_cellname_length)));
@ -76,6 +77,7 @@ GDS2WriterOptionPage::commit (db::FormatSpecificWriterOptions *o, const db::Tech
unsigned int n; unsigned int n;
options->multi_xy_records = mp_ui->multi_xy_cbx->isChecked (); options->multi_xy_records = mp_ui->multi_xy_cbx->isChecked ();
options->resolve_skew_arrays = mp_ui->resolve_skew_arrays_cbx->isChecked ();
options->write_timestamps = mp_ui->write_timestamps->isChecked (); options->write_timestamps = mp_ui->write_timestamps->isChecked ();
options->write_cell_properties = mp_ui->write_cell_properties->isChecked (); options->write_cell_properties = mp_ui->write_cell_properties->isChecked ();
options->write_file_properties = mp_ui->write_file_properties->isChecked (); options->write_file_properties = mp_ui->write_file_properties->isChecked ();

View File

@ -83,6 +83,19 @@ TEST(1)
run_test (_this, "arefs.gds", "arefs_ref.gds"); run_test (_this, "arefs.gds", "arefs_ref.gds");
} }
TEST(1a)
{
db::GDS2WriterOptions opt;
run_test (_this, "arefs_skew.gds", "arefs_skew1.gds", false, opt);
}
TEST(1b)
{
db::GDS2WriterOptions opt;
opt.resolve_skew_arrays = true;
run_test (_this, "arefs_skew.gds", "arefs_skew2.gds", false, opt);
}
TEST(2) TEST(2)
{ {
db::Manager m (false); db::Manager m (false);

View File

@ -338,7 +338,7 @@ public:
: m_obj (obj), mp_cls (0), m_members (Qnil) : m_obj (obj), mp_cls (0), m_members (Qnil)
{ {
rb_gc_register_address (&m_obj); rb_gc_register_address (&m_obj);
mp_cls = find_cclass (rb_class_of (m_obj)); mp_cls = find_cclass_maybe_null (rb_class_of (m_obj));
m_members = rb_obj_instance_variables (m_obj); m_members = rb_obj_instance_variables (m_obj);
rb_gc_register_address (&m_members); rb_gc_register_address (&m_members);

View File

@ -940,6 +940,13 @@ bool is_registered (const gsi::ClassBase *cls)
} }
const gsi::ClassBase *find_cclass (VALUE k) const gsi::ClassBase *find_cclass (VALUE k)
{
const gsi::ClassBase *cls = find_cclass_maybe_null (k);
tl_assert (cls != 0);
return cls;
}
const gsi::ClassBase *find_cclass_maybe_null (VALUE k)
{ {
std::map <VALUE, const gsi::ClassBase *>::const_iterator cls; std::map <VALUE, const gsi::ClassBase *>::const_iterator cls;
@ -954,8 +961,7 @@ const gsi::ClassBase *find_cclass (VALUE k)
} }
} }
tl_assert (cls != cls_map.end ()); return cls != cls_map.end () ? cls->second : 0;
return cls->second;
} }
} }

View File

@ -218,6 +218,11 @@ void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls);
*/ */
const gsi::ClassBase *find_cclass (VALUE k); const gsi::ClassBase *find_cclass (VALUE k);
/**
* @brief Find the class declaration from the Ruby object
*/
const gsi::ClassBase *find_cclass_maybe_null (VALUE k);
/** /**
* @brief Finds the Ruby class for a gsi class * @brief Finds the Ruby class for a gsi class
*/ */

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections. # This section lists the mask layers (drawing or derived) and their connections.
# Mask layers # Mask layers
layer(bulk '1/0') layer(bulk)
layer(nwell '1/0') layer(nwell '1/0')
layer(poly '3/0') layer(poly '3/0')
layer(poly_lbl '3/1') layer(poly_lbl '3/1')

View File

@ -22,6 +22,7 @@ l1.drc(-90 < corners(as_dots) <= 90.0).output(101, 0)
l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners
l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners
l1.drc(corners(as_boxes) <= -90).output(104, 0) l1.drc(corners(as_boxes) <= -90).output(104, 0)
l1.drc(corners(as_edge_pairs) == 90).output(105, 0)
l1.drc(middle).output(110, 0) l1.drc(middle).output(110, 0)
l1.drc(middle(as_dots)).output(111, 0) l1.drc(middle(as_dots)).output(111, 0)

Binary file not shown.

Binary file not shown.

View File

@ -3,7 +3,17 @@ dbu 0.001
target($drc_test_target, "TOP") target($drc_test_target, "TOP")
def self.expect_count(layer, c, hc, where)
if layer.count != c
raise(where + ": Layer count #{layer.count} does not equal #{c}")
end
if layer.hier_count != hc
raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}")
end
end
x = polygon_layer x = polygon_layer
expect_count(x, 0, 0, "empty layer")
x.is_empty? == true || raise("unexpected value") x.is_empty? == true || raise("unexpected value")
x.is_box? == false || raise("unexpected value") x.is_box? == false || raise("unexpected value")
x.insert(box(4.0, 0, 4.7, 0.7)) x.insert(box(4.0, 0, 4.7, 0.7))
@ -12,6 +22,7 @@ x.is_box? == true || raise("unexpected value")
x.insert(polygon([ p(0, 0), p(2.0, 0), p(1.0, 1.0) ])) x.insert(polygon([ p(0, 0), p(2.0, 0), p(1.0, 1.0) ]))
x.insert(polygon([ p(0, -5.0), p(2.0, -5.0), p(1.0, -6.0) ])) x.insert(polygon([ p(0, -5.0), p(2.0, -5.0), p(1.0, -6.0) ]))
x.insert(path([ p(0, -2), p(2.0, -2) ], 0.2)) x.insert(path([ p(0, -2), p(2.0, -2) ], 0.2))
expect_count(x, 4, 4, "after 3x insert")
x.is_box? == false || raise("unexpected value") x.is_box? == false || raise("unexpected value")
x.output(10, 0) x.output(10, 0)

View File

@ -2,6 +2,15 @@
target($drc_test_target, "TOP") target($drc_test_target, "TOP")
source($drc_test_source, "TOP") source($drc_test_source, "TOP")
def self.expect_count(layer, c, hc, where)
if layer.count != c
raise(where + ": Layer count #{layer.count} does not equal #{c}")
end
if layer.hier_count != hc
raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}")
end
end
a1 = input(1) a1 = input(1)
b1 = input(2) b1 = input(2)
c1 = input(3) c1 = input(3)
@ -57,6 +66,9 @@ a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0)
a1.corners.sized(0.05).output(1060, 0) a1.corners.sized(0.05).output(1060, 0)
a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0) a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0)
a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0) a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0)
a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0)
a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0)
a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0)
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0) a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0) a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
@ -89,3 +101,6 @@ a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.transformed(RBA::VCplx
a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0) a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0)
a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0) a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0)
expect_count(a1.edges, 9, 9, "a1.edges")
expect_count(a1.width(1.5), 5, 5, "a1.width(1.5)")

View File

@ -9,10 +9,18 @@ b.output(0, 0)
a.output(1, 0) a.output(1, 0)
a.with_density(0..0.1, tile_size(10.um), tile_boundary(b)).output(100, 0) a.with_density(0..0.1, tile_size(10.um), tile_boundary(b)).output(100, 0)
a.without_density(0..0.1, tile_size(10.um)).output(101, 0) a.without_density(0..0.1, tile_size(10.um), padding_zero).output(101, 0)
a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um)).output(102, 0) a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um)).output(102, 0)
a.with_density(0.1, nil, tile_size(10.um, 20.um)).output(103, 0) a.with_density(0.1, nil, tile_size(10.um, 20.um)).output(103, 0)
a.with_density(0.1, 1.0, tile_size(10.um), tile_step(10.um, 20.um)).output(104, 0) a.with_density(0.1, 1.0, tile_size(10.um), tile_step(10.um, 20.um)).output(104, 0)
a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), tile_count(10, 15)).output(105, 0) a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), tile_count(10, 15)).output(105, 0)
a.with_density(0..0.1, tile_size(100.um), tile_step(10.um)).output(110, 0) a.with_density(0..0.1, tile_size(100.um), tile_step(10.um), padding_zero).output(110, 0)
a.with_density(0..0.1, tile_size(10.um), tile_boundary(b), padding_ignore).output(200, 0)
a.without_density(0..0.1, tile_size(10.um), padding_ignore).output(201, 0)
a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), padding_ignore).output(202, 0)
a.with_density(0.1, nil, tile_size(10.um, 20.um), padding_ignore).output(203, 0)
a.with_density(0.1, 1.0, tile_size(10.um), tile_step(10.um, 20.um), padding_ignore).output(204, 0)
a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), tile_count(10, 15), padding_ignore).output(205, 0)
a.with_density(0..0.1, tile_size(100.um), tile_step(10.um), padding_ignore).output(210, 0)

Binary file not shown.

View File

@ -4,6 +4,15 @@
source($drc_test_source, "TOPTOP_SMALL") source($drc_test_source, "TOPTOP_SMALL")
target($drc_test_target) target($drc_test_target)
def self.expect_count(layer, c, hc, where)
if layer.count != c
raise(where + ": Layer count #{layer.count} does not equal #{c}")
end
if layer.hier_count != hc
raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}")
end
end
cell("TOPTOP_SMALL") cell("TOPTOP_SMALL")
l1_flat = input(1) l1_flat = input(1)
@ -66,12 +75,16 @@ r.output(1023, 0)
r.extents.output(1123, 0) r.extents.output(1123, 0)
r = l1.space(0.5) r = l1.space(0.5)
expect_count(r, 3, 1, "r on l1")
r.output(1010, 0) r.output(1010, 0)
r.extents.output(1110, 0) r.extents.output(1110, 0)
expect_count(l1, 15, 5, "l1 before flatten")
l1.flatten l1.flatten
expect_count(l1, 15, 15, "l1 after flatten")
r = l1.space(0.5) r = l1.space(0.5)
r.output(1011, 0) r.output(1011, 0)
r.extents.output(1111, 0) r.extents.output(1111, 0)
expect_count(r, 3, 3, "r on l1.flatten")

44
testdata/drc/drcSimpleTests_48.drc vendored Normal file
View File

@ -0,0 +1,44 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l1.output(1, 0)
l2.output(2, 0)
l1.drc(separation(l2, projection) < 1.0).output(100, 0)
l1.drc(separation(l2, whole_edges, projection) < 1.0).output(101, 0)
l1.drc(separation(l2, projection) >= 1.0).output(102, 0)
l1.separation(l2, projection, 1.0).output(110, 0)
l1.separation(l2, projection, 1.0, whole_edges).output(111, 0)
l2.drc(separation(l1, projection) < 1.0).output(200, 0)
l2.drc(separation(l1, whole_edges, projection) < 1.0).output(201, 0)
l2.drc(separation(l1, projection) >= 1.0).output(202, 0)
l2.separation(l1, projection, 1.0).output(210, 0)
l2.separation(l1, projection, 1.0, whole_edges).output(211, 0)
(l1 + l2).drc(space(projection) < 1.0).output(300, 0)
(l1 + l2).drc(space(whole_edges, projection) < 1.0).output(301, 0)
(l1 + l2).drc(space(projection) >= 1.0).output(302, 0)
(l1 + l2).space(projection, 1.0).output(310, 0)
(l1 + l2).space(projection, 1.0, whole_edges).output(311, 0)
l1.drc(enclosing(l2, projection) < 1.0).output(400, 0)
l1.drc(enclosing(l2, whole_edges, projection) < 1.0).output(401, 0)
l1.drc(enclosing(l2, projection) >= 1.0).output(402, 0)
l1.enclosing(l2, projection, 1.0).output(410, 0)
l1.enclosing(l2, projection, 1.0, whole_edges).output(411, 0)
l1.drc(overlap(l2, projection) < 1.0).output(500, 0)
l1.drc(overlap(l2, whole_edges, projection) < 1.0).output(501, 0)
l1.drc(overlap(l2, projection) >= 1.0).output(502, 0)
l1.overlap(l2, projection, 1.0).output(510, 0)
l1.overlap(l2, projection, 1.0, whole_edges).output(511, 0)

BIN
testdata/drc/drcSimpleTests_48.gds vendored Normal file

Binary file not shown.

41
testdata/drc/drcSimpleTests_49.drc vendored Normal file
View File

@ -0,0 +1,41 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
ep = input(1, 0).drc(space(projection) < 0.5)
ep.polygons(0).output(100, 0)
ep.with_distance(0..0.1).polygons(0).output(110, 0)
ep.with_distance(0.25, nil).polygons(0).output(111, 0)
ep.without_distance(0..0.1).polygons(0).output(120, 0)
ep.without_distance(0.25, nil).polygons(0).output(121, 0)
ep.with_angle(45.0).polygons(0).output(200, 0)
ep.with_angle(0.0).polygons(0).output(201, 0)
ep.with_angle(45.0..91.0).polygons(0).output(202, 0)
ep.with_angle(45.0, both).polygons(0).output(210, 0)
ep.with_angle(0.0, both).polygons(0).output(211, 0)
ep.with_angle(45.0..91.0, both).polygons(0).output(212, 0)
ep.without_angle(45.0).polygons(0).output(220, 0)
ep.without_angle(0.0).polygons(0).output(221, 0)
ep.without_angle(45.0..91.0).polygons(0).output(222, 0)
ep.without_angle(45.0, both).polygons(0).output(230, 0)
ep.without_angle(0.0, both).polygons(0).output(231, 0)
ep.without_angle(45.0..91.0, both).polygons(0).output(232, 0)
ep.with_length(0.5).polygons(0).output(300, 0)
ep.with_length(0.4..0.51).polygons(0).output(301, 0)
ep.with_length(0.5, both).polygons(0).output(310, 0)
ep.with_length(0.4..0.51, both).polygons(0).output(311, 0)
ep.without_length(0.5).polygons(0).output(320, 0)
ep.without_length(0.4..0.51).polygons(0).output(321, 0)
ep.without_length(0.5, both).polygons(0).output(330, 0)
ep.without_length(0.4..0.51, both).polygons(0).output(331, 0)

BIN
testdata/drc/drcSimpleTests_49.gds vendored Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
# Hierarchical antenna check # Flat antenna check
source($drc_test_source, "RINGO") source($drc_test_source, "RINGO")
target($drc_test_target) target($drc_test_target)
@ -14,6 +14,17 @@ metal2 = input(8, 0)
gate = diff & poly gate = diff & poly
connect(gate, poly)
connect(poly, poly_cont)
connect(poly_cont, metal1)
antenna_check(gate, metal1, 1.0).output(201)
antenna_check(gate, metal1, 2.0).output(202)
antenna_check(gate, metal1, 3.0).output(203)
antenna_check(gate, metal1, 4.0).output(204)
clear_connections
connect(gate, poly) connect(gate, poly)
connect(poly, poly_cont) connect(poly, poly_cont)
connect(poly_cont, metal1) connect(poly_cont, metal1)
@ -24,3 +35,4 @@ antenna_check(gate, metal2, 1.0).output(101)
antenna_check(gate, metal2, 5.0).output(105) antenna_check(gate, metal2, 5.0).output(105)
antenna_check(gate, metal2, 10.0).output(110) antenna_check(gate, metal2, 10.0).output(110)
antenna_check(gate, metal2, 50.0).output(150) antenna_check(gate, metal2, 50.0).output(150)

33
testdata/drc/drcSimpleTests_5i.drc vendored Normal file
View File

@ -0,0 +1,33 @@
# Flat antenna check
source($drc_test_source, "RINGO")
target($drc_test_target)
diff = input(2, 0)
poly = input(3, 0)
contact = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
via1 = input(7, 0)
metal2 = input(8, 0)
gate = diff & poly
connect(gate, poly)
connect(poly, poly_cont)
connect(poly_cont, metal1)
antenna_check(gate, metal1, 1.0).output(201)
antenna_check(gate, metal1, 2.0).output(202)
antenna_check(gate, metal1, 3.0).output(203)
antenna_check(gate, metal1, 4.0).output(204)
connect(metal1, via1)
connect(via1, metal2)
antenna_check(gate, metal2, 1.0).output(101)
antenna_check(gate, metal2, 5.0).output(105)
antenna_check(gate, metal2, 10.0).output(110)
antenna_check(gate, metal2, 50.0).output(150)

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au48.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au48d.gds vendored Normal file

Binary file not shown.

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