/* KLayout Layout Viewer Copyright (C) 2006-2019 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 "layStreamImporter.h" #include "layLayoutView.h" #include "tlStream.h" #include "tlString.h" #include "tlString.h" #include "tlLog.h" #include "dbEdgeProcessor.h" #include "dbCellMapping.h" #include "dbLayoutUtils.h" #include #include #include #include namespace lay { // --------------------------------------------------------------------------------------- // Implementation of StreamImporter StreamImporter::StreamImporter () : m_cell_mapping (StreamImportData::Simple), m_layer_mapping (StreamImportData::Original) { // .. nothing yet .. } void StreamImporter::read (db::Layout &target, db::cell_index_type target_cell_index, std::vector &new_layers) { // Clear the undo buffer if (target.manager () && !target.manager ()->transacting ()) { target.manager ()->clear (); } tl::log << tl::to_string (QObject::tr ("Importing stream data")); // tl::Progress m_progress (tl::to_string (QObject::tr ("Importing stream data")), m_files.size (), 1); // TODO: this code should be available otherwise ... // derive the actual global transformation from the reference points db::DCplxTrans global_trans (m_global_trans); if (! m_reference_points.empty ()) { db::DPoint p1_pcb = m_reference_points[0].first; db::DPoint p1_ly = m_reference_points[0].second; if (m_reference_points.size () > 1) { db::DPoint p2_pcb = m_reference_points[1].first; db::DPoint p2_ly = m_reference_points[1].second; db::DVector d12_pcb = (p2_pcb - p1_pcb) * (1.0 / p2_pcb.distance (p1_pcb)); db::DVector d12_ly = (p2_ly - p1_ly) * (1.0 / p2_ly.distance (p1_ly)); int ru = -1; int rm = -1; for (int f = 0; f < 8; ++f) { db::DVector d12 = db::DTrans (f) * d12_pcb; if ((d12 - d12_ly).length () < 0.1) { if (f < 4) { ru = f; } else { rm = f; } } } if (ru < 0 || rm < 0) { throw tl::Exception (tl::to_string (QObject::tr ("Unable to deduce rotation from reference points p1 and p2 (imported and existing layout)"))); } if (m_reference_points.size () > 2) { db::DPoint p3_pcb = m_reference_points[2].first; db::DPoint p3_ly = m_reference_points[2].second; db::DVector d13_pcb = (p3_pcb - p1_pcb) * (1.0 / p3_pcb.distance (p1_pcb)); db::DVector d13_ly = (p3_ly - p1_ly) * (1.0 / p3_ly.distance (p1_ly)); double vp_pcb = d13_pcb.x () * d12_pcb.y () - d13_pcb.y () * d12_pcb.x (); double vp_gds = d13_ly.x () * d12_ly.y () - d13_ly.y () * d12_ly.x (); if (vp_pcb * vp_gds < 0.0) { global_trans = db::DCplxTrans (db::DFTrans (rm)); } else { global_trans = db::DCplxTrans (db::DFTrans (ru)); } } else { if (global_trans.is_mirror ()) { global_trans = db::DCplxTrans (db::DFTrans (rm)); } else { global_trans = db::DCplxTrans (db::DFTrans (ru)); } } } global_trans = db::DCplxTrans (p1_ly - (db::DPoint () + global_trans.disp ())) * global_trans * db::DCplxTrans (db::DPoint () - p1_pcb); } // 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 ()) { if (QMessageBox::warning (QApplication::activeWindow (), QObject::tr ("Complex Transformation"), tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("The specified transformation (%s) is complex.\nGrid snapping to the database unit grid can occur and\neffectively alter the geometry of the layout.\nPress 'Ok' to continue.")), global_trans.to_string ())), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) { return; } } // 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; } m_cell_mapping = StreamImportData::Simple; } for (size_t file_index = 0; file_index < m_files.size (); ++file_index) { std::string file = m_files [file_index]; // Prepare the layout to read db::Layout source; // Load the layout { tl::InputStream stream (file); db::Reader reader (stream); 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); } // 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::sprintf (tl::to_string (QObject::tr ("Source layout '%s' does not have a top cell")), file)); } source_topcell = *t; ++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)); } } 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; } 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 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); cell_map.insert (cm.begin (), cm.end ()); } // 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); } } }