klayout/src/plugins/tools/bool/lay_plugin/layBooleanOperationsPlugin.cc

507 lines
17 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2022 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 "layBooleanOperationsDialogs.h"
#include "layPlugin.h"
#include "layTipDialog.h"
#include "layLayoutViewBase.h"
#include "dbShapeProcessor.h"
#include <QApplication>
namespace lay
{
class BooleanOperationsPlugin
: public lay::Plugin
{
public:
BooleanOperationsPlugin (Plugin *parent, lay::LayoutViewBase *view)
: lay::Plugin (parent), mp_view (view)
{
m_boolean_cva = -1;
m_boolean_cvb = -1;
m_boolean_cvr = -1;
m_boolean_layera = -1;
m_boolean_layerb = -1;
m_boolean_layerr = -1;
m_boolean_hier_mode = 0;
m_boolean_mode = 0;
m_boolean_mincoh = true;
m_boolean_minwc = 0;
m_boolean_sizex = m_boolean_sizey = 0.0;
m_boolean_size_mode = 2;
}
~BooleanOperationsPlugin ()
{
// ...
}
void menu_activated (const std::string &symbol)
{
if (symbol == "lay::boolean") {
boolean ();
} else if (symbol == "lay::merge") {
merge ();
} else if (symbol == "lay::size") {
size ();
}
}
void boolean ()
{
struct { int *cv; int *layer; } specs [] = {
{ &m_boolean_cva, &m_boolean_layera },
{ &m_boolean_cvb, &m_boolean_layerb },
{ &m_boolean_cvr, &m_boolean_layerr }
};
for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) {
int &cv = *(specs[i].cv);
int &layer = *(specs[i].layer);
if (cv >= int (mp_view->cellviews ())) {
cv = -1;
}
int index = mp_view->active_cellview_index ();
if (cv < 0) {
cv = index;
}
if (cv < 0 || ! mp_view->cellview (cv)->layout ().is_valid_layer ((unsigned int) layer)) {
layer = -1;
}
}
lay::BooleanOptionsDialog dialog (mp_view->widget ());
if (dialog.exec_dialog (mp_view, m_boolean_cva, m_boolean_layera, m_boolean_cvb, m_boolean_layerb, m_boolean_cvr, m_boolean_layerr, m_boolean_mode, m_boolean_hier_mode, m_boolean_mincoh)) {
mp_view->cancel ();
bool supports_undo = true;
if (mp_view->manager () && mp_view->manager ()->is_enabled ()) {
lay::TipDialog td (QApplication::activeWindow (),
tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")),
"boolean-undo-buffering",
lay::TipDialog::yesnocancel_buttons);
lay::TipDialog::button_type button = lay::TipDialog::null_button;
td.exec_dialog (button);
if (button == lay::TipDialog::cancel_button) {
return;
}
supports_undo = (button == lay::TipDialog::yes_button);
} else {
supports_undo = false;
}
if (mp_view->manager ()) {
if (! supports_undo) {
mp_view->manager ()->clear ();
} else {
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Boolean operation")));
}
}
try {
db::BooleanOp::BoolOp op_mode;
switch (m_boolean_mode) {
default:
op_mode = db::BooleanOp::Or;
break;
case 1:
op_mode = db::BooleanOp::And;
break;
case 2:
op_mode = db::BooleanOp::ANotB;
break;
case 3:
op_mode = db::BooleanOp::BNotA;
break;
case 4:
op_mode = db::BooleanOp::Xor;
break;
}
if (m_boolean_hier_mode == 0) {
// flat mode
db::ShapeProcessor p (true);
p.boolean (mp_view->cellview (m_boolean_cva)->layout (), *mp_view->cellview (m_boolean_cva).cell (), m_boolean_layera,
mp_view->cellview (m_boolean_cvb)->layout (), *mp_view->cellview (m_boolean_cvb).cell (), m_boolean_layerb,
mp_view->cellview (m_boolean_cvr).cell ()->shapes (m_boolean_layerr), op_mode, true, true, m_boolean_mincoh);
// clear the result layer for all called cells in flat mode
std::set<db::cell_index_type> called_cells;
mp_view->cellview (m_boolean_cvr).cell ()->collect_called_cells (called_cells);
for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
mp_view->cellview (m_boolean_cvr)->layout ().cell (*c).shapes (m_boolean_layerr).clear ();
}
} else if (m_boolean_hier_mode == 1) {
// top cell only mode
db::ShapeProcessor p (true);
p.boolean (mp_view->cellview (m_boolean_cva)->layout (), *mp_view->cellview (m_boolean_cva).cell (), m_boolean_layera,
mp_view->cellview (m_boolean_cvb)->layout (), *mp_view->cellview (m_boolean_cvb).cell (), m_boolean_layerb,
mp_view->cellview (m_boolean_cvr).cell ()->shapes (m_boolean_layerr), op_mode, false, true, m_boolean_mincoh);
} else if (m_boolean_hier_mode == 2) {
// subcells cell by cell
std::set<db::cell_index_type> called_cells;
mp_view->cellview (m_boolean_cva).cell ()->collect_called_cells (called_cells);
called_cells.insert (mp_view->cellview (m_boolean_cva).cell_index ());
db::ShapeProcessor p (true);
db::Layout &layout = mp_view->cellview (m_boolean_cva)->layout ();
for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
db::Cell &cell = layout.cell (*c);
p.boolean (layout, cell, m_boolean_layera,
layout, cell, m_boolean_layerb,
cell.shapes (m_boolean_layerr), op_mode, false, true, m_boolean_mincoh);
}
}
if (supports_undo && mp_view->manager ()) {
mp_view->manager ()->commit ();
}
} catch (...) {
if (supports_undo && mp_view->manager ()) {
mp_view->manager ()->commit ();
}
throw;
}
}
}
void merge ()
{
struct { int *cv; int *layer; } specs [] = {
{ &m_boolean_cva, &m_boolean_layera },
{ &m_boolean_cvr, &m_boolean_layerr }
};
for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) {
int &cv = *(specs[i].cv);
int &layer = *(specs[i].layer);
if (cv >= int (mp_view->cellviews ())) {
cv = -1;
}
int index = mp_view->active_cellview_index ();
if (cv < 0) {
cv = index;
}
if (cv < 0 || ! mp_view->cellview (cv)->layout ().is_valid_layer ((unsigned int) layer)) {
layer = -1;
}
}
lay::MergeOptionsDialog dialog (mp_view->widget ());
if (dialog.exec_dialog (mp_view, m_boolean_cva, m_boolean_layera, m_boolean_cvr, m_boolean_layerr, m_boolean_minwc, m_boolean_hier_mode, m_boolean_mincoh)) {
mp_view->cancel ();
bool supports_undo = true;
if (mp_view->manager () && mp_view->manager ()->is_enabled ()) {
lay::TipDialog td (QApplication::activeWindow (),
tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")),
"merge-undo-buffering",
lay::TipDialog::yesnocancel_buttons);
lay::TipDialog::button_type button = lay::TipDialog::null_button;
td.exec_dialog (button);
if (button == lay::TipDialog::cancel_button) {
return;
}
supports_undo = (button == lay::TipDialog::yes_button);
} else {
supports_undo = false;
}
if (mp_view->manager ()) {
if (! supports_undo) {
mp_view->manager ()->clear ();
} else {
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Merge operation")));
}
}
try {
if (m_boolean_hier_mode == 0) {
// flat mode
db::ShapeProcessor p (true);
p.merge (mp_view->cellview (m_boolean_cva)->layout (), *mp_view->cellview (m_boolean_cva).cell (), m_boolean_layera,
mp_view->cellview (m_boolean_cvr).cell ()->shapes (m_boolean_layerr), true, m_boolean_minwc, true, m_boolean_mincoh);
// clear the result layer for all called cells in flat mode
std::set<db::cell_index_type> called_cells;
mp_view->cellview (m_boolean_cvr).cell ()->collect_called_cells (called_cells);
for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
mp_view->cellview (m_boolean_cvr)->layout ().cell (*c).shapes (m_boolean_layerr).clear ();
}
} else if (m_boolean_hier_mode == 1) {
// top cell only mode
db::ShapeProcessor p (true);
p.merge (mp_view->cellview (m_boolean_cva)->layout (), *mp_view->cellview (m_boolean_cva).cell (), m_boolean_layera,
mp_view->cellview (m_boolean_cvr).cell ()->shapes (m_boolean_layerr), false, m_boolean_minwc, true, m_boolean_mincoh);
} else if (m_boolean_hier_mode == 2) {
// subcells cell by cell
std::set<db::cell_index_type> called_cells;
mp_view->cellview (m_boolean_cva).cell ()->collect_called_cells (called_cells);
called_cells.insert (mp_view->cellview (m_boolean_cva).cell_index ());
db::ShapeProcessor p (true);
db::Layout &layout = mp_view->cellview (m_boolean_cva)->layout ();
for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
db::Cell &cell = layout.cell (*c);
p.merge (layout, cell, m_boolean_layera,
cell.shapes (m_boolean_layerr), false, m_boolean_minwc, true, m_boolean_mincoh);
}
}
if (supports_undo && mp_view->manager ()) {
mp_view->manager ()->commit ();
}
} catch (...) {
if (supports_undo && mp_view->manager ()) {
mp_view->manager ()->commit ();
}
throw;
}
}
}
void size ()
{
struct { int *cv; int *layer; } specs [] = {
{ &m_boolean_cva, &m_boolean_layera },
{ &m_boolean_cvr, &m_boolean_layerr }
};
for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) {
int &cv = *(specs[i].cv);
int &layer = *(specs[i].layer);
if (cv >= int (mp_view->cellviews ())) {
cv = -1;
}
int index = mp_view->active_cellview_index ();
if (cv < 0) {
cv = index;
}
if (cv < 0 || ! mp_view->cellview (cv)->layout ().is_valid_layer ((unsigned int) layer)) {
layer = -1;
}
}
lay::SizingOptionsDialog dialog (mp_view->widget ());
if (dialog.exec_dialog (mp_view, m_boolean_cva, m_boolean_layera, m_boolean_cvr, m_boolean_layerr, m_boolean_sizex, m_boolean_sizey, m_boolean_size_mode, m_boolean_hier_mode, m_boolean_mincoh)) {
mp_view->cancel ();
bool supports_undo = true;
if (mp_view->manager () && mp_view->manager ()->is_enabled ()) {
lay::TipDialog td (QApplication::activeWindow (),
tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")),
"size-undo-buffering",
lay::TipDialog::yesnocancel_buttons);
lay::TipDialog::button_type button = lay::TipDialog::null_button;
td.exec_dialog (button);
if (button == lay::TipDialog::cancel_button) {
return;
}
supports_undo = (button == lay::TipDialog::yes_button);
} else {
supports_undo = false;
}
db::Coord dx_int, dy_int;
dx_int = db::coord_traits<db::Coord>::rounded (m_boolean_sizex / mp_view->cellview (m_boolean_cva)->layout ().dbu ());
dy_int = db::coord_traits<db::Coord>::rounded (m_boolean_sizey / mp_view->cellview (m_boolean_cva)->layout ().dbu ());
if (mp_view->manager ()) {
if (! supports_undo) {
mp_view->manager ()->clear ();
} else {
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Sizing operation")));
}
}
try {
if (m_boolean_hier_mode == 0) {
// flat mode
db::ShapeProcessor p (true);
p.size (mp_view->cellview (m_boolean_cva)->layout (), *mp_view->cellview (m_boolean_cva).cell (), m_boolean_layera,
mp_view->cellview (m_boolean_cvr).cell ()->shapes (m_boolean_layerr), dx_int, dy_int, m_boolean_size_mode, true, true, m_boolean_mincoh);
// clear the result layer for all called cells in flat mode
std::set<db::cell_index_type> called_cells;
mp_view->cellview (m_boolean_cvr).cell ()->collect_called_cells (called_cells);
for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
mp_view->cellview (m_boolean_cvr)->layout ().cell (*c).shapes (m_boolean_layerr).clear ();
}
} else if (m_boolean_hier_mode == 1) {
// top cell only mode
db::ShapeProcessor p (true);
p.size (mp_view->cellview (m_boolean_cva)->layout (), *mp_view->cellview (m_boolean_cva).cell (), m_boolean_layera,
mp_view->cellview (m_boolean_cvr).cell ()->shapes (m_boolean_layerr), dx_int, dy_int, m_boolean_size_mode, false, true, m_boolean_mincoh);
} else if (m_boolean_hier_mode == 2) {
// subcells cell by cell
std::set<db::cell_index_type> called_cells;
mp_view->cellview (m_boolean_cva).cell ()->collect_called_cells (called_cells);
called_cells.insert (mp_view->cellview (m_boolean_cva).cell_index ());
db::ShapeProcessor p (true);
db::Layout &layout = mp_view->cellview (m_boolean_cva)->layout ();
for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
db::Cell &cell = layout.cell (*c);
p.size (layout, cell, m_boolean_layera,
cell.shapes (m_boolean_layerr), dx_int, dy_int, m_boolean_size_mode, false, true, m_boolean_mincoh);
}
}
if (supports_undo && mp_view->manager ()) {
mp_view->manager ()->commit ();
}
} catch (...) {
if (supports_undo && mp_view->manager ()) {
mp_view->manager ()->commit ();
}
throw;
}
}
}
private:
lay::LayoutViewBase *mp_view;
int m_boolean_cva, m_boolean_cvb, m_boolean_cvr;
int m_boolean_layera, m_boolean_layerb, m_boolean_layerr;
int m_boolean_hier_mode, m_boolean_mode;
bool m_boolean_mincoh;
unsigned int m_boolean_minwc;
double m_boolean_sizex, m_boolean_sizey;
unsigned int m_boolean_size_mode;
};
class BooleanOperationsPluginDeclaration
: public lay::PluginDeclaration
{
public:
BooleanOperationsPluginDeclaration ()
{
// .. nothing yet ..
}
virtual void get_options (std::vector < std::pair<std::string, std::string> > & /*options*/) const
{
// .. nothing yet ..
}
virtual lay::ConfigPage *config_page (QWidget * /*parent*/, std::string & /*title*/) const
{
// .. nothing yet ..
return 0;
}
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
{
lay::PluginDeclaration::get_menu_entries (menu_entries);
menu_entries.push_back (lay::separator ("ops_group", "edit_menu.layer_menu.end"));
menu_entries.push_back (lay::menu_item ("lay::boolean", "boolean:edit:edit_mode", "edit_menu.layer_menu.end", tl::to_string (QObject::tr ("Boolean Operations"))));
menu_entries.push_back (lay::menu_item ("lay::merge", "merge:edit:edit_mode", "edit_menu.layer_menu.end", tl::to_string (QObject::tr ("Merge"))));
menu_entries.push_back (lay::menu_item ("lay::size", "size:edit:edit_mode", "edit_menu.layer_menu.end", tl::to_string (QObject::tr ("Size"))));
}
virtual bool configure (const std::string & /*name*/, const std::string & /*value*/)
{
return false;
}
virtual void config_finalize ()
{
// .. nothing yet ..
}
lay::Plugin *create_plugin (db::Manager *, lay::Dispatcher *root, lay::LayoutViewBase *view) const
{
return new BooleanOperationsPlugin (root, view);
}
};
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new lay::BooleanOperationsPluginDeclaration (), 3010, "lay::BooleanOperationsPlugin");
}