2017-02-12 13:21:08 +01:00
/*
KLayout Layout Viewer
2019-01-08 00:58:45 +01:00
Copyright ( C ) 2006 - 2019 Matthias Koefferlein
2017-02-12 13:21:08 +01:00
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
*/
2018-06-17 09:43:25 +02:00
# include "layStreamImporter.h"
2017-02-12 13:21:08 +01:00
# 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>
2018-06-17 09:43:25 +02:00
namespace lay
2017-02-12 13:21:08 +01:00
{
// ---------------------------------------------------------------------------------------
// 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 ) ;
}
2019-08-26 22:28:35 +02:00
// 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 ( ) ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
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. \n Grid snapping to the database unit grid can occur and \n effectively alter the geometry of the layout. \n Press 'Ok' to continue. " ) ) , global_trans . to_string ( ) ) ) ,
QMessageBox : : Ok | QMessageBox : : Cancel ,
QMessageBox : : Ok ) ! = QMessageBox : : Ok ) {
return ;
}
2017-02-12 13:21:08 +01:00
}
2019-08-26 22:28:35 +02:00
// TODO: Currently no merging is provided for non-unity transformations
if ( m_cell_mapping = = StreamImportData : : Merge & & ! global_trans . equal ( db : : DCplxTrans ( ) ) ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
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). \n Simple mode will be used instead. \n Press 'Ok' to continue. " ) ) , global_trans . to_string ( ) ) ) ,
QMessageBox : : Ok | QMessageBox : : Cancel ,
QMessageBox : : Ok ) ! = QMessageBox : : Ok ) {
return ;
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
m_cell_mapping = StreamImportData : : Simple ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
for ( size_t file_index = 0 ; file_index < m_files . size ( ) ; + + file_index ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
std : : string file = m_files [ file_index ] ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// Prepare the layout to read
db : : Layout source ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// Load the layout
{
tl : : InputStream stream ( file ) ;
db : : Reader reader ( stream ) ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
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 ) ;
2017-02-12 13:21:08 +01:00
}
2019-08-26 22:28:35 +02:00
// Locate the top cell in the source file
db : : cell_index_type source_topcell ;
std : : vector < db : : cell_index_type > source_cells ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
if ( m_cell_mapping ! = StreamImportData : : Extra | | ! m_topcell . empty ( ) ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
if ( m_topcell . empty ( ) ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
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 ) ) ;
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
source_topcell = * t ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
+ + 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 ) ) ;
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
} 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 ;
2017-02-12 13:21:08 +01:00
}
2019-08-26 22:28:35 +02:00
source_cells . push_back ( source_topcell ) ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
} else {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// 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 ) ;
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// 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 ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
db : : LayerProperties lp ( * ( * l ) . second ) ;
if ( m_layer_mapping = = StreamImportData : : Offset ) {
lp = m_layer_offset . apply ( lp ) ;
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
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 ;
}
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
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 ) ;
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
}
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// Computes the final global transformation
db : : DCplxTrans gt = global_trans ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// Create a cell map
std : : map < db : : cell_index_type , db : : cell_index_type > cell_map ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
if ( m_cell_mapping = = StreamImportData : : Simple ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
cell_map . insert ( std : : make_pair ( source_topcell , target_cell_index ) ) ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
} else if ( m_cell_mapping = = StreamImportData : : Extra ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// 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 ) ) ;
2017-02-12 13:21:08 +01:00
}
2019-08-26 22:28:35 +02:00
} else if ( m_cell_mapping = = StreamImportData : : Instantiate ) {
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
// 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 ( ) ) ) ;
2017-02-12 13:21:08 +01:00
db : : CellMapping cm ;
cm . create_from_geometry ( target , target_cell_index , source , source_topcell ) ;
cell_map . insert ( cm . begin ( ) , cm . end ( ) ) ;
}
2019-08-26 22:28:35 +02:00
// 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 ) ;
2017-02-12 13:21:08 +01:00
2019-08-26 22:28:35 +02:00
}
2017-02-12 13:21:08 +01:00
}
}