WIP: more consistent text handling

Texts are not only kept inside original layers, but
also inside deep layers. This enables using texts
from DRC.

However, texts in deep layers are kept as markers.
Mostly they are converted back to texts, but the
orientation will be lost.

The change eliminates the need to using Iterators
in DRC instead of original layers and use of
label layers in deep mode.

A drawback is the presence of marker shapes in
deep mode (unless polygon layers are created).
Also, text output to RDB is not supported from
deep layers currently.
This commit is contained in:
Matthias Koefferlein 2019-03-06 00:32:29 +01:00
parent c07d7e92d4
commit 8b29b30ff9
17 changed files with 851 additions and 297 deletions

View File

@ -461,13 +461,20 @@ unsigned int
DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans)
{ {
layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, trans)); layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, trans));
if (l == m_layout_map.end ()) { if (l == m_layout_map.end () || m_layouts[l->second] == 0) {
unsigned int layout_index = (unsigned int) m_layouts.size (); unsigned int layout_index;
if (l != m_layout_map.end ()) {
// reuse discarded entry
layout_index = l->second;
m_layouts[layout_index] = new LayoutHolder (trans);
} else {
layout_index = (unsigned int) m_layouts.size ();
m_layouts.push_back (new LayoutHolder (trans)); m_layouts.push_back (new LayoutHolder (trans));
}
db::Layout &layout = m_layouts.back ()->layout; db::Layout &layout = m_layouts[layout_index]->layout;
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier); layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) { if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ()); layout.dbu (si.layout ()->dbu () / trans.mag ());
@ -577,6 +584,32 @@ DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator
return DeepLayer (this, layout_index, layer_index); return DeepLayer (this, layout_index, layer_index);
} }
DeepLayer DeepShapeStore::create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe)
{
tl_assert (source.store () == this);
unsigned int from_layer_index = source.layer ();
db::Layout &ly = layout ();
unsigned int layer_index = ly.insert_layer ();
// Build the working hierarchy from the recursive shape iterator
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
db::Box region = db::Box::world ();
db::ICplxTrans trans;
for (db::Layout::iterator c = ly.begin (); c != ly.end (); ++c) {
db::Shapes &into = c->shapes (layer_index);
const db::Shapes &from = c->shapes (from_layer_index);
for (db::Shapes::shape_iterator s = from.begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
pipe->push (*s, trans, region, 0, &into);
}
}
return DeepLayer (this, source.layout_index (), layer_index);
}
DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const db::ICplxTrans &trans) DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const db::ICplxTrans &trans)
{ {
unsigned int layout_index = layout_for_iter (si, trans); unsigned int layout_index = layout_for_iter (si, trans);
@ -725,6 +758,74 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
return cm->second; return cm->second;
} }
namespace
{
class DeepShapeStoreToShapeTransformer
: public ShapesTransformer
{
public:
DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout)
: mp_layout (& layout)
{
// gets the text annotation property ID -
// this is how the texts are passed for annotating the net names
m_text_annot_name_id = std::pair<bool, db::property_names_id_type> (false, 0);
if (! dss.text_property_name ().is_nil ()) {
m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ());
}
}
void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const
{
if (! m_text_annot_name_id.first) {
// fast shortcut
into.insert_transformed (from, trans, pm);
} else {
for (db::Shapes::shape_iterator i = from.begin (db::ShapeIterator::All); ! i.at_end (); ++i) {
bool is_text = false;
if (i->prop_id () > 0) {
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (i->prop_id ());
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) {
if (j->first == m_text_annot_name_id.second) {
db::Text text (j->second.to_string (), db::Trans (i->bbox ().center () - db::Point ()));
text.transform (trans);
if (into.layout ()) {
into.insert (db::TextRef (text, into.layout ()->shape_repository ()));
} else {
into.insert (text);
}
is_text = true;
}
}
}
if (! is_text) {
into.insert (*i, trans, pm);
}
}
}
}
private:
std::pair<bool, db::property_names_id_type> m_text_annot_name_id;
const db::Layout *mp_layout;
};
}
void void
DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer)
{ {
@ -750,8 +851,11 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db
std::vector <db::cell_index_type> source_cells; std::vector <db::cell_index_type> source_cells;
source_cells.push_back (*source_layout.begin_top_down()); source_cells.push_back (*source_layout.begin_top_down());
// prepare a transformer to convert text-annotated markers back to texts (without transformation however)
DeepShapeStoreToShapeTransformer dsst (*this, source_layout);
// actually copy the shapes // actually copy the shapes
db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm); db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm, &dsst);
} }
void void

View File

@ -334,6 +334,11 @@ public:
*/ */
DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ()); DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ());
/**
* @brief Creates a deep layer as a copy from an existing one
*/
DeepLayer create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe);
/** /**
* @brief Gets the empty working layer * @brief Gets the empty working layer
* *

View File

@ -58,8 +58,6 @@ LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_i
if (dss->is_valid_layout_index (m_layout_index)) { if (dss->is_valid_layout_index (m_layout_index)) {
m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set<unsigned int> ()); m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set<unsigned int> ());
} }
init ();
} }
LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu)
@ -131,9 +129,7 @@ db::Region *LayoutToNetlist::make_layer (const std::string &n)
si.shape_flags (db::ShapeIterator::Nothing); si.shape_flags (db::ShapeIterator::Nothing);
std::auto_ptr <db::Region> region (new db::Region (si, dss ())); std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n); register_layer (*region, n);
}
return region.release (); return region.release ();
} }
@ -144,9 +140,7 @@ db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::st
si.shape_flags (db::ShapeIterator::All); si.shape_flags (db::ShapeIterator::All);
std::auto_ptr <db::Region> region (new db::Region (si, dss ())); std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n); register_layer (*region, n);
}
return region.release (); return region.release ();
} }
@ -157,9 +151,7 @@ db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const st
si.shape_flags (db::ShapeIterator::Texts); si.shape_flags (db::ShapeIterator::Texts);
std::auto_ptr <db::Region> region (new db::Region (si, dss ())); std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n); register_layer (*region, n);
}
return region.release (); return region.release ();
} }
@ -170,9 +162,7 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const
si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes);
std::auto_ptr <db::Region> region (new db::Region (si, dss ())); std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n); register_layer (*region, n);
}
return region.release (); return region.release ();
} }

View File

@ -259,7 +259,8 @@ copy_or_propagate_shapes (db::Layout &target,
db::cell_index_type source_parent_cell_index, db::cell_index_type source_parent_cell_index,
unsigned int target_layer, unsigned int source_layer, unsigned int target_layer, unsigned int source_layer,
const std::set<db::cell_index_type> &all_cells_to_copy, const std::set<db::cell_index_type> &all_cells_to_copy,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping) const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const ShapesTransformer *transformer)
{ {
const db::Cell &source_cell = source.cell (source_cell_index); const db::Cell &source_cell = source.cell (source_cell_index);
const db::Cell &source_parent_cell = source.cell (source_parent_cell_index); const db::Cell &source_parent_cell = source.cell (source_parent_cell_index);
@ -273,7 +274,7 @@ copy_or_propagate_shapes (db::Layout &target,
const db::CellInstArray &cell_inst = p->child_inst ().cell_inst (); const db::CellInstArray &cell_inst = p->child_inst ().cell_inst ();
for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) { for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) {
db::ICplxTrans t = db::ICplxTrans (cell_inst.complex_trans (*a)) * propagate_trans; db::ICplxTrans t = db::ICplxTrans (cell_inst.complex_trans (*a)) * propagate_trans;
copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping); copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping, transformer);
} }
} }
@ -282,8 +283,7 @@ copy_or_propagate_shapes (db::Layout &target,
} else if (cm->second != DropCell) { } else if (cm->second != DropCell) {
db::Cell &target_cell = target.cell (cm->second); db::Cell &target_cell = target.cell (cm->second);
target_cell.shapes (target_layer).insert_transformed (source_cell.shapes (source_layer), trans * propagate_trans, pm); transformer->insert_transformed (target_cell.shapes (target_layer), source_cell.shapes (source_layer), trans * propagate_trans, pm);
} }
} }
@ -294,7 +294,9 @@ copy_or_move_shapes (db::Layout &target,
const std::vector<db::cell_index_type> &source_cells, const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping, const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping, const std::map<unsigned int, unsigned int> &layer_mapping,
bool move) const ShapesTransformer *transformer,
bool move
)
{ {
// collect all called cells and all top level cells // collect all called cells and all top level cells
std::set<db::cell_index_type> all_top_level_cells; std::set<db::cell_index_type> all_top_level_cells;
@ -311,7 +313,7 @@ copy_or_move_shapes (db::Layout &target,
for (std::set<db::cell_index_type>::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { for (std::set<db::cell_index_type>::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) {
for (std::map<unsigned int, unsigned int>::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) { for (std::map<unsigned int, unsigned int>::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) {
++progress; ++progress;
copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping); copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping, transformer);
if (move) { if (move) {
source.cell (*c).shapes (lm->first).clear (); source.cell (*c).shapes (lm->first).clear ();
} }
@ -319,15 +321,33 @@ copy_or_move_shapes (db::Layout &target,
} }
} }
namespace
{
class StandardShapesTransformer
: public ShapesTransformer
{
public:
void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const
{
into.insert_transformed (from, trans, pm);
}
};
}
void void
copy_shapes (db::Layout &target, copy_shapes (db::Layout &target,
const db::Layout &source, const db::Layout &source,
const db::ICplxTrans &trans, const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells, const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping, const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping) const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer)
{ {
copy_or_move_shapes (target, const_cast<db::Layout &> (source), trans, source_cells, cell_mapping, layer_mapping, false); StandardShapesTransformer st;
if (! transformer) {
transformer = &st;
}
copy_or_move_shapes (target, const_cast<db::Layout &> (source), trans, source_cells, cell_mapping, layer_mapping, transformer, false);
} }
void void
@ -336,9 +356,14 @@ move_shapes (db::Layout &target,
const db::ICplxTrans &trans, const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells, const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping, const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping) const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer)
{ {
copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, true); StandardShapesTransformer st;
if (! transformer) {
transformer = &st;
}
copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, transformer, true);
} }
// ------------------------------------------------------------ // ------------------------------------------------------------

View File

@ -140,6 +140,21 @@ merge_layouts (db::Layout &target, const db::Layout &source, const db::ICplxTran
const std::map<unsigned int, unsigned int> &layer_mapping, const std::map<unsigned int, unsigned int> &layer_mapping,
std::map<db::cell_index_type, db::cell_index_type> *final_cell_mapping = 0); std::map<db::cell_index_type, db::cell_index_type> *final_cell_mapping = 0);
/**
* @brief An interface for the shape inserter
*
* This interface is used by copy_shapes and move_shapes to insert
* a shape collection into a another one. By reimplementing this interface,
* more shape transformations can be provided.
*/
class DB_PUBLIC ShapesTransformer
{
public:
ShapesTransformer () { }
virtual ~ShapesTransformer () { }
virtual void insert_transformed (db::Shapes &into, const db::Shapes &from, const db::ICplxTrans &trans, db::PropertyMapper &pm) const = 0;
};
/** /**
* @brief Copy shapes from one layout to another * @brief Copy shapes from one layout to another
* *
@ -155,7 +170,8 @@ copy_shapes (db::Layout &target,
const db::ICplxTrans &trans, const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells, const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping, const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping); const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer = 0);
/** /**
* @brief Move shapes from one layout to another * @brief Move shapes from one layout to another
@ -172,7 +188,8 @@ move_shapes (db::Layout &target,
const db::ICplxTrans &trans, const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells, const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping, const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping); const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer = 0);
/** /**
* @brief Find an example cell instance from a child to a top cell * @brief Find an example cell instance from a child to a top cell

View File

@ -149,13 +149,13 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layo
// provide a substitute empty layer // provide a substitute empty layer
layers.push_back (dss.empty_layer (layout_index).layer ()); layers.push_back (dss.empty_layer (layout_index).layer ());
} else { } else {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: must be of deep region kind")), ld->name)); throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): must be of deep region kind")), ld->name, name ()));
} }
} else { } else {
if (&dr->deep_layer ().layout () != &dss.layout (layout_index) || &dr->deep_layer ().initial_cell () != &dss.initial_cell (layout_index)) { if (&dr->deep_layer ().layout () != &dss.layout (layout_index) || &dr->deep_layer ().initial_cell () != &dss.initial_cell (layout_index)) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: not originating from the same source")), ld->name)); throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): not originating from the same source")), ld->name, name ()));
} }
layers.push_back (dr->deep_layer ().layer ()); layers.push_back (dr->deep_layer ().layer ());

View File

@ -250,6 +250,20 @@ OriginalLayerRegion::init ()
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
} }
void
OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
{
db::Shapes &sh = layout->cell (into_cell).shapes (into_layer);
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
// lock the layout against updates while inserting
db::LayoutLocker locker (layout);
for (db::RecursiveShapeIterator i = m_iter; !i.at_end (); ++i) {
tl::ident_map<db::properties_id_type> pm;
sh.insert (*i, i.trans (), pm);
}
}
void void
OriginalLayerRegion::ensure_merged_polygons_valid () const OriginalLayerRegion::ensure_merged_polygons_valid () const
{ {
@ -284,165 +298,4 @@ OriginalLayerRegion::ensure_merged_polygons_valid () const
} }
} }
namespace
{
template <class Container>
struct dot_delivery
{
typedef Container container_type;
dot_delivery ()
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Edge (pt, pt));
}
};
template <class Container>
struct box_delivery
{
typedef Container container_type;
box_delivery (db::Coord enl)
: m_d (enl, enl)
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Box (pt - m_d, pt + m_d));
}
private:
db::Vector m_d;
};
template <class Iter, class Delivery>
static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const Delivery &delivery, typename Delivery::container_type *container)
{
tl::GlobPattern glob_pat;
bool all = false;
if (pattern) {
if (pat == "*") {
all = true;
} else {
glob_pat = tl::GlobPattern (pat);
}
}
for (Iter si = iter; ! si.at_end (); ++si) {
if (si->is_text () &&
(all || (pattern && glob_pat.match (si->text_string ())) || (!pattern && si->text_string () == pat))) {
db::Text t;
si->text (t);
t.transform (si.trans ());
delivery.insert (t.box ().center (), container);
}
}
}
template <class Delivery>
class text_shape_receiver
: public db::HierarchyBuilderShapeReceiver
{
public:
text_shape_receiver (const Delivery &delivery, const std::string &pat, bool pattern)
: m_delivery (delivery), m_glob_pat (), m_all (false), m_pattern (pattern), m_pat (pat)
{
if (pattern) {
if (m_pat == "*") {
m_all = true;
} else {
m_glob_pat = tl::GlobPattern (pat);
}
}
}
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &region, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
{
if (shape.is_text () &&
(m_all || (m_pattern && m_glob_pat.match (shape.text_string ())) || (!m_pattern && shape.text_string () == m_pat))) {
db::Point pt = shape.bbox ().center ();
if (! complex_region) {
if (region.contains (pt)) {
m_delivery.insert (pt.transformed (trans), target);
}
} else {
if (! complex_region->begin_overlapping (db::Box (pt, pt), db::box_convert<db::Box> ()).at_end ()) {
m_delivery.insert (pt.transformed (trans), target);
}
}
}
}
virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
private:
Delivery m_delivery;
tl::GlobPattern m_glob_pat;
bool m_all;
bool m_pattern;
std::string m_pat;
};
}
db::EdgesDelegate *
OriginalLayerRegion::texts_as_dots (const std::string &pat, bool pattern) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
std::auto_ptr<db::FlatEdges> res (new db::FlatEdges ());
res->set_merged_semantics (false);
fill_texts (iter, pat, pattern, dot_delivery<db::FlatEdges> (), res.get ());
return res.release ();
}
db::EdgesDelegate *
OriginalLayerRegion::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
text_shape_receiver<dot_delivery<db::Shapes> > pipe = text_shape_receiver<dot_delivery<db::Shapes> > (dot_delivery<db::Shapes> (), pat, pattern);
return new db::DeepEdges (store.create_custom_layer (iter, &pipe));
}
db::RegionDelegate *
OriginalLayerRegion::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
std::auto_ptr<db::FlatRegion> res (new db::FlatRegion ());
res->set_merged_semantics (false);
fill_texts (iter, pat, pattern, box_delivery<db::FlatRegion> (enl), res.get ());
return res.release ();
}
db::RegionDelegate *
OriginalLayerRegion::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
text_shape_receiver<box_delivery<db::Shapes> > pipe = text_shape_receiver<box_delivery<db::Shapes> > (box_delivery<db::Shapes> (enl), pat, pattern);
return new db::DeepRegion (store.create_custom_layer (iter, &pipe));
}
} }

View File

@ -68,10 +68,7 @@ public:
virtual bool equals (const Region &other) const; virtual bool equals (const Region &other) const;
virtual bool less (const Region &other) const; virtual bool less (const Region &other) const;
db::EdgesDelegate *texts_as_dots (const std::string &pat, bool pattern) const; virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
db::EdgesDelegate *texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const;
db::RegionDelegate *texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const;
db::RegionDelegate *texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const;
protected: protected:
virtual void merged_semantics_changed (); virtual void merged_semantics_changed ();

View File

@ -26,7 +26,10 @@
#include "dbEmptyRegion.h" #include "dbEmptyRegion.h"
#include "dbFlatRegion.h" #include "dbFlatRegion.h"
#include "dbDeepRegion.h" #include "dbDeepRegion.h"
#include "dbDeepEdges.h"
#include "dbFlatEdges.h"
#include "dbPolygonTools.h" #include "dbPolygonTools.h"
#include "tlGlobPattern.h"
namespace db namespace db
{ {
@ -398,6 +401,291 @@ Region::hulls () const
return Region (processed (HullExtractionProcessor ())); return Region (processed (HullExtractionProcessor ()));
} }
namespace
{
template <class Container>
struct dot_delivery
{
typedef Container container_type;
dot_delivery ()
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Edge (pt, pt));
}
};
template <class Container>
struct box_delivery
{
typedef Container container_type;
box_delivery (db::Coord enl)
: m_d (enl, enl)
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Box (pt - m_d, pt + m_d));
}
private:
db::Vector m_d;
};
template <class Iter, class Delivery>
static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const Delivery &delivery, typename Delivery::container_type *container, const db::ICplxTrans &trans, const db::DeepRegion *org_deep)
{
std::pair<bool, db::property_names_id_type> text_annot_name_id;
const db::Layout *layout = 0;
if (org_deep) {
layout = &org_deep->deep_layer ().layout ();
const db::DeepShapeStore *store = org_deep->deep_layer ().store ();
if (! store->text_property_name ().is_nil ()) {
text_annot_name_id = layout->properties_repository ().get_id_of_name (store->text_property_name ());
}
}
tl::GlobPattern glob_pat;
bool all = false;
if (pattern) {
if (pat == "*") {
all = true;
} else {
glob_pat = tl::GlobPattern (pat);
}
}
for (Iter si = iter; ! si.at_end (); ++si) {
bool is_text = false;
std::string text_string;
if (si->is_text ()) {
// a raw text
is_text = true;
text_string = si->text_string ();
} else if (layout && text_annot_name_id.first && si->prop_id () > 0) {
// a text marker
const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (si->prop_id ());
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) {
if (j->first == text_annot_name_id.second) {
text_string = j->second.to_string ();
is_text = true;
}
}
}
if (is_text &&
(all || (pattern && glob_pat.match (text_string)) || (!pattern && text_string == pat))) {
delivery.insert (si.trans () * (trans * si->bbox ().center ()), container);
}
}
}
template <class Delivery>
class text_shape_receiver
: public db::HierarchyBuilderShapeReceiver
{
public:
text_shape_receiver (const Delivery &delivery, const std::string &pat, bool pattern, const db::DeepRegion *org_deep)
: m_delivery (delivery), m_glob_pat (), m_all (false), m_pattern (pattern), m_pat (pat), m_text_annot_name_id (false, 0), mp_layout (0)
{
if (org_deep) {
mp_layout = & org_deep->deep_layer ().layout ();
const db::DeepShapeStore *store = org_deep->deep_layer ().store ();
if (! store->text_property_name ().is_nil ()) {
m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (store->text_property_name ());
}
}
if (pattern) {
if (m_pat == "*") {
m_all = true;
} else {
m_glob_pat = tl::GlobPattern (pat);
}
}
}
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &region, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
{
bool is_text = false;
std::string text_string;
if (shape.is_text ()) {
// a raw text
is_text = true;
text_string = shape.text_string ();
} else if (mp_layout && m_text_annot_name_id.first && shape.prop_id () > 0) {
// a text marker
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (shape.prop_id ());
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) {
if (j->first == m_text_annot_name_id.second) {
text_string = j->second.to_string ();
is_text = true;
}
}
}
if (is_text &&
(m_all || (m_pattern && m_glob_pat.match (text_string)) || (!m_pattern && text_string == m_pat))) {
db::Point pt = shape.bbox ().center ();
if (! complex_region) {
if (region.contains (pt)) {
m_delivery.insert (pt.transformed (trans), target);
}
} else {
if (! complex_region->begin_overlapping (db::Box (pt, pt), db::box_convert<db::Box> ()).at_end ()) {
m_delivery.insert (pt.transformed (trans), target);
}
}
}
}
virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
private:
Delivery m_delivery;
tl::GlobPattern m_glob_pat;
bool m_all;
bool m_pattern;
std::string m_pat;
std::pair<bool, db::property_names_id_type> m_text_annot_name_id;
const db::Layout *mp_layout;
};
}
Edges
Region::texts_as_dots (const std::string &pat, bool pattern) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
if (dr) {
return texts_as_dots (pat, pattern, const_cast<db::DeepShapeStore &> (*dr->deep_layer ().store ()));
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
std::auto_ptr<db::FlatEdges> res (new db::FlatEdges ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, dot_delivery<db::FlatEdges> (), res.get (), si.second, dr);
return Edges (res.release ());
}
Edges
Region::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
if (! si.first.layout ()) {
// flat fallback if the source isn't a deep or original layer
std::auto_ptr<db::FlatEdges> res (new db::FlatEdges ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, dot_delivery<db::FlatEdges> (), res.get (), si.second, dr);
return Edges (res.release ());
}
text_shape_receiver<dot_delivery<db::Shapes> > pipe = text_shape_receiver<dot_delivery<db::Shapes> > (dot_delivery<db::Shapes> (), pat, pattern, dr);
if (dr && dr->deep_layer ().store () == &store) {
return Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe)));
} else {
return Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second)));
}
}
Region
Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
if (dr) {
return texts_as_boxes (pat, pattern, enl, const_cast<db::DeepShapeStore &> (*dr->deep_layer ().store ()));
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
std::auto_ptr<db::FlatRegion> res (new db::FlatRegion ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, box_delivery<db::FlatRegion> (enl), res.get (), si.second, dr);
return Region (res.release ());
}
Region
Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
if (! si.first.layout ()) {
// flat fallback if the source isn't a deep or original layer
std::auto_ptr<db::FlatRegion> res (new db::FlatRegion ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, box_delivery<db::FlatRegion> (enl), res.get (), si.second, dr);
return Region (res.release ());
}
text_shape_receiver<box_delivery<db::Shapes> > pipe = text_shape_receiver<box_delivery<db::Shapes> > (box_delivery<db::Shapes> (enl), pat, pattern, dr);
if (dr && dr->deep_layer ().store () == &store) {
return Region (new db::DeepRegion (store.create_copy (dr->deep_layer (), &pipe)));
} else {
return Region (new db::DeepRegion (store.create_custom_layer (si.first, &pipe, si.second)));
}
}
} }
namespace tl namespace tl

View File

@ -1516,6 +1516,40 @@ public:
return mp_delegate->insert_into (layout, into_cell, into_layer); return mp_delegate->insert_into (layout, into_cell, into_layer);
} }
/**
* @brief Delivers texts as dots (degenerated edges)
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text..
*/
db::Edges texts_as_dots (const std::string &pat, bool as_pattern) const;
/**
* @brief Delivers texts as dots (degenerated edges) in a deep edge collection
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text..
*/
db::Edges texts_as_dots (const std::string &pat, bool as_pattern, db::DeepShapeStore &store) const;
/**
* @brief Delivers texts as boxes
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.
* "enl" is the half size of the box (the box is 2*enl wide and 2*enl high).
*/
db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl) const;
/**
* @brief Delivers texts as boxes in a deep region
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.
* "enl" is the half size of the box (the box is 2*enl wide and 2*enl high).
*/
db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl, db::DeepShapeStore &store) const;
private: private:
friend class Edges; friend class Edges;
friend class EdgePairs; friend class EdgePairs;

View File

@ -392,12 +392,12 @@ static bool is_deep (const db::Edges *e)
static db::Edges *new_texts_as_dots1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern) static db::Edges *new_texts_as_dots1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern)
{ {
return new db::Edges (db::OriginalLayerRegion (si, false).texts_as_dots (pat, pattern)); return new db::Edges (db::Region (si).texts_as_dots (pat, pattern));
} }
static db::Edges *new_texts_as_dots2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern) static db::Edges *new_texts_as_dots2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern)
{ {
return new db::Edges (db::OriginalLayerRegion (si, false).texts_as_dots (pat, pattern, dss)); return new db::Edges (db::Region (si).texts_as_dots (pat, pattern, dss));
} }
static size_t id (const db::Edges *e) static size_t id (const db::Edges *e)

View File

@ -80,41 +80,32 @@ static db::Region *new_shapes (const db::Shapes &s)
static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl) static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl)
{ {
return new db::Region (db::OriginalLayerRegion (si, false).texts_as_boxes (pat, pattern, enl)); return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl));
} }
static db::Region *new_texts_as_boxes2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl) static db::Region *new_texts_as_boxes2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl)
{ {
return new db::Region (db::OriginalLayerRegion (si, false).texts_as_boxes (pat, pattern, enl, dss)); return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl, dss));
}
static const db::OriginalLayerRegion *org_layer (const db::Region *r)
{
const db::OriginalLayerRegion *org_layer = dynamic_cast<const db::OriginalLayerRegion *> (r->delegate ());
if (! org_layer) {
throw tl::Exception (tl::to_string (tr ("Texts can only be identified on an original layer")));
}
return org_layer;
} }
static db::Edges *texts_as_dots1 (const db::Region *r, const std::string &pat, bool pattern) static db::Edges *texts_as_dots1 (const db::Region *r, const std::string &pat, bool pattern)
{ {
return new db::Edges (org_layer (r)->texts_as_dots (pat, pattern)); return new db::Edges (r->texts_as_dots (pat, pattern));
} }
static db::Edges *texts_as_dots2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern) static db::Edges *texts_as_dots2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern)
{ {
return new db::Edges (org_layer (r)->texts_as_dots (pat, pattern, dss)); return new db::Edges (r->texts_as_dots (pat, pattern, dss));
} }
static db::Region *texts_as_boxes1 (const db::Region *r, const std::string &pat, bool pattern, db::Coord enl) static db::Region *texts_as_boxes1 (const db::Region *r, const std::string &pat, bool pattern, db::Coord enl)
{ {
return new db::Region (org_layer (r)->texts_as_boxes (pat, pattern, enl)); return new db::Region (r->texts_as_boxes (pat, pattern, enl));
} }
static db::Region *texts_as_boxes2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl) static db::Region *texts_as_boxes2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl)
{ {
return new db::Region (org_layer (r)->texts_as_boxes (pat, pattern, enl, dss)); return new db::Region (r->texts_as_boxes (pat, pattern, enl, dss));
} }
static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end) static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end)
@ -142,6 +133,11 @@ static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICpl
return new db::Region (si, trans); return new db::Region (si, trans);
} }
static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count)
{
return new db::Region (si, dss, trans, area_ratio, max_vertex_count);
}
static std::string to_string0 (const db::Region *r) static std::string to_string0 (const db::Region *r)
{ {
return r->to_string (); return r->to_string ();
@ -653,9 +649,8 @@ Class<db::Region> decl_Region ("db", "Region",
"\n" "\n"
"This constructor has been introduced in version 0.25." "This constructor has been introduced in version 0.25."
) + ) +
constructor ("new", &new_si, constructor ("new", &new_si, gsi::arg ("shape_iterator"),
"@brief Constructor from a hierarchical shape set\n" "@brief Constructor from a hierarchical shape set\n"
"@args shape_iterator\n"
"\n" "\n"
"This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n" "This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n"
"Text objects and edges are not inserted, because they cannot be converted to polygons.\n" "Text objects and edges are not inserted, because they cannot be converted to polygons.\n"
@ -668,9 +663,8 @@ Class<db::Region> decl_Region ("db", "Region",
"r = RBA::Region::new(layout.begin_shapes(cell, layer))\n" "r = RBA::Region::new(layout.begin_shapes(cell, layer))\n"
"@/code\n" "@/code\n"
) + ) +
constructor ("new", &new_si2, constructor ("new", &new_si2, gsi::arg ("shape_iterator"), gsi::arg ("trans"),
"@brief Constructor from a hierarchical shape set with a transformation\n" "@brief Constructor from a hierarchical shape set with a transformation\n"
"@args shape_iterator, trans\n"
"\n" "\n"
"This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n" "This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n"
"Text objects and edges are not inserted, because they cannot be converted to polygons.\n" "Text objects and edges are not inserted, because they cannot be converted to polygons.\n"
@ -698,7 +692,24 @@ Class<db::Region> decl_Region ("db", "Region",
"@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n" "@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n"
"@param deep_shape_store The hierarchical heap (see there)\n" "@param deep_shape_store The hierarchical heap (see there)\n"
"@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n" "@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n"
"@param" "\n"
"This method has been introduced in version 0.26.\n"
) +
constructor ("new", &new_sid2, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("trans"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)),
"@brief Constructor for a deep region from a hierarchical shape set\n"
"\n"
"This constructor creates a hierarchical region. Use a \\DeepShapeStore object to "
"supply the hierarchical heap. See \\DeepShapeStore for more details.\n"
"\n"
"'area_ratio' and 'max_vertex' supply two optimization parameters which control how "
"big polygons are split to reduce the region's polygon complexity.\n"
"\n"
"The transformation is useful to scale to a specific database unit for example.\n"
"\n"
"@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n"
"@param deep_shape_store The hierarchical heap (see there)\n"
"@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n"
"@param trans The transformation to apply when storing the layout data\n"
"\n" "\n"
"This method has been introduced in version 0.26.\n" "This method has been introduced in version 0.26.\n"
) + ) +

View File

@ -1198,19 +1198,19 @@ TEST(23_Texts)
unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1)); unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1));
unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1)); unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1));
db::Region r6boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100, dss)); db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100, dss));
db::Region r6dots; db::Region r6dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20); db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20);
db::Region r8boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100, dss)); db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100, dss));
db::Region r8dots; db::Region r8dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20); db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20);
db::Region rf6boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100)); db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100));
db::Region rf6dots; db::Region rf6dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20); db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20);
db::Region rf8boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100)); db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100));
db::Region rf8dots; db::Region rf8dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20); db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20);
{ {
db::Layout target; db::Layout target;
@ -1230,6 +1230,59 @@ TEST(23_Texts)
} }
} }
TEST(24_TextsFromDeep)
{
db::Layout ly;
{
std::string fn (tl::testsrc ());
fn += "/testdata/algo/deep_region_l1.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("textstring"));
unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1));
unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1));
db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100, dss));
db::Region r6dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20);
db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100, dss));
db::Region r8dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20);
db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100));
db::Region rf6dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20);
db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100));
db::Region rf8dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20);
{
db::Layout target;
unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6dots);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), rf6boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rf6dots);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r8boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r8dots);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rf8boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rf8dots);
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au24.gds");
}
}
TEST(100_Integration) TEST(100_Integration)
{ {
db::Layout ly; db::Layout ly;

View File

@ -3488,10 +3488,21 @@ CODE
# @li @tt METAL (17/0) @/tt: A layer named "METAL" or layer 17, datatype 0 (for GDS, which does # @li @tt METAL (17/0) @/tt: A layer named "METAL" or layer 17, datatype 0 (for GDS, which does
# not have names)@/li # not have names)@/li
# @/ul # @/ul
#
# Layers created with "input" contain both texts and polygons. There is a subtle
# difference between flat and deep mode: in flat mode, texts are not visible in polygon
# operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode,
# some operations such as clipping are not fully supported for texts. Also, texts will
# vanish in most polygon operations such as booleans etc.
#
# Texts can alway be selected by using the \texts method.
#
# If you don't want to see texts, use \polygons to create an input layer with polygon data
# only. If you only want to see texts, use \labels to create an input layer with texts only.
def input(*args) def input(*args)
layers = parse_input_layers(*args) layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, false)) DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll))
end end
# %DRC% # %DRC%
@ -3501,26 +3512,45 @@ CODE
# @synopsis source.labels(layer, datatype) # @synopsis source.labels(layer, datatype)
# @synopsis source.labels(layer_into) # @synopsis source.labels(layer_into)
# @synopsis source.labels(filter, ...) # @synopsis source.labels(filter, ...)
#
# Creates a layer with the labels from the given layer of the source. # Creates a layer with the labels from the given layer of the source.
# The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo
# object or by a sequence of filters.
# Filters are expressions describing ranges
# of layers and/or datatype numbers or layer names. Multiple filters
# can be given and all layers matching at least one of these filter
# expressions are joined to render the label collection. See "input" for
# more details about the input layer specification.
# #
# Label layers currently can only be passed to an output layer. # This method is identical to \input, but takes only texts from the given input
# Processing of labels is not supported. See "texts" for a way to filter # layer.
# texts and use the text locations in geometrical operations.
#
# @code
# labels(1, 0).output(100, 0)
# @/code
def labels(*args) def labels(*args)
layers = parse_input_layers(*args) layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, true)) DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts))
end
# %DRC%
# @name polygons
# @brief Gets the polygon shapes (or shapes that can be converted polygons) from an input layer
# @synopsis source.polygons(layer)
# @synopsis source.polygons(layer, datatype)
# @synopsis source.polygons(layer_into)
# @synopsis source.polygons(filter, ...)
#
# Creates a layer with the polygon shapes from the given layer of the source.
# With "polygon shapes" we mean all kind of shapes that can be converted to polygons.
# Those are boxes, paths and real polygons.
#
# This method is identical to \input with respect to the options supported.
def polygons(*args)
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons))
end
# %DRC%
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchy of the layout
# @synopsis make_layer
# This method delivers a new empty original layer.
def make_layer
layers = []
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll))
end end
# %DRC% # %DRC%
@ -3690,6 +3720,73 @@ CODE
modified modified
end end
# %DRC%
# @name connect_global
# @brief Connects a layer with a global net
# @synopsis connect_global(l, name)
# Connects the shapes from the given layer l to a global net with the given name.
# Global nets are common to all cells. Global nets automatically connect to parent
# cells throughs implied pins. An example is the substrate (bulk) net which connects
# to shapes belonging to tie-down diodes.
def connect_global(l, name)
l.is_a?(DRC::DRCLayer) || raise("Layer argument of Netter#connect_global must be a layer")
l.requires_region("Netter#connect_global (layer argument)")
@layers[l.data.data_id] = l.data
@global_connections &lt;&lt; [ l.data.data_id, name.to_s ]
end
# %DRC%
# @name extract_devices
# @brief Extracts devices based on the given extractor class, name and device layer selection
# @synopsis extract_devices(extractor, layer_hash)
# Runs the device extraction for given device extractor class.
#
# The device extractor is either an instance of one of the predefined extractor
# classes (e.g. RBA::DeviceExtractorMOS4Transistor) or a custom class. It provides the
# algorithms for deriving the device parameters from the device geometry. It needs
# several device recognition layers which are passed in the layer hash.
#
# Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance
# of device extractor. The device extractor beside the algorithm and specific
# extraction settings defines the name of the device to be built.
#
# The layer hash is a map of device type specific functional names (key) and
# polygon layers (value). Here is an example:
#
# @code
# deep
#
# nwell = input(1, 0)
# active = input(2, 0)
# poly = input(3, 0)
# bulk = make_layer # renders an empty layer used for putting the terminals on
#
# nactive = active - nwell # active area of NMOS
# nsd = nactive - poly # source/drain area
# gate = nactive &amp; poly # gate area
#
# mos4_ex = RBA::DeviceExtractorMOS4Transistor::new("NMOS4")
# extract_devices(mos4_ex, { :SD => nsd, :G => gate, :P => poly, :W => bulk })
# @/code
def extract_devices(devex, layer_selection)
devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance")
layer_selection.is_a?(Hash) || raise("Second argument of Netter#extract_devices must be a hash")
ls = {}
layer_selection.each do |n,l|
l.requires_region("Netter#extract_devices (#{n} layer)")
@layers[l.data.data_id] = l.data
ls[n.to_s] = l.data
end
@devices_to_extract &lt;&lt; [ devex, ls ]
modified
end
# %DRC% # %DRC%
# @name clear_connections # @name clear_connections
# @brief Clears all connections stored so far # @brief Clears all connections stored so far
@ -3697,7 +3794,9 @@ CODE
# See \connect for more details. # See \connect for more details.
def clear_connections def clear_connections
@devices_to_extract = []
@connections = [] @connections = []
@global_connections = []
@layers = {} @layers = {}
modified modified
end end
@ -3804,6 +3903,18 @@ CODE
end end
# %DRC%
# @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object
# @synopsis l2n_data
# The RBA::LayoutToNetlist object provides access to the internal details of
# the netter object.
def l2n_data
@l2n || make_l2n
@l2n
end
def _finish def _finish
clear_connections clear_connections
# cleans up the L2N object # cleans up the L2N object
@ -3830,8 +3941,14 @@ CODE
end end
@layers.each { |id,l| @l2n.register(l, "l" + id.to_s) } @layers.each { |id,l| @l2n.register(l, "l" + id.to_s) }
@devices_to_extract.each do |devex,ls|
@engine._cmd(@l2n, :extract_devices, devex, ls)
end
@layers.each { |id,l| @l2n.connect(l) } @layers.each { |id,l| @l2n.connect(l) }
@connections.each { |a,b| @l2n.connect(@layers[a], @layers[b]) } @connections.each { |a,b| @l2n.connect(@layers[a], @layers[b]) }
@global_connections.each { |l,n| @l2n.connect_global(@layers[l], n) }
# run extraction in a timed environment # run extraction in a timed environment
@engine._cmd(@l2n, :extract_netlist) @engine._cmd(@l2n, :extract_netlist)
@ -4200,12 +4317,33 @@ CODE
@tt = n.to_i @tt = n.to_i
end end
# %DRC%
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchical scheme selected
# @synopsis make_layer
# The intention of this method is to provide an empty polygon layer based on the
# hierarchical scheme selected. This will create a new layer with the hierarchy
# of the current layout in deep mode and a flat layer in flat mode.
# This method is similar to \polygon_layer, but the latter does not create
# a hierarchical layer. Hence the layer created by \make_layer is suitable
# for use in device extraction for example, while the one
# delivered by \polygon_layer is not.
#
# On the other hand, a layer created by the \make_layer method is not intended to be
# filled with \Layer#insert.
def make_layer
layout.make_layer
end
# %DRC% # %DRC%
# @name polygon_layer # @name polygon_layer
# @brief Creates an empty polygon layer # @brief Creates an empty polygon layer
# @synopsis polygon_layer # @synopsis polygon_layer
# The intention of that method is to create an empty layer which can be # The intention of that method is to create an empty layer which can be
# filled with polygon-like objects using \Layer#insert. # filled with polygon-like objects using \Layer#insert.
# A similar method which creates a hierarchical layer in deep mode is
# \make_layer. This other layer is better suited for use with device extraction.
def polygon_layer def polygon_layer
DRCLayer::new(self, RBA::Region::new) DRCLayer::new(self, RBA::Region::new)
@ -4621,12 +4759,24 @@ CODE
# @name input # @name input
# @brief Fetches the shapes from the specified input from the default source # @brief Fetches the shapes from the specified input from the default source
# @synopsis input(args) # @synopsis input(args)
# See \Source#input for a description of that function. # See \Source#input for a description of that function. This method will fetch
# polygons and labels. See \polygons and \labels for more specific versions of
# this method.
def input(*args) def input(*args)
layout.input(*args) layout.input(*args)
end end
# %DRC%
# @name polygons
# @brief Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source
# @synopsis polygons(args)
# See \Source#polygons for a description of that function.
def polygons(*args)
layout.polygons(*args)
end
# %DRC% # %DRC%
# @name labels # @name labels
# @brief Gets the labels (text) from an original layer # @brief Gets the labels (text) from an original layer
@ -4754,6 +4904,12 @@ CODE
# @synopsis connect(a, b) # @synopsis connect(a, b)
# See \Netter#connect for a description of that function. # See \Netter#connect for a description of that function.
# %DRC%
# @name connect_global
# @brief Specifies a connection to a global net
# @synopsis connect_global(l, name)
# See \Netter#connect_global for a description of that function.
# %DRC% # %DRC%
# @name clear_connections # @name clear_connections
# @brief Clears all connections stored so far # @brief Clears all connections stored so far
@ -4766,7 +4922,19 @@ CODE
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ]) # @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ])
# See \Netter#antenna_check for a description of that function # See \Netter#antenna_check for a description of that function
%w(connect clear_connections antenna_check).each do |f| # %DRC%
# @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object for the default \Netter
# @synopsis l2n_data
# See \Netter#l2n_data for a description of that function
# %DRC%
# @name extract_devices
# @brief Extracts devices for a given device extractor and device layer selection
# @synopsis extract_devices(extractor, layer_hash)
# See \Netter#extract_devices for a description of that function
%w(connect connect_global clear_connections antenna_check l2n_data extract_devices).each do |f|
eval &lt;&lt;"CODE" eval &lt;&lt;"CODE"
def #{f}(*args) def #{f}(*args)
_netter.#{f}(*args) _netter.#{f}(*args)
@ -5033,10 +5201,12 @@ CODE
end end
end end
def _input(layout, cell_index, layers, sel, box, clip, overlapping, labels_only) def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags)
if layers.empty? &amp;&amp; ! @deep
if layers.empty?
r = RBA::Region::new r = RBA::Region::new
else else
if box if box
@ -5044,9 +5214,7 @@ CODE
else else
iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers) iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers)
end end
if labels_only iter.shape_flags = shape_flags
iter.shape_flags = RBA::Shapes::STexts
end
sel.each do |s| sel.each do |s|
if s == "-" if s == "-"
@ -5064,24 +5232,12 @@ CODE
end end
end end
if labels_only
# for labels layers, the iterator is the data (no region)
r = iter
else
sf = layout.dbu / self.dbu sf = layout.dbu / self.dbu
if (sf - 1.0).abs &gt; 1e-6
if @deep if @deep
raise("DBU scaling (" + ("%.12g" % layout.dbu) + " to " + ("%.12g" % self.dbu) + ") is not supported in deep mode currently")
end
r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f))
elsif @deep
@dss ||= RBA::DeepShapeStore::new @dss ||= RBA::DeepShapeStore::new
r = RBA::Region::new(iter, @dss) r = RBA::Region::new(iter, @dss, RBA::ICplxTrans::new(sf.to_f))
else else
r = RBA::Region::new(iter) r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f))
end end
# clip if a box is specified # clip if a box is specified
@ -5091,8 +5247,6 @@ CODE
end end
end
r r
end end
@ -5112,11 +5266,7 @@ CODE
cat = @output_rdb.create_category(args[0].to_s) cat = @output_rdb.create_category(args[0].to_s)
args[1] &amp;&amp; cat.description = args[1] args[1] &amp;&amp; cat.description = args[1]
if data.is_a?(RBA::RecursiveShapeIterator)
cat.scan_shapes(data)
else
cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data) cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data)
end
else else
@ -5173,8 +5323,6 @@ CODE
@used_output_layers[li] = true @used_output_layers[li] = true
end end
if data.respond_to?(:is_deep?) &amp;&amp; data.is_deep?
# insert the data into the output layer # insert the data into the output layer
if data.is_a?(RBA::EdgePairs) if data.is_a?(RBA::EdgePairs)
data.insert_into_as_polygons(output, output_cell.cell_index, tmp, 1) data.insert_into_as_polygons(output, output_cell.cell_index, tmp, 1)
@ -5182,17 +5330,6 @@ CODE
data.insert_into(output, output_cell.cell_index, tmp) data.insert_into(output, output_cell.cell_index, tmp)
end end
else
# insert the data into the output layer
if data.is_a?(RBA::EdgePairs)
output_cell.shapes(tmp).insert_as_polygons(data, 1)
else
output_cell.shapes(tmp).insert(data)
end
end
# make the temp layer the output layer # make the temp layer the output layer
if tmp != li if tmp != li
output.swap_layers(tmp, li) output.swap_layers(tmp, li)

View File

@ -302,3 +302,43 @@ TEST(7_AntennaWithDiodes)
db::compare_layouts (_this, layout, au, db::NoNormalization); db::compare_layouts (_this, layout, au, db::NoNormalization);
} }
TEST(8_FlatTextsAndPolygons)
{
std::string rs = tl::testsrc ();
rs += "/testdata/drc/drcSimpleTests_8.drc";
std::string input = tl::testsrc ();
input += "/testdata/drc/texts.gds";
std::string au = tl::testsrc ();
au += "/testdata/drc/drcSimpleTests_au8.oas";
std::string output = this->tmp_file ("tmp.gds");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (_this, layout, au, db::NoNormalization);
}

BIN
testdata/algo/deep_region_au24.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/texts.gds vendored Normal file

Binary file not shown.