mirror of https://github.com/KLayout/klayout.git
commit
63f4d727e9
|
|
@ -269,6 +269,7 @@ gen = Gen::new
|
|||
|
||||
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) >= 1.2).first_edges)", "drc_separation1un.png"
|
||||
|
||||
class Gen
|
||||
def produce(s1, s2)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ GenericWriterOptions::GenericWriterOptions ()
|
|||
m_gds2_max_vertex_count (8000),
|
||||
m_gds2_no_zero_length_paths (false),
|
||||
m_gds2_multi_xy_records (false),
|
||||
m_gds2_resolve_skew_arrays (false),
|
||||
m_gds2_max_cellname_length (32000),
|
||||
m_gds2_libname ("LIB"),
|
||||
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 "
|
||||
"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 +
|
||||
"#--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 "
|
||||
|
|
@ -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_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_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_libname", m_gds2_libname);
|
||||
save_options.set_option_by_name ("gds2_user_units", m_gds2_user_units);
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ private:
|
|||
unsigned int m_gds2_max_vertex_count;
|
||||
bool m_gds2_no_zero_length_paths;
|
||||
bool m_gds2_multi_xy_records;
|
||||
bool m_gds2_resolve_skew_arrays;
|
||||
unsigned int m_gds2_max_cellname_length;
|
||||
std::string m_gds2_libname;
|
||||
double m_gds2_user_units;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ SOURCES = \
|
|||
dbCompoundOperation.cc \
|
||||
dbEdge.cc \
|
||||
dbEdgePair.cc \
|
||||
dbEdgePairFilters.cc \
|
||||
dbEdgePairRelations.cc \
|
||||
dbEdgePairs.cc \
|
||||
dbEdgeProcessor.cc \
|
||||
|
|
@ -233,6 +234,7 @@ HEADERS = \
|
|||
dbCompoundOperation.h \
|
||||
dbEdge.h \
|
||||
dbEdgePair.h \
|
||||
dbEdgePairFilters.h \
|
||||
dbEdgePairRelations.h \
|
||||
dbEdgePairs.h \
|
||||
dbEdgeProcessor.h \
|
||||
|
|
|
|||
|
|
@ -46,7 +46,17 @@ AsIfFlatEdgePairs::AsIfFlatEdgePairs ()
|
|||
AsIfFlatEdgePairs::AsIfFlatEdgePairs (const AsIfFlatEdgePairs &other)
|
||||
: 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 ()
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ protected:
|
|||
void invalidate_bbox ();
|
||||
|
||||
private:
|
||||
friend class DeepEdgePairs;
|
||||
|
||||
AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other);
|
||||
|
||||
mutable bool m_bbox_valid;
|
||||
|
|
|
|||
|
|
@ -167,22 +167,6 @@ AsIfFlatRegion::in (const Region &other, bool invert) const
|
|||
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
|
||||
AsIfFlatRegion::is_box () const
|
||||
{
|
||||
|
|
@ -1077,8 +1061,14 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
|
|||
others.push_back (begin_merged ());
|
||||
} else {
|
||||
foreign.push_back (false);
|
||||
others.push_back (other->begin ());
|
||||
other_is_merged = other->is_merged ();
|
||||
if (options.whole_edges) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,6 @@ public:
|
|||
virtual ~AsIfFlatRegion ();
|
||||
|
||||
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 perimeter_type perimeter (const db::Box &box) const;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
return AsIfFlatEdgePairs::filter_in_place (filter);
|
||||
// TODO: implement to be really in-place
|
||||
*this = *apply_filter (filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
EdgePairsDelegate *DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
|
||||
EdgePairsDelegate *
|
||||
DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
|
||||
{
|
||||
// TODO: implement
|
||||
return AsIfFlatEdgePairs::filtered (filter);
|
||||
return apply_filter (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 *
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
DeepEdgePairs &DeepEdgePairs::operator= (const DeepEdgePairs &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
AsIfFlatEdgePairs::operator= (other);
|
||||
DeepShapeCollectionDelegateBase::operator= (other);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ private:
|
|||
|
||||
void init ();
|
||||
EdgesDelegate *generic_edges (bool first, bool second) const;
|
||||
DeepEdgePairs *apply_filter (const EdgePairFilterBase &filter) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,24 +273,33 @@ void DeepEdges::reserve (size_t)
|
|||
// 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 ()) {
|
||||
|
||||
db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
|
||||
|
||||
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 ()));
|
||||
}
|
||||
|
||||
layout.clear_layer (deep_layer ().layer ());
|
||||
top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes);
|
||||
layout.clear_layer (deep_layer.layer ());
|
||||
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 *
|
||||
DeepEdges::begin () const
|
||||
{
|
||||
|
|
@ -518,7 +527,7 @@ DeepEdges::ensure_merged_edges_valid () const
|
|||
|
||||
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 ());
|
||||
|
||||
|
|
|
|||
|
|
@ -284,26 +284,37 @@ void DeepRegion::reserve (size_t)
|
|||
// 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 ()) {
|
||||
|
||||
db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
|
||||
|
||||
db::Shapes flat_shapes (layout.is_editable ());
|
||||
for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) {
|
||||
db::Polygon poly;
|
||||
iter->polygon (poly);
|
||||
flat_shapes.insert (poly.transformed (iter.trans ()));
|
||||
for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) {
|
||||
if (iter->is_polygon ()) {
|
||||
db::Polygon poly;
|
||||
iter->polygon (poly);
|
||||
flat_shapes.insert (db::PolygonRef (poly.transformed (iter.trans ()), layout.shape_repository ()));
|
||||
}
|
||||
}
|
||||
|
||||
layout.clear_layer (deep_layer ().layer ());
|
||||
top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes);
|
||||
layout.clear_layer (deep_layer.layer ());
|
||||
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 *
|
||||
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 *>
|
||||
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
|
||||
{
|
||||
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) {
|
||||
ep.insert (s->second);
|
||||
|
|
@ -1637,8 +1656,14 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
|
|||
if (! other_deep) {
|
||||
return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options);
|
||||
}
|
||||
other_layer = other_deep->deep_layer ().layer ();
|
||||
other_is_merged = other->is_merged ();
|
||||
if (options.whole_edges) {
|
||||
// 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 ();
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ public:
|
|||
virtual RegionDelegate *and_with (const Region &other) const;
|
||||
virtual RegionDelegate *not_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 RegionDelegate *add_in_place (const Region &other);
|
||||
|
|
|
|||
|
|
@ -403,9 +403,15 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
|
|||
{
|
||||
unsigned int layer_index = layout.insert_layer ();
|
||||
|
||||
if (si.layout () && si.layer () < si.layout ()->layers ()) {
|
||||
if (si.layout ()) {
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -79,18 +79,6 @@ bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) cons
|
|||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool AllDeviceParametersAreEqual::equal (const db::Device &a, const db::Device &b) const
|
||||
{
|
||||
const std::vector<db::DeviceParameterDefinition> ¶meters = 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
|
||||
|
||||
|
|
@ -293,7 +268,7 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b)
|
|||
}
|
||||
|
||||
if (pcd != 0) {
|
||||
return pcd->equal (a, b);
|
||||
return ! pcd->less (a, b) && ! pcd->less (b, a);
|
||||
} else {
|
||||
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = a.device_class ()->parameter_definitions ();
|
||||
|
|
|
|||
|
|
@ -297,7 +297,6 @@ public:
|
|||
|
||||
virtual DeviceParameterCompareDelegate *clone () 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);
|
||||
|
||||
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
|
||||
{
|
||||
|
|
@ -345,7 +343,6 @@ public:
|
|||
AllDeviceParametersAreEqual (double relative);
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -856,6 +856,9 @@ public:
|
|||
* line through the edge. If the edge is degenerated, the distance
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -204,6 +204,24 @@ public:
|
|||
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)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -38,6 +38,9 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
// EdgePairs implementation
|
||||
|
||||
EdgePairs::EdgePairs ()
|
||||
: mp_delegate (new EmptyEdgePairs ())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,17 +42,13 @@ class Edges;
|
|||
class Region;
|
||||
class DeepShapeStore;
|
||||
class TransformationReducer;
|
||||
class EdgeFilterBase;
|
||||
|
||||
/**
|
||||
* @brief An edge pair set iterator
|
||||
*
|
||||
* 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
|
||||
: public generic_shape_iterator<db::EdgePair>
|
||||
{
|
||||
|
|
@ -117,7 +113,7 @@ public:
|
|||
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 bool wants_variants () const = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "dbShapeCollection.h"
|
||||
#include "dbShapeCollectionUtils.h"
|
||||
#include "dbGenericShapeIterator.h"
|
||||
#include "dbHash.h"
|
||||
|
||||
#include <list>
|
||||
#include <unordered_set>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "dbLayoutVsSchematic.h"
|
||||
#include "dbLayoutToNetlistFormatDefs.h"
|
||||
#include "dbLayoutVsSchematicFormatDefs.h"
|
||||
#include "dbShapeProcessor.h"
|
||||
#include "tlGlobPattern.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void LayoutToNetlist::connect (const db::Region &l)
|
||||
void LayoutToNetlist::reset_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)) {
|
||||
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)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
}
|
||||
reset_extracted ();
|
||||
|
||||
if (! is_persisted (a)) {
|
||||
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)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
}
|
||||
reset_extracted ();
|
||||
|
||||
if (! is_persisted (l)) {
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
bool skip = false;
|
||||
|
||||
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
|
||||
|
||||
db::Region rdiode;
|
||||
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode, 0);
|
||||
db::Polygon::area_type adiode_int = 0;
|
||||
db::Polygon::perimeter_type pdiode_int = 0;
|
||||
|
||||
compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (*d->first), adiode_int, pdiode_int);
|
||||
|
||||
if (fabs (d->second) < db::epsilon) {
|
||||
if (rdiode.area () > 0) {
|
||||
if (adiode_int > 0) {
|
||||
skip = true;
|
||||
}
|
||||
} else {
|
||||
r += rdiode.area () * dbu * dbu * d->second;
|
||||
r += adiode_int * dbu * dbu * d->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! skip) {
|
||||
|
||||
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);
|
||||
db::Polygon::area_type agate_int = 0;
|
||||
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) {
|
||||
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
|
||||
for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) {
|
||||
shapes.insert (*r);
|
||||
if (agate > dbu * dbu) {
|
||||
|
||||
db::Polygon::area_type ametal_int = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -359,6 +359,14 @@ public:
|
|||
*/
|
||||
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.
|
||||
* 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 ();
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
const TS &subject_shape = interactions.subject_shape (i->first);
|
||||
|
||||
shape_interactions<TS, TI> single_interactions;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
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));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
|
|||
os << format_name (dev.device_class ()->name ());
|
||||
}
|
||||
|
||||
} else if (res) {
|
||||
} else if (res || res3) {
|
||||
|
||||
os << "R";
|
||||
os << format_name (dev.expanded_name ());
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbDeepEdges.h"
|
||||
#include "dbDeepRegion.h"
|
||||
#include "dbDeepShapeStore.h"
|
||||
#include "dbCellGraphUtils.h"
|
||||
#include "tlGlobPattern.h"
|
||||
|
||||
namespace db
|
||||
|
|
@ -186,6 +187,96 @@ OriginalLayerRegion::min_coherence_changed ()
|
|||
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 *
|
||||
OriginalLayerRegion::begin () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ public:
|
|||
virtual bool empty () 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 bool has_valid_polygons () const;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
validate (0);
|
||||
if (m_has_layers) {
|
||||
validate (0);
|
||||
}
|
||||
return m_layer;
|
||||
}
|
||||
|
||||
|
|
@ -562,7 +564,7 @@ public:
|
|||
* @brief Gets the layers from which the shapes are taken from
|
||||
*
|
||||
* 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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@
|
|||
#include "dbShapeCollection.h"
|
||||
#include "dbGenericShapeIterator.h"
|
||||
#include "dbRegionLocalOperations.h"
|
||||
#include "dbHash.h"
|
||||
|
||||
#include <list>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace db {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// captured.
|
||||
db::EdgeProcessor ep;
|
||||
ep.set_base_verbosity (50);
|
||||
|
||||
ep.clear ();
|
||||
size_t i = 0;
|
||||
|
|
@ -561,6 +562,7 @@ void interacting_local_operation<TS, TI, TR>::do_compute_local (db::Layout * /*l
|
|||
}
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
ep.set_base_verbosity (50);
|
||||
|
||||
std::set<TI> others;
|
||||
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
|
||||
db::EdgeProcessor ep_merge;
|
||||
ep_merge.set_base_verbosity (50);
|
||||
|
||||
size_t i = 0;
|
||||
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 ();
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
ep.set_base_verbosity (50);
|
||||
|
||||
std::set<TI> others;
|
||||
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo
|
|||
db::Point pn = ctr [j];
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace db
|
|||
class DB_PUBLIC CornerPointDelivery
|
||||
{
|
||||
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)
|
||||
{ }
|
||||
|
||||
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)));
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ public:
|
|||
: 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));
|
||||
}
|
||||
|
|
@ -86,6 +86,26 @@ private:
|
|||
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
|
||||
*/
|
||||
|
|
@ -155,6 +175,31 @@ public:
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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>
|
||||
void
|
||||
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.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.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) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p1);
|
||||
if (take_all || interact (common_box, *e)) {
|
||||
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) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p2);
|
||||
if (take_all || interact (common_box, *e)) {
|
||||
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);
|
||||
|
||||
tl_assert (m_edges.size () == vertices (o1) + vertices (o2));
|
||||
|
||||
// temporarily disable intra-polygon check in that step .. we do that later in finish()
|
||||
// if required (#650).
|
||||
bool no_intra = mp_output->different_polygons ();
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ public:
|
|||
* @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)
|
||||
*/
|
||||
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
|
||||
|
|
@ -641,6 +641,14 @@ public:
|
|||
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
|
||||
*/
|
||||
|
|
@ -649,6 +657,14 @@ public:
|
|||
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
|
||||
*/
|
||||
|
|
@ -828,9 +844,13 @@ protected:
|
|||
if (layer == 0) {
|
||||
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) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1250,6 +1250,20 @@ public:
|
|||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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*/);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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"),
|
||||
"@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),
|
||||
"@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."
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include "dbEdges.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbDeepEdgePairs.h"
|
||||
#include "dbEdgesUtils.h"
|
||||
#include "dbEdgePairFilters.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
|
@ -176,6 +178,74 @@ static size_t id (const db::EdgePairs *ep)
|
|||
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;
|
||||
|
||||
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 "
|
||||
"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,
|
||||
"@brief Converts the edge pairs to polygons\n"
|
||||
"This method creates polygons from the edge pairs. Each polygon will be a triangle or quadrangle "
|
||||
|
|
|
|||
|
|
@ -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 not having the given angle are returned.\n"
|
||||
"\n"
|
||||
"This will filter horizontal edges:\n"
|
||||
"This will select horizontal edges:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"horizontal = edges.with_orientation(0, true)\n"
|
||||
"horizontal = edges.with_orientation(0, false)\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),
|
||||
|
|
|
|||
|
|
@ -394,6 +394,18 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
|||
"\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"),
|
||||
"@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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
@ -903,13 +894,12 @@ Class<db::EqualDeviceParameters> decl_dbEqualDeviceParameters ("db", "EqualDevic
|
|||
);
|
||||
|
||||
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"),
|
||||
"@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."
|
||||
"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"
|
||||
"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 "
|
||||
"implementing a custom comparer class.\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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return new db::Region (si);
|
||||
|
|
@ -1373,14 +1378,22 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"A similar function that reports corners as point-like edges is \\corners_dots.\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),
|
||||
"@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 \\Edges collection with dot-like edges for each corner.\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,
|
||||
"@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 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 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"
|
||||
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
|
||||
"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. "
|
||||
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\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"
|
||||
"\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),
|
||||
"@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 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 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"
|
||||
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
|
||||
"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. "
|
||||
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\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"
|
||||
"\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),
|
||||
"@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 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 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"
|
||||
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
|
||||
"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. "
|
||||
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\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"
|
||||
"\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),
|
||||
"@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 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 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"
|
||||
"If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole "
|
||||
"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. "
|
||||
"\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\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"
|
||||
"\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,
|
||||
"@brief The area of the region\n"
|
||||
|
|
|
|||
|
|
@ -202,3 +202,17 @@ TEST(3_symmetric)
|
|||
eh.insert (db::EdgePair (e2, e1, true));
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).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,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)
|
||||
|
|
|
|||
|
|
@ -787,14 +787,14 @@ CODE
|
|||
# The "corners" method is available as a plain function or as a method on \DRC# expressions.
|
||||
# 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
|
||||
if as_dots.is_a?(DRCAsDots)
|
||||
as_dots = as_dots.value
|
||||
if output_mode.is_a?(DRCOutputMode)
|
||||
output_mode = output_mode.value
|
||||
else
|
||||
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
|
||||
end
|
||||
DRCOpNodeCornersFilter::new(@engine, as_dots, self)
|
||||
DRCOpNodeCornersFilter::new(@engine, output_mode, self)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -859,8 +859,8 @@ CODE
|
|||
args.each_with_index do |a,ia|
|
||||
if a.is_a?(1.0.class) && :#{f} != :middle
|
||||
f << a
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_edges = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
as_edges = (a.value == :edges || a.value == :dots)
|
||||
elsif @@std_refs[a] && :#{f} != :middle
|
||||
f = @@std_refs[a]
|
||||
else
|
||||
|
|
@ -1996,11 +1996,11 @@ end
|
|||
class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
|
||||
|
||||
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)
|
||||
self.as_dots = as_dots
|
||||
self.output_mode = output_mode
|
||||
self.input = input
|
||||
self.description = "corners"
|
||||
end
|
||||
|
|
@ -2011,8 +2011,10 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
|
|||
args << (self.gt ? false : true)
|
||||
args << (self.lt ? self.lt : (self.le ? self.le : 180.0))
|
||||
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)
|
||||
elsif self.output_mode == :edge_pairs
|
||||
RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args)
|
||||
else
|
||||
args << 2 # dimension is 2x2 DBU
|
||||
RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args)
|
||||
|
|
|
|||
|
|
@ -781,7 +781,7 @@ CODE
|
|||
|
||||
# %DRC%
|
||||
# @name corners
|
||||
# @brief Selects all polygons which are rectilinear
|
||||
# @brief Selects corners of polygons
|
||||
# @synopsis corners([ options ]) (in condition)
|
||||
# @synopsis corners(layer [, options ])
|
||||
#
|
||||
|
|
@ -791,15 +791,16 @@ CODE
|
|||
# \DRC# expressions (see \Layer#drc and \DRC#corners for more details).
|
||||
#
|
||||
# Like the layer-based version, the "corners" operator accepts the
|
||||
# output type option: "as_dots" for dot-like edges and "as_boxes" for
|
||||
# small (2x2 DBU) box markers.
|
||||
# output type option: "as_dots" for dot-like edges, "as_boxes" for
|
||||
# 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
|
||||
# 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.
|
||||
return primary.corners(as_dots)
|
||||
return primary.corners(output_mode)
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -1024,7 +1025,7 @@ CODE
|
|||
#
|
||||
# With a lower and upper limit, the results are edges marking the positions on the
|
||||
# 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
|
||||
# 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
|
||||
# @/tr
|
||||
# @/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%
|
||||
# @name separation
|
||||
|
|
@ -1048,7 +1055,7 @@ CODE
|
|||
# as metrics, projection and angle constraints etc. This check also features
|
||||
# opposite and rectangle filtering. See \Layer#separation for details about opposite and
|
||||
# rectangle error filtering.
|
||||
#
|
||||
#
|
||||
# @h3 Classic mode @/h3
|
||||
#
|
||||
# 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
|
||||
# @/tr
|
||||
# @/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%
|
||||
# @name overlap
|
||||
|
|
@ -1124,6 +1148,12 @@ CODE
|
|||
# @td @img(/images/drc_overlap2u.png) @/td
|
||||
# @/tr
|
||||
# @/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%
|
||||
# @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
|
||||
# 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
|
||||
# shape.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ module DRC
|
|||
|
||||
end
|
||||
|
||||
def both
|
||||
DRCBothEdges::new
|
||||
end
|
||||
|
||||
def shift(x, y)
|
||||
self._context("shift") do
|
||||
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)
|
||||
end
|
||||
|
||||
def padding_zero
|
||||
DRCDensityPadding::new(:zero)
|
||||
end
|
||||
|
||||
def padding_ignore
|
||||
DRCDensityPadding::new(:ignore)
|
||||
end
|
||||
|
||||
def diamond_limit
|
||||
DRCSizingMode::new(0)
|
||||
end
|
||||
|
|
@ -214,15 +226,19 @@ module DRC
|
|||
end
|
||||
|
||||
def as_dots
|
||||
DRCAsDots::new(true)
|
||||
DRCOutputMode::new(:dots)
|
||||
end
|
||||
|
||||
def as_edges
|
||||
DRCAsDots::new(true)
|
||||
DRCOutputMode::new(:edges)
|
||||
end
|
||||
|
||||
def as_boxes
|
||||
DRCAsDots::new(false)
|
||||
DRCOutputMode::new(:boxes)
|
||||
end
|
||||
|
||||
def as_edge_pairs
|
||||
DRCOutputMode::new(:edge_pairs)
|
||||
end
|
||||
|
||||
def area_only(r)
|
||||
|
|
@ -1793,6 +1809,7 @@ CODE
|
|||
with_bbox_min
|
||||
with_bbox_width
|
||||
with_length
|
||||
with_distance
|
||||
without_angle
|
||||
without_area
|
||||
without_area_ratio
|
||||
|
|
|
|||
|
|
@ -276,16 +276,37 @@ module DRC
|
|||
end
|
||||
|
||||
# %DRC%
|
||||
# @name size
|
||||
# @name count
|
||||
# @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
|
||||
# regions or edges. It is more efficent to call this method on output layers than
|
||||
# on input layers.
|
||||
# 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.
|
||||
#
|
||||
# See \hier_count for a hierarchical (each cell counts once) count.
|
||||
|
||||
def size
|
||||
self.data.size
|
||||
def count
|
||||
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
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -678,13 +699,28 @@ CODE
|
|||
# @synopsis layer.with_length(min .. max)
|
||||
# @synopsis layer.with_length(value)
|
||||
# @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).
|
||||
# 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
|
||||
# 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%
|
||||
# @name without_length
|
||||
|
|
@ -692,11 +728,21 @@ CODE
|
|||
# @synopsis layer.without_length(min .. max)
|
||||
# @synopsis layer.without_length(value)
|
||||
# @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
|
||||
# of the edge layer which do not have the given length (second form) or are
|
||||
# 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|
|
||||
[true, false].each do |inv|
|
||||
|
|
@ -706,18 +752,96 @@ CODE
|
|||
|
||||
@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
|
||||
a = args[0]
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
@ -733,6 +857,7 @@ CODE
|
|||
# @synopsis layer.with_angle(min .. max)
|
||||
# @synopsis layer.with_angle(value)
|
||||
# @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,
|
||||
# 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.
|
||||
# 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.
|
||||
#
|
||||
# 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(value)
|
||||
# @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
|
||||
# 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).
|
||||
#
|
||||
# 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|
|
||||
mn = (inv ? "without" : "with") + "_angle"
|
||||
eval <<"CODE"
|
||||
def #{mn}(*args)
|
||||
%w(angle).each do |f|
|
||||
[true, false].each do |inv|
|
||||
mn = (inv ? "without" : "with") + "_" + f
|
||||
eval <<"CODE"
|
||||
def #{mn}(*args)
|
||||
|
||||
@engine._context("#{mn}") do
|
||||
@engine._context("#{mn}") do
|
||||
|
||||
requires_edges_or_region
|
||||
result_class = self.data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges
|
||||
if args.size == 1
|
||||
a = args[0]
|
||||
if a.is_a?(Range)
|
||||
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a.begin, a.end, #{inv.inspect}))
|
||||
else
|
||||
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a, #{inv.inspect}))
|
||||
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
|
||||
elsif args.size == 2
|
||||
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect}))
|
||||
else
|
||||
raise("Invalid number of arguments (1 or 2 expected)")
|
||||
|
||||
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
|
||||
if args.size == 1
|
||||
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
|
||||
CODE
|
||||
end
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -954,8 +1118,8 @@ CODE
|
|||
elsif a.is_a?(DRCPattern)
|
||||
as_pattern = a.as_pattern
|
||||
pattern = a.pattern
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_dots = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
as_dots = (a.value == :edges || a.value == :dots)
|
||||
else
|
||||
raise("Invalid argument type #{a.inspect}")
|
||||
end
|
||||
|
|
@ -1003,6 +1167,8 @@ CODE
|
|||
# @ul
|
||||
# @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_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
|
||||
#
|
||||
# The following images show the effect of this method:
|
||||
|
|
@ -1021,7 +1187,7 @@ CODE
|
|||
|
||||
requires_region
|
||||
|
||||
as_dots = false
|
||||
output_mode = :boxes
|
||||
amin = -180.0
|
||||
amax = 180.0
|
||||
|
||||
|
|
@ -1035,14 +1201,23 @@ CODE
|
|||
elsif a.is_a?(1.0.class) || a.is_a?(1.class)
|
||||
amin = a.to_f
|
||||
amax = a.to_f
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_dots = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
output_mode = a.value
|
||||
else
|
||||
raise("Invalid argument #{a.inspect}")
|
||||
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
|
||||
|
||||
|
|
@ -1211,8 +1386,8 @@ CODE
|
|||
args.each do |a|
|
||||
if a.is_a?(1.0.class) && :#{f} != :middle
|
||||
f << a
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_edges = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
as_edges = (a.value == :edges || a.value == :dots)
|
||||
elsif @@std_refs[a] && :#{f} != :middle
|
||||
f = @@std_refs[a]
|
||||
else
|
||||
|
|
@ -1335,7 +1510,7 @@ CODE
|
|||
# %DRC%
|
||||
# @name collect_to_region
|
||||
# @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
|
||||
# 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.
|
||||
|
|
@ -1343,7 +1518,7 @@ CODE
|
|||
# %DRC%
|
||||
# @name collect_to_edges
|
||||
# @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
|
||||
# deliver objects that can be converted into edges. If polygon-like objects are returned, their
|
||||
# contours will be turned into edge sequences.
|
||||
|
|
@ -1351,7 +1526,7 @@ CODE
|
|||
# %DRC%
|
||||
# @name collect_to_edge_pairs
|
||||
# @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
|
||||
# deliver RBA::EdgePair, RBA::DEdgePair or RBA::EdgePairs objects.
|
||||
|
||||
|
|
@ -1613,6 +1788,9 @@ CODE
|
|||
# @td @img(/images/drc_or2.png) @/td
|
||||
# @/tr
|
||||
# @/table
|
||||
#
|
||||
# In deep mode, "or" or "|" does not imply merging. In deep mode,
|
||||
# "or" is an alias for "+" ("add").
|
||||
|
||||
def or(other)
|
||||
@engine._context("or") do
|
||||
|
|
@ -3344,15 +3522,15 @@ CODE
|
|||
|
||||
# %DRC%
|
||||
# @name isolated
|
||||
# @brief An isolation check
|
||||
# @brief An inter-polygon isolation check
|
||||
# @synopsis layer.isolated(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
|
||||
# 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.
|
||||
# In contrast to \space, this
|
||||
# See \space for a description of this method. "isolated" is the space check variant which checks different polygons only.
|
||||
# In contrast to \space, the "isolated"
|
||||
# method is available for polygon layers only, since only on such layers
|
||||
# different polygons can be identified.
|
||||
#
|
||||
|
|
@ -3368,14 +3546,15 @@ CODE
|
|||
|
||||
# %DRC%
|
||||
# @name notch
|
||||
# @brief An intra-region spacing check
|
||||
# @brief An intra-polygon spacing check
|
||||
# @synopsis layer.notch(value [, options])
|
||||
#
|
||||
# @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.
|
||||
#
|
||||
# See \space for a description of this method.
|
||||
# In contrast to \space, this
|
||||
# See \space for a description of this method.
|
||||
# "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
|
||||
# different polygons can be identified. Also, opposite and rectangle error
|
||||
# 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):
|
||||
# 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
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
|
@ -3752,6 +3945,7 @@ CODE
|
|||
tile_origin = nil
|
||||
tile_count = nil
|
||||
tile_boundary = nil
|
||||
padding_mode = :zero
|
||||
|
||||
n = 1
|
||||
args.each do |a|
|
||||
|
|
@ -3765,6 +3959,8 @@ CODE
|
|||
tile_count = a.get
|
||||
elsif a.is_a?(DRCTileBoundary)
|
||||
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
|
||||
nlimits < 2 || raise("Too many values specified")
|
||||
limits[nlimits] = @engine._make_numeric_value_with_nil(a)
|
||||
|
|
@ -3809,18 +4005,34 @@ CODE
|
|||
tp.threads = (@engine.threads || 1)
|
||||
if tile_boundary
|
||||
tp.input("boundary", tile_boundary.data)
|
||||
else
|
||||
tp.input("boundary", RBA::Region::new(self.data.bbox))
|
||||
end
|
||||
|
||||
tp.var("vmin", limits[0] || 0.0)
|
||||
tp.var("vmax", limits[1] || 1.0)
|
||||
tp.var("inverse", inverse)
|
||||
tp.queue(<<"TP_SCRIPT")
|
||||
_tile && (
|
||||
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)
|
||||
)
|
||||
|
||||
if padding_mode == :zero
|
||||
tp.queue(<<"TP_SCRIPT")
|
||||
_tile && (
|
||||
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
|
||||
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
|
||||
tp.execute("Tiled \"#{method}\" in: #{@engine.src_line}")
|
||||
|
|
@ -4535,13 +4747,17 @@ END
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def requires_edges
|
||||
self.data.is_a?(RBA::Edges) || raise("Requires an edge layer")
|
||||
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
|
||||
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("Requires an edge or polygon layer")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ module DRC
|
|||
|
||||
def initialize(engine)
|
||||
@engine = engine
|
||||
@netlisted = false
|
||||
@connect_implicit = []
|
||||
@connect_implicit_per_cell = {}
|
||||
@connect_explicit = []
|
||||
|
|
@ -240,7 +239,6 @@ module DRC
|
|||
# See \connect for more details.
|
||||
|
||||
def clear_connections
|
||||
@netlisted = false
|
||||
@connect_implicit = []
|
||||
@connect_implicit_per_cell = {}
|
||||
@connect_explicit = []
|
||||
|
|
@ -387,6 +385,25 @@ module DRC
|
|||
# errors = antenna_check(gate, metal1, 50.0)
|
||||
# @/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
|
||||
# which create a safe current path for discharging the metal
|
||||
# islands. Such diodes can be identified with a recognition layer
|
||||
|
|
@ -544,7 +561,7 @@ module DRC
|
|||
ensure_data
|
||||
|
||||
# run extraction in a timed environment
|
||||
if ! @netlisted
|
||||
if ! @l2n.is_extracted?
|
||||
|
||||
# configure implicit net connections
|
||||
@l2n.clear_join_net_names
|
||||
|
|
@ -569,7 +586,6 @@ module DRC
|
|||
end
|
||||
|
||||
@engine._cmd(@l2n, :extract_netlist)
|
||||
@netlisted = true
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -609,13 +625,13 @@ module DRC
|
|||
end
|
||||
|
||||
def _l2n_data
|
||||
@netlisted && self.l2n_data
|
||||
@l2n && @l2n.is_extracted? && self.l2n_data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cleanup
|
||||
@netlisted && clear_connections
|
||||
@l2n && @l2n.is_extracted? && clear_connections
|
||||
end
|
||||
|
||||
def ensure_data
|
||||
|
|
@ -644,14 +660,13 @@ module DRC
|
|||
def register_layer(data)
|
||||
|
||||
id = data.data_id
|
||||
ensure_data
|
||||
|
||||
if @layers && @layers[id]
|
||||
# already registered
|
||||
return
|
||||
end
|
||||
|
||||
ensure_data
|
||||
|
||||
@layers[id] = data
|
||||
@lnum += 1
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,18 @@ module DRC
|
|||
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
|
||||
class DRCSizingMode
|
||||
attr_accessor :value
|
||||
|
|
@ -65,7 +77,7 @@ module DRC
|
|||
# A wrapper for the "as_dots" or "as_boxes" flag for
|
||||
# some DRC functions. The purpose of this class
|
||||
# is to identify the value by the class.
|
||||
class DRCAsDots
|
||||
class DRCOutputMode
|
||||
attr_accessor :value
|
||||
def initialize(v)
|
||||
self.value = v
|
||||
|
|
|
|||
|
|
@ -225,6 +225,46 @@ TEST(5_FlatAntenna)
|
|||
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)
|
||||
{
|
||||
std::string rs = tl::testdata ();
|
||||
|
|
@ -1232,3 +1272,23 @@ TEST(47_fillWithOverlappingBoxesTiled)
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ See <a href="/about/drc_ref_netter.xml#connect_global">Netter#connect_global</a>
|
|||
<p>
|
||||
See <a href="/about/drc_ref_netter.xml#connect_implicit">Netter#connect_implicit</a> for a description of that function.
|
||||
</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"/>
|
||||
<p>Usage:</p>
|
||||
<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).
|
||||
</p><p>
|
||||
Like the layer-based version, the "corners" operator accepts the
|
||||
output type option: "as_dots" for dot-like edges and "as_boxes" for
|
||||
small (2x2 DBU) box markers.
|
||||
output type option: "as_dots" for dot-like edges, "as_boxes" for
|
||||
small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs.
|
||||
The default output type is "as_boxes".
|
||||
</p><p>
|
||||
The "corners" operator can be put into a condition which means it's
|
||||
applied to corners meeting a particular angle constraint.
|
||||
|
|
@ -586,7 +587,7 @@ These markers indicate the presence of the specified condition.
|
|||
</p><p>
|
||||
With a lower and upper limit, the results are edges marking the positions on the
|
||||
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
|
||||
primary shape while the second edge is attached to the shape of the "other" layer.
|
||||
</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>
|
||||
</tr>
|
||||
</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>
|
||||
<a name="error"/><h2>"error" - Prints an error</h2>
|
||||
<keyword name="error"/>
|
||||
|
|
@ -1216,6 +1222,11 @@ errors = in.drc(overlap(other) < 0.2.um)
|
|||
<td><img src="/images/drc_overlap2u.png"/></td>
|
||||
</tr>
|
||||
</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>
|
||||
<a name="overlapping"/><h2>"overlapping" - Selects shapes overlapping with other shapes</h2>
|
||||
<keyword name="overlapping"/>
|
||||
|
|
@ -1511,6 +1522,23 @@ work in generating error markers.
|
|||
<td><img src="/images/drc_separation1u.png"/></td>
|
||||
</tr>
|
||||
</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) >= 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>
|
||||
<a name="silent"/><h2>"silent" - Resets verbose mode</h2>
|
||||
<keyword name="silent"/>
|
||||
|
|
@ -1863,7 +1891,7 @@ errors = in.drc(0.1.um <= width < 0.2.um)
|
|||
</p><p>
|
||||
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.
|
||||
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
|
||||
shape.
|
||||
</p><p>
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ new_layer = layer.collect { |polygon| polygon.transformed(t) }
|
|||
<keyword name="collect_to_edge_pairs"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>layer.collect { |object| ... }</tt></li>
|
||||
<li><tt>layer.collect_to_edge_pairs { |object| ... }</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
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"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>layer.collect { |object| ... }</tt></li>
|
||||
<li><tt>layer.collect_to_edges { |object| ... }</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
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"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>layer.collect { |object| ... }</tt></li>
|
||||
<li><tt>layer.collect_to_region { |object| ... }</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
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>
|
||||
<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_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>
|
||||
</p><p>
|
||||
The following images show the effect of this method:
|
||||
|
|
@ -275,6 +277,21 @@ The following images show the effect of this method:
|
|||
</tr>
|
||||
</table>
|
||||
</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>
|
||||
<keyword name="covering"/>
|
||||
<p>Usage:</p>
|
||||
|
|
@ -1187,6 +1204,21 @@ l = nil
|
|||
</p><p>
|
||||
By setting the layer to nil, it is ensured that it can no longer be accessed.
|
||||
</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>
|
||||
<keyword name="holes"/>
|
||||
<p>Usage:</p>
|
||||
|
|
@ -1438,7 +1470,7 @@ See <a href="#clean">clean</a> for a discussion of the raw state.
|
|||
<p>
|
||||
See <a href="#isolated">isolated</a> for a description of that method
|
||||
</p>
|
||||
<a name="isolated"/><h2>"isolated" - An isolation check</h2>
|
||||
<a name="isolated"/><h2>"isolated" - An inter-polygon isolation check</h2>
|
||||
<keyword name="isolated"/>
|
||||
<p>Usage:</p>
|
||||
<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
|
||||
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>
|
||||
See <a href="#space">space</a> for a description of this method.
|
||||
In contrast to <a href="#space">space</a>, this
|
||||
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>, the "isolated"
|
||||
method is available for polygon layers only, since only on such layers
|
||||
different polygons can be identified.
|
||||
</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
|
||||
is <a href="#select_not_overlapping">select_not_overlapping</a>.
|
||||
</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"/>
|
||||
<p>Usage:</p>
|
||||
<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
|
||||
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>
|
||||
See <a href="#space">space</a> for a description of this method.
|
||||
In contrast to <a href="#space">space</a>, this
|
||||
See <a href="#space">space</a> for a description of this method.
|
||||
"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
|
||||
different polygons can be identified. Also, opposite and rectangle error
|
||||
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>
|
||||
</tr>
|
||||
</table>
|
||||
</p><p>
|
||||
In deep mode, "or" or "|" does not imply merging. In deep mode,
|
||||
"or" is an alias for "+" ("add").
|
||||
</p>
|
||||
<a name="output"/><h2>"output" - Outputs the content of the layer</h2>
|
||||
<keyword name="output"/>
|
||||
|
|
@ -2688,12 +2724,17 @@ The following images show the effect of various forms of the "sized" method:
|
|||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>layer.smoothed(d)</tt></li>
|
||||
<li><tt>layer.smoothed(d, hv_keep)</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
"Smoothing" returns a simplified version of the polygons. Simplification is
|
||||
achieved by removing vertices unless the resulting polygon deviates by more
|
||||
than the given distance d from the original polygon.
|
||||
</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
|
||||
method (see <a href="#raw">raw</a> and <a href="#clean">clean</a>).
|
||||
</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(value)</tt></li>
|
||||
<li><tt>layer.with_angle(min, max)</tt></li>
|
||||
<li><tt>edge_pairlayer.with_angle(min, max [, both])</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
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.
|
||||
For each corner, an edge pair containing the edges forming in the angle is returned.
|
||||
</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>.
|
||||
</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>
|
||||
|
|
@ -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))
|
||||
</pre>
|
||||
</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>.
|
||||
</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>
|
||||
<keyword name="with_holes"/>
|
||||
<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(value)</tt></li>
|
||||
<li><tt>layer.with_length(min, max)</tt></li>
|
||||
<li><tt>edge_pairlayer.with_length(min, max [, both])</tt></li>
|
||||
</ul>
|
||||
<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).
|
||||
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
|
||||
max indicating that there is no lower or upper limit.
|
||||
</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 <= 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)
|
||||
</pre>
|
||||
</p>
|
||||
<a name="with_perimeter"/><h2>"with_perimeter" - Selects polygons by perimeter</h2>
|
||||
<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(value)</tt></li>
|
||||
<li><tt>layer.without_angle(min, max)</tt></li>
|
||||
<li><tt>edge_pairlayer.without_angle(min, max [, both])</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
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
|
||||
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>
|
||||
<a name="without_area"/><h2>"without_area" - Selects polygons by area</h2>
|
||||
<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
|
||||
tiles where the density is not within the given range.
|
||||
</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>
|
||||
<keyword name="without_holes"/>
|
||||
<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(value)</tt></li>
|
||||
<li><tt>layer.without_length(min, max)</tt></li>
|
||||
<li><tt>edge_pairlayer.with_length(min, max [, both])</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
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
|
||||
not inside the given interval (first and third form).
|
||||
</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>
|
||||
<a name="without_perimeter"/><h2>"without_perimeter" - Selects polygons by perimeter</h2>
|
||||
<keyword name="without_perimeter"/>
|
||||
|
|
|
|||
|
|
@ -99,6 +99,25 @@ connect(contact, metal1)
|
|||
errors = antenna_check(gate, metal1, 50.0)
|
||||
</pre>
|
||||
</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
|
||||
which create a safe current path for discharging the metal
|
||||
islands. Such diodes can be identified with a recognition layer
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
|
|
@ -18,6 +18,7 @@
|
|||
<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_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_separation3.png">doc/images/drc_separation3.png</file>
|
||||
<file alias="drc_separation4.png">doc/images/drc_separation4.png</file>
|
||||
|
|
|
|||
|
|
@ -120,9 +120,8 @@ LogFile::LogFile (size_t max_entries, bool register_global)
|
|||
{
|
||||
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
|
||||
|
||||
m_timer.setSingleShot (false);
|
||||
m_timer.setInterval (100);
|
||||
m_timer.start ();
|
||||
m_timer.setSingleShot (true);
|
||||
m_timer.setInterval (0);
|
||||
|
||||
if (register_global) {
|
||||
tl::info.add (&m_info_receiver, false);
|
||||
|
|
@ -253,30 +252,10 @@ LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continu
|
|||
void
|
||||
LogFile::yield ()
|
||||
{
|
||||
#if 0
|
||||
// This looked like a good idea, but in fact it introduces a hell lot of instability
|
||||
// as it potentially leads to a recursion of events inside innocent functions. Remember
|
||||
// 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;
|
||||
}
|
||||
// will update on next processEvents
|
||||
if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread ()) {
|
||||
m_timer.start ();
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include "ui_LogViewerDialog.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
#include "layCommon.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
|
@ -233,7 +232,6 @@ private:
|
|||
size_t m_last_generation_id;
|
||||
bool m_has_errors, m_has_warnings;
|
||||
bool m_last_attn;
|
||||
tl::Clock m_last_yield;
|
||||
|
||||
/**
|
||||
* @brief Adds an error
|
||||
|
|
|
|||
|
|
@ -206,3 +206,8 @@ TEST(23_issue709)
|
|||
run_test (_this, "empty_subcells", "empty_subcells.gds");
|
||||
}
|
||||
|
||||
// empty gds
|
||||
TEST(24_issue806)
|
||||
{
|
||||
run_test (_this, "custom_compare", "custom_compare.gds");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ TEST(16_private)
|
|||
TEST(17_private)
|
||||
{
|
||||
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)
|
||||
|
|
@ -170,6 +170,6 @@ TEST(19_private)
|
|||
TEST(20_private)
|
||||
{
|
||||
// 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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ class GDS2FormatDeclaration
|
|||
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::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_cellname_length, "max-cellname-length") +
|
||||
tl::make_member (&db::GDS2WriterOptions::libname, "libname")
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ public:
|
|||
: max_vertex_count (8000),
|
||||
no_zero_length_paths (false),
|
||||
multi_xy_records (false),
|
||||
resolve_skew_arrays (false),
|
||||
max_cellname_length (32000),
|
||||
libname ("LIB"),
|
||||
user_units (1.0),
|
||||
|
|
@ -146,6 +147,13 @@ public:
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4);
|
||||
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.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 ()) {
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
unsigned long 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) {
|
||||
|
||||
db::Trans t = *ii;
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ protected:
|
|||
/**
|
||||
* @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
|
||||
|
|
|
|||
|
|
@ -65,6 +65,16 @@ static bool get_gds2_multi_xy_records (const db::SaveLayoutOptions *options)
|
|||
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)
|
||||
{
|
||||
options->get_options<db::GDS2WriterOptions> ().write_file_properties = n;
|
||||
|
|
@ -129,7 +139,7 @@ static double get_gds2_user_units (const db::SaveLayoutOptions *options)
|
|||
static
|
||||
gsi::ClassExt<db::SaveLayoutOptions> gds2_writer_options (
|
||||
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"
|
||||
"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"
|
||||
|
|
@ -138,24 +148,37 @@ gsi::ClassExt<db::SaveLayoutOptions> gds2_writer_options (
|
|||
"\nThis property has been added in version 0.18.\n"
|
||||
) +
|
||||
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."
|
||||
"\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"),
|
||||
"@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"
|
||||
"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"
|
||||
"\nThis property has been added in version 0.18.\n"
|
||||
) +
|
||||
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."
|
||||
"\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"),
|
||||
"@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"
|
||||
"If this property is set to false, the time fields will all be zero. This somewhat simplifies compare and diff "
|
||||
"applications.\n"
|
||||
|
|
|
|||
|
|
@ -50,58 +50,13 @@
|
|||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Max. vertices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="3">
|
||||
<item row="7" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="write_timestamps">
|
||||
<property name="text">
|
||||
<string>Write current time to time stamps (BGNLIB, BGNSTR)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>(<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">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
|
|
@ -109,13 +64,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="no_zero_length_paths">
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Eliminate zero-length paths (convert to BOUNDARY)</string>
|
||||
<string>(<4000 recommended, absolute limit 8191)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="cell_name_length_le"/>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
|
|
@ -181,6 +139,55 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ GDS2WriterOptionPage::setup (const db::FormatSpecificWriterOptions *o, const db:
|
|||
mp_ui->write_file_properties->setChecked (options->write_file_properties);
|
||||
mp_ui->no_zero_length_paths->setChecked (options->no_zero_length_paths);
|
||||
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->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)));
|
||||
|
|
@ -76,6 +77,7 @@ GDS2WriterOptionPage::commit (db::FormatSpecificWriterOptions *o, const db::Tech
|
|||
|
||||
unsigned int n;
|
||||
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_cell_properties = mp_ui->write_cell_properties->isChecked ();
|
||||
options->write_file_properties = mp_ui->write_file_properties->isChecked ();
|
||||
|
|
|
|||
|
|
@ -83,6 +83,19 @@ TEST(1)
|
|||
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)
|
||||
{
|
||||
db::Manager m (false);
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ public:
|
|||
: m_obj (obj), mp_cls (0), m_members (Qnil)
|
||||
{
|
||||
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);
|
||||
rb_gc_register_address (&m_members);
|
||||
|
||||
|
|
|
|||
|
|
@ -940,6 +940,13 @@ bool is_registered (const gsi::ClassBase *cls)
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -954,8 +961,7 @@ const gsi::ClassBase *find_cclass (VALUE k)
|
|||
}
|
||||
}
|
||||
|
||||
tl_assert (cls != cls_map.end ());
|
||||
return cls->second;
|
||||
return cls != cls_map.end () ? cls->second : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,6 +218,11 @@ void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls);
|
|||
*/
|
||||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ layout(
|
|||
# This section lists the mask layers (drawing or derived) and their connections.
|
||||
|
||||
# Mask layers
|
||||
layer(bulk '1/0')
|
||||
layer(bulk)
|
||||
layer(nwell '1/0')
|
||||
layer(poly '3/0')
|
||||
layer(poly_lbl '3/1')
|
||||
|
|
|
|||
|
|
@ -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(103, 0) # inner corners
|
||||
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(as_dots)).output(111, 0)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -3,7 +3,17 @@ dbu 0.001
|
|||
|
||||
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
|
||||
expect_count(x, 0, 0, "empty layer")
|
||||
x.is_empty? == true || raise("unexpected value")
|
||||
x.is_box? == false || raise("unexpected value")
|
||||
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, -5.0), p(2.0, -5.0), p(1.0, -6.0) ]))
|
||||
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.output(10, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
target($drc_test_target, "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)
|
||||
b1 = input(2)
|
||||
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(-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_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.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_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)")
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,18 @@ b.output(0, 0)
|
|||
a.output(1, 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, 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, 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.
|
|
@ -4,6 +4,15 @@
|
|||
source($drc_test_source, "TOPTOP_SMALL")
|
||||
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")
|
||||
|
||||
l1_flat = input(1)
|
||||
|
|
@ -66,12 +75,16 @@ r.output(1023, 0)
|
|||
r.extents.output(1123, 0)
|
||||
|
||||
r = l1.space(0.5)
|
||||
expect_count(r, 3, 1, "r on l1")
|
||||
r.output(1010, 0)
|
||||
r.extents.output(1110, 0)
|
||||
|
||||
expect_count(l1, 15, 5, "l1 before flatten")
|
||||
l1.flatten
|
||||
expect_count(l1, 15, 15, "l1 after flatten")
|
||||
r = l1.space(0.5)
|
||||
r.output(1011, 0)
|
||||
r.extents.output(1111, 0)
|
||||
expect_count(r, 3, 3, "r on l1.flatten")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
Binary file not shown.
|
|
@ -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)
|
||||
|
||||
Binary file not shown.
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
# Hierarchical antenna check
|
||||
# Flat antenna check
|
||||
|
||||
source($drc_test_source, "RINGO")
|
||||
target($drc_test_target)
|
||||
|
|
@ -14,6 +14,17 @@ 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)
|
||||
|
||||
clear_connections
|
||||
|
||||
connect(gate, poly)
|
||||
connect(poly, poly_cont)
|
||||
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, 10.0).output(110)
|
||||
antenna_check(gate, metal2, 50.0).output(150)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue