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
+ << "| " << tl::to_string (QObject::tr ("Layer")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Boxes")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Polygons")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Paths")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Texts")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("Edges")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("User objects")) << " | " << std::endl
+ << "
" << std::endl
+
+ << "" << std::endl
+ << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << tl::to_string (QObject::tr ("(single)")) << " | " << tl::to_string (QObject::tr ("(arrays)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << std::endl
+ << "" << tl::to_string (QObject::tr ("(total)")) << " | " << 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
+ << "| " << layout.get_properties (*l).to_string () << " | " << std::endl
+ // Boxes (total, single, array)
+ << "" << st_hier.box_total () << " " << st_flat.box_total () << " | " << std::endl
+ << "" << st_hier.box_single () << " " << st_flat.box_single () << " | " << std::endl
+ << "" << st_hier.box_array () << " " << st_flat.box_array () << " | " << std::endl
+ // Polygons (total, single, array)
+ << "" << st_hier.polygon_total () << " " << st_flat.polygon_total () << " | " << std::endl
+ << "" << st_hier.polygon_single () << " " << st_flat.polygon_single () << " | " << std::endl
+ << "" << st_hier.polygon_array () << " " << st_flat.polygon_array () << " | " << std::endl
+ // Paths (total, single, array)
+ << "" << st_hier.path_total () << " " << st_flat.path_total () << " | " << std::endl
+ << "" << st_hier.path_single () << " " << st_flat.path_single () << " | " << std::endl
+ << "" << st_hier.path_array () << " " << st_flat.path_array () << " | " << std::endl
+ // Texts (total, single, array)
+ << "" << st_hier.text_total () << " " << st_flat.text_total () << " | " << std::endl
+ << "" << st_hier.text_single () << " " << st_flat.text_single () << " | " << std::endl
+ << "" << st_hier.text_array () << " " << st_flat.text_array () << " | " << std::endl
+ // Edges (total)
+ << "" << st_hier.edge_total () << " " << st_flat.edge_total () << " | " << std::endl
+ // User objects (total)
+ << "" << st_hier.user_total () << " " << st_flat.user_total () << " | " << std::endl
+ // ...
+ << "" << tl::to_string (QObject::tr ("(hier)")) << " " << tl::to_string (QObject::tr ("(flat)")) << " | " << std::endl
+ << "
" << std::endl
+ ;
+
+ }
+
+ os << "
" << 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
<< ""
<< "| " << tl::to_string (QObject::tr ("Path")) << ": | " << m_h->filename () << " | "
@@ -454,49 +700,61 @@ StatisticsSource::get (const std::string &url)
os << "
| " << layout.cell_name (*tc) << " |
" << std::endl;
}
os << "
" << 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
- << "| " << tl::to_string (QObject::tr ("Layer/Datatype")) << " | ";
- if (! layers_with_oasis_names.empty ()) {
- os << "" << tl::to_string (QObject::tr ("OASIS layer name")) << " | ";
- }
- 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 << ""
- << "| " << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << " | ";
- if (! layers_with_oasis_names.empty ()) {
- os << "" << lp.name << " | ";
- }
- os << "
" << std::endl;
- }
- }
-
- os << "
" << 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
- << "| " << tl::to_string (QObject::tr ("OASIS layer name")) << " | " << tl::to_string (QObject::tr ("Layer/Datatype")) << " |
" << std::endl;
+ << "| " << tl::to_string (QObject::tr ("Layer/Datatype")) << " | ";
+ if (! layers_with_oasis_names.empty ()) {
+ os << "" << tl::to_string (QObject::tr ("Layer name")) << " | ";
+ }
+ 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 << ""
+ << "| " << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << " | ";
+ if (! layers_with_oasis_names.empty ()) {
+ os << "" << lp.name << " | ";
+ }
+ os << "
" << std::endl;
+ }
+ }
+
+ os << "
" << 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
+ << "| " << tl::to_string (QObject::tr ("Layer name")) << " | " << tl::to_string (QObject::tr ("Layer/Datatype")) << " |
" << 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 << "
" << std::endl;
+ os << "" << std::endl;
}