mirror of https://github.com/KLayout/klayout.git
Merge pull request #1765 from KLayout/faster-latchup-rules
Faster latchup rules
This commit is contained in:
commit
2c0a7f797a
28
Changelog
28
Changelog
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 (); }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue