/* KLayout Layout Viewer Copyright (C) 2006-2017 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 "dbCell.h" #include "dbLayout.h" #include "dbManager.h" #include "dbBox.h" #include "dbPCellVariant.h" namespace db { struct CellOp : public db::Op { CellOp () { } virtual ~CellOp () { } virtual void redo (db::Cell *) const = 0; virtual void undo (db::Cell *) const = 0; }; class SwapLayerOp : public CellOp { public: SwapLayerOp (unsigned int a, unsigned int b) : m_a (a), m_b (b) { } virtual void redo (db::Cell *cell) const { cell->swap (m_a, m_b); } virtual void undo (db::Cell *cell) const { cell->swap (m_a, m_b); } private: unsigned int m_a, m_b; }; struct SetCellPropId : public CellOp { SetCellPropId (db::properties_id_type f, db::properties_id_type t) : m_from (f), m_to (t) { } virtual void redo (db::Cell *cell) const { cell->prop_id (m_to); } virtual void undo (db::Cell *cell) const { cell->prop_id (m_from); } private: db::properties_id_type m_from, m_to; }; Cell::box_type Cell::ms_empty_box = Cell::box_type (); Cell::Cell (cell_index_type ci, db::Layout &l) : db::Object (l.manager ()), m_cell_index (ci), mp_layout (&l), m_instances (this), m_prop_id (0), m_hier_levels (0), m_bbox_needs_update (false), m_ghost_cell (false), mp_last (0), mp_next (0) { // .. nothing yet } Cell::Cell (const Cell &d) : db::Object (d), gsi::ObjectBase (), mp_layout (d.mp_layout), m_instances (this), m_prop_id (d.m_prop_id), m_hier_levels (d.m_hier_levels), mp_last (0), mp_next (0) { m_cell_index = d.m_cell_index; operator= (d); } Cell & Cell::operator= (const Cell &d) { if (this != &d) { // Note: the cell index is part of the cell's identity - hence we do not change it here. It's copied in // the copy ctor however. invalidate_hier (); clear_shapes_no_invalidate (); for (shapes_map::const_iterator s = d.m_shapes_map.begin (); s != d.m_shapes_map.end (); ++s) { shapes (s->first) = s->second; } m_ghost_cell = d.m_ghost_cell; m_instances = d.m_instances; m_bbox = d.m_bbox; m_bboxes = d.m_bboxes; m_hier_levels = d.m_hier_levels; m_prop_id = d.m_prop_id; m_bbox_needs_update = d.m_bbox_needs_update; } return *this; } Cell::~Cell () { clear_shapes (); } Cell * Cell::clone (db::Layout &layout) const { Cell *new_cell = new Cell (cell_index (), layout); *new_cell = *this; return new_cell; } unsigned int Cell::layers () const { if (m_shapes_map.empty ()) { return 0; } else { shapes_map::const_iterator s = m_shapes_map.end (); --s; return s->first + 1; } } bool Cell::empty () const { if (! m_instances.empty ()) { return false; } for (shapes_map::const_iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { if (! s->second.empty ()) { return false; } } return true; } void Cell::clear (unsigned int index) { shapes_map::iterator s = m_shapes_map.find(index); if (s != m_shapes_map.end() && ! s->second.empty ()) { mp_layout->invalidate_bboxes (); // HINT: must come before the change is done! s->second.clear (); m_bbox_needs_update = true; } } Cell::shapes_type & Cell::shapes (unsigned int index) { shapes_map::iterator s = m_shapes_map.find(index); if (s == m_shapes_map.end()) { s = m_shapes_map.insert (std::make_pair(index, shapes_type (0, this, mp_layout ? mp_layout->is_editable () : true))).first; s->second.manager (manager ()); } return s->second; } const Cell::shapes_type & Cell::shapes (unsigned int index) const { shapes_map::const_iterator s = m_shapes_map.find(index); if (s != m_shapes_map.end()) { return s->second; } else { // Because of a gcc bug it seems to be not possible // to instantiate a simple static object here: static const shapes_type *empty_shapes = 0; if (! empty_shapes) { empty_shapes = new shapes_type (); } return *empty_shapes; } } void Cell::clear_shapes () { mp_layout->invalidate_bboxes (); // HINT: must come before the change is done! clear_shapes_no_invalidate (); } void Cell::update_relations () { m_instances.update_relations (mp_layout, cell_index ()); } bool Cell::is_shape_bbox_dirty () const { if (m_bbox_needs_update) { return true; } for (shapes_map::const_iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { if (s->second.is_bbox_dirty ()) { return true; } } return false; } bool Cell::update_bbox (unsigned int layers) { unsigned int l; // determine the bounding box box_type org_bbox = m_bbox; m_bbox = box_type (); // save the original boxes for simple compare box_map org_bboxes; org_bboxes.swap (m_bboxes); // compute the per-layer bboxes of the cell instances // exploit the fact that these are sorted by instance, // rotation and magnification. for (instances_type::sorted_inst_iterator o = m_instances.begin_sorted_insts (); o != m_instances.end_sorted_insts (); ) { const cell_inst_array_type *o1_inst = *o; instances_type::sorted_inst_iterator oo = o; while (++oo != m_instances.end_sorted_insts () && (*oo)->raw_equal (*o1_inst)) ; box_type raw_box; while (o != oo) { raw_box += (*o)->raw_bbox (); ++o; } for (l = 0; l < layers; ++l) { // the per-layer bounding boxes db::box_convert bc (*mp_layout, l); box_type lbox = o1_inst->bbox_from_raw_bbox (raw_box, bc); if (! lbox.empty ()) { m_bbox += lbox; box_map::iterator b = m_bboxes.find (l); if (b == m_bboxes.end ()) { m_bboxes.insert (std::make_pair (l, lbox)); } else { b->second += lbox; } } } } // update the bboxes of the shapes lists for (shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { s->second.update_bbox (); box_type sbox (s->second.bbox ()); if (! sbox.empty ()) { m_bbox += sbox; box_map::iterator b = m_bboxes.find (s->first); if (b == m_bboxes.end ()) { m_bboxes.insert (std::make_pair (s->first, sbox)); } else { b->second += sbox; } } } // reset "dirty child instances" flag m_bbox_needs_update = false; // return true, if anything has changed with the box return (org_bbox != m_bbox || org_bboxes != m_bboxes); } void Cell::copy (unsigned int src, unsigned int dest) { if (src != dest) { db::Shapes &dest_shapes = shapes (dest); db::Cell::shape_iterator src_shape = begin (src, db::Shapes::shape_iterator::All); while (! src_shape.at_end ()) { dest_shapes.insert (*src_shape); ++src_shape; } } else { // When duplicating the layer, first create a copy to avoid problems with non-stable containers // Hint: using the assignment and not the copy ctor does not copy the db::Manager association. db::Shapes shape_copy; shape_copy = shapes (src); db::Shapes &dest_shapes = shapes (dest); for (db::Cell::shape_iterator src_shape = shape_copy.begin (db::Shapes::shape_iterator::All); ! src_shape.at_end (); ++src_shape) { dest_shapes.insert (*src_shape); } } } void Cell::move (unsigned int src, unsigned int dest) { if (src != dest) { copy (src, dest); clear (src); } } void Cell::swap (unsigned int i1, unsigned int i2) { if (i1 != i2) { if (manager () && manager ()->transacting ()) { manager ()->queue (this, new SwapLayerOp (i1, i2)); } shapes (i1).swap (shapes (i2)); m_bbox_needs_update = true; } } void Cell::sort_shapes () { for (shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { s->second.sort (); } } void Cell::prop_id (db::properties_id_type id) { if (m_prop_id != id) { if (manager () && manager ()->transacting ()) { manager ()->queue (this, new SetCellPropId (m_prop_id, id)); } m_prop_id = id; } } const Cell::box_type & Cell::bbox () const { mp_layout->update (); return m_bbox; } const Cell::box_type & Cell::bbox (unsigned int l) const { mp_layout->update (); box_map::const_iterator b = m_bboxes.find (l); if (b != m_bboxes.end ()) { return b->second; } else { return ms_empty_box; } } Cell::const_iterator Cell::begin () const { mp_layout->update (); return m_instances.begin (); } Cell::overlapping_iterator Cell::begin_overlapping (const box_type &b) const { mp_layout->update (); return m_instances.begin_overlapping (b, mp_layout); } Cell::touching_iterator Cell::begin_touching (const box_type &b) const { mp_layout->update (); return m_instances.begin_touching (b, mp_layout); } Cell::parent_inst_iterator Cell::begin_parent_insts () const { mp_layout->update (); return m_instances.begin_parent_insts (mp_layout); } Cell::child_cell_iterator Cell::begin_child_cells () const { mp_layout->update (); return m_instances.begin_child_cells (); } size_t Cell::child_cells () const { mp_layout->update (); return m_instances.child_cells (); } size_t Cell::parent_cells () const { mp_layout->update (); return m_instances.parent_cells (); } Cell::parent_cell_iterator Cell::begin_parent_cells () const { mp_layout->update (); return m_instances.begin_parent_cells (); } Cell::parent_cell_iterator Cell::end_parent_cells () const { mp_layout->update (); return m_instances.end_parent_cells (); } bool Cell::is_top () const { mp_layout->update (); return m_instances.is_top (); } bool Cell::is_leaf () const { return m_instances.empty (); } unsigned int Cell::hierarchy_levels () const { mp_layout->update (); return m_hier_levels; } void Cell::collect_caller_cells (std::set &callers) const { collect_caller_cells (callers, -1); } void Cell::collect_caller_cells (std::set &callers, const std::set &cone, int levels) const { if (levels != 0) { for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) { if (cone.find (*cc) != cone.end () && callers.find (*cc) == callers.end ()) { callers.insert (*cc); mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1); } } } } void Cell::collect_caller_cells (std::set &callers, int levels) const { if (levels != 0) { for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) { if (callers.find (*cc) == callers.end ()) { callers.insert (*cc); mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1); } } } } void Cell::collect_called_cells (std::set &called) const { collect_called_cells (called, -1); } void Cell::collect_called_cells (std::set &called, int levels) const { if (levels != 0) { for (child_cell_iterator cc = begin_child_cells (); ! cc.at_end (); ++cc) { if (called.find (*cc) == called.end ()) { called.insert (*cc); mp_layout->cell (*cc).collect_called_cells (called, levels < 0 ? levels : levels - 1); } } } } void Cell::invalidate_insts () { mp_layout->invalidate_hier (); // HINT: must come before the change is done! mp_layout->invalidate_bboxes (); m_bbox_needs_update = true; } void Cell::invalidate_hier () { mp_layout->invalidate_hier (); // HINT: must come before the change is done! } void Cell::redo (db::Op *op) { db::CellOp *cell_op = dynamic_cast (op); if (cell_op) { // redo operation cell_op->redo (this); } else { // other actions are only queued by the instance list - this is should be // responsible for the handling of the latter. // HACK: this is not really a nice concept, but it saves us a pointer to the manager. m_instances.redo (op); } } void Cell::undo (db::Op *op) { db::CellOp *cell_op = dynamic_cast (op); if (cell_op) { // undo operation cell_op->undo (this); } else { // other actions are only queued by the instance list - this is should be // responsible for the handling of the latter. // HACK: this is not really a nice concept, but it saves us a pointer to the manager. m_instances.undo (op); } } void Cell::collect_mem_stat (db::MemStatistics &m) const { m.cell_info (m_cell_index); m.cell_info (mp_layout); m.cell_info (m_shapes_map); m.cell_info (m_bbox); m.cell_info (m_bboxes); m.cell_info (m_hier_levels); m.cell_info (m_bbox_needs_update); m_instances.collect_mem_stat (m); for (shapes_map::const_iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { m.cell_info (size_t (-sizeof(s->second)), size_t (-sizeof(s->second))); s->second.collect_mem_stat (m); } } void Cell::clear_shapes_no_invalidate () { // Hint: we can't simply clear the map because of the undo stack for (shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { s->second.clear (); } m_bbox_needs_update = true; } unsigned int Cell::count_hier_levels () const { unsigned int l = 0; for (const_iterator c = begin (); !c.at_end (); ++c) { l = std::max (l, (unsigned int) mp_layout->cell (c->cell_index ()).m_hier_levels + 1); } return l; } void Cell::count_parent_insts (std::vector &count) const { m_instances.count_parent_insts (count); } void Cell::clear_parent_insts (size_t sz) { m_instances.clear_parent_insts (sz); } void Cell::sort_child_insts () { m_instances.sort_child_insts (); } std::pair Cell::is_pcell_instance (const instance_type &ref) const { return mp_layout->is_pcell_instance (ref.cell_index ()); } std::map Cell::get_named_pcell_parameters (const instance_type &ref) const { return mp_layout->get_named_pcell_parameters (ref.cell_index ()); } tl::Variant Cell::get_pcell_parameter (const instance_type &ref, const std::string &name) const { return mp_layout->get_pcell_parameter (ref.cell_index (), name); } const std::vector & Cell::get_pcell_parameters (const instance_type &ref) const { return mp_layout->get_pcell_parameters (ref.cell_index ()); } Cell::instance_type Cell::change_pcell_parameters (const instance_type &ref, const std::vector &new_parameters) { cell_index_type new_cell_index = mp_layout->get_pcell_variant_cell (ref.cell_index (), new_parameters); if (new_cell_index != ref.cell_index ()) { CellInstArray new_cell_inst (ref.cell_inst ()); new_cell_inst.object () = db::CellInst (new_cell_index); return m_instances.replace (ref, new_cell_inst); } else { return ref; } } void Cell::sort_inst_tree () { m_instances.sort_inst_tree (mp_layout); // update the number of hierarchy levels m_hier_levels = count_hier_levels (); } std::string Cell::get_basic_name () const { tl_assert (layout () != 0); return layout ()->cell_name (cell_index ()); } std::string Cell::get_qualified_name () const { return get_basic_name (); } std::string Cell::get_display_name () const { tl_assert (layout () != 0); if (is_ghost_cell () && empty ()) { return std::string ("(") + layout ()->cell_name (cell_index ()) + std::string (")"); } else { return layout ()->cell_name (cell_index ()); } } void Cell::set_name (const std::string &name) { tl_assert (layout () != 0); layout ()->rename_cell (cell_index (), name.c_str ()); } }