From f6492c80c55fa02c2d4e35be578cc64f7f612f50 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 26 Aug 2019 22:28:35 +0200 Subject: [PATCH] Stream import: support multiple files Multiple files can now be selected for "File/Import/Other Files into current". NOTE: performance for many files may not be excellent due to re-sorting of hierarchy and bounding box re-computation. --- src/klayout.pro | 3 + .../import/lay_plugin/StreamImportDialog.ui | 786 ++++++++++-------- .../import/lay_plugin/layStreamImport.cc | 2 +- .../lay_plugin/layStreamImportDialog.cc | 23 +- .../import/lay_plugin/layStreamImportDialog.h | 2 +- .../import/lay_plugin/layStreamImporter.cc | 223 ++--- .../import/lay_plugin/layStreamImporter.h | 12 +- 7 files changed, 602 insertions(+), 449 deletions(-) diff --git a/src/klayout.pro b/src/klayout.pro index fbf20df44..226b13f29 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -101,3 +101,6 @@ plugins.depends += lib rdb db unit_tests.depends += plugins $$MAIN_DEPENDS +RESOURCES += \ + plugins/tools/import/lay_plugin/layResources.qrc + diff --git a/src/plugins/tools/import/lay_plugin/StreamImportDialog.ui b/src/plugins/tools/import/lay_plugin/StreamImportDialog.ui index 1d13a0003..9ada7a02d 100644 --- a/src/plugins/tools/import/lay_plugin/StreamImportDialog.ui +++ b/src/plugins/tools/import/lay_plugin/StreamImportDialog.ui @@ -1,7 +1,8 @@ - + + StreamImportDialog - - + + 0 0 @@ -9,32 +10,42 @@ 604 - + Dialog - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + Back - - :/back.png + + + :/back.png:/back.png - + - + Qt::Horizontal - + 331 20 @@ -42,46 +53,47 @@ - - - + + + Import - - - + + + Next - - :/forward.png + + + :/forward.png:/forward.png - - - + + + Cancel - - - + + + Qt::Horizontal - + - + Qt::Vertical - + QSizePolicy::Fixed - + 736 16 @@ -89,22 +101,22 @@ - - - + + + Qt::Horizontal - + - + Qt::Vertical - + QSizePolicy::Fixed - + 736 16 @@ -112,41 +124,50 @@ - - - + + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + 12 75 true - + SectionHeader - + Qt::Horizontal - + 40 20 @@ -155,8 +176,8 @@ - - + + Reset @@ -164,140 +185,117 @@ - - - - 2 + + + + 0 - - - - 9 - - + + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - + + Input - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - - Top cell - - - - - - - - - + + + ... - - - - File - - - - - - - - 5 - 5 - 1 - 0 - - - - (leave empty for selecting the top cell automatically) - - - - - - - - 7 - 0 + + + + 1 0 - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - + + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + Stream reader options - - + + Edit ... - - + + Reset - + Qt::Horizontal - + 40 20 @@ -308,58 +306,141 @@ + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Top cell + + + + + + + + 1 + 0 + + + + (leave empty for selecting the top cell automatically) + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Files + + + + + + + + + + + + + 0 + 0 + + + + true + + + - - + + Import Mode - - - 9 - - + + 0 + + 9 + + + 9 + + + 9 + + + 9 + - - + + Merge - Merge new cell into current cell and create a new cell hierarchy below - - + + Extra cells - Import as extra cells - - + + Instantiate - Instantiate a new cell tree below the current cell - - + + Merge hierarchy - Merge into current cell's hierarchy (attempt to identify corresponding cells) - + Qt::Vertical - + QSizePolicy::Fixed - + 20 10 @@ -368,11 +449,11 @@ - - - Note: All modes except "Extra cells" require a unique top cell for the input file. Either one must be specified or the input file must have a single top cell. + + + Note: All modes except "Extra cells" require a unique top cell for the input file. Either one must be specified or the input file must have a single top cell. - + true @@ -382,13 +463,13 @@ - + Qt::Vertical - + QSizePolicy::Expanding - + 20 16 @@ -398,74 +479,101 @@ - - - - 0 - - + + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + QFrame::StyledPanel - + QFrame::Plain - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + Layer mapping - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - - Use "layer/datatype" notation to specify an offset to be added to layer and/or datatype. + + + + Use "layer/datatype" notation to specify an offset to be added to layer and/or datatype. - + true - - + + - - - + + + On original layers plus offset - - - + + + No mapping - use original layers - + - + Qt::Horizontal - + 211 20 @@ -476,12 +584,12 @@ - + - + Qt::Vertical - + 20 40 @@ -494,172 +602,190 @@ - - - - 0 - - + + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + QFrame::StyledPanel - + QFrame::Plain - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - + + - - + + - - - + + + Point 1 - - - + + + y (micron) - - - + + + Reference points coordinates on layout to import - - + + - - - + + + Point 3 - - + + - - - + + + Reference point coordinates in existing layout - - + + - - + + - - - + + + x (micron) - - - + + + Qt::Horizontal - - - + + + Point 2 - - + + - - - + + + Point 3 - - - + + + Point 1 - - + + - - + + - - - + + + Qt::Horizontal - - + + - - - + + + y (micron) - - - + + + x (micron) - - - + + + Point 2 - - + + - - + + - + - + Qt::Horizontal - + QSizePolicy::Fixed - + 16 20 @@ -667,15 +793,15 @@ - + - + Qt::Vertical - + QSizePolicy::Fixed - + 691 8 @@ -683,15 +809,15 @@ - + - + Qt::Vertical - + QSizePolicy::Fixed - + 20 15 @@ -699,38 +825,47 @@ - - - + + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + Explicit transformation - - - Examples: "r90", "m45", "r90 (2000,-5000)" + + + Examples: "r90", "m45", "r90 (2000,-5000)" - - + + (Reference points have higher priority) @@ -738,14 +873,14 @@ - - - + + + Leave fields empty to specify less reference points. One point is used to derive the displacement, further points are used to derive the orientation. Currently, no magnification is implied and only simple rotations are derived from the mapping points. -Alternatively the transformation (imported to existing layout) can be specified using the explicit transformation below. This allows one to specify an arbitrary transformation. Use the common notation (i.e. "*2 r90 10,-100"). +Alternatively the transformation (imported to existing layout) can be specified using the explicit transformation below. This allows one to specify an arbitrary transformation. Use the common notation (i.e. "*2 r90 10,-100"). - + true @@ -755,10 +890,10 @@ Alternatively the transformation (imported to existing layout) can be specified - + Qt::Vertical - + 20 40 @@ -773,7 +908,6 @@ Alternatively the transformation (imported to existing layout) can be specified - file_le file_pb topcell_le edit_options_pb @@ -804,7 +938,7 @@ Alternatively the transformation (imported to existing layout) can be specified cancel_pb - + @@ -813,11 +947,11 @@ Alternatively the transformation (imported to existing layout) can be specified StreamImportDialog accept() - + 572 598 - + 574 614 @@ -829,11 +963,11 @@ Alternatively the transformation (imported to existing layout) can be specified StreamImportDialog reject() - + 679 597 - + 683 613 diff --git a/src/plugins/tools/import/lay_plugin/layStreamImport.cc b/src/plugins/tools/import/lay_plugin/layStreamImport.cc index 99331d7d1..f220b282d 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImport.cc +++ b/src/plugins/tools/import/lay_plugin/layStreamImport.cc @@ -61,7 +61,7 @@ public: virtual void get_menu_entries (std::vector &menu_entries) const { lay::PluginDeclaration::get_menu_entries (menu_entries); - menu_entries.push_back (lay::MenuEntry ("lay::import_stream", "import_stream:edit", "file_menu.import_menu.end", tl::to_string (QObject::tr ("Other File Into Current")))); + menu_entries.push_back (lay::MenuEntry ("lay::import_stream", "import_stream:edit", "file_menu.import_menu.end", tl::to_string (QObject::tr ("Other Files Into Current")))); } virtual bool configure (const std::string &name, const std::string &value) diff --git a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc index d920ef3ef..df7a168eb 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc +++ b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.cc @@ -126,7 +126,7 @@ StreamImportData::setup_importer (StreamImporter *importer) importer->set_reference_points (reference_points); importer->set_cell_mapping (mode); importer->set_layer_mapping (layer_mode); - importer->set_file (file); + importer->set_files (files); importer->set_topcell (topcell); importer->set_layer_offset (layer_offset); importer->set_reader_options (options); @@ -136,9 +136,12 @@ static tl::XMLElementList xml_elements () { typedef std::pair ref_point; typedef std::vector ref_point_v; + typedef std::vector string_v; return - tl::make_member (&StreamImportData::file, "file") + + tl::make_element (&StreamImportData::files, "files", + tl::make_member (&string_v::begin, &string_v::end, &string_v::push_back, "file") + ) + tl::make_member (&StreamImportData::topcell, "cell-name") + tl::make_member (&StreamImportData::layer_offset, "layer-offset") + tl::make_member (&StreamImportData::layer_mode, "layer-mode", LayerModeConverter ()) + @@ -217,10 +220,14 @@ StreamImportDialog::reset_options () void StreamImportDialog::browse_filename () { - QString file = mp_ui->file_le->text (); - file = QFileDialog::getOpenFileName (this, QObject::tr ("File To Import"), file, QObject::tr ("All files (*)")); - if (! file.isNull ()) { - mp_ui->file_le->setText (file); + QStringList files = mp_ui->files_te->toPlainText ().split (QString::fromUtf8 ("\n")); + QString file; + if (! files.isEmpty ()) { + file = files.front (); + } + files = QFileDialog::getOpenFileNames (this, QObject::tr ("File To Import"), file, QObject::tr ("All files (*)")); + if (! files.isEmpty ()) { + mp_ui->files_te->setPlainText (files.join (QString::fromUtf8 ("\n"))); } } @@ -319,7 +326,7 @@ StreamImportDialog::commit_page () if (page == 0) { // --- General page - mp_data->file = tl::to_string (mp_ui->file_le->text ()); + mp_data->files = tl::split (tl::to_string (mp_ui->files_te->toPlainText ()), "\n"); mp_data->topcell = tl::to_string (mp_ui->topcell_le->text ()); if (mp_ui->import_simple_rb->isChecked ()) { mp_data->mode = StreamImportData::Simple; @@ -426,7 +433,7 @@ StreamImportDialog::update () mp_ui->section_header_lbl->setText (tl::to_qstring (section_headers [page])); // --- General page - mp_ui->file_le->setText (tl::to_qstring (mp_data->file)); + mp_ui->files_te->setPlainText (tl::to_qstring (tl::join (mp_data->files, "\n"))); mp_ui->topcell_le->setText (tl::to_qstring (mp_data->topcell)); mp_ui->import_simple_rb->setChecked (mp_data->mode == StreamImportData::Simple); mp_ui->import_extra_rb->setChecked (mp_data->mode == StreamImportData::Extra); diff --git a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h index 7283d9ecb..96137e812 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h +++ b/src/plugins/tools/import/lay_plugin/layStreamImportDialog.h @@ -73,7 +73,7 @@ public: enum layer_mode_type { Original = 0, Offset = 1 }; mode_type mode; - std::string file; + std::vector files; std::string topcell; std::vector > reference_points; db::DCplxTrans explicit_trans; diff --git a/src/plugins/tools/import/lay_plugin/layStreamImporter.cc b/src/plugins/tools/import/lay_plugin/layStreamImporter.cc index 7f0f4a7c4..cb773709a 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImporter.cc +++ b/src/plugins/tools/import/lay_plugin/layStreamImporter.cc @@ -126,61 +126,6 @@ StreamImporter::read (db::Layout &target, db::cell_index_type target_cell_index, } - // Prepare the layout to read - db::Layout source; - - // Load the layout - { - tl::InputStream stream (m_file); - db::Reader reader (stream); - - tl::log << tl::to_string (QObject::tr ("Loading file: ")) << m_file; - tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading"))); - reader.read (source, m_options); - } - - // Locate the top cell in the source file - db::cell_index_type source_topcell; - std::vector source_cells; - - if (m_cell_mapping != StreamImportData::Extra || !m_topcell.empty ()) { - - if (m_topcell.empty ()) { - - db::Layout::top_down_const_iterator t = source.begin_top_down (); - if (t == source.end_top_down ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Source layout does not have a top cell"))); - } - - source_topcell = *t; - - ++t; - if (t != source.end_top_cells ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Source layout does not have a unique top cell - specify one explicitly"))); - } - - } else { - - std::pair t = source.cell_by_name (m_topcell.c_str ()); - if (! t.first) { - throw tl::Exception (tl::to_string (QObject::tr ("Source layout does not have a cell named '%s'")), m_topcell); - } - - source_topcell = t.second; - - } - - source_cells.push_back (source_topcell); - - } else { - - // collect source cells - for (db::Layout::top_down_const_iterator t = source.begin_top_down (); t != source.end_top_cells (); ++t) { - source_cells.push_back (*t); - } - - } - // Issue a warning, if the transformation is not ortho etc. if (fabs (global_trans.mag () - floor (global_trans.mag () + 0.5)) > 1e-6 || ! global_trans.is_ortho ()) { @@ -193,76 +138,141 @@ StreamImporter::read (db::Layout &target, db::cell_index_type target_cell_index, } } - - // Create a layer map - std::map layer_map; - for (db::Layout::layer_iterator l = source.begin_layers (); l != source.end_layers (); ++l) { - - db::LayerProperties lp (*(*l).second); - if (m_layer_mapping == StreamImportData::Offset) { - lp = m_layer_offset.apply (lp); + + // TODO: Currently no merging is provided for non-unity transformations + if (m_cell_mapping == StreamImportData::Merge && ! global_trans.equal (db::DCplxTrans ())) { + + if (QMessageBox::warning (QApplication::activeWindow (), + QObject::tr ("Merge Mode Is Not Available"), + tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("Merge mode is not supported for the specified transformation (%s).\nSimple mode will be used instead.\nPress 'Ok' to continue.")), global_trans.to_string ())), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok) != QMessageBox::Ok) { + return; } - bool layer_found = false; - for (db::Layout::layer_iterator ll = target.begin_layers (); ll != target.end_layers () && ! layer_found; ++ll) { - if ((*ll).second->log_equal (lp)) { - layer_map.insert (std::make_pair ((*l).first, (*ll).first)); - layer_found = true; - } - } - - if (! layer_found) { - unsigned int new_layer = target.insert_layer (lp); - layer_map.insert (std::make_pair ((*l).first, new_layer)); - new_layers.push_back (new_layer); - } + m_cell_mapping = StreamImportData::Simple; } - // Create a cell map - std::map cell_map; + for (size_t file_index = 0; file_index < m_files.size (); ++file_index) { - if (m_cell_mapping == StreamImportData::Simple) { + std::string file = m_files [file_index]; - cell_map.insert (std::make_pair (source_topcell, target_cell_index)); + // Prepare the layout to read + db::Layout source; - } else if (m_cell_mapping == StreamImportData::Extra) { + // Load the layout + { + tl::InputStream stream (file); + db::Reader reader (stream); - // create new top cells for each source top cell - for (std::vector::const_iterator t = source_cells.begin (); t != source_cells.end (); ++t) { - db::cell_index_type new_top = target.add_cell (source.cell_name (*t)); - cell_map.insert (std::make_pair (*t, new_top)); + tl::log << tl::to_string (QObject::tr ("Loading file: ")) << file; + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file: ")) + file); + reader.read (source, m_options); } - } else if (m_cell_mapping == StreamImportData::Instantiate) { + // Locate the top cell in the source file + db::cell_index_type source_topcell; + std::vector source_cells; - // Create a new top cell for importing into and use the cell reference to produce the first part of the transformation + if (m_cell_mapping != StreamImportData::Extra || !m_topcell.empty ()) { - db::cell_index_type new_top = target.add_cell (source.cell_name (source_topcell)); - cell_map.insert (std::make_pair (source_topcell, new_top)); + if (m_topcell.empty ()) { - db::ICplxTrans gt_dbu = db::VCplxTrans (1.0 / target.dbu ()) * global_trans * db::CplxTrans (source.dbu ()); - target.cell (target_cell_index).insert (db::CellInstArray (new_top, gt_dbu * db::ICplxTrans (1.0 / global_trans.mag ()))); + db::Layout::top_down_const_iterator t = source.begin_top_down (); + if (t == source.end_top_down ()) { + throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Source layout '%s' does not have a top cell")), file)); + } - global_trans = db::DCplxTrans (global_trans.mag ()); + source_topcell = *t; - } else if (m_cell_mapping == StreamImportData::Merge) { + ++t; + if (t != source.end_top_cells ()) { + throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Source layout '%s' does not have a unique top cell - specify one explicitly")), file)); + } - // Create the cell mapping - // TODO: Currently no merging is provided for non-unity transformations - if (! global_trans.equal (db::DCplxTrans ())) { + } else { + + std::pair t = source.cell_by_name (m_topcell.c_str ()); + if (! t.first) { + throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Source layout '%s' does not have a cell named '%s'")), file, m_topcell)); + } + + source_topcell = t.second; - if (QMessageBox::warning (QApplication::activeWindow (), - QObject::tr ("Merge Mode Is Not Available"), - tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("Merge mode is not supported for the specified transformation (%s).\nSimple mode will be used instead.\nPress 'Ok' to continue.")), global_trans.to_string ())), - QMessageBox::Ok | QMessageBox::Cancel, - QMessageBox::Ok) != QMessageBox::Ok) { - return; } + source_cells.push_back (source_topcell); + + } else { + + // collect source cells + for (db::Layout::top_down_const_iterator t = source.begin_top_down (); t != source.end_top_cells (); ++t) { + source_cells.push_back (*t); + } + + } + + // Create a layer map + std::map layer_map; + for (db::Layout::layer_iterator l = source.begin_layers (); l != source.end_layers (); ++l) { + + db::LayerProperties lp (*(*l).second); + if (m_layer_mapping == StreamImportData::Offset) { + lp = m_layer_offset.apply (lp); + } + + bool layer_found = false; + for (db::Layout::layer_iterator ll = target.begin_layers (); ll != target.end_layers () && ! layer_found; ++ll) { + if ((*ll).second->log_equal (lp)) { + layer_map.insert (std::make_pair ((*l).first, (*ll).first)); + layer_found = true; + } + } + + if (! layer_found) { + unsigned int new_layer = target.insert_layer (lp); + layer_map.insert (std::make_pair ((*l).first, new_layer)); + new_layers.push_back (new_layer); + } + + } + + // Computes the final global transformation + db::DCplxTrans gt = global_trans; + + // Create a cell map + std::map cell_map; + + if (m_cell_mapping == StreamImportData::Simple) { + cell_map.insert (std::make_pair (source_topcell, target_cell_index)); - } else { + } else if (m_cell_mapping == StreamImportData::Extra) { + + // create new top cells for each source top cell + for (std::vector::const_iterator t = source_cells.begin (); t != source_cells.end (); ++t) { + db::cell_index_type new_top = target.add_cell (source.cell_name (*t)); + cell_map.insert (std::make_pair (*t, new_top)); + } + + } else if (m_cell_mapping == StreamImportData::Instantiate) { + + // Create a new top cell for importing into and use the cell reference to produce the first part of the transformation + + db::cell_index_type new_top = target.add_cell (source.cell_name (source_topcell)); + cell_map.insert (std::make_pair (source_topcell, new_top)); + + db::ICplxTrans gt_dbu = db::VCplxTrans (1.0 / target.dbu ()) * gt * db::CplxTrans (source.dbu ()); + target.cell (target_cell_index).insert (db::CellInstArray (new_top, gt_dbu * db::ICplxTrans (1.0 / gt.mag ()))); + + gt = db::DCplxTrans (gt.mag ()); + + } else if (m_cell_mapping == StreamImportData::Merge) { + + // Create the cell mapping + // TODO: Currently no merging is provided for non-unity transformations + tl_assert (gt.equal (db::DCplxTrans ())); db::CellMapping cm; cm.create_from_geometry (target, target_cell_index, source, source_topcell); @@ -270,11 +280,10 @@ StreamImporter::read (db::Layout &target, db::cell_index_type target_cell_index, } + // And actually merge + db::merge_layouts (target, source, db::VCplxTrans (1.0 / target.dbu ()) * gt * db::CplxTrans (source.dbu ()), source_cells, cell_map, layer_map); + } - - // And actually merge - db::merge_layouts (target, source, db::VCplxTrans (1.0 / target.dbu ()) * global_trans * db::CplxTrans (source.dbu ()), source_cells, cell_map, layer_map); - } } diff --git a/src/plugins/tools/import/lay_plugin/layStreamImporter.h b/src/plugins/tools/import/lay_plugin/layStreamImporter.h index 86e57d2ec..f9cb85768 100644 --- a/src/plugins/tools/import/lay_plugin/layStreamImporter.h +++ b/src/plugins/tools/import/lay_plugin/layStreamImporter.h @@ -93,19 +93,19 @@ public: } /** - * @brief Set file which is read. + * @brief Set files which are read. */ - void set_file (const std::string &file) + void set_files (const std::vector &files) { - m_file = file; + m_files = files; } /** * @brief Get the file which is read */ - const std::string &file () const + const std::vector &files () const { - return m_file; + return m_files; } /** @@ -192,7 +192,7 @@ public: } private: - std::string m_file; + std::vector m_files; std::string m_topcell; db::DCplxTrans m_global_trans; std::vector > m_reference_points;