/* Creates the footprint shape list. * Since module shape is customizable after the placement we cannot share them; * instead we opt for the one-module-one-shape-one-component-one-device approach */ static void CreateShapesSection( FILE* aFile, BOARD* aPcb ) { MODULE* module; D_PAD* pad; const char* layer; wxString pinname; const char* mirror = "0"; fputs( "$SHAPES\n", aFile ); const LSET all_cu = LSET::AllCuMask(); for( module = aPcb->m_Modules; module; module = module->Next() ) { FootprintWriteShape( aFile, module ); for( pad = module->Pads(); pad; pad = pad->Next() ) { /* Funny thing: GenCAD requires the pad side even if you use * padstacks (which are theorically optional but gerbtools *requires* them). Now the trouble thing is that 'BOTTOM' * is interpreted by someone as a padstack flip even * if the spec explicitly says it's not... */ layer = "ALL"; if( ( pad->GetLayerSet() & all_cu ) == LSET( B_Cu ) ) { layer = module->GetFlag() ? "TOP" : "BOTTOM"; } else if( ( pad->GetLayerSet() & all_cu ) == LSET( F_Cu ) ) { layer = module->GetFlag() ? "BOTTOM" : "TOP"; } pad->StringPadName( pinname ); if( pinname.IsEmpty() ) pinname = wxT( "none" ); double orient = pad->GetOrientation() - module->GetOrientation(); NORMALIZE_ANGLE_POS( orient ); // Bottom side modules use the flipped padstack fprintf( aFile, (module->GetFlag()) ? "PIN %s PAD%dF %g %g %s %g %s\n" : "PIN %s PAD%d %g %g %s %g %s\n", TO_UTF8( pinname ), pad->GetSubRatsnest(), pad->GetPos0().x / SCALE_FACTOR, -pad->GetPos0().y / SCALE_FACTOR, layer, orient / 10.0, mirror ); } } fputs( "$ENDSHAPES\n\n", aFile ); }
/** * Function PickModule * find the "best" module place * The criteria are: * - Maximum ratsnest with modules already placed * - Max size, and number of pads max */ static MODULE* PickModule( PCB_EDIT_FRAME* pcbframe, wxDC* DC ) { MODULE* Module; std::vector <MODULE*> moduleList; // Build sorted footprints list (sort by decreasing size ) Module = pcbframe->GetBoard()->m_Modules; for( ; Module != NULL; Module = Module->Next() ) { Module->CalculateBoundingBox(); moduleList.push_back( Module ); } sort( moduleList.begin(), moduleList.end(), Tri_PlaceModules ); for( unsigned ii = 0; ii < moduleList.size(); ii++ ) { Module = moduleList[ii]; Module->SetFlag( 0 ); if( !Module->NeedsPlaced() ) continue; pcbframe->GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; pcbframe->SetMsgPanel( Module ); pcbframe->build_ratsnest_module( Module ); // Calculate external ratsnest. for( unsigned ii = 0; ii < pcbframe->GetBoard()->m_LocalRatsnest.size(); ii++ ) { if( ( pcbframe->GetBoard()->m_LocalRatsnest[ii].m_Status & LOCAL_RATSNEST_ITEM ) == 0 ) Module->IncrementFlag(); } } pcbframe->GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; sort( moduleList.begin(), moduleList.end(), sortFootprintsByRatsnestSize ); // Search for "best" module. MODULE* bestModule = NULL; MODULE* altModule = NULL; for( unsigned ii = 0; ii < moduleList.size(); ii++ ) { Module = moduleList[ii]; if( !Module->NeedsPlaced() ) continue; altModule = Module; if( Module->GetFlag() == 0 ) continue; bestModule = Module; break; } if( bestModule ) return bestModule; else return altModule; }
/* Creates the section $COMPONENTS (Footprints placement) * Bottom side components are difficult to handle: shapes must be mirrored or * flipped, silk layers need to be handled correctly and so on. Also it seems * that *noone* follows the specs... */ static void CreateComponentsSection( FILE* aFile, BOARD* aPcb ) { fputs( "$COMPONENTS\n", aFile ); int cu_count = aPcb->GetCopperLayerCount(); for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { const char* mirror; const char* flip; double fp_orient = module->GetOrientation(); if( module->GetFlag() ) { mirror = "0"; flip = "FLIP"; NEGATE_AND_NORMALIZE_ANGLE_POS( fp_orient ); } else { mirror = "0"; flip = "0"; } fprintf( aFile, "\nCOMPONENT %s\n", TO_UTF8( module->GetReference() ) ); fprintf( aFile, "DEVICE %s_%s\n", TO_UTF8( module->GetReference() ), TO_UTF8( module->GetValue() ) ); fprintf( aFile, "PLACE %g %g\n", MapXTo( module->GetPosition().x ), MapYTo( module->GetPosition().y ) ); fprintf( aFile, "LAYER %s\n", (module->GetFlag()) ? "BOTTOM" : "TOP" ); fprintf( aFile, "ROTATION %g\n", fp_orient / 10.0 ); fprintf( aFile, "SHAPE %s %s %s\n", TO_UTF8( module->GetReference() ), mirror, flip ); // Text on silk layer: RefDes and value (are they actually useful?) TEXTE_MODULE *textmod = &module->Reference(); for( int ii = 0; ii < 2; ii++ ) { double txt_orient = textmod->GetOrientation(); std::string layer = GenCADLayerName( cu_count, module->GetFlag() ? B_SilkS : F_SilkS ); fprintf( aFile, "TEXT %g %g %g %g %s %s \"%s\"", textmod->GetPos0().x / SCALE_FACTOR, -textmod->GetPos0().y / SCALE_FACTOR, textmod->GetSize().x / SCALE_FACTOR, txt_orient / 10.0, mirror, layer.c_str(), TO_UTF8( textmod->GetText() ) ); // Please note, the width is approx fprintf( aFile, " 0 0 %g %g\n", ( textmod->GetSize().x * textmod->GetLength() ) / SCALE_FACTOR, textmod->GetSize().y / SCALE_FACTOR ); textmod = &module->Value(); // Dirty trick for the second iteration } // The SHEET is a 'generic description' for referencing the component fprintf( aFile, "SHEET \"RefDes: %s, Value: %s\"\n", TO_UTF8( module->GetReference() ), TO_UTF8( module->GetValue() ) ); } fputs( "$ENDCOMPONENTS\n\n", aFile ); }
/* Driver function: processing starts here */ void PCB_EDIT_FRAME::ExportToGenCAD( wxCommandEvent& aEvent ) { wxFileName fn = GetBoard()->GetFileName(); FILE* file; wxString ext = wxT( "cad" ); wxString wildcard = _( "GenCAD 1.4 board files (.cad)|*.cad" ); fn.SetExt( ext ); wxString pro_dir = wxPathOnly( Prj().GetProjectFullName() ); wxFileDialog dlg( this, _( "Save GenCAD Board File" ), pro_dir, fn.GetFullName(), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if( dlg.ShowModal() == wxID_CANCEL ) return; if( ( file = wxFopen( dlg.GetPath(), wxT( "wt" ) ) ) == NULL ) { wxString msg; msg.Printf( _( "Unable to create <%s>" ), GetChars( dlg.GetPath() ) ); DisplayError( this, msg ); return; } SetLocaleTo_C_standard(); // No pesky decimal separators in gencad // Update some board data, to ensure a reliable gencad export GetBoard()->ComputeBoundingBox(); // Save the auxiliary origin for the rest of the module GencadOffsetX = GetAuxOrigin().x; GencadOffsetY = GetAuxOrigin().y; // No idea on *why* this should be needed... maybe to fix net names? Compile_Ratsnest( NULL, true ); /* Temporary modification of footprints that are flipped (i.e. on bottom * layer) to convert them to non flipped footprints. * This is necessary to easily export shapes to GenCAD, * that are given as normal orientation (non flipped, rotation = 0)) * these changes will be undone later */ BOARD* pcb = GetBoard(); MODULE* module; for( module = pcb->m_Modules; module; module = module->Next() ) { module->SetFlag( 0 ); if( module->GetLayer() == B_Cu ) { module->Flip( module->GetPosition() ); module->SetFlag( 1 ); } } /* Gencad has some mandatory and some optional sections: some importer * need the padstack section (which is optional) anyway. Also the * order of the section *is* important */ CreateHeaderInfoData( file, this ); // Gencad header CreateBoardSection( file, pcb ); // Board perimeter CreatePadsShapesSection( file, pcb ); // Pads and padstacks CreateArtworksSection( file ); // Empty but mandatory /* Gencad splits a component info in shape, component and device. * We don't do any sharing (it would be difficult since each module is * customizable after placement) */ CreateShapesSection( file, pcb ); CreateComponentsSection( file, pcb ); CreateDevicesSection( file, pcb ); // In a similar way the netlist is split in net, track and route CreateSignalsSection( file, pcb ); CreateTracksInfoData( file, pcb ); CreateRoutesSection( file, pcb ); fclose( file ); SetLocaleTo_Default(); // revert to the current locale // Undo the footprints modifications (flipped footprints) for( module = pcb->m_Modules; module; module = module->Next() ) { if( module->GetFlag() ) { module->Flip( module->GetPosition() ); module->SetFlag( 0 ); } } }