WIP: first version hierarchy builder

This commit is contained in:
Matthias Koefferlein 2018-11-14 02:22:53 +01:00
parent 2bfccca462
commit 6f4988a764
4 changed files with 437 additions and 11 deletions

View File

@ -0,0 +1,304 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbRecursiveShapeIterator.h"
#include "dbHierarchyBuilder.h"
#include "dbClip.h"
#include "dbRegion.h"
namespace db
{
// -------------------------------------------------------------------------------------------
int
compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2)
{
// basic source (layout, top_cell) needs to be the same of course
if (iter1.layout () != iter2.layout ()) {
// NOTE: pointer compare :-(
return iter1.layout () < iter2.layout () ? -1 : 1;
}
if (iter1.top_cell ()->cell_index () != iter2.top_cell ()->cell_index ()) {
return iter1.top_cell ()->cell_index () < iter2.top_cell ()->cell_index () ? -1 : 1;
}
// max depth controls the main hierarchical appearance
if (iter1.max_depth () != iter2.max_depth ()) {
return iter1.max_depth () < iter2.max_depth () ? -1 : 1;
}
// if a region is set, the hierarchical appearance is the same only if the layers and
// complex region are indentical
if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) {
return (iter1.region () == db::Box::world ()) < (iter2.region () == db::Box::world ()) ? -1 : 1;
}
if (iter1.region () != db::Box::world ()) {
if (iter1.has_complex_region () != iter2.has_complex_region ()) {
return iter1.has_complex_region () < iter2.has_complex_region () ? -1 : 1;
}
if (iter1.has_complex_region () && iter1.complex_region () != iter2.complex_region ()) {
return iter1.complex_region () < iter2.complex_region () ? -1 : 1;
}
if (iter1.multiple_layers () != iter2.multiple_layers ()) {
return iter1.multiple_layers () < iter2.multiple_layers () ? -1 : 1;
}
if (iter1.multiple_layers ()) {
if (iter1.layers () != iter2.layers ()) {
return iter1.layers () < iter2.layers ();
}
} else {
if (iter1.layer () != iter2.layer ()) {
return iter1.layer () < iter2.layer ();
}
}
}
return 0;
}
// -------------------------------------------------------------------------------------------
static bool is_outside (const db::Box &obj, const std::set<db::Box> &region)
{
if (region.empty ()) {
return false;
}
for (std::set<db::Box>::const_iterator b = region.begin (); b != region.end (); ++b) {
if (obj.touches (*b)) {
return false;
}
}
return true;
}
static bool is_inside (const db::Box &obj, const std::set<db::Box> &region)
{
if (region.empty ()) {
return true;
}
for (std::set<db::Box>::const_iterator b = region.begin (); b != region.end (); ++b) {
if (obj.inside (*b)) {
return true;
}
}
if (is_outside (obj, region)) {
return false;
} else {
// TODO: basically a detailed analysis is required here
return false;
}
}
/**
* @brief Computes the clip variant (a box set) from a cell bbox, a region and a complex region (optional)
*/
static std::set<db::Box> compute_clip_variant (const db::Box &cell_bbox, const db::Box &region, const db::RecursiveShapeReceiver::box_tree_type *complex_region)
{
std::set<db::Box> clip_variant;
if (region != db::Box::world () && ! cell_bbox.inside (region)) {
db::Box rect_box = region & cell_bbox;
if (complex_region) {
for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert<db::Box> ()); ! cr.at_end (); ++cr) {
if (rect_box.overlaps (*cr)) {
clip_variant.insert (rect_box * *cr);
}
}
if (clip_variant.empty ()) {
// an empty clip variant should not happen, but who knows
clip_variant.insert (db::Box ());
}
} else {
clip_variant.insert (rect_box);
}
}
return clip_variant;
}
static void insert_clipped (const db::Box &box, const std::set<db::Box> &clip, db::Shapes &shapes)
{
for (std::set<db::Box>::const_iterator b = clip.begin (); b != clip.end (); ++b) {
db::Box bb = *b & box;
if (! bb.empty ()) {
shapes.insert (bb);
}
}
}
static void insert_clipped (const db::Polygon &poly, const std::set<db::Box> &clip, db::Shapes &shapes)
{
// TODO: is this good way to clip a polygon at a complex boundary?
for (std::set<db::Box>::const_iterator b = clip.begin (); b != clip.end (); ++b) {
std::vector<db::Polygon> clipped_poly;
db::clip_poly (poly, *b, clipped_poly);
for (std::vector<db::Polygon>::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) {
shapes.insert (*p);
}
}
}
HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes)
: mp_target (target), m_clip_shapes (clip_shapes), m_initial_pass (true), m_target_layer (target_layer)
{
// .. nothing yet ..
}
void
HierarchyBuilder::begin (const RecursiveShapeIterator *iter)
{
if (m_initial_pass) {
m_ref_iter = *iter;
} else {
tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_ref_iter, *iter) == 0);
}
m_cells_seen.clear ();
m_cm_entry = cell_map_type::const_iterator ();
m_cell_stack.clear ();
db::Cell &new_top = mp_target->cell (mp_target->add_cell (iter->layout ()->cell_name (iter->top_cell ()->cell_index ())));
m_cell_stack.push_back (&new_top);
}
void
HierarchyBuilder::end (const RecursiveShapeIterator * /*iter*/)
{
m_initial_pass = false;
m_cells_seen.clear ();
m_cell_stack.pop_back ();
}
void
HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/)
{
tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ());
m_cells_seen.insert (m_cm_entry->first);
m_cell_stack.push_back (&mp_target->cell (m_cm_entry->second));
}
void
HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/)
{
m_cell_stack.pop_back ();
}
bool
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
{
if (all) {
std::pair<db::cell_index_type, std::set<db::Box> > key (inst.object ().cell_index (), std::set<db::Box> ());
m_cm_entry = m_cell_map.find (key);
if (m_initial_pass) {
if (m_cm_entry == m_cell_map.end ()) {
db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ()));
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
}
db::CellInstArray new_inst = inst;
new_inst.object () = db::CellInst (m_cm_entry->second);
m_cell_stack.back ()->insert (new_inst);
}
return (m_cells_seen.find (key) == m_cells_seen.end ());
} else {
// iterate by instance array members
return true;
}
}
bool
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region)
{
db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox ();
std::set<db::Box> clip_variant = compute_clip_variant (cell_bbox, region, complex_region);
std::pair<db::cell_index_type, std::set<db::Box> > key (inst.object ().cell_index (), clip_variant);
m_cm_entry = m_cell_map.find (key);
if (m_initial_pass) {
if (m_cm_entry == m_cell_map.end ()) {
std::string suffix;
if (! key.second.empty ()) {
suffix = "$CLIP_VAR";
}
db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ());
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
}
db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans);
m_cell_stack.back ()->insert (new_inst);
}
return (m_cells_seen.find (key) == m_cells_seen.end ());
}
void
HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/)
{
tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ());
// TODO: property mapping?
db::Shapes &shapes = m_cell_stack.back ()->shapes (m_target_layer);
if (m_cm_entry->first.second.empty () || is_inside (shape.bbox (), m_cm_entry->first.second)) {
shapes.insert (shape);
} else if (! is_outside (shape.bbox (), m_cm_entry->first.second)) {
// clip the shape if required
if (! m_clip_shapes || shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) {
shapes.insert (shape);
} else if (shape.is_box ()) {
insert_clipped (shape.box (), m_cm_entry->first.second, shapes);
} else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) {
insert_clipped (shape.polygon (), m_cm_entry->first.second, shapes);
}
}
}
}

View File

@ -0,0 +1,92 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbHierarchyBuilder
#define HDR_dbHierarchyBuilder
#include "dbCommon.h"
#include "dbRecursiveShapeIterator.h"
#include "dbLayout.h"
#include <map>
#include <vector>
#include <set>
namespace db
{
/**
* @brief A helper function comparing two recursive shape iterators for compatibility with respect to hierarchy building
*
* This function will return -1, 0 or 1 depending on whether the two iterators
* can be used with the same builder (0) or whether they are less (-1) or greater (1).
*/
int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2);
/**
* @brief A class building a hierarchy from a recursive shape iterator in push mode
*
* This class is a RecursiveShapeReceiver which acts on the hierarchy events and
* uses them to rebuild a hierarchy in the target layout. In can be used multiple
* times and will reuse the hierarchy as far as possible.
*
* The hierarchy builder can form clip variants for cells and clip the shapes
* according to the selected region.
*
* NOTE: the hierarchy build should not be used in multiple passes with regions
* as the hierarchy is sampled in the first pass and the hierarchy builder will
* rely on precisely the same hierarchy arrangement. This is not given with
* region selections.
*/
class HierarchyBuilder
: public db::RecursiveShapeReceiver
{
public:
typedef std::map<std::pair<db::cell_index_type, std::set<db::Box> >, db::cell_index_type> cell_map_type;
HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes);
virtual void begin (const RecursiveShapeIterator *iter);
virtual void end (const RecursiveShapeIterator * /*iter*/);
virtual void enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/);
virtual void leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/);
virtual bool new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box &region, const box_tree_type *complex_region, bool all);
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box &region, const box_tree_type *complex_region);
virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans);
private:
tl::weak_ptr<db::Layout> mp_target;
bool m_clip_shapes;
bool m_initial_pass;
db::RecursiveShapeIterator m_ref_iter;
cell_map_type m_cell_map;
std::set<cell_map_type::key_type> m_cells_seen;
cell_map_type::const_iterator m_cm_entry;
unsigned int m_target_layer;
std::vector<db::Cell *> m_cell_stack;
};
}
#endif

View File

@ -39,6 +39,7 @@
#include "tlVector.h"
#include "tlString.h"
#include "tlThreads.h"
#include "tlObject.h"
#include "gsi.h"
#include <cstring>
@ -457,7 +458,8 @@ public:
class DB_PUBLIC Layout
: public db::Object,
public db::LayoutStateModel,
public gsi::ObjectBase
public gsi::ObjectBase,
public tl::Object
{
public:
typedef db::Box box_type;

View File

@ -266,6 +266,29 @@ public:
return m_max_depth;
}
/**
* @brief Specify the minimum hierarchy depth to look into
*
* A depth of 0 instructs the iterator to deliver shapes from the top level.
* 1 instructs to deliver shapes from the first child level.
* The minimum depth must be specified before the shapes are being retrieved.
*/
void min_depth (int depth)
{
if (m_min_depth != depth) {
m_min_depth = depth;
m_needs_reinit = true;
}
}
/**
* @brief Gets the minimum hierarchy depth to search for
*/
int min_depth () const
{
return m_min_depth;
}
/**
* @brief Gets the iterated shapes
*
@ -418,18 +441,23 @@ public:
void reset_selection ();
/**
* @brief Specify the minimum hierarchy depth to look into
* @brief Returns the cells in the "enable" selection
*
* A depth of 0 instructs the iterator to deliver shapes from the top level.
* 1 instructs to deliver shapes from the first child level.
* The minimum depth must be specified before the shapes are being retrieved.
* Cells in this set make the iterator become active, while cells in the
* disable selection make the iterator inactive. Only when active, the
* iterator will deliver shapes.
*/
void min_depth (int depth)
{
if (m_min_depth != depth) {
m_min_depth = depth;
m_needs_reinit = true;
}
const std::set<db::cell_index_type> &enables () const
{
return m_start;
}
/**
* @brief Returns the cells in the "disable" selection
*/
const std::set<db::cell_index_type> &disables () const
{
return m_stop;
}
/**