Merge pull request #1765 from KLayout/faster-latchup-rules

Faster latchup rules
This commit is contained in:
Matthias Köfferlein 2024-07-02 23:57:04 +02:00 committed by GitHub
commit 2c0a7f797a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 1403 additions and 21 deletions

View File

@ -1,3 +1,31 @@
0.29.3 (2024-07-02):
* Enhancement: %GITHUB%/issues/1655 Marker object lifetime management
* Bug: %GITHUB%/issues/1743 strmxor shows no difference, klayout xor shows 85
* Bug: %GITHUB%/issues/1757 Fixed memory issue
* Enhancement: %GITHUB%/issues/1741 Tooltip strings for PCell parameters
* Enhancement: %GITHUB%/issues/1747 Transformations on negative floats/ints
* Bug: %GITHUB%/issues/1751 Code sanity issue fixed
* Bug: %GITHUB%/issues/1750 Code sanity issue fixed
* Bug: %GITHUB%/issues/1733 Instance selecion in object properties does not match with view port object highlight
* Enhancement: DRC - step-wise size with "inside" and "outside" constraint
This feature is handy for implementing latch-up rules more efficiently.
* Bugfix: OASIS reader - avoiding slight rounding of DBU In python read/write cycle (discussion-2526)
* Bugfix: Proper tracking of references of RecursiveShapeIterator.
Related to issue #1742, but not directly.
* Bugfix: Symlinks in the salt paths might have lead to macro duplication
* Bugfix: Ruby binding - "return" inside block was behaving like "break".
Now, "return" will leave the current function.
* Enhancement: LVS layer naming now also accepts an optional layer/datatype information.
* Enhancement: XOR performance enhanced in deep mode for "almost same"
inputs.
* Bugfix: Macro debugger now does not prevent paint events and
screen refresh should work while debugging. In addition, the debugger
does not deadlock the desktop when using the help browser's search
function. Side effects are yet unknown - maybe debugging Qt event
handlers now becomes less stable.
* Bugfix: During modal dialogs, the debugger's run/stop and step buttons
were not working.
0.29.2 (2024-06-06):
* Enhancement: %GITHUB%/issues/1724 Don't read duplicate LEF files
* Bug: %GITHUB%/issues/1722 [macOS] Crash when opening layout having Custom Macro Menus

View File

@ -1,3 +1,10 @@
klayout (0.29.3-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Tue, 02 Jul 2024 12:00:00 +0200
klayout (0.29.2-1) unstable; urgency=low
* New features and bugfixes

View File

@ -914,6 +914,36 @@ run_demo gen, "input.sized(1.um, octagon_limit)", "drc_sized4.png"
run_demo gen, "input.sized(1.um, square_limit)", "drc_sized5.png"
run_demo gen, "input.sized(1.um, acute_limit)", "drc_sized6.png"
class Gen
def produce(s1, s2)
pts = [
RBA::Point::new(1000, 1000),
RBA::Point::new(1000, 2000),
RBA::Point::new(2000, 2000),
RBA::Point::new(2000, 1000)
];
s1.insert(RBA::Polygon::new(pts))
pts = [
RBA::Point::new(1000, 1000),
RBA::Point::new(1000, 7000),
RBA::Point::new(6000, 7000),
RBA::Point::new(6000, 1000),
RBA::Point::new(5000, 1000),
RBA::Point::new(5000, 6000),
RBA::Point::new(2000, 6000),
RBA::Point::new(2000, 1000)
];
s2.insert(RBA::Polygon::new(pts))
end
end
gen = Gen::new
run_demo gen, "input1.sized(1.um, steps(1), size_inside(input2))", "drc_sized_inside1.png"
run_demo gen, "input1.sized(2.um, steps(2), size_inside(input2))", "drc_sized_inside2.png"
run_demo gen, "input1.sized(3.um, steps(3), size_inside(input2))", "drc_sized_inside3.png"
run_demo gen, "input1.sized(10.um, steps(10), size_inside(input2))", "drc_sized_inside4.png"
class Gen
def produce(s1, s2)
pts = [

View File

@ -1377,6 +1377,97 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const
}
}
RegionDelegate *
AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const
{
return sized_inside (inside, outside, d, d, steps, mode);
}
RegionDelegate *
AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const
{
// empirical value
const int max_steps = 25;
if (steps <= 0 || empty ()) {
// Nothing to do - NOTE: don't return EmptyRegion because we want to
// maintain "deepness"
return clone ();
}
if (dx < 0 || dy < 0) {
throw tl::Exception (tl::to_string (tr ("'sized_inside' operation does not make sense with negative sizing")));
}
if (dx == 0 && dy == 0) {
steps = 1;
}
// NOTE: it does not provide benefits to merge the outside region, so just don't
auto inside_polygons = outside ? inside.begin () : inside.begin_merged ();
bool inside_polygons_is_merged = outside ? inside.is_merged () : true;
auto polygons = begin_merged ();
std::unique_ptr<RegionDelegate> res (new FlatRegion ());
std::unique_ptr<RegionDelegate> prev;
int steps_from = 0;
while (steps > 0) {
db::Coord dx_chunk = dx, dy_chunk = dy;
int steps_chunk = steps;
// We perform at most max_steps in one chunk.
// This is supposed to limit the search range and merge shapes instead of creating
// heavily overlapping ones.
if (steps > max_steps) {
steps_chunk = max_steps;
dx_chunk = db::coord_traits<db::Coord>::rounded (dx * max_steps / double (steps));
dy_chunk = db::coord_traits<db::Coord>::rounded (dy * max_steps / double (steps));
}
steps -= steps_chunk;
dx -= dx_chunk;
dy -= dy_chunk;
// NOTE: as we merge the inside region in the inside case, we can use distance 0
db::Coord dist = outside ? std::max (dx_chunk, dy_chunk) : 0;
db::sized_inside_local_operation<db::Polygon, db::Polygon, db::Polygon> op (dx_chunk, dy_chunk, steps_chunk, mode, dist, outside, inside_polygons_is_merged);
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
// indicate chunk in the progress description
proc.set_description (proc.description (&op) + tl::sprintf (tl::to_string (tr (" (steps %d..%d)")), steps_from + 1, steps_from + steps_chunk + 1));
steps_from += steps_chunk;
std::vector<db::generic_shape_iterator<db::Polygon> > others;
others.push_back (inside_polygons);
std::vector<db::Shapes *> results;
db::FlatRegion *res_flat = dynamic_cast<db::FlatRegion *> (res.get ());
tl_assert (res_flat != 0);
results.push_back (&res_flat->raw_polygons ());
proc.run_flat (prev.get () ? prev->begin () : polygons, others, std::vector<bool> (), &op, results);
// NOTE: in the last step we apply a polygon breaker in addition to "merge" so the
// result is granular for better deep mode performance
if (steps > 0) {
prev.reset (res->merged ());
res.reset (new db::FlatRegion ());
} else {
res.reset (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ())));
}
}
return res.release ();
}
RegionDelegate *
AsIfFlatRegion::and_with (const Region &other, PropertyConstraint property_constraint) const
{

View File

@ -121,6 +121,8 @@ public:
virtual RegionDelegate *sized (coord_type d, unsigned int mode) const;
virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const;
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const;
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const;
virtual RegionDelegate *and_with (const Region &other, PropertyConstraint property_constraint) const;
virtual RegionDelegate *not_with (const Region &other, PropertyConstraint property_constraint) const;

View File

@ -1884,6 +1884,98 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const
return res.release ();
}
RegionDelegate *
DeepRegion::sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const
{
return sized_inside (inside, outside, d, d, steps, mode);
}
RegionDelegate *
DeepRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const
{
// empirical value
const int max_steps = 25;
if (steps <= 0 || empty ()) {
// Nothing to do - NOTE: don't return EmptyRegion because we want to
// maintain "deepness"
return clone ();
}
if (dx < 0 || dy < 0) {
throw tl::Exception (tl::to_string (tr ("'sized_inside' operation does not make sense with negative sizing")));
}
if (dx == 0 && dy == 0) {
steps = 1;
}
const db::DeepRegion *inside_deep = dynamic_cast<const db::DeepRegion *> (inside.delegate ());
if (! inside_deep) {
return db::AsIfFlatRegion::sized_inside (inside, outside, dx, dy, steps, mode);
}
// NOTE: it does not provide benefits to merge the outside region, so just don't
const db::DeepLayer &inside_polygons = outside ? inside_deep->deep_layer () : inside_deep->merged_deep_layer ();
bool inside_polygons_is_merged = outside ? inside_deep->is_merged () : true;
const db::DeepLayer &polygons = merged_deep_layer ();
std::unique_ptr<db::DeepRegion> res (new db::DeepRegion (polygons.derived ()));
std::unique_ptr<db::DeepRegion> prev;
int steps_from = 0;
while (steps > 0) {
db::Coord dx_chunk = dx, dy_chunk = dy;
int steps_chunk = steps;
// We perform at most max_steps in one chunk.
// This is supposed to limit the search range and merge shapes instead of creating
// heavily overlapping ones.
if (steps > max_steps) {
steps_chunk = max_steps;
dx_chunk = db::coord_traits<db::Coord>::rounded (dx * max_steps / double (steps));
dy_chunk = db::coord_traits<db::Coord>::rounded (dy * max_steps / double (steps));
}
steps -= steps_chunk;
dx -= dx_chunk;
dy -= dy_chunk;
// NOTE: as we merge the inside region in the inside case, we can use distance 0
db::Coord dist = outside ? std::max (dx_chunk, dy_chunk) : 0;
db::sized_inside_local_operation<db::PolygonRef, db::PolygonRef, db::PolygonRef> op (dx_chunk, dy_chunk, steps_chunk, mode, dist, outside, inside_polygons_is_merged);
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &inside_polygons.layout (), &inside_polygons.initial_cell (), polygons.breakout_cells (), inside_polygons.breakout_cells ());
configure_proc (proc);
proc.set_threads (polygons.store ()->threads ());
proc.set_area_ratio (polygons.store ()->max_area_ratio ());
proc.set_max_vertex_count (polygons.store ()->max_vertex_count ());
// indicate chunk in the progress description
proc.set_description (proc.description (&op) + tl::sprintf (tl::to_string (tr (" (steps %d..%d)")), steps_from + 1, steps_from + steps_chunk + 1));
steps_from += steps_chunk;
proc.run (&op, prev.get () ? prev->deep_layer ().layer () : polygons.layer (), inside_polygons.layer (), res->deep_layer ().layer ());
// NOTE: in the last step we apply a polygon breaker in addition to "merge" so the
// result is granular for better deep mode performance
if (steps > 0) {
prev.reset (dynamic_cast<db::DeepRegion *> (res->merged ()));
tl_assert (prev.get () != 0);
res.reset (new db::DeepRegion (polygons.derived ()));
} else {
res.reset (dynamic_cast<db::DeepRegion *> (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ()))));
tl_assert (res.get () != 0);
}
}
return res.release ();
}
template <class TR, class Output>
static
Output *region_cop_impl (DeepRegion *region, db::CompoundRegionOperationNode &node)

View File

@ -136,6 +136,8 @@ public:
virtual RegionDelegate *sized (coord_type d, unsigned int mode) const;
virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const;
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const;
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const;
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;

View File

@ -61,7 +61,7 @@ public:
/**
* @brief Destructor
*/
virtual ~EdgeSink () { };
virtual ~EdgeSink () { }
/**
* @brief Start event
@ -1095,6 +1095,34 @@ private:
void redo_or_process (const std::vector<std::pair<db::EdgeSink *, db::EdgeEvaluatorBase *> > &gen, bool redo);
};
/**
* @brief An edge sink feeding into an EdgeProcessor
*/
class DB_PUBLIC EdgesToEdgeProcessor
: public EdgeSink
{
public:
EdgesToEdgeProcessor (db::EdgeProcessor &ep, db::EdgeProcessor::property_type prop)
: mp_ep (&ep), m_prop (prop)
{
// .. nothing yet ..
}
virtual void put (const db::Edge &edge)
{
mp_ep->insert (edge, m_prop);
}
virtual void put (const db::Edge &edge, int /*tag*/)
{
mp_ep->insert (edge, m_prop);
}
private:
db::EdgeProcessor *mp_ep;
db::EdgeProcessor::property_type m_prop;
};
}
#endif

View File

@ -97,6 +97,8 @@ public:
virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); }
virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); }
virtual RegionDelegate *sized_inside (const Region &, bool, coord_type, int, unsigned int) const { return new EmptyRegion (); }
virtual RegionDelegate *sized_inside (const Region &, bool, coord_type, coord_type, int, unsigned int) const { return new EmptyRegion (); }
virtual RegionDelegate *and_with (const Region &, db::PropertyConstraint) const { return new EmptyRegion (); }
virtual RegionDelegate *not_with (const Region &, db::PropertyConstraint) const { return new EmptyRegion (); }

View File

@ -294,6 +294,22 @@ public:
}
}
/**
* @brief Compatibility with path_ref
*/
path<C> instantiate () const
{
return *this;
}
/**
* @brief Compatibility with path_ref
*/
void instantiate (path<C> &p) const
{
p = *this;
}
/**
* @brief The (dummy) translation operator
*/

View File

@ -1667,6 +1667,22 @@ public:
return !equal (b);
}
/**
* @brief Compatibility with polygon_ref
*/
polygon<C> instantiate () const
{
return *this;
}
/**
* @brief Compatibility with polygon_ref
*/
void instantiate (polygon<C> &poly) const
{
poly = *this;
}
/**
* @brief Returns true, if the polygon is a simple box
*/
@ -2508,6 +2524,22 @@ public:
m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, true /*normalize*/, remove_reflected);
}
/**
* @brief Compatibility with polygon_ref
*/
simple_polygon<C> instantiate () const
{
return *this;
}
/**
* @brief Compatibility with polygon_ref
*/
void instantiate (simple_polygon<C> &poly) const
{
poly = *this;
}
/**
* @brief The (dummy) translation operator
*/

View File

@ -531,6 +531,30 @@ private:
unsigned int m_mode;
};
/**
* @brief A polygon sink feeding into an EdgeProcessor
*/
class DB_PUBLIC PolygonsToEdgeProcessor
: public PolygonSink
{
public:
PolygonsToEdgeProcessor (db::EdgeProcessor &ep, db::EdgeProcessor::property_type prop, db::EdgeProcessor::property_type prop_step)
: mp_ep (&ep), m_prop (prop), m_prop_step (prop_step)
{
// .. nothing yet ..
}
virtual void put (const db::Polygon &polygon)
{
mp_ep->insert (polygon, m_prop);
m_prop += m_prop_step;
}
private:
db::EdgeProcessor *mp_ep;
db::EdgeProcessor::property_type m_prop, m_prop_step;
};
}
#endif

View File

@ -309,6 +309,32 @@ Region::sized (coord_type dx, coord_type dy, unsigned int mode) const
return Region (mp_delegate->sized (dx, dy, mode));
}
Region &
Region::size_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode)
{
set_delegate (mp_delegate->sized_inside (inside, outside, d, steps, mode));
return *this;
}
Region &
Region::size_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode)
{
set_delegate (mp_delegate->sized_inside (inside, outside, dx, dy, steps, mode));
return *this;
}
Region
Region::sized_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const
{
return Region (mp_delegate->sized_inside (inside, outside, d, steps, mode));
}
Region
Region::sized_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const
{
return Region (mp_delegate->sized_inside (inside, outside, dx, dy, steps, mode));
}
void
Region::round_corners (double rinner, double router, unsigned int n)
{

View File

@ -1027,6 +1027,59 @@ public:
*/
Region sized (coord_type dx, coord_type dy, unsigned int mode = 2) const;
/**
* @brief Size the region incrementally
*
* This method applies an incremental sizing to the region. Before the sizing is done, the
* region is merged if this is not the case already. Incremental sizing is confined to be inside a certain region.
* Only positive or zero sizing values are supported.
*
* @param inside The confinement region
* @param outside If true, "inside" is negative - i.e. sizing is performed outside the "inside" region
* @param d The (isotropic) sizing value
* @param steps The number of steps to take
* @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance.
* @return A reference to self
*/
Region &size_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode = 2);
/**
* @brief Size the region incrementally and anisotropically
*
* This method applies an incremental sizing to the region. Before the sizing is done, the
* region is merged if this is not the case already. Incremental sizing is confined to be inside a certain region.
* Only positive or zero sizing values are supported.
*
* @param inside The confinement region
* @param outside If true, "inside" is negative - i.e. sizing is performed outside the "inside" region
* @param dx The x sizing value
* @param dy The y sizing value
* @param steps The number of steps to take
* @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance.
* @return A reference to self
*/
Region &size_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2);
/**
* @brief Returns the sized region
*
* This is an out-of-place version of the size method with isotropic sizing
* "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge).
*
* Merged semantics applies.
*/
Region sized_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode = 2) const;
/**
* @brief Returns the sized region
*
* This is an out-of-place version of the size method with anisotropic sizing
* "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge).
*
* Merged semantics applies.
*/
Region sized_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2) const;
/**
* @brief Boolean AND operator
*/

View File

@ -226,6 +226,8 @@ public:
virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0;
virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0;
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const = 0;
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const = 0;
virtual RegionDelegate *and_with (const Region &other, PropertyConstraint prop_constraint) const = 0;
virtual RegionDelegate *not_with (const Region &other, PropertyConstraint prop_constraint) const = 0;

View File

@ -1944,6 +1944,165 @@ std::string two_bool_and_not_local_operation_with_properties<TS, TI, TR>::descri
template class DB_PUBLIC two_bool_and_not_local_operation_with_properties<db::PolygonRef, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC two_bool_and_not_local_operation_with_properties<db::Polygon, db::Polygon, db::Polygon>;
// ---------------------------------------------------------------------------------------------
// sized_inside_local_operation implementation
template <class TS, class TI, class TR>
sized_inside_local_operation<TS, TI, TR>::sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, db::Coord dist, bool outside, bool inside_is_merged)
: m_dx (dx), m_dy (dy), m_dist (dist), m_steps (steps), m_mode (mode), m_outside (outside), m_inside_is_merged (inside_is_merged)
{
// .. nothing yet ..
}
template <class TS, class TI, class TR>
db::Coord
sized_inside_local_operation<TS, TI, TR>::dist () const
{
return m_dist;
}
template <class TS, class TI, class TR>
OnEmptyIntruderHint
sized_inside_local_operation<TS, TI, TR>::on_empty_intruder_hint () const
{
return m_outside ? Ignore : Drop;
}
template <class TS, class TI, class TR>
std::string
sized_inside_local_operation<TS, TI, TR>::description () const
{
return tl::to_string (tr ("Sized inside"));
}
template <class TS, class TI, class TR>
void
sized_inside_local_operation<TS, TI, TR>::do_compute_local (db::Layout *layout, db::Cell *subject_cell, const shape_interactions<TS, TI> &interactions, std::vector<std::unordered_set<TR> > &results, const db::LocalProcessorBase *proc) const
{
tl_assert (results.size () == 1);
std::unordered_set<TR> &result = results.front ();
double mag = 1.0;
double angle = 1.0;
if (proc->vars ()) {
const db::ICplxTrans &tr = proc->vars ()->single_variant_transformation (subject_cell->cell_index ());
mag = tr.mag ();
angle = tr.angle ();
}
double dx_with_mag = m_dx / mag;
double dy_with_mag = m_dy / mag;
if (fabs (angle - 90.0) < 45.0) {
// TODO: how to handle x/y swapping on arbitrary angles?
std::swap (dx_with_mag, dy_with_mag);
}
// collect subjects and intruder shapes
std::vector<db::Polygon> subjects;
std::set<const TI *> inside;
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
for (typename shape_interactions<TS, TI>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
inside.insert (&interactions.intruder_shape (*j).second);
}
}
// For empty intruders we can optimize the size sequence to a single step
// (NOTE: this is not strictly true as it ignores effects due to corner clipping
// which can be accumulative)
int steps = inside.empty () ? 1 : m_steps;
// Merge the inside region shapes as we are going to use them multiple times
std::vector<db::Edge> inside_merged;
if (inside.size () > 1 && ! m_inside_is_merged && m_steps > 1) {
db::EdgeProcessor ep;
db::SimpleMerge op;
db::EdgeContainer ec (inside_merged);
db::EdgeProcessor::property_type p = 0;
for (auto i = inside.begin (); i != inside.end (); ++i, ++p) {
ep.insert (**i, p);
}
ep.process (ec, op);
inside.clear ();
}
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
subjects.push_back (db::Polygon ());
interactions.subject_shape (i->first).instantiate (subjects.back ());
}
// prepare the edge processors involved
db::EdgeProcessor ep_and;
ep_and.set_base_verbosity (50);
// the main sizing loop
db::Coord sx_last = 0, sy_last = 0;
for (int step = 0; step < steps && ! subjects.empty (); ++step) {
db::Coord sx = db::coord_traits<db::Coord>::rounded (dx_with_mag * (step + 1) / double (steps));
db::Coord sy = db::coord_traits<db::Coord>::rounded (dy_with_mag * (step + 1) / double (steps));
db::Coord dx = sx - sx_last;
db::Coord dy = sy - sy_last;
sx_last = sx;
sy_last = sy;
if (! inside.empty () || ! inside_merged.empty ()) {
ep_and.clear ();
db::EdgesToEdgeProcessor e2ep (ep_and, 0);
db::SizingPolygonFilter siz (e2ep, dx, dy, m_mode);
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
siz.put (*i);
}
db::EdgeProcessor::property_type p = 1;
for (auto i = inside.begin (); i != inside.end (); ++i) {
ep_and.insert (**i, p);
p += 2;
}
for (auto i = inside_merged.begin (); i != inside_merged.end (); ++i) {
ep_and.insert (*i, p);
}
db::PolygonContainer pc (subjects, true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, false /*min. coherence*/);
db::BooleanOp op (m_outside ? db::BooleanOp::ANotB : db::BooleanOp::And);
ep_and.process (pg, op);
} else {
std::vector<db::Polygon> sized_subjects;
db::PolygonContainer pc (sized_subjects, true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, false /*min. coherence*/);
db::SizingPolygonFilter siz (pg, dx, dy, m_mode);
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
siz.put (*i);
}
sized_subjects.swap (subjects);
}
}
db::polygon_ref_generator<TR> gen (layout, result);
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
gen.put (*i);
}
}
template class DB_PUBLIC sized_inside_local_operation<db::PolygonRef, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC sized_inside_local_operation<db::Polygon, db::Polygon, db::Polygon>;
// ---------------------------------------------------------------------------------------------
SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count)

View File

@ -463,6 +463,38 @@ private:
typedef two_bool_and_not_local_operation_with_properties<db::PolygonRef, db::PolygonRef, db::PolygonRef> TwoBoolAndNotLocalOperationWithProperties;
/**
* @brief Implements "sized_inside"
*/
template <class TS, class TI, class TR>
class DB_PUBLIC sized_inside_local_operation
: public local_operation<TS, TI, TR>
{
public:
sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, db::Coord dist, bool outside, bool inside_is_merged);
virtual db::Coord dist () const;
virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
virtual std::string description () const;
virtual void do_compute_local (db::Layout *layout, db::Cell *subject_cell, const shape_interactions<TS, TI> &interactions, std::vector<std::unordered_set<TR> > &results, const db::LocalProcessorBase * /*proc*/) const;
virtual const db::TransformationReducer *vars () const
{
return m_dx != m_dy ? (const db::TransformationReducer *) &m_vars_anisotropic : (const db::TransformationReducer *) &m_vars_isotropic;
}
private:
db::Coord m_dx, m_dy;
db::Coord m_dist;
int m_steps;
unsigned int m_mode;
bool m_outside;
bool m_inside_is_merged;
db::MagnificationAndOrientationReducer m_vars_anisotropic;
db::MagnificationReducer m_vars_isotropic;
};
/**
* @brief Implements a merge operation with an overlap count
* With a given wrap_count, the result will only contains shapes where

View File

@ -1008,6 +1008,84 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode)
return *region;
}
static db::Region
sized_inside_ddm (const db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode)
{
return region->sized_inside (inside, false, dx, dy, steps, mode);
}
static db::Region
sized_inside_dvm (const db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode)
{
return region->sized_inside (inside, false, dv.x (), dv.y (), steps, mode);
}
static db::Region
sized_inside_dm (const db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode)
{
return region->sized_inside (inside, false, d, steps, mode);
}
static db::Region &
size_inside_ddm (db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode)
{
region->size_inside (inside, false, dx, dy, steps, mode);
return *region;
}
static db::Region &
size_inside_dvm (db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode)
{
region->size_inside (inside, false, dv.x (), dv.y (), steps, mode);
return *region;
}
static db::Region &
size_inside_dm (db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode)
{
region->size_inside (inside, false, d, steps, mode);
return *region;
}
static db::Region
sized_outside_ddm (const db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode)
{
return region->sized_inside (inside, true, dx, dy, steps, mode);
}
static db::Region
sized_outside_dvm (const db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode)
{
return region->sized_inside (inside, true, dv.x (), dv.y (), steps, mode);
}
static db::Region
sized_outside_dm (const db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode)
{
return region->sized_inside (inside, true, d, steps, mode);
}
static db::Region &
size_outside_ddm (db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode)
{
region->size_inside (inside, true, dx, dy, steps, mode);
return *region;
}
static db::Region &
size_outside_dvm (db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode)
{
region->size_inside (inside, true, dv.x (), dv.y (), steps, mode);
return *region;
}
static db::Region &
size_outside_dm (db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode)
{
region->size_inside (inside, true, d, steps, mode);
return *region;
}
static db::Edges
edges (const db::Region *region, db::PolygonToEdgeProcessor::EdgeMode mode)
{
@ -1929,6 +2007,130 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
) +
method_ext ("size_inside", &size_inside_ddm, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"),
"@brief Incremental, anisotropic sizing inside of another region\n"
"\n"
"@param inside The region the incremental sizing will stay inside.\n"
"@param dx The x sizing value\n"
"@param dy The y sizing value\n"
"@param steps The number of steps to take\n"
"@param mode The sizing mode (see \\size)\n"
"\n"
"@return The region after the sizing has been applied (self)\n"
"\n"
"Sizes the region, keeping inside another region and performing the size in discrete steps.\n"
"\n"
"Using this method is equivalent to applying a single-step size and consecutively doing a boolean AND with the 'inside' region. "
"This is repeated until the full sizing value is applied.\n"
"\n"
"This operation is employed to implement latch-up rules, where a device needs to be close to a well tap within the "
"same well. For this, the tap footprint is incrementally sized, with the well as the 'inside' region. The steps is chosen so "
"the per-step sizing is somewhat less than the minimum well space. Sizing the tap shape results in a growing footprint that "
"follows the well contours and a small enough per-step sizing value ensures the sized contour does not cross well gaps.\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("size_outside", &size_outside_ddm, gsi::arg ("outside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"),
"@brief Incremental, anisotropic sizing outside of another region\n"
"\n"
"This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. "
"Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("size_inside", &size_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Incremental, anisotropic sizing inside of another region\n"
"\n"
"@return The region after the sizing has applied (self)\n"
"\n"
"This method is equivalent to \"size_inside(dv.x, dv.y, steps, mode)\".\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("size_outside", &size_outside_dvm, gsi::arg ("outside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Incremental, anisotropic sizing outside of another region\n"
"\n"
"This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. "
"Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("size_inside", &size_inside_dm, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Incremental, isotropic sizing inside of another region\n"
"\n"
"@return The region after the sizing has applied (self)\n"
"\n"
"This method is equivalent to \"size_inside(d, d, steps, mode)\".\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("size_outside", &size_outside_dm, gsi::arg ("outside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Incremental, anisotropic sizing outside of another region\n"
"\n"
"This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. "
"Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("sized_inside", &sized_inside_ddm, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"),
"@brief Returns the incrementally and anisotropically sized region\n"
"\n"
"@return The sized region\n"
"\n"
"This method returns the incrementally sized region (see \\size_inside), but does not modify self.\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
) +
method_ext ("sized_outside", &sized_outside_ddm, gsi::arg ("outside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"),
"@brief Incremental, anisotropic sizing outside of another region\n"
"\n"
"This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. "
"Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("sized_inside", &sized_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Returns the incrementally and anisotropically sized region\n"
"\n"
"@return The sized region\n"
"\n"
"This method returns the incrementally sized region (see \\size_inside), but does not modify self.\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
"\n"
"This variant has been introduced in version 0.28."
) +
method_ext ("sized_outside", &sized_outside_dvm, gsi::arg ("outside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Incremental, anisotropic sizing outside of another region\n"
"\n"
"This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. "
"Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("sized_inside", &sized_inside_dm, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Returns the incrementally sized region\n"
"\n"
"@return The sized region\n"
"\n"
"This method returns the incrementally sized region (see \\size_inside), but does not modify self.\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
) +
method_ext ("sized_outside", &sized_outside_dm, gsi::arg ("outside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2),
"@brief Incremental, anisotropic sizing outside of another region\n"
"\n"
"This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. "
"Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n"
"\n"
"This method has been introduced in version 0.29.3."
) +
method_ext ("andnot", &andnot, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"),
"@brief Returns the boolean AND and NOT between self and the other region\n"
"\n"

View File

@ -2566,6 +2566,292 @@ TEST(56_RegionsFromShapes)
EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5), false).area (), 10000);
}
TEST(60_sized_inside)
{
db::Region r, inside;
r.insert (db::Box (-10, 20, 20, 60));
r.insert (db::Box (20, 20, 30, 60));
inside.insert (db::Box (-10, 10, 100, 100));
EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 40, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (inside, false, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (inside, false, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (inside, false, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (inside, false, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (inside, false, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)");
EXPECT_EQ (r.sized_inside (inside, false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)");
db::Region d;
d = r;
d.size_inside (inside, false, 0, 20, 10);
EXPECT_EQ (d.to_string (), "(-10,10;-10,80;30,80;30,10)");
d = r;
d.size_inside (inside, false, 1, 2, 0);
EXPECT_EQ (d.to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
d = r;
d.size_inside (inside, false, 10, 20, 10);
EXPECT_EQ (d.to_string (), "(-10,10;-10,80;40,80;40,10)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 0, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 20, 10).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 10, 20, 10).to_string (), "");
try {
// no negative sizing
r.sized_inside (inside, false, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (inside, false, -1, 1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (inside, false, 1, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
}
TEST(61_sized_outside)
{
db::Region r, outside;
r.insert (db::Box (-10, 20, 20, 60));
r.insert (db::Box (20, 20, 30, 60));
outside.insert (db::Box (-20, 0, -10, 110));
outside.insert (db::Box (100, 0, 110, 110));
outside.insert (db::Box (-20, 0, 110, 10));
outside.insert (db::Box (-20, 100, 110, 110));
EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 40, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (outside, true, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (outside, true, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (outside, true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (outside, true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (outside, true, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)");
EXPECT_EQ (r.sized_inside (outside, true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 20, 10).to_string (), "(-10,0;-10,80;30,80;30,0)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 10, 20, 10).to_string (), "(-20,0;-20,80;40,80;40,0)");
try {
// no negative sizing
r.sized_inside (outside, true, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (outside, true, -1, 1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (outside, true, 1, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
}
TEST(62_sized_inside_deep)
{
db::DeepShapeStore dss ("TOP", 0.001);
db::Region r (dss), inside (dss), empty (dss);
r.insert (db::Box (-10, 20, 20, 60));
r.insert (db::Box (20, 20, 30, 60));
inside.insert (db::Box (-10, 10, 100, 100));
EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 40, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (inside, false, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (inside, false, 40, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (empty, false, 0, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (empty, false, 40, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (inside, false, 0, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (inside, false, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (inside, false, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (inside, false, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (inside, false, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)");
EXPECT_EQ (r.sized_inside (inside, false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (inside, false, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 0, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 20, 10).to_string (), "");
EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), false, 10, 20, 10).to_string (), "");
EXPECT_EQ (r.sized_inside (empty, false, 0, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (empty, false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, false, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (empty, false, 0, 0, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (empty, false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, false, 40, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (empty, false, 0, 20, 10).to_string (), "");
EXPECT_EQ (r.sized_inside (empty, false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, false, 10, 20, 10).to_string (), "");
try {
// no negative sizing
r.sized_inside (inside, false, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (inside, false, -1, 1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (inside, false, 1, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
}
TEST(63_sized_outside_deep)
{
db::DeepShapeStore dss ("TOP", 0.001);
db::Region r (dss), outside (dss), empty (dss);
r.insert (db::Box (-10, 20, 20, 60));
r.insert (db::Box (20, 20, 30, 60));
outside.insert (db::Box (-20, 0, -10, 110));
outside.insert (db::Box (100, 0, 110, 110));
outside.insert (db::Box (-20, 0, 110, 10));
outside.insert (db::Box (-20, 100, 110, 110));
EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 40, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (outside, true, 0, 40).to_string (), "");
EXPECT_EQ (db::Region ().sized_inside (outside, true, 40, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (empty, true, 0, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (empty, true, 40, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (outside, true, 0, 40).to_string (), "");
EXPECT_EQ (empty.sized_inside (outside, true, 40, 40).to_string (), "");
EXPECT_EQ (r.sized_inside (outside, true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (outside, true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)");
EXPECT_EQ (r.sized_inside (outside, true, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)");
EXPECT_EQ (r.sized_inside (outside, true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (outside, true, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 20, 10).to_string (), "(-10,0;-10,80;30,80;30,0)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (db::Region (), true, 10, 20, 10).to_string (), "(-20,0;-20,80;40,80;40,0)");
EXPECT_EQ (r.sized_inside (empty, true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, true, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)");
EXPECT_EQ (r.sized_inside (empty, true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, true, 40, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)");
EXPECT_EQ (r.sized_inside (empty, true, 0, 20, 10).to_string (), "(-10,0;-10,80;30,80;30,0)");
EXPECT_EQ (r.sized_inside (empty, true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)");
EXPECT_EQ (r.sized_inside (empty, true, 10, 20, 10).to_string (), "(-20,0;-20,80;40,80;40,0)");
try {
// no negative sizing
r.sized_inside (outside, true, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (outside, true, -1, 1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
try {
// no negative sizing
r.sized_inside (outside, true, 1, -1, 1);
EXPECT_EQ (true, false);
} catch (...) {
}
}
TEST(100_Processors)
{
db::Region r;

View File

@ -3056,23 +3056,34 @@ The following images shows the effect of some rectangle filter modes:
<p>Usage:</p>
<ul>
<li><tt>layer.size(d [, mode])</tt></li>
<li><tt>layer.size(dx, dy [, mode]))</tt></li>
<li><tt>layer.size(d, size_inside(l) [, steps(n)] [, mode])</tt></li>
<li><tt>layer.size(d, size_outside(l) [, steps(n)] [, mode])</tt></li>
<li><tt>layer.size(dx, dy [, mode])</tt></li>
<li><tt>layer.size(dx, dy, size_inside(l) [, steps(n)] [, mode])</tt></li>
<li><tt>layer.size(dx, dy, size_outside(l) [, steps(n)] [, mode])</tt></li>
</ul>
<p>
See <a href="#sized">sized</a>. The size method basically does the same but modifies the layer
See <a href="#sized">sized</a> for a description of the options.
The size method basically does the same but modifies the layer
it is called on. The input layer is returned and available for further processing.
</p>
<a name="sized"/><h2>"sized" - Polygon sizing (per-edge biasing)</h2>
<keyword name="sized"/>
<p>Usage:</p>
<ul>
<li><tt>layer.sized(d [, mode])</tt></li>
<li><tt>layer.sized(dx, dy [, mode]))</tt></li>
<li><tt>layer.sized(d [, mode] [, size_inside(l) [, steps(n)]])</tt></li>
<li><tt>layer.sized(d, size_inside(l) [, steps(n)] [, mode])</tt></li>
<li><tt>layer.sized(d, size_outside(l) [, steps(n)] [, mode])</tt></li>
<li><tt>layer.sized(dx, dy [, mode])</tt></li>
<li><tt>layer.sized(dx, dy, size_inside(l) [, steps(n)] [, mode])</tt></li>
<li><tt>layer.sized(dx, dy, size_outside(l) [, steps(n)] [, mode])</tt></li>
</ul>
<p>
This method requires a polygon layer. It will apply a bias per edge of the polygons
and return the biased layer. The layer that this method is called on is not modified.
</p><p>
The alternative method <a href="#size">size</a> works like <a href="#sized">sized</a> but modifies the layer it is called on.
</p><p>
In the single-value form, that bias is applied both in horizontal or vertical direction.
In the two-value form, the horizontal and vertical bias can be specified separately.
</p><p>
@ -3100,8 +3111,6 @@ layer.sized(300.nm).raw.merged(2)
Bias values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
<a href="#size">size</a> is working like <a href="#sized">sized</a> but modifies the layer it is called on.
</p><p>
The following images show the effect of various forms of the "sized" method:
</p><p>
<table>
@ -3118,6 +3127,44 @@ The following images show the effect of various forms of the "sized" method:
<td><img src="/images/drc_sized6.png"/></td>
</tr>
</table>
</p><p>
The "size_inside" option and the "steps" option implement incremental size. Incremental
size means that the sizing value is applied in n steps. Between the steps, the sized
shape is confined to the "size_inside" layer by means of a boolean "AND" operation.
</p><p>
This scheme is used to implement latch-up rules where a device active region has to
be close to a well tap. By using the well layer as the "size_inside" layer, the size function
follows the well contours. The steps have to selected such that the per-step size value
is smaller than the minimum space of the well shapes. With that, the sized shapes will
not cross over to neighbor well regions. Specifically, the per-step size has to be less
than about 70% of the minimum space to account for the minimum corner-to-corner case
with Euclidian space measurements.
</p><p>
"size_inside" and "steps" can be used with positive sizing values only.
A steps value of 0 will not execute any sizing at all.
</p><p>
"size_outside" acts like "size_inside", but instead of confining the sized region to the
inside of the given layer, it is confined to be outside of that layer. Technically,
a boolean "NOT" is performed instead of a boolean "AND".
</p><p>
An example for the "size_inside" option is this:
</p><p>
<pre>
ntap.sized(30.um, size_inside(nwell), steps(100))
</pre>
</p><p>
The effect of the "size_inside" option is shown here:
</p><p>
<table>
<tr>
<td><img src="/images/drc_sized_inside1.png"/></td>
<td><img src="/images/drc_sized_inside2.png"/></td>
</tr>
<tr>
<td><img src="/images/drc_sized_inside3.png"/></td>
<td><img src="/images/drc_sized_inside4.png"/></td>
</tr>
</table>
</p>
<a name="smoothed"/><h2>"smoothed" - Smoothes the polygons of the region</h2>
<keyword name="smoothed"/>

View File

@ -427,6 +427,8 @@ the netter object.
<p>Usage:</p>
<ul>
<li><tt>name(layer, name)</tt></li>
<li><tt>name(layer, name, layer_number, datatype_number)</tt></li>
<li><tt>name(layer, name, layer_info)</tt></li>
</ul>
<p>
Layer names are listed in the LayoutToNetlist (L2N) or LVS database. They
@ -455,6 +457,12 @@ first time.
</p><p>
<a href="#name">name</a> can only be used once on a layer and the layer names must be
unique (not taken by another layer).
</p><p>
The layer/datatype or LayerInfo specification is optional and will
be used to configure the internal layout. This information is also
persisted inside database files. Specifying a layer/datatype information
is useful, if a layer is not an original layer, but is to be restored
to an actual layout layer later.
</p>
<a name="name_prefix"/><h2>"name_prefix" - Specifies the name prefix for auto-generated layer names</h2>
<keyword name="name_prefix"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -125,6 +125,10 @@
<file alias="drc_sized4.png">doc/images/drc_sized4.png</file>
<file alias="drc_sized5.png">doc/images/drc_sized5.png</file>
<file alias="drc_sized6.png">doc/images/drc_sized6.png</file>
<file alias="drc_sized_inside1.png">doc/images/drc_sized_inside1.png</file>
<file alias="drc_sized_inside2.png">doc/images/drc_sized_inside2.png</file>
<file alias="drc_sized_inside3.png">doc/images/drc_sized_inside3.png</file>
<file alias="drc_sized_inside4.png">doc/images/drc_sized_inside4.png</file>
<file alias="drc_with_angle1.png">doc/images/drc_with_angle1.png</file>
<file alias="drc_with_angle2.png">doc/images/drc_with_angle2.png</file>
<file alias="drc_with_angle3.png">doc/images/drc_with_angle3.png</file>

View File

@ -313,6 +313,18 @@ module DRC
DRCEdgeMode::new(RBA::EdgeMode::NotStep)
end
def steps(arg)
DRCSizingSteps::new(arg)
end
def size_inside(arg)
DRCSizingInside::new(arg)
end
def size_outside(arg)
DRCSizingOutside::new(arg)
end
def padding_zero
DRCDensityPadding::new(:zero)
end

View File

@ -4333,7 +4333,7 @@ CODE
value && raise("Value already specified")
value = @engine._make_value(a)
else
raise("Parameter #" + n.to_s + " does not have an expected type")
raise("Parameter #" + n.to_s + " if of unexpected type")
end
n += 1
end
@ -4518,7 +4518,7 @@ CODE
limits = [ @engine._make_numeric_value_with_nil(a.begin), @engine._make_numeric_value_with_nil(a.end) ]
nlimits = 2
else
raise("Parameter #" + n.to_s + " does not have an expected type")
raise("Parameter #" + n.to_s + " is of unexpected type")
end
n += 1
end
@ -4678,12 +4678,18 @@ TP_SCRIPT
# %DRC%
# @name sized
# @brief Polygon sizing (per-edge biasing)
# @synopsis layer.sized(d [, mode])
# @synopsis layer.sized(dx, dy [, mode]))
# @synopsis layer.sized(d [, mode] [, size_inside(l) [, steps(n)]])
# @synopsis layer.sized(d, size_inside(l) [, steps(n)] [, mode])
# @synopsis layer.sized(d, size_outside(l) [, steps(n)] [, mode])
# @synopsis layer.sized(dx, dy [, mode])
# @synopsis layer.sized(dx, dy, size_inside(l) [, steps(n)] [, mode])
# @synopsis layer.sized(dx, dy, size_outside(l) [, steps(n)] [, mode])
#
# This method requires a polygon layer. It will apply a bias per edge of the polygons
# and return the biased layer. The layer that this method is called on is not modified.
#
# The alternative method \size works like \sized but modifies the layer it is called on.
#
# In the single-value form, that bias is applied both in horizontal or vertical direction.
# In the two-value form, the horizontal and vertical bias can be specified separately.
#
@ -4711,8 +4717,6 @@ TP_SCRIPT
# Bias values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
# \size is working like \sized but modifies the layer it is called on.
#
# The following images show the effect of various forms of the "sized" method:
#
# @table
@ -4729,14 +4733,58 @@ TP_SCRIPT
# @td @img(/images/drc_sized6.png) @/td
# @/tr
# @/table
#
# The "size_inside" option and the "steps" option implement incremental size. Incremental
# size means that the sizing value is applied in n steps. Between the steps, the sized
# shape is confined to the "size_inside" layer by means of a boolean "AND" operation.
#
# This scheme is used to implement latch-up rules where a device active region has to
# be close to a well tap. By using the well layer as the "size_inside" layer, the size function
# follows the well contours. The steps have to selected such that the per-step size value
# is smaller than the minimum space of the well shapes. With that, the sized shapes will
# not cross over to neighbor well regions. Specifically, the per-step size has to be less
# than about 70% of the minimum space to account for the minimum corner-to-corner case
# with Euclidian space measurements.
#
# "size_inside" and "steps" can be used with positive sizing values only.
# A steps value of 0 will not execute any sizing at all.
#
# "size_outside" acts like "size_inside", but instead of confining the sized region to the
# inside of the given layer, it is confined to be outside of that layer. Technically,
# a boolean "NOT" is performed instead of a boolean "AND".
#
# An example for the "size_inside" option is this:
#
# @code
# ntap.sized(30.um, size_inside(nwell), steps(100))
# @/code
#
# The effect of the "size_inside" option is shown here:
#
# @table
# @tr
# @td @img(/images/drc_sized_inside1.png) @/td
# @td @img(/images/drc_sized_inside2.png) @/td
# @/tr
# @tr
# @td @img(/images/drc_sized_inside3.png) @/td
# @td @img(/images/drc_sized_inside4.png) @/td
# @/tr
# @/table
#
# %DRC%
# @name size
# @brief Polygon sizing (per-edge biasing, modifies the layer)
# @synopsis layer.size(d [, mode])
# @synopsis layer.size(dx, dy [, mode]))
# @synopsis layer.size(d, size_inside(l) [, steps(n)] [, mode])
# @synopsis layer.size(d, size_outside(l) [, steps(n)] [, mode])
# @synopsis layer.size(dx, dy [, mode])
# @synopsis layer.size(dx, dy, size_inside(l) [, steps(n)] [, mode])
# @synopsis layer.size(dx, dy, size_outside(l) [, steps(n)] [, mode])
#
# See \sized. The size method basically does the same but modifies the layer
# See \sized for a description of the options.
# The size method basically does the same but modifies the layer
# it is called on. The input layer is returned and available for further processing.
%w(size sized).each do |f|
@ -4749,8 +4797,13 @@ TP_SCRIPT
dist = 0
steps = nil
inside = nil
outside = nil
mode = 2
values = []
n = 1
args.each do |a|
if a.is_a?(1.class) || a.is_a?(Float)
v = @engine._make_value(a)
@ -4758,10 +4811,62 @@ TP_SCRIPT
values.push(v)
elsif a.is_a?(DRCSizingMode)
mode = a.value
elsif a.is_a?(DRCSizingSteps)
steps = a.value
elsif a.is_a?(DRCSizingInside)
inside = a.value
elsif a.is_a?(DRCSizingOutside)
outside = a.value
else
raise("Parameter #" + n.to_s + " is of unexpected type")
end
n += 1
end
aa = []
f_size = :size
f_sized = :sized
if inside && outside
raise "Cannot use 'inside' and 'outside' together"
end
if steps
if !inside && !outside
raise "'steps' is only allowed with 'inside'"
end
if !steps.is_a?(1.class)
raise "'steps' must be an integer value"
end
end
if inside || outside
if inside
inside.is_a?(DRCLayer) || raise("'inside' argument needs to be a DRC layer")
inside.data.is_a?(RBA::Region) || raise("'inside' requires a polygon layer")
aa.push(inside.data)
f_size = :size_inside
f_sized = :sized_inside
else
outside.is_a?(DRCLayer) || raise("'outside' argument needs to be a DRC layer")
outside.data.is_a?(RBA::Region) || raise("'outside' requires a polygon layer")
aa.push(outside.data)
f_size = :size_outside
f_sized = :sized_outside
end
steps ||= 1
end
if values.size < 1
raise "Method requires one or two sizing values"
elsif values.size > 2
@ -4771,17 +4876,21 @@ TP_SCRIPT
aa.push(values[-1])
end
if inside || outside
aa.push(steps)
end
aa.push(mode)
if :#{f} == :size && @engine.is_tiled?
# in tiled mode, no modifying versions are available
self.data = @engine._tcmd(self.data, dist, RBA::Region, :sized, *aa)
self.data = @engine._tcmd(self.data, dist, RBA::Region, f_sized, *aa)
self
elsif :#{f} == :size
@engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa)
@engine._tcmd(self.data, dist, RBA::Region, f_size, *aa)
self
else
DRCLayer::new(@engine, @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa))
DRCLayer::new(@engine, @engine._tcmd(self.data, dist, RBA::Region, f_sized, *aa))
end
end

View File

@ -57,6 +57,30 @@ module DRC
end
end
# A wrapper for the sizing steps value
class DRCSizingSteps
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the sizing "inside" value
class DRCSizingInside
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the sizing "outside" value
class DRCSizingOutside
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the edge mode value for Region#edges
class DRCEdgeMode
attr_accessor :value

View File

@ -1943,3 +1943,13 @@ TEST(122_NamedLayers)
compare_text_files (output, au_output);
}
TEST(130_size_inside_outside)
{
run_test (_this, "130", false);
}
TEST(130d_size_inside_outside)
{
run_test (_this, "130", true);
}

54
testdata/drc/drcSimpleTests_130.drc vendored Normal file
View File

@ -0,0 +1,54 @@
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.sized(0.0, steps(50), size_inside(l2)).output(100, 0)
l1.sized(20.0, steps(0), size_inside(l2)).output(101, 0)
l1.sized(20.0, steps(50), size_inside(l2)).output(110, 0)
l1.sized(50.0, steps(50), size_outside(l2)).output(111, 0)
l1d = l1.dup
l1d.size(20.0, steps(50), size_inside(l2))
l1d.output(120, 0)
l1d = l1.dup
l1d.size(50.0, steps(50), size_outside(l2))
l1d.output(121, 0)
l1.sized(10.0, 20.0, steps(50), size_inside(l2)).output(130, 0)
l1.sized(25.0, 50.0, steps(50), size_outside(l2)).output(131, 0)
error = nil
begin
l2.sized(-1.0, steps(50), size_outside(l2))
rescue
error = true
end
if !error
raise "error expected!"
end
error = nil
begin
l2.sized(-1.0, 2.0, steps(50), size_outside(l2))
rescue
error = true
end
if !error
raise "error expected!"
end

BIN
testdata/drc/drcSimpleTests_130.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au130.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au130d.gds vendored Normal file

Binary file not shown.

View File

@ -2,10 +2,10 @@
# This script is sourced to define the main version parameters
# The main version
KLAYOUT_VERSION="0.29.2"
KLAYOUT_VERSION="0.29.3"
# The version used for PyPI (don't use variables here!)
KLAYOUT_PYPI_VERSION="0.29.2"
KLAYOUT_PYPI_VERSION="0.29.3"
# The build date
KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")