From 61ef30f9ad56cac948b56da8926e8d300bb7a43b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 Jun 2017 22:10:12 +0200 Subject: [PATCH] Enhanced layout statistics form The form offers a detailed shape statistics page. --- src/db/dbShape.cc | 52 +++++ src/db/dbShape.h | 5 + src/lay/layLayoutStatisticsForm.cc | 323 ++++++++++++++++++++++++++--- 3 files changed, 348 insertions(+), 32 deletions(-) diff --git a/src/db/dbShape.cc b/src/db/dbShape.cc index cc576e5bc..07116486d 100644 --- a/src/db/dbShape.cc +++ b/src/db/dbShape.cc @@ -566,6 +566,58 @@ Shape::perimeter_type Shape::perimeter () const } } +size_t Shape::array_size () const +{ + switch (m_type) { + case Null: + return 0; + case Polygon: + case PolygonRef: + case PolygonPtrArrayMember: + return 1; + case PolygonPtrArray: + { + const polygon_ptr_array_type *arr = basic_ptr (polygon_ptr_array_type::tag ()); + return arr->size (); + } + case SimplePolygon: + case SimplePolygonRef: + case SimplePolygonPtrArrayMember: + return 1; + case SimplePolygonPtrArray: + { + const simple_polygon_ptr_array_type *arr = basic_ptr (simple_polygon_ptr_array_type::tag ()); + return arr->size (); + } + case Path: + case PathRef: + case PathPtrArrayMember: + return 1; + case PathPtrArray: + { + const path_ptr_array_type *arr = basic_ptr (path_ptr_array_type::tag ()); + return arr->size (); + } + case BoxArray: + { + const box_array_type *arr = basic_ptr (box_array_type::tag ()); + return arr->size (); + } + case ShortBoxArray: + { + const short_box_array_type *arr = basic_ptr (short_box_array_type::tag ()); + return arr->size (); + } + case Box: + case ShortBox: + case BoxArrayMember: + case ShortBoxArrayMember: + return 1; + default: + return 1; + } +} + Shape::area_type Shape::area () const { switch (m_type) { diff --git a/src/db/dbShape.h b/src/db/dbShape.h index ff3a324eb..0593f0dfa 100644 --- a/src/db/dbShape.h +++ b/src/db/dbShape.h @@ -2419,6 +2419,11 @@ public: m_type == TextPtrArrayMember; } + /** + * @brief Returns the array size if the shape references an array + */ + size_t array_size () const; + /** * @brief Array instance member transformation * diff --git a/src/lay/layLayoutStatisticsForm.cc b/src/lay/layLayoutStatisticsForm.cc index a95e0da92..21cce432e 100644 --- a/src/lay/layLayoutStatisticsForm.cc +++ b/src/lay/layLayoutStatisticsForm.cc @@ -28,6 +28,7 @@ #include "tlExpression.h" #include "tlTimer.h" #include "dbLayoutQuery.h" +#include "dbCellGraphUtils.h" #include #include @@ -243,7 +244,7 @@ StatisticsTemplateProcessor::process_child_nodes (const QDomElement &element, tl void StatisticsTemplateProcessor::process (const QDomElement &element, tl::Eval &eval, QXmlStreamWriter &writer) { - static QString template_namespace_uri = QString::fromUtf8 ("www.klayout.de/layout-statistics-template"); + static QString template_namespace_uri = QString::fromUtf8 ("www.klayout.org/layout-statistics-template"); static QString template_cmd_if = QString::fromUtf8 ("if"); static QString template_cmd_true = QString::fromUtf8 ("true"); static QString template_cmd_false = QString::fromUtf8 ("false"); @@ -377,7 +378,130 @@ StatisticsTemplateProcessor::process (const QDomElement &element, tl::Eval &eval // ------------------------------------------------------------ -class StatisticsSource +class ShapeStatistics +{ +public: + ShapeStatistics () + { + // .. nothing yet .. + } + + void compute (const db::Shapes &shapes) + { + for (db::Shapes::shape_iterator i = shapes.begin (db::ShapeIterator::All); ! i.at_end (); ++i) { + size_t n = 1; + if (i.in_array ()) { + n = i.array ().array_size (); + m_count [i.array ().type ()] += 1; + i.finish_array (); + } + m_count [i->type ()] += n; + } + } + + void operator*= (size_t f) + { + for (std::map::iterator c = m_count.begin (); c != m_count.end (); ++c) { + c->second *= f; + } + } + + void operator+= (const ShapeStatistics &other) + { + for (std::map::const_iterator c = other.m_count.begin (); c != other.m_count.end (); ++c) { + m_count [c->first] += c->second; + } + } + + size_t count (db::Shape::object_type t) const + { + std::map::const_iterator c = m_count.find (t); + if (c != m_count.end ()) { + return c->second; + } else { + return 0; + } + } + + size_t box_total () const + { + return count (db::Shape::Box) + count (db::Shape::BoxArrayMember) + count (db::Shape::ShortBox) + count (db::Shape::ShortBoxArrayMember); + } + + size_t box_single () const + { + return count (db::Shape::Box) + count (db::Shape::ShortBox); + } + + size_t box_array () const + { + return count (db::Shape::BoxArray) + count (db::Shape::ShortBoxArray); + } + + size_t polygon_total () const + { + return count (db::Shape::Polygon) + count (db::Shape::PolygonRef) + count (db::Shape::PolygonPtrArrayMember) + + count (db::Shape::SimplePolygon) + count (db::Shape::SimplePolygonRef) + count (db::Shape::SimplePolygonPtrArrayMember); + } + + size_t polygon_single () const + { + return count (db::Shape::Polygon) + count (db::Shape::PolygonRef) + + count (db::Shape::SimplePolygon) + count (db::Shape::SimplePolygonRef); + } + + size_t polygon_array () const + { + return count (db::Shape::PolygonPtrArray) + count (db::Shape::SimplePolygonPtrArray); + } + + size_t path_total () const + { + return count (db::Shape::Path) + count (db::Shape::PathRef) + count (db::Shape::PathPtrArrayMember); + } + + size_t path_single () const + { + return count (db::Shape::Path) + count (db::Shape::PathRef); + } + + size_t path_array () const + { + return count (db::Shape::PathPtrArray); + } + + size_t text_total () const + { + return count (db::Shape::Text) + count (db::Shape::TextRef) + count (db::Shape::TextPtrArrayMember); + } + + size_t text_single () const + { + return count (db::Shape::Text) + count (db::Shape::TextRef); + } + + size_t text_array () const + { + return count (db::Shape::TextPtrArray); + } + + size_t edge_total () const + { + return count (db::Shape::Edge); + } + + size_t user_total () const + { + return count (db::Shape::UserObject); + } + +private: + std::map m_count; +}; + +// ------------------------------------------------------------ + +class StatisticsSource : public lay::BrowserSource { public: @@ -396,6 +520,9 @@ private: std::string StatisticsSource::get (const std::string &url) { + static QString s_per_layer_stat_path_ld = QString::fromUtf8 ("per-layer-stat-ld"); + static QString s_per_layer_stat_path_name = QString::fromUtf8 ("per-layer-stat-name"); + QUrl qurl (tl::to_qstring (url)); QFileInfo fi (qurl.path ()); @@ -406,6 +533,124 @@ StatisticsSource::get (const std::string &url) std::string r = tp.get ().constData (); return r; + } else if (fi.baseName () == s_per_layer_stat_path_ld || fi.baseName () == s_per_layer_stat_path_name) { + + // This is the default top level page + // TODO: handle other input as well + + const db::Layout &layout = m_h->layout (); + + std::ostringstream os; + os.imbue (std::locale ("C")); + + std::vector layers; + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + layers.push_back (i); + } + } + + if (fi.baseName () == s_per_layer_stat_path_ld) { + std::sort (layers.begin (), layers.end (), CompareLDName (layout)); + } else { + std::sort (layers.begin (), layers.end (), CompareNameLD (layout)); + } + + os << "" << std::endl + << "" << std::endl + << "

" << tl::to_string (QObject::tr ("Detailed Layer Statistics for '")) << m_h->name () << "'

" << std::endl + + << "

" << std::endl + << "" << std::endl + + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + << "" << std::endl + ; + + db::CellCounter cc (&layout); + + tl::RelativeProgress progress (tl::to_string (QObject::tr ("Collecting statistics")), layers.size () * layout.cells (), 100000); + for (std::vector ::const_iterator l = layers.begin (); l != layers.end (); ++l) { + + ShapeStatistics st_hier; + ShapeStatistics st_flat; + + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + + ShapeStatistics st; + st.compute (layout.cell (*c).shapes (*l)); + + st_hier += st; + st *= cc.weight (*c); + st_flat += st; + + ++progress; + + } + + os << "" << std::endl + << "" << std::endl + // Boxes (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Polygons (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Paths (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Texts (total, single, array) + << "" << std::endl + << "" << std::endl + << "" << std::endl + // Edges (total) + << "" << std::endl + // User objects (total) + << "" << std::endl + // ... + << "" << std::endl + << "" << std::endl + ; + + } + + os << "
" << tl::to_string (QObject::tr ("Layer")) << "" << tl::to_string (QObject::tr ("Boxes")) << "" << tl::to_string (QObject::tr ("Polygons")) << "" << tl::to_string (QObject::tr ("Paths")) << "" << tl::to_string (QObject::tr ("Texts")) << "" << tl::to_string (QObject::tr ("Edges")) << "" << tl::to_string (QObject::tr ("User objects")) << "
" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(single)")) << "" << tl::to_string (QObject::tr ("(arrays)")) << "" << tl::to_string (QObject::tr ("(total)")) << "" << tl::to_string (QObject::tr ("(total)")) << "
" << layout.get_properties (*l).to_string () << "" << st_hier.box_total () << "

" << st_flat.box_total () << "
" << st_hier.box_single () << "

" << st_flat.box_single () << "
" << st_hier.box_array () << "

" << st_flat.box_array () << "
" << st_hier.polygon_total () << "

" << st_flat.polygon_total () << "
" << st_hier.polygon_single () << "

" << st_flat.polygon_single () << "
" << st_hier.polygon_array () << "

" << st_flat.polygon_array () << "
" << st_hier.path_total () << "

" << st_flat.path_total () << "
" << st_hier.path_single () << "

" << st_flat.path_single () << "
" << st_hier.path_array () << "

" << st_flat.path_array () << "
" << st_hier.text_total () << "

" << st_flat.text_total () << "
" << st_hier.text_single () << "

" << st_flat.text_single () << "
" << st_hier.text_array () << "

" << st_flat.text_array () << "
" << st_hier.edge_total () << "

" << st_flat.edge_total () << "
" << st_hier.user_total () << "

" << st_flat.user_total () << "
" << tl::to_string (QObject::tr ("(hier)")) << "

" << tl::to_string (QObject::tr ("(flat)")) << "
" << std::endl + << "

" << std::endl + << tl::to_string (QObject::tr ("

Note

" + "

" + "\"(hier)\" is the object count where each cell counts once. " + "\"(flat)\" is the \"as if flat\" count where the cells count as many times as they are seen from the top cells." + "

" + "

" + "\"(total)\" is the effective number of shapes. \"(single)\" are the single shapes. " + "\"(arrays)\" is the number of shape arrays where each array counts as one, but contributes many individual shapes to \"(total)\"." + "

" + )) + << "" << std::endl + << ""; + + return os.str (); + } else { // This is the default top level page @@ -428,6 +673,7 @@ StatisticsSource::get (const std::string &url) os << "" << std::endl << "" << std::endl << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << m_h->name () << "'

" << std::endl + << "

" << std::endl << "" << std::endl << "" << "" @@ -454,49 +700,61 @@ StatisticsSource::get (const std::string &url) os << "" << std::endl; } os << "
" << tl::to_string (QObject::tr ("Path")) << ": " << m_h->filename () << "
" << layout.cell_name (*tc) << "
" << std::endl; + os << "

" << std::endl; std::vector layers_with_oasis_names; std::vector layers_sorted_by_ld; - layers_sorted_by_ld.reserve (layout.layers ()); + layers_sorted_by_ld.reserve (num_layers); for (unsigned int i = 0; i < layout.layers (); ++i) { - layers_sorted_by_ld.push_back (i); - const db::LayerProperties &lp = layout.get_properties (i); - if (! lp.name.empty ()) { - layers_with_oasis_names.push_back (i); + if (layout.is_valid_layer (i)) { + layers_sorted_by_ld.push_back (i); + const db::LayerProperties &lp = layout.get_properties (i); + if (! lp.name.empty ()) { + layers_with_oasis_names.push_back (i); + } } } std::sort (layers_sorted_by_ld.begin (), layers_sorted_by_ld.end (), CompareLDName (layout)); std::sort (layers_with_oasis_names.begin (), layers_with_oasis_names.end (), CompareNameLD (layout)); - os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl - << "" << std::endl - << ""; - if (! layers_with_oasis_names.empty ()) { - os << ""; - } - os << "" << std::endl; + if (! layers_sorted_by_ld.empty ()) { - for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) { - if (layout.is_valid_layer (*i)) { - const db::LayerProperties &lp = layout.get_properties (*i); - os << "" - << ""; - if (! layers_with_oasis_names.empty ()) { - os << ""; - } - os << "" << std::endl; - } - } - - os << "
" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  " << tl::to_string (QObject::tr ("OASIS layer name")) << "
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << lp.name << "
" << std::endl; - - if (! layers_with_oasis_names.empty ()) { - - os << "

" << tl::to_string (QObject::tr ("Layers (sorted by OASIS layer names)")) << "

" << std::endl + os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl + << "

Detailed layer statistics

" << std::endl + << "

" << std::endl << "" << std::endl - << "" << std::endl; + << ""; + if (! layers_with_oasis_names.empty ()) { + os << ""; + } + os << "" << std::endl; + + for (std::vector ::const_iterator i = layers_sorted_by_ld.begin (); i != layers_sorted_by_ld.end (); ++i) { + if (layout.is_valid_layer (*i)) { + const db::LayerProperties &lp = layout.get_properties (*i); + os << "" + << ""; + if (! layers_with_oasis_names.empty ()) { + os << ""; + } + os << "" << std::endl; + } + } + + os << "
" << tl::to_string (QObject::tr ("OASIS layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  " << tl::to_string (QObject::tr ("Layer name")) << "
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << lp.name << "
" << std::endl; + os << "

" << std::endl; + + } + + if (! layers_with_oasis_names.empty ()) { + + os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "

" << std::endl + << "

Detailed layer statistics

" << std::endl + << "

" << std::endl + << "" << std::endl + << "" << std::endl; for (std::vector ::const_iterator i = layers_with_oasis_names.begin (); i != layers_with_oasis_names.end (); ++i) { if (layout.is_valid_layer (*i)) { @@ -511,6 +769,7 @@ StatisticsSource::get (const std::string &url) } os << "
" << tl::to_string (QObject::tr ("Layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << std::endl; + os << "

" << std::endl; }