Added feature for computing area and perimeter from selection

This commit is contained in:
Matthias Koefferlein 2022-11-01 13:46:48 +01:00
parent 2dc5c98416
commit 54833db00b
7 changed files with 258 additions and 3 deletions

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AreaAndPerimeterDialog</class>
<widget class="QDialog" name="AreaAndPerimeterDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>367</width>
<height>205</height>
</rect>
</property>
<property name="windowTitle">
<string>Area And Perimeter</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0" colspan="3">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>11</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="perimeter_le">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Area</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>µm²</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Perimeter</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="area_le">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Note: area and perimeter are computed in &quot;merged mode&quot;. This means, overlapping shapes are counted once for area calculation.
The perimeter calculation only takes true outside edges into account. Internal edges are ignored.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AreaAndPerimeterDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>303</x>
<y>185</y>
</hint>
<hint type="destinationlabel">
<x>311</x>
<y>201</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -27,7 +27,8 @@ DEFINES += MAKE_EDT_LIBRARY
RoundCornerOptionsDialog.ui \
TextPropertiesPage.ui \
DistributeOptionsDialog.ui \
EditorOptionsInstPCellParam.ui
EditorOptionsInstPCellParam.ui \
AreaAndPerimeterDialog.ui
}

View File

@ -683,6 +683,29 @@ BEGIN_PROTECTED;
END_PROTECTED;
}
// --------------------------------------------------------------------------------
// AreaAndPerimeterDialog implementation
AreaAndPerimeterDialog::AreaAndPerimeterDialog (QWidget *parent)
: QDialog (parent)
{
setupUi (this);
}
AreaAndPerimeterDialog::~AreaAndPerimeterDialog ()
{
// .. nothing yet ..
}
bool
AreaAndPerimeterDialog::exec_dialog (double area, double perimeter)
{
area_le->setText (tl::to_qstring (tl::sprintf ("%.12g", area)));
perimeter_le->setText (tl::to_qstring (tl::sprintf ("%.12g", perimeter)));
return exec () != 0;
}
}
#endif

View File

@ -43,6 +43,7 @@
#include "ui_MakeCellOptionsDialog.h"
#include "ui_MakeArrayOptionsDialog.h"
#include "ui_RoundCornerOptionsDialog.h"
#include "ui_AreaAndPerimeterDialog.h"
namespace lay
{
@ -205,6 +206,22 @@ private:
bool m_has_extracted;
};
/**
* @brief Result dialog for "area and perimeter"
*/
class AreaAndPerimeterDialog
: public QDialog,
private Ui::AreaAndPerimeterDialog
{
Q_OBJECT
public:
AreaAndPerimeterDialog (QWidget *parent);
~AreaAndPerimeterDialog ();
bool exec_dialog (double area, double perimeter);
};
} // namespace edt
#endif

View File

@ -25,6 +25,7 @@
#include "dbPolygonTools.h"
#include "dbLibrary.h"
#include "dbLibraryManager.h"
#include "dbRegion.h"
#include "tlExceptions.h"
#include "layLayoutView.h"
#include "laySelector.h"
@ -82,6 +83,7 @@ MainService::MainService (db::Manager *manager, lay::LayoutViewBase *view, lay::
{
#if defined(HAVE_QT)
mp_round_corners_dialog = 0;
mp_area_and_perimeter_dialog = 0;
mp_align_options_dialog = 0;
mp_distribute_options_dialog = 0;
mp_flatten_inst_options_dialog = 0;
@ -106,6 +108,15 @@ MainService::round_corners_dialog ()
return mp_round_corners_dialog;
}
edt::AreaAndPerimeterDialog *
MainService::area_and_perimeter_dialog ()
{
if (! mp_area_and_perimeter_dialog) {
mp_area_and_perimeter_dialog = new edt::AreaAndPerimeterDialog (lay::widget_from_view (view ()));
}
return mp_area_and_perimeter_dialog;
}
edt::AlignOptionsDialog *
MainService::align_options_dialog ()
{
@ -168,6 +179,8 @@ MainService::menu_activated (const std::string &symbol)
cm_tap ();
} else if (symbol == "edt::sel_round_corners") {
cm_round_corners ();
} else if (symbol == "edt::sel_area_perimeter") {
cm_area_perimeter ();
} else if (symbol == "edt::sel_convert_to_pcell") {
cm_convert_to_pcell ();
} else if (symbol == "edt::sel_convert_to_cell") {
@ -1346,6 +1359,67 @@ MainService::cm_convert_to_pcell ()
}
}
void MainService::cm_area_perimeter ()
{
#if ! defined(HAVE_QT)
tl_assert (false); // see TODO
#endif
double dbu = 0.0;
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
db::Region region;
// get (common) cellview index of the primary selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) {
if (s->is_cell_inst ()) {
continue;
}
db::Polygon poly;
if (!s->shape ().polygon (poly)) {
continue;
}
double shape_dbu = view ()->cellview (s->cv_index ())->layout ().dbu ();
if (dbu == 0.0) {
// first CV is used for reference DBU
dbu = shape_dbu;
}
if (fabs (shape_dbu - dbu) < db::epsilon) {
region.insert (s->trans () * poly);
} else {
region.insert ((db::ICplxTrans (shape_dbu / dbu) * s->trans ()) * poly);
}
}
}
#if defined(HAVE_QT)
if (region.count () > 100000) {
if (QMessageBox::warning (lay::widget_from_view (view ()), tr ("Warning: Big Selection"),
tr ("The selection contains many shapes. Area and perimeter computation may take a long time.\nContinue anyway?"),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
return;
}
}
#endif
double area = region.area () * dbu * dbu;
double perimeter = region.perimeter () * dbu;
#if defined(HAVE_QT)
area_and_perimeter_dialog ()->exec_dialog (area, perimeter);
#endif
}
static bool extract_rad (std::vector <db::Polygon> &poly, double &rinner, double &router, unsigned int &n)
{
std::vector <db::Point> new_pts;
@ -1356,7 +1430,7 @@ static bool extract_rad (std::vector <db::Polygon> &poly, double &rinner, double
db::Polygon new_poly;
new_pts.clear ();
if (! extract_rad_from_contour (p->begin_hull (), p->end_hull (), rinner, router, n, &new_pts) &&
if (! extract_rad_from_contour (p->begin_hull (), p->end_hull (), rinner, router, n, &new_pts) &&
! extract_rad_from_contour (p->begin_hull (), p->end_hull (), rinner, router, n, &new_pts, true)) {
// ultimate fallback: assign original contour
new_poly.assign_hull (p->begin_hull (), p->end_hull (), false /*don't compress*/);
@ -1368,7 +1442,7 @@ static bool extract_rad (std::vector <db::Polygon> &poly, double &rinner, double
for (unsigned int h = 0; h < p->holes (); ++h) {
new_pts.clear ();
if (! extract_rad_from_contour (p->begin_hole (h), p->end_hole (h), rinner, router, n, &new_pts) &&
if (! extract_rad_from_contour (p->begin_hole (h), p->end_hole (h), rinner, router, n, &new_pts) &&
! extract_rad_from_contour (p->begin_hole (h), p->end_hole (h), rinner, router, n, &new_pts, true)) {
// ultimate fallback: assign original contour
new_poly.insert_hole (p->begin_hole (h), p->end_hole (h), false /*don't compress*/);

View File

@ -48,6 +48,7 @@ class Service;
class EditorOptionsPages;
class EditorOptionsPage;
class RoundCornerOptionsDialog;
class AreaAndPerimeterDialog;
class MakeCellOptionsDialog;
class MakeArrayOptionsDialog;
class AlignOptionsDialog;
@ -104,6 +105,11 @@ public:
*/
void cm_round_corners ();
/**
* @brief Show area and perimeter of selection
*/
void cm_area_perimeter ();
/**
* @brief Convert selection to PCell
*/
@ -223,6 +229,7 @@ private:
bool m_undo_before_apply;
#if defined(HAVE_QT)
edt::RoundCornerOptionsDialog *mp_round_corners_dialog;
edt::AreaAndPerimeterDialog *mp_area_and_perimeter_dialog;
edt::AlignOptionsDialog *mp_align_options_dialog;
edt::DistributeOptionsDialog *mp_distribute_options_dialog;
lay::FlattenInstOptionsDialog *mp_flatten_inst_options_dialog;
@ -234,6 +241,7 @@ private:
void check_no_guiding_shapes ();
#if defined(HAVE_QT)
edt::RoundCornerOptionsDialog *round_corners_dialog ();
edt::AreaAndPerimeterDialog *area_and_perimeter_dialog ();
edt::AlignOptionsDialog *align_options_dialog ();
edt::DistributeOptionsDialog *distribute_options_dialog ();
lay::FlattenInstOptionsDialog *flatten_inst_options_dialog ();

View File

@ -342,6 +342,8 @@ public:
menu_entries.push_back (lay::menu_item ("edt::sel_make_cell_variants", "make_cell_variants:edit_mode", "edit_menu.selection_menu.end", tl::to_string (tr ("Make Cell Variants"))));
menu_entries.push_back (lay::menu_item ("edt::sel_convert_to_pcell", "convert_to_pcell:edit_mode", "edit_menu.selection_menu.end", tl::to_string (tr ("Convert To PCell"))));
menu_entries.push_back (lay::menu_item ("edt::sel_convert_to_cell", "convert_to_cell:edit_mode", "edit_menu.selection_menu.end", tl::to_string (tr ("Convert To Static Cell"))));
menu_entries.push_back (lay::separator ("hier_group:edit_info", "edit_menu.selection_menu.end"));
menu_entries.push_back (lay::menu_item ("edt::sel_area_perimeter", "area_perimeter", "edit_menu.selection_menu.end", tl::to_string (tr ("Area and Perimeter"))));
menu_entries.push_back (lay::menu_item ("edt::combine_mode", "combine_mode:edit_mode", "@toolbar.end_modes", tl::to_string (tr ("Combine{Select background combination mode}"))));
}