export_to_pcbnew.cpp 15.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2007-2014 Jean-Pierre Charras  jp.charras at wanadoo.fr
 * Copyright (C) 1992-2014 KiCad Developers, see change_log.txt for contributors.
 *
 * 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, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

25 26
/**
 * @file export_to_pcbnew.cpp
27
 * @brief Export the layers to Pcbnew.
28
 */
29

30 31
#include <vector>

32 33 34 35 36 37 38 39
#include <fctsys.h>
#include <common.h>
#include <confirm.h>
#include <macros.h>
#include <kicad_string.h>
#include <gestfich.h>
#include <trigo.h>
#include <gerbview.h>
40
#include <gerbview_frame.h>
41
#include <class_gerber_draw_item.h>
42
#include <class_GERBER.h>
43
#include <select_layers_to_pcb.h>
44 45
#include <build_version.h>
#include <wildcards_and_files_ext.h>
46

47 48 49 50
// Imported function
extern const wxString GetPCBDefaultLayerName( LAYER_NUM aLayerNumber );

#define TO_PCB_UNIT( x ) ( x / IU_PER_MM)
51 52

#define TRACK_TYPE  0
53

54
/* A helper class to export a Gerber set of files to Pcbnew
55
 */
56 57
class GBR_TO_PCB_EXPORTER
{
58 59 60 61 62 63 64 65
private:
    GERBVIEW_FRAME*         m_gerbview_frame;   // the main gerber frame
    wxString                m_pcb_file_name;    // BOARD file to write to
    FILE*                   m_fp;               // the board file
    int                     m_pcbCopperLayersCount;
    std::vector<wxPoint>    m_vias_coordinates; // list of already generated vias,
                                                // used to export only once a via
                                                // having a given coordinate
66
public:
67
    GBR_TO_PCB_EXPORTER( GERBVIEW_FRAME* aFrame, const wxString& aFileName );
68
    ~GBR_TO_PCB_EXPORTER();
69 70 71

    /**
     * Function ExportPcb
72
     * saves a board from a set of Gerber images.
73
     */
74
    bool    ExportPcb( LAYER_NUM* aLayerLookUpTable, int aCopperLayers );
75 76

private:
77 78 79 80 81 82
    /**
     * Function export_non_copper_item
     * write a non copper line or arc to the board file.
     * @param aGbrItem = the Gerber item (line, arc) to export
     * @param aLayer = the technical layer to use
     */
83
    void    export_non_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
84 85 86 87 88 89 90

    /**
     * Function export_copper_item
     * write a track or via) to the board file.
     * @param aGbrItem = the Gerber item (line, arc, flashed) to export
     * @param aLayer = the copper layer to use
     */
91
    void    export_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
92 93 94 95 96 97

    /**
     * Function export_flashed_copper_item
     * write a via to the board file (always uses a via through).
     * @param aGbrItem = the flashed Gerber item to export
     */
98
    void    export_flashed_copper_item( GERBER_DRAW_ITEM* aGbrItem );
99 100 101 102 103 104 105

    /**
     * Function export_segline_copper_item
     * write a track (not via) to the board file.
     * @param aGbrItem = the Gerber item (line only) to export
     * @param aLayer = the copper layer to use
     */
106
    void    export_segline_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
107 108 109 110 111 112 113 114

    /**
     * Function export_segarc_copper_item
     * write a set of tracks (arcs are approximated by track segments)
     * to the board file.
     * @param aGbrItem = the Gerber item (arc only) to export
     * @param aLayer = the copper layer to use
     */
115
    void    export_segarc_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
116 117 118

    /**
     * function writePcbLineItem
119 120 121 122 123 124 125 126 127 128
     * basic write function to write a DRAWSEGMENT item or a TRACK item
     * to the board file, from a non flashed item
     */
    void    writePcbLineItem( bool aIsArc, wxPoint& aStart, wxPoint& aEnd,
                              int aWidth, LAYER_NUM aLayer, double aAngle = 0 );

    /**
     * function writeCopperLineItem
     * basic write function to write a a TRACK item
     * to the board file, from a non flashed item
129
     */
130 131
    void    writeCopperLineItem( wxPoint& aStart, wxPoint& aEnd,
                                 int aWidth, LAYER_NUM aLayer );
132 133 134 135 136

    /**
     * function writePcbHeader
     * Write a very basic header to the board file
     */
137
    void    writePcbHeader( LAYER_NUM* aLayerLookUpTable );
138 139
};

140

141
GBR_TO_PCB_EXPORTER::GBR_TO_PCB_EXPORTER( GERBVIEW_FRAME* aFrame, const wxString& aFileName )
142
{
143 144
    m_gerbview_frame    = aFrame;
    m_pcb_file_name     = aFileName;
145 146
}

147

148 149 150 151
GBR_TO_PCB_EXPORTER::~GBR_TO_PCB_EXPORTER()
{
}

152

153
/* Export data in Pcbnew format
154
 * remember Pcbnew uses a Y reversed axis, so we must negate all Y coordinates
155
 */
156
void GERBVIEW_FRAME::ExportDataInPcbnewFormat( wxCommandEvent& event )
157
{
158
    int layercount = 0;
159

160
    // Count the Gerber layers which are actually currently used
161
    for( LAYER_NUM ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
162
    {
163
        if( g_GERBER_List.GetGbrImage( ii ) )
164
            layercount++;
165 166
    }

167
    if( layercount == 0 )
168
    {
169
        DisplayInfoMessage( this,
170
                            _( "None of the Gerber layers contain any data" ) );
171 172 173
        return;
    }

174
    wxString        fileName;
175
    wxString        path = wxGetCwd();
176

177
    wxFileDialog    filedlg( this, _( "Board file name:" ),
178
                             path, fileName, PcbFileWildcard,
179
                             wxFD_SAVE );
180 181

    if( filedlg.ShowModal() == wxID_CANCEL )
182 183
        return;

184 185
    fileName = filedlg.GetPath();

186
    /* Install a dialog frame to choose the mapping
187
     * between gerber layers and Pcbnew layers
188
     */
189 190 191
    LAYERS_MAP_DIALOG* layerdlg = new LAYERS_MAP_DIALOG( this );
    int ok = layerdlg->ShowModal();
    layerdlg->Destroy();
192

193 194 195
    if( ok != wxID_OK )
        return;

196
    if( wxFileExists( fileName ) )
197
    {
198
        if( !IsOK( this, _( "OK to change the existing file ?" ) ) )
199 200
            return;
    }
201

202
    GBR_TO_PCB_EXPORTER gbr_exporter( this, fileName );
203

204 205
    gbr_exporter.ExportPcb( layerdlg->GetLayersLookUpTable(),
                            layerdlg->GetCopperLayersCount() );
206 207
}

208

209
bool GBR_TO_PCB_EXPORTER::ExportPcb( LAYER_NUM* aLayerLookUpTable, int aCopperLayers )
210
{
211 212
    LOCALE_IO   toggle;     // toggles on, then off, the C locale.

213
    m_fp = wxFopen( m_pcb_file_name, wxT( "wt" ) );
214

215 216 217
    if( m_fp == NULL )
    {
        wxString msg;
218
        msg.Printf( _( "Cannot create file '%s'" ), GetChars( m_pcb_file_name ) );
219 220
        DisplayError( m_gerbview_frame, msg );
        return false;
221 222
    }

223
    m_pcbCopperLayersCount = aCopperLayers;
224

225
    writePcbHeader( aLayerLookUpTable );
226

dickelbeck's avatar
dickelbeck committed
227
    // create an image of gerber data
228 229
    // First: non copper layers:
    GERBER_DRAW_ITEM* gerb_item = m_gerbview_frame->GetItemsList();
230
    int pcbCopperLayerMax = 31;
231

232
    for( ; gerb_item; gerb_item = gerb_item->Next() )
233
    {
234
        int layer = gerb_item->GetLayer();
235
        LAYER_NUM pcb_layer_number = aLayerLookUpTable[layer];
236

237
        if( !IsPcbLayer( pcb_layer_number ) )
238
            continue;
239

240
        if( pcb_layer_number > pcbCopperLayerMax )
241
            export_non_copper_item( gerb_item, pcb_layer_number );
242 243
    }

244 245
    // Copper layers
    gerb_item = m_gerbview_frame->GetItemsList();
246

247
    for( ; gerb_item; gerb_item = gerb_item->Next() )
248
    {
249
        int layer = gerb_item->GetLayer();
250
        LAYER_NUM pcb_layer_number = aLayerLookUpTable[layer];
251

252
        if( pcb_layer_number < 0 || pcb_layer_number > pcbCopperLayerMax )
253
            continue;
254

255 256
        else
            export_copper_item( gerb_item, pcb_layer_number );
257
    }
258

259
    fprintf( m_fp, ")\n" );
260 261 262

    fclose( m_fp );
    m_fp = NULL;
263 264 265
    return true;
}

266

267
void GBR_TO_PCB_EXPORTER::export_non_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
268
{
269
    bool isArc = false;
270

271 272 273
    double     angle   = 0;
    wxPoint seg_start   = aGbrItem->m_Start;
    wxPoint seg_end     = aGbrItem->m_End;
274 275 276

    if( aGbrItem->m_Shape == GBR_ARC )
    {
277 278 279 280
        double  a = atan2( (double) ( aGbrItem->m_Start.y - aGbrItem->m_ArcCentre.y),
                           (double) ( aGbrItem->m_Start.x - aGbrItem->m_ArcCentre.x ) );
        double  b = atan2( (double) ( aGbrItem->m_End.y - aGbrItem->m_ArcCentre.y ),
                           (double) ( aGbrItem->m_End.x - aGbrItem->m_ArcCentre.x ) );
281

282 283
        isArc       = true;
        angle       = RAD2DEG(b - a);
284
        seg_start   = aGbrItem->m_ArcCentre;
285

286
        // Ensure arc orientation is CCW
287
        if( angle < 0 )
288
            angle += 360.0;
289 290
    }

291
    // Reverse Y axis:
292 293
    NEGATE( seg_start.y );
    NEGATE( seg_end.y );
294
    writePcbLineItem( isArc, seg_start, seg_end, aGbrItem->m_Size.x, aLayer, angle );
295
}
296

297

298
void GBR_TO_PCB_EXPORTER::export_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
299 300 301
{
    switch( aGbrItem->m_Shape )
    {
302 303 304 305 306 307 308 309 310 311 312 313 314 315
    case GBR_SPOT_CIRCLE:
    case GBR_SPOT_RECT:
    case GBR_SPOT_OVAL:
        // replace spots with vias when possible
        export_flashed_copper_item( aGbrItem );
        break;

    case GBR_ARC:
        export_segarc_copper_item( aGbrItem, aLayer );
        break;

    default:
        export_segline_copper_item( aGbrItem, aLayer );
        break;
316 317
    }
}
318

319

320
void GBR_TO_PCB_EXPORTER::export_segline_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
321
{
322 323 324 325
    wxPoint seg_start, seg_end;

    seg_start   = aGbrItem->m_Start;
    seg_end     = aGbrItem->m_End;
326 327

    // Reverse Y axis:
328 329
    NEGATE( seg_start.y );
    NEGATE( seg_end.y );
330

331 332 333 334 335 336 337 338 339 340 341 342 343 344
    writeCopperLineItem( seg_start, seg_end, aGbrItem->m_Size.x, aLayer );
}


void GBR_TO_PCB_EXPORTER::writeCopperLineItem( wxPoint& aStart, wxPoint& aEnd,
                                               int aWidth, LAYER_NUM aLayer )
{
  fprintf( m_fp, "(segment (start %s %s) (end %s %s) (width %s) (layer %s) (net 0))\n",
                  Double2Str( TO_PCB_UNIT(aStart.x) ).c_str(),
                  Double2Str( TO_PCB_UNIT(aStart.y) ).c_str(),
                  Double2Str( TO_PCB_UNIT(aEnd.x) ).c_str(),
                  Double2Str( TO_PCB_UNIT(aEnd.y) ).c_str(),
                  Double2Str( TO_PCB_UNIT( aWidth ) ).c_str(),
                  TO_UTF8( GetPCBDefaultLayerName( aLayer ) ) );
345 346
}

347

348
void GBR_TO_PCB_EXPORTER::export_segarc_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
349
{
350 351 352 353 354 355 356
    double  a = atan2( (double) ( aGbrItem->m_Start.y - aGbrItem->m_ArcCentre.y ),
                       (double) ( aGbrItem->m_Start.x - aGbrItem->m_ArcCentre.x ) );
    double  b = atan2( (double) ( aGbrItem->m_End.y - aGbrItem->m_ArcCentre.y ),
                       (double) ( aGbrItem->m_End.x - aGbrItem->m_ArcCentre.x ) );

    wxPoint start   = aGbrItem->m_Start;
    wxPoint end     = aGbrItem->m_End;
357 358

    /* Because Pcbnew does not know arcs in tracks,
359
     * approximate arc by segments (SEG_COUNT__CIRCLE segment per 360 deg)
360
     * The arc is drawn anticlockwise from the start point to the end point.
361
     */
362 363
    #define SEG_COUNT_CIRCLE    16
    #define DELTA_ANGLE         2 * M_PI / SEG_COUNT_CIRCLE
364

365 366 367
    // calculate the number of segments from a to b.
    // we want CNT_PER_360 segments fo a circle
    if( a > b )
368
        b += 2 * M_PI;
369

370
    wxPoint curr_start = start;
371 372 373
    wxPoint seg_start, seg_end;

    int     ii = 1;
374

375
    for( double rot = a; rot < (b - DELTA_ANGLE); rot += DELTA_ANGLE, ii++ )
376
    {
377
        seg_start = curr_start;
378
        wxPoint curr_end = start;
379
        RotatePoint( &curr_end, aGbrItem->m_ArcCentre,
380
                     -RAD2DECIDEG( DELTA_ANGLE * ii ) );
381
        seg_end = curr_end;
382
        // Reverse Y axis:
383 384
        NEGATE( seg_start.y );
        NEGATE( seg_end.y );
385
        writeCopperLineItem( seg_start, seg_end, aGbrItem->m_Size.x, aLayer );
386 387
        curr_start = curr_end;
    }
388

389 390
    if( end != curr_start )
    {
391 392
        seg_start   = curr_start;
        seg_end     = end;
393
        // Reverse Y axis:
394 395
        NEGATE( seg_start.y );
        NEGATE( seg_end.y );
396
        writeCopperLineItem( seg_start, seg_end, aGbrItem->m_Size.x, aLayer );
397 398 399 400 401 402 403 404 405
    }
}


/*
 * creates a via from a flashed gerber item.
 * Flashed items are usually pads or vias, so we try to export all of them
 * using vias
 */
406
void GBR_TO_PCB_EXPORTER::export_flashed_copper_item( GERBER_DRAW_ITEM* aGbrItem )
407
{
408 409 410 411 412 413 414 415
    // First, explore already created vias, before creating a new via
    for( unsigned ii = 0; ii < m_vias_coordinates.size(); ii++ )
    {
        if( m_vias_coordinates[ii] == aGbrItem->m_Start ) // Already created
            return;
    }

    m_vias_coordinates.push_back( aGbrItem->m_Start );
416

417 418
    wxPoint via_pos = aGbrItem->m_Start;
    int width   = (aGbrItem->m_Size.x + aGbrItem->m_Size.y) / 2;
419
    // Reverse Y axis:
420 421
    NEGATE( via_pos.y );

422 423 424 425 426
    // Layers are Front to Back
    fprintf( m_fp, " (via (at %s %s) (size %s)",
                  Double2Str( TO_PCB_UNIT(via_pos.x) ).c_str(),
                  Double2Str( TO_PCB_UNIT(via_pos.y) ).c_str(),
                  Double2Str( TO_PCB_UNIT( width ) ).c_str() );
427

428 429 430 431
    fprintf( m_fp, " (layers %s %s))\n",
                  TO_UTF8( GetPCBDefaultLayerName( F_Cu ) ),
                  TO_UTF8( GetPCBDefaultLayerName( B_Cu ) ) );
}
432

433 434 435 436
void GBR_TO_PCB_EXPORTER::writePcbHeader( LAYER_NUM* aLayerLookUpTable )
{
    fprintf( m_fp, "(kicad_pcb (version 4) (host Gerbview \"%s\")\n\n",
             TO_UTF8( GetBuildVersion() ) );
437

438 439
    // Write layers section
    fprintf( m_fp, "  (layers \n" );
440

441 442 443
    for( int ii = 0; ii < m_pcbCopperLayersCount; ii++ )
    {
        int id = ii;
444

445 446
        if( ii == m_pcbCopperLayersCount-1)
            id = B_Cu;
447

448 449
        fprintf( m_fp, "    (%d %s signal)\n", id, TO_UTF8( GetPCBDefaultLayerName( id ) ) );
    }
450

451 452 453 454
    for( int ii = B_Adhes; ii < LAYER_ID_COUNT; ii++ )
    {
        fprintf( m_fp, "    (%d %s user)\n", ii, TO_UTF8( GetPCBDefaultLayerName( ii ) ) );
    }
455

456 457
    fprintf( m_fp, "  )\n\n" );
}
458 459


460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
void GBR_TO_PCB_EXPORTER::writePcbLineItem( bool aIsArc, wxPoint& aStart, wxPoint& aEnd,
                                            int aWidth, LAYER_NUM aLayer, double aAngle )
{
    if( aIsArc && ( aAngle == 360.0 ||  aAngle == 0 ) )
    {
        fprintf( m_fp, "(gr_circle (center %s %s) (end %s %s)(layer %s) (width %s))\n",
                 Double2Str( TO_PCB_UNIT(aStart.x) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aStart.y) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aEnd.x) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aEnd.y) ).c_str(),
                 TO_UTF8( GetPCBDefaultLayerName( aLayer ) ),
                 Double2Str( TO_PCB_UNIT( aWidth ) ).c_str()
                 );
    }
    else if( aIsArc )
    {
        fprintf( m_fp, "(gr_arc (start %s %s) (end %s %s) (angle %s)(layer %s) (width %s))\n",
                 Double2Str( TO_PCB_UNIT(aStart.x) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aStart.y) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aEnd.x) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aEnd.y) ).c_str(),
                 Double2Str( aAngle ).c_str(),
                 TO_UTF8( GetPCBDefaultLayerName( aLayer ) ),
                 Double2Str( TO_PCB_UNIT( aWidth ) ).c_str()
                 );
    }
    else
    {
        fprintf( m_fp, "(gr_line (start %s %s) (end %s %s)(layer %s) (width %s))\n",
                 Double2Str( TO_PCB_UNIT(aStart.x) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aStart.y) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aEnd.x) ).c_str(),
                 Double2Str( TO_PCB_UNIT(aEnd.y) ).c_str(),
                 TO_UTF8( GetPCBDefaultLayerName( aLayer ) ),
                 Double2Str( TO_PCB_UNIT( aWidth ) ).c_str()
                 );
    }
497
}