Merge pull request #813 from KLayout/wip

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

View File

@ -269,6 +269,7 @@ gen = Gen::new
run_demo gen, "input1.separation(input2, 1.2, euclidian)", "drc_separation1.png"
run_demo gen, "input1.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)

View File

@ -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);

View File

@ -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;

View File

@ -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 \

View File

@ -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 ()

View File

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

View File

@ -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;
}

View File

@ -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;

View File

@ -346,16 +346,87 @@ EdgePairsDelegate *DeepEdgePairs::add (const EdgePairs &other) const
}
}
EdgePairsDelegate *DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
EdgePairsDelegate *
DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
{
// TODO: implement
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;
}
}

View File

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

View File

@ -273,24 +273,33 @@ void DeepEdges::reserve (size_t)
// Not implemented for deep regions
}
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 ());

View File

@ -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 ();

View File

@ -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);

View File

@ -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;

View File

@ -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> &parameters = a.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator c = parameters.begin (); c != parameters.end (); ++c) {
int cmp = compare_parameters (a.parameter_value (c->id ()), b.parameter_value (c->id ()), 0.0, m_relative);
if (cmp != 0) {
return false;
}
}
return true;
}
// --------------------------------------------------------------------------------
// DeviceClass class implementation
@ -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 ();

View File

@ -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
{

View File

@ -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
*

View File

@ -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)
*/

View File

@ -0,0 +1,81 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbCommon.h"
#include "dbEdgePairFilters.h"
#include "dbEdges.h"
namespace db
{
// ---------------------------------------------------------------------------------------------------
// EdgeFilterBasedEdgePairFilter implementation
EdgeFilterBasedEdgePairFilter::EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match)
: mp_edge_filter (edge_filter), m_one_must_match (one_must_match)
{
// .. nothing yet ..
}
EdgeFilterBasedEdgePairFilter::~EdgeFilterBasedEdgePairFilter ()
{
// .. nothing yet ..
}
bool EdgeFilterBasedEdgePairFilter::selected (const db::EdgePair &edge_pair) const
{
if (m_one_must_match) {
return mp_edge_filter->selected (edge_pair.first ()) || mp_edge_filter->selected (edge_pair.second ());
} else {
return mp_edge_filter->selected (edge_pair.first ()) && mp_edge_filter->selected (edge_pair.second ());
}
}
const TransformationReducer *EdgeFilterBasedEdgePairFilter::vars () const
{
return mp_edge_filter->vars ();
}
bool EdgeFilterBasedEdgePairFilter::wants_variants () const
{
return mp_edge_filter->wants_variants ();
}
// ---------------------------------------------------------------------------------------------------
// EdgePairFilterByDistance implementation
EdgePairFilterByDistance::EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted)
: m_min_distance (min_distance), m_max_distance (max_distance), m_inverted (inverted)
{
// .. nothing yet ..
}
bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair) const
{
distance_type dist = edge_pair.distance ();
bool sel = (dist >= m_min_distance && dist < m_max_distance);
return m_inverted ? !sel : sel;
}
}

View File

@ -0,0 +1,82 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbEdgePairFilters
#define HDR_dbEdgePairFilters
#include "dbEdgePairs.h"
namespace db
{
class EdgeFilterBase;
/**
* @brief A base class for edge pair filters based on edge filters
*
* If "one_must_match" is true, it is sufficient for one edge to be selected.
* If it is false, both edges need to be selected to make the edge pair selected.
*
* NOTE: the edge filter is not owned by the edge pair filter object.
*/
class DB_PUBLIC EdgeFilterBasedEdgePairFilter
: public EdgePairFilterBase
{
public:
EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match);
virtual ~EdgeFilterBasedEdgePairFilter ();
virtual bool selected (const db::EdgePair &edge_pair) const;
virtual const TransformationReducer *vars () const;
virtual bool wants_variants () const;
private:
EdgeFilterBase *mp_edge_filter;
bool m_one_must_match;
};
/**
* @brief Filters edge pairs based on the distance of the edges.
*
* The distance is measured as the smallest distance between each of the points of the two edges.
*/
class DB_PUBLIC EdgePairFilterByDistance
: public EdgePairFilterBase
{
public:
typedef db::coord_traits<db::Coord>::distance_type distance_type;
EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted);
virtual bool selected (const db::EdgePair &edge_pair) const;
virtual const TransformationReducer *vars () const { return 0; }
virtual bool wants_variants () const { return false; }
private:
distance_type m_min_distance, m_max_distance;
bool m_inverted;
};
}
#endif

View File

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

View File

@ -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;
};

View File

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

View File

@ -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);
}
}
}

View File

@ -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
*

View File

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

View File

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

View File

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

View File

@ -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
{

View File

@ -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;

View File

@ -550,11 +550,13 @@ public:
}
/**
* @brief Get the layer of the current shape
* @brief Gets the layer of the current shape
*/
unsigned int layer () const
{
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
{

View File

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

View File

@ -240,6 +240,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
// the processor. Reason: the search range is limited, hence not all necessary components may have been
// 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) {

View File

@ -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;

View File

@ -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

View File

@ -154,13 +154,16 @@ Edge2EdgeCheckBase::finish (const Edge *o, const size_t &p)
}
}
void
bool
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
{
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 ();

View File

@ -601,7 +601,7 @@ public:
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
* (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
}
};

View File

@ -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
*/

View File

@ -226,6 +226,12 @@ static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionO
return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
}
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."

View File

@ -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 "

View File

@ -575,10 +575,10 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"edges which have the given angle to the x-axis are returned. If \"inverse\" is true, "
"edges 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),

View File

@ -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"

View File

@ -843,15 +843,6 @@ public:
}
}
virtual bool equal (const db::Device &a, const db::Device &b) const
{
if (cb_equal.can_issue ()) {
return cb_equal.issue<db::EqualDeviceParameters, bool, const db::Device &, const db::Device &> (&db::EqualDeviceParameters::equal, a, b);
} else {
return db::EqualDeviceParameters::equal (a, b);
}
}
gsi::Callback cb_less, cb_equal;
};
@ -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)

View File

@ -123,6 +123,11 @@ static db::Region corners_to_boxes (const db::Region *r, double angle_start, dou
return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim));
}
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"

View File

@ -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);
}

View File

@ -106,6 +106,17 @@ TEST(2)
EXPECT_EQ (db::Edge (10,20,110,222).distance_abs (db::Point (100, 200)), db::Edge::distance_type (1));
EXPECT_EQ (db::Edge (10,20,110,222).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)

View File

@ -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)

View File

@ -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.
#

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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) &lt; 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) &gt;= 1.0).first_edges)
</pre>
</p><p>
The following image shows the effect of such a negative-output separation check:
</p><p>
<table>
<tr>
<td><img src="/images/drc_separation1un.png"/></td>
</tr>
</table>
</p>
<a name="silent"/><h2>"silent" - Resets verbose mode</h2>
<keyword name="silent"/>
@ -1863,7 +1891,7 @@ errors = in.drc(0.1.um &lt;= width &lt; 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>

View File

@ -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 &lt;= l &lt; 2.0
ep1 = edge_pairs.with_length(1.um .. 2.um)
# both edges need to have a length of exactly 2 um
ep2 = edge_pairs.with_length(2.um, both)
</pre>
</p>
<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"/>

View File

@ -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

View File

@ -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>

View 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

View File

@ -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

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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")

View File

@ -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
*

View File

@ -130,6 +130,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8);
size_t max_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;

View File

@ -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

View File

@ -65,6 +65,16 @@ static bool get_gds2_multi_xy_records (const db::SaveLayoutOptions *options)
return options->get_options<db::GDS2WriterOptions> ().multi_xy_records;
}
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"

View File

@ -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>(&lt;4000 recommended, absolute limit 8191)</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="libname_le"/>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>(keep empty for unspecified limit)</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<widget class="QCheckBox" name="multi_xy_cbx">
<property name="text">
<string>Multi-XY record mode for boundaries
(enables infinitely large polygons/paths at the cost of compatibility)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="cell_name_length_le"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Library name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<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>(&lt;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>

View File

@ -61,6 +61,7 @@ GDS2WriterOptionPage::setup (const db::FormatSpecificWriterOptions *o, const db:
mp_ui->write_file_properties->setChecked (options->write_file_properties);
mp_ui->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 ();

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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
*/

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -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')

View File

@ -22,6 +22,7 @@ l1.drc(-90 < corners(as_dots) <= 90.0).output(101, 0)
l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners
l1.drc(corners(as_boxes) == 90).output(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.

View File

@ -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)

View File

@ -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)")

View File

@ -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.

View File

@ -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")

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

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

BIN
testdata/drc/drcSimpleTests_48.gds vendored Normal file

Binary file not shown.

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

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

BIN
testdata/drc/drcSimpleTests_49.gds vendored Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
# Hierarchical antenna check
# Flat antenna check
source($drc_test_source, "RINGO")
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)

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

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au48.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au48d.gds vendored Normal file

Binary file not shown.

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