export_to_pcbnew.cpp 14.7 KB
Newer Older
1 2
/**
 * @file export_to_pcbnew.cpp
3
 * @brief Export the layers to Pcbnew.
4
 */
5

6 7
#include <vector>

8 9 10 11 12 13 14 15
#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>
16
#include <gerbview_frame.h>
17 18
#include <class_gerber_draw_item.h>
#include <select_layers_to_pcb.h>
19 20
#include <build_version.h>
#include <wildcards_and_files_ext.h>
21

22 23 24 25
// Imported function
extern const wxString GetPCBDefaultLayerName( LAYER_NUM aLayerNumber );

#define TO_PCB_UNIT( x ) ( x / IU_PER_MM)
26 27

#define TRACK_TYPE  0
28

29
/* A helper class to export a Gerber set of files to Pcbnew
30
 */
31 32
class GBR_TO_PCB_EXPORTER
{
33 34 35 36 37 38 39 40
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
41
public:
42
    GBR_TO_PCB_EXPORTER( GERBVIEW_FRAME* aFrame, const wxString& aFileName );
43
    ~GBR_TO_PCB_EXPORTER();
44 45 46

    /**
     * Function ExportPcb
47
     * saves a board from a set of Gerber images.
48
     */
49
    bool    ExportPcb( LAYER_NUM* aLayerLookUpTable, int aCopperLayers );
50 51

private:
52 53 54 55 56 57
    /**
     * 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
     */
58
    void    export_non_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
59 60 61 62 63 64 65

    /**
     * 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
     */
66
    void    export_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
67 68 69 70 71 72

    /**
     * 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
     */
73
    void    export_flashed_copper_item( GERBER_DRAW_ITEM* aGbrItem );
74 75 76 77 78 79 80

    /**
     * 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
     */
81
    void    export_segline_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
82 83 84 85 86 87 88 89

    /**
     * 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
     */
90
    void    export_segarc_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer );
91 92 93

    /**
     * function writePcbLineItem
94 95 96 97 98 99 100 101 102 103
     * 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
104
     */
105 106
    void    writeCopperLineItem( wxPoint& aStart, wxPoint& aEnd,
                                 int aWidth, LAYER_NUM aLayer );
107 108 109 110 111

    /**
     * function writePcbHeader
     * Write a very basic header to the board file
     */
112
    void    writePcbHeader( LAYER_NUM* aLayerLookUpTable );
113 114
};

115

116
GBR_TO_PCB_EXPORTER::GBR_TO_PCB_EXPORTER( GERBVIEW_FRAME* aFrame, const wxString& aFileName )
117
{
118 119
    m_gerbview_frame    = aFrame;
    m_pcb_file_name     = aFileName;
120 121
}

122

123 124 125 126
GBR_TO_PCB_EXPORTER::~GBR_TO_PCB_EXPORTER()
{
}

127

128
/* Export data in Pcbnew format
129
 * remember Pcbnew uses a Y reversed axis, so we must negate all Y coordinates
130
 */
131
void GERBVIEW_FRAME::ExportDataInPcbnewFormat( wxCommandEvent& event )
132
{
133
    int layercount = 0;
134

135
    // Count the Gerber layers which are actually currently used
136
    for( LAYER_NUM ii = 0; ii < GERBER_DRAWLAYERS_COUNT; ++ii )
137
    {
138
        if( g_GERBER_List[ii] != NULL )
139
            layercount++;
140 141
    }

142
    if( layercount == 0 )
143
    {
144
        DisplayInfoMessage( this,
145
                            _( "None of the Gerber layers contain any data" ) );
146 147 148
        return;
    }

149 150
    wxString        fileName;
    wxString        path = wxGetCwd();;
151

152
    wxFileDialog    filedlg( this, _( "Board file name:" ),
153
                             path, fileName, PcbFileWildcard,
154
                             wxFD_SAVE );
155 156

    if( filedlg.ShowModal() == wxID_CANCEL )
157 158
        return;

159 160
    fileName = filedlg.GetPath();

161
    /* Install a dialog frame to choose the mapping
162
     * between gerber layers and Pcbnew layers
163
     */
164 165 166
    LAYERS_MAP_DIALOG* layerdlg = new LAYERS_MAP_DIALOG( this );
    int ok = layerdlg->ShowModal();
    layerdlg->Destroy();
167

168 169 170
    if( ok != wxID_OK )
        return;

171
    if( wxFileExists( fileName ) )
172
    {
173
        if( !IsOK( this, _( "OK to change the existing file ?" ) ) )
174 175
            return;
    }
176

177
    GBR_TO_PCB_EXPORTER gbr_exporter( this, fileName );
178

179 180
    gbr_exporter.ExportPcb( layerdlg->GetLayersLookUpTable(),
                            layerdlg->GetCopperLayersCount() );
181 182
}

183

184
bool GBR_TO_PCB_EXPORTER::ExportPcb( LAYER_NUM* aLayerLookUpTable, int aCopperLayers )
185
{
186 187
    LOCALE_IO   toggle;     // toggles on, then off, the C locale.

188
    m_fp = wxFopen( m_pcb_file_name, wxT( "wt" ) );
189

190 191 192
    if( m_fp == NULL )
    {
        wxString msg;
193
        msg.Printf( _( "Cannot create file '%s'" ), GetChars( m_pcb_file_name ) );
194 195
        DisplayError( m_gerbview_frame, msg );
        return false;
196 197
    }

198
    m_pcbCopperLayersCount = aCopperLayers;
199

200
    writePcbHeader( aLayerLookUpTable );
201

dickelbeck's avatar
dickelbeck committed
202
    // create an image of gerber data
203 204
    // First: non copper layers:
    GERBER_DRAW_ITEM* gerb_item = m_gerbview_frame->GetItemsList();
205
    int pcbCopperLayerMax = 31;
206

207
    for( ; gerb_item; gerb_item = gerb_item->Next() )
208
    {
209
        int layer = gerb_item->GetLayer();
210
        LAYER_NUM pcb_layer_number = aLayerLookUpTable[layer];
211

212
        if( !IsPcbLayer( pcb_layer_number ) )
213
            continue;
214

215
        if( pcb_layer_number > pcbCopperLayerMax )
216
            export_non_copper_item( gerb_item, pcb_layer_number );
217 218
    }

219 220
    // Copper layers
    gerb_item = m_gerbview_frame->GetItemsList();
221

222
    for( ; gerb_item; gerb_item = gerb_item->Next() )
223
    {
224
        int layer = gerb_item->GetLayer();
225
        LAYER_NUM pcb_layer_number = aLayerLookUpTable[layer];
226

227
        if( pcb_layer_number < 0 || pcb_layer_number > pcbCopperLayerMax )
228
            continue;
229

230 231
        else
            export_copper_item( gerb_item, pcb_layer_number );
232
    }
233

234
    fprintf( m_fp, ")\n" );
235 236 237

    fclose( m_fp );
    m_fp = NULL;
238 239 240
    return true;
}

241

242
void GBR_TO_PCB_EXPORTER::export_non_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
243
{
244
    bool isArc = false;
245

246 247 248
    double     angle   = 0;
    wxPoint seg_start   = aGbrItem->m_Start;
    wxPoint seg_end     = aGbrItem->m_End;
249 250 251

    if( aGbrItem->m_Shape == GBR_ARC )
    {
252 253 254 255
        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 ) );
256

257 258
        isArc       = true;
        angle       = RAD2DEG(b - a);
259
        seg_start   = aGbrItem->m_ArcCentre;
260

261
        // Ensure arc orientation is CCW
262
        if( angle < 0 )
263
            angle += 360.0;
264 265
    }

266
    // Reverse Y axis:
267 268
    NEGATE( seg_start.y );
    NEGATE( seg_end.y );
269
    writePcbLineItem( isArc, seg_start, seg_end, aGbrItem->m_Size.x, aLayer, angle );
270
}
271

272

273
void GBR_TO_PCB_EXPORTER::export_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
274 275 276
{
    switch( aGbrItem->m_Shape )
    {
277 278 279 280 281 282 283 284 285 286 287 288 289 290
    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;
291 292
    }
}
293

294

295
void GBR_TO_PCB_EXPORTER::export_segline_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
296
{
297 298 299 300
    wxPoint seg_start, seg_end;

    seg_start   = aGbrItem->m_Start;
    seg_end     = aGbrItem->m_End;
301 302

    // Reverse Y axis:
303 304
    NEGATE( seg_start.y );
    NEGATE( seg_end.y );
305

306 307 308 309 310 311 312 313 314 315 316 317 318 319
    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 ) ) );
320 321
}

322

323
void GBR_TO_PCB_EXPORTER::export_segarc_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer )
324
{
325 326 327 328 329 330 331
    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;
332 333

    /* Because Pcbnew does not know arcs in tracks,
334
     * approximate arc by segments (SEG_COUNT__CIRCLE segment per 360 deg)
335
     * The arc is drawn anticlockwise from the start point to the end point.
336
     */
337 338
    #define SEG_COUNT_CIRCLE    16
    #define DELTA_ANGLE         2 * M_PI / SEG_COUNT_CIRCLE
339

340 341 342
    // calculate the number of segments from a to b.
    // we want CNT_PER_360 segments fo a circle
    if( a > b )
343
        b += 2 * M_PI;
344

345
    wxPoint curr_start = start;
346 347 348
    wxPoint seg_start, seg_end;

    int     ii = 1;
349

350
    for( double rot = a; rot < (b - DELTA_ANGLE); rot += DELTA_ANGLE, ii++ )
351
    {
352
        seg_start = curr_start;
353
        wxPoint curr_end = start;
354
        RotatePoint( &curr_end, aGbrItem->m_ArcCentre,
355
                     -RAD2DECIDEG( DELTA_ANGLE * ii ) );
356
        seg_end = curr_end;
357
        // Reverse Y axis:
358 359
        NEGATE( seg_start.y );
        NEGATE( seg_end.y );
360
        writeCopperLineItem( seg_start, seg_end, aGbrItem->m_Size.x, aLayer );
361 362
        curr_start = curr_end;
    }
363

364 365
    if( end != curr_start )
    {
366 367
        seg_start   = curr_start;
        seg_end     = end;
368
        // Reverse Y axis:
369 370
        NEGATE( seg_start.y );
        NEGATE( seg_end.y );
371
        writeCopperLineItem( seg_start, seg_end, aGbrItem->m_Size.x, aLayer );
372 373 374 375 376 377 378 379 380
    }
}


/*
 * 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
 */
381
void GBR_TO_PCB_EXPORTER::export_flashed_copper_item( GERBER_DRAW_ITEM* aGbrItem )
382
{
383 384 385 386 387 388 389 390
    // 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 );
391

392 393
    wxPoint via_pos = aGbrItem->m_Start;
    int width   = (aGbrItem->m_Size.x + aGbrItem->m_Size.y) / 2;
394
    // Reverse Y axis:
395 396
    NEGATE( via_pos.y );

397 398 399 400 401
    // 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() );
402

403 404 405 406
    fprintf( m_fp, " (layers %s %s))\n",
                  TO_UTF8( GetPCBDefaultLayerName( F_Cu ) ),
                  TO_UTF8( GetPCBDefaultLayerName( B_Cu ) ) );
}
407

408 409 410 411
void GBR_TO_PCB_EXPORTER::writePcbHeader( LAYER_NUM* aLayerLookUpTable )
{
    fprintf( m_fp, "(kicad_pcb (version 4) (host Gerbview \"%s\")\n\n",
             TO_UTF8( GetBuildVersion() ) );
412

413 414
    // Write layers section
    fprintf( m_fp, "  (layers \n" );
415

416 417 418
    for( int ii = 0; ii < m_pcbCopperLayersCount; ii++ )
    {
        int id = ii;
419

420 421
        if( ii == m_pcbCopperLayersCount-1)
            id = B_Cu;
422

423 424
        fprintf( m_fp, "    (%d %s signal)\n", id, TO_UTF8( GetPCBDefaultLayerName( id ) ) );
    }
425

426 427 428 429
    for( int ii = B_Adhes; ii < LAYER_ID_COUNT; ii++ )
    {
        fprintf( m_fp, "    (%d %s user)\n", ii, TO_UTF8( GetPCBDefaultLayerName( ii ) ) );
    }
430

431 432
    fprintf( m_fp, "  )\n\n" );
}
433 434


435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
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()
                 );
    }
472
}