mirror of https://github.com/KLayout/klayout.git
291 lines
9.7 KiB
C++
291 lines
9.7 KiB
C++
|
|
/*
|
|
|
|
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 <QDir>
|
|
#include <QMessageBox>
|
|
#include <QApplication>
|
|
|
|
#include <math.h>
|
|
|
|
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 <unsigned int> &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 <db::cell_index_type> 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<bool, db::cell_index_type> 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 <unsigned int, unsigned int> 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 <db::cell_index_type, db::cell_index_type> 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<db::cell_index_type>::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);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|