diff --git a/src/edt/edt/AreaAndPerimeterDialog.ui b/src/edt/edt/AreaAndPerimeterDialog.ui new file mode 100644 index 000000000..9fd1a263a --- /dev/null +++ b/src/edt/edt/AreaAndPerimeterDialog.ui @@ -0,0 +1,130 @@ + + + AreaAndPerimeterDialog + + + + 0 + 0 + 367 + 205 + + + + Area And Perimeter + + + + + + Qt::Vertical + + + + 10 + 11 + + + + + + + + true + + + + + + + Area + + + + + + + µm + + + + + + + µm² + + + + + + + Perimeter + + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + Note: area and perimeter are computed in "merged mode". This means, overlapping shapes are counted once for area calculation. +The perimeter calculation only takes true outside edges into account. Internal edges are ignored. + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + + buttonBox + accepted() + AreaAndPerimeterDialog + accept() + + + 303 + 185 + + + 311 + 201 + + + + + diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index 24075ca3b..7680a9946 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -27,7 +27,8 @@ DEFINES += MAKE_EDT_LIBRARY RoundCornerOptionsDialog.ui \ TextPropertiesPage.ui \ DistributeOptionsDialog.ui \ - EditorOptionsInstPCellParam.ui + EditorOptionsInstPCellParam.ui \ + AreaAndPerimeterDialog.ui } diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 1486b2abf..498eb8e47 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -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 diff --git a/src/edt/edt/edtDialogs.h b/src/edt/edt/edtDialogs.h index 5864a8d54..a108585b5 100644 --- a/src/edt/edt/edtDialogs.h +++ b/src/edt/edt/edtDialogs.h @@ -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 diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 0f97e0166..c7b51e1b9 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -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_services = view ()->get_plugins (); + + db::Region region; + + // get (common) cellview index of the primary selection + for (std::vector::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 &poly, double &rinner, double &router, unsigned int &n) { std::vector new_pts; @@ -1356,7 +1430,7 @@ static bool extract_rad (std::vector &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 &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*/); diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index e6b26f98b..cc9fc2426 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -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 (); diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 8df9edfa7..f4aed23d9 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -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}")))); }