From 8ef962305ab13d0b3b194caa00ebe1611b9e2ca6 Mon Sep 17 00:00:00 2001 From: dickelbeck Date: Fri, 29 Feb 2008 06:49:34 +0000 Subject: [PATCH] more amazing free specctra software --- pcbnew/specctra.cpp | 13 ++-- pcbnew/specctra.h | 59 +++++++++------- pcbnew/specctra_export.cpp | 141 +++++++++++++++++++++++++------------ pcbnew/specctra_import.cpp | 39 +++++++--- 4 files changed, 170 insertions(+), 82 deletions(-) diff --git a/pcbnew/specctra.cpp b/pcbnew/specctra.cpp index 79b0e856ad..92de4aa17e 100644 --- a/pcbnew/specctra.cpp +++ b/pcbnew/specctra.cpp @@ -1251,10 +1251,10 @@ void SPECCTRA_DB::doPROPERTIES( PROPERTIES* growth ) throw( IOError ) if( tok != T_LEFT ) expecting( T_LEFT ); - needSYMBOL(); + needSYMBOLorNUMBER(); property.name = lexer->CurText(); - needSYMBOL(); + needSYMBOLorNUMBER(); property.value = lexer->CurText(); growth->push_back( property ); @@ -1848,7 +1848,7 @@ void SPECCTRA_DB::doPLACE( PLACE* growth ) throw( IOError ) case T_pn: if( growth->part_number.size() ) unexpected( tok ); - needSYMBOL(); + needSYMBOLorNUMBER(); growth->part_number = lexer->CurText(); needRIGHT(); break; @@ -2561,7 +2561,7 @@ void SPECCTRA_DB::doCLASS( CLASS* growth ) throw( IOError ) doTOPOLOGY( growth->topology ); break; - default: // handle all the circuit_descriptor here as strings + case T_circuit: // handle all the circuit_descriptor here as strings { std::string builder; int bracketNesting = 1; // we already saw the opening T_LEFT @@ -2607,6 +2607,10 @@ void SPECCTRA_DB::doCLASS( CLASS* growth ) throw( IOError ) if( tok==T_EOF ) unexpected( T_EOF ); } // scope bracket + break; + + default: + unexpected( lexer->CurText() ); } // switch tok = nextTok(); @@ -3486,6 +3490,7 @@ const char* OUTPUTFORMATTER::GetQuoteChar( const char* wrapee, const char* quote { static const char quoteThese[] = "\t ()" "%" // per Alfons of freerouting.net, he does not like this unquoted as of 1-Feb-2008 + "{}" // guessing that these are problems too ; // if the string to be wrapped (wrapee) has a delimiter in it, diff --git a/pcbnew/specctra.h b/pcbnew/specctra.h index ed8dbee1db..1ec720370e 100644 --- a/pcbnew/specctra.h +++ b/pcbnew/specctra.h @@ -225,8 +225,8 @@ struct POINT /** * Function FixNegativeZero * will change negative zero to positive zero in the IEEE floating point - * storage format. Basically turns off the sign bit if the mantiss and exponent - * would say the value is zero. + * storage format. Basically turns off the sign bit if the mantissa and + * exponent say the value is otherwise zero. */ void FixNegativeZero() { @@ -536,7 +536,7 @@ class RECTANGLE : public ELEM std::string layer_id; - POINT point0; + POINT point0; ///< one of two opposite corners POINT point1; public: @@ -882,11 +882,12 @@ class WINDOW : public ELEM friend class SPECCTRA_DB; protected: - /* shape holds one of these - PATH* path; ///< used for both path and polygon - RECTANGLE* rectangle; - CIRCLE* circle; - QARC* qarc; + /* ::= + [ | + | + | + | + ] */ ELEM* shape; @@ -2561,6 +2562,7 @@ public: out->Print( 0, "\n" ); } }; +typedef boost::ptr_vector COMP_ORDERS; class NET : public ELEM @@ -2677,6 +2679,7 @@ public: out->Print( nestLevel, ")\n" ); } }; +typedef boost::ptr_vector NETS; class TOPOLOGY : public ELEM @@ -2685,7 +2688,6 @@ class TOPOLOGY : public ELEM FROMTOS fromtos; - typedef boost::ptr_vector COMP_ORDERS; COMP_ORDERS comp_orders; public: @@ -2739,35 +2741,45 @@ public: void Format( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError ) { - const int RIGHTMARGIN = 80; - const char* quote = out->GetQuoteChar( class_id.c_str() ); int perLine = out->Print( nestLevel, "(%s %s%s%s", LEXER::GetTokenText( Type() ), quote, class_id.c_str(), quote ); + const int RIGHTMARGIN = 72; + for( STRINGS::iterator i=net_ids.begin(); i!=net_ids.end(); ++i ) { + const char* space = " "; if( perLine > RIGHTMARGIN ) { out->Print( 0, "\n" ); perLine = out->Print( nestLevel+1, "%s", "" ); + space = ""; // no space at first net_id of the line } quote = out->GetQuoteChar( i->c_str() ); - perLine += out->Print( 0, " %s%s%s", quote, i->c_str(), quote ); + perLine += out->Print( 0, "%s%s%s%s", space, quote, i->c_str(), quote ); } bool newLine = false; - if( circuit.size() || layer_rules.size() || topology ) + if( circuit.size() || rules || layer_rules.size() || topology ) { out->Print( 0, "\n" ); newLine = true; } - for( STRINGS::iterator i=circuit.begin(); i!=circuit.end(); ++i ) - out->Print( nestLevel+1, "%s\n", i->c_str() ); + if( circuit.size() ) + { + out->Print( nestLevel+1, "(circuit\n" ); + for( STRINGS::iterator i=circuit.begin(); i!=circuit.end(); ++i ) + out->Print( nestLevel+2, "%s\n", i->c_str() ); + out->Print( nestLevel+1, ")\n" ); + } + + if( rules ) + rules->Format( out, nestLevel+1 ); for( LAYER_RULES::iterator i=layer_rules.begin(); i!=layer_rules.end(); ++i ) i->Format( out, nestLevel+1 ); @@ -2778,16 +2790,14 @@ public: out->Print( newLine ? nestLevel : 0, ")\n" ); } }; +typedef boost::ptr_vector CLASSLIST; class NETWORK : public ELEM { friend class SPECCTRA_DB; - typedef boost::ptr_vector NETS; NETS nets; - - typedef boost::ptr_vector CLASSLIST; CLASSLIST classes; @@ -3637,6 +3647,7 @@ class SPECCTRA_DB : public OUTPUTFORMATTER */ int findLayerName( const std::string& aLayerName ) const; + /** * Function nextTok * returns the next token from the lexer. @@ -3843,12 +3854,6 @@ class SPECCTRA_DB : public OUTPUTFORMATTER nets.clear(); } - /** - * Function flipMODULEs - * flips the modules which are on the back side of the board to the front. - */ - void flipMODULEs( BOARD* aBoard ); - //---------------------------------------------------------- /** @@ -4002,6 +4007,12 @@ public: */ void ExportSESSION( wxString aFilename ); + /** + * Function FlipMODULEs + * flips the modules which are on the back side of the board to the front. + */ + void FlipMODULEs( BOARD* aBoard ); + /** * Function RevertMODULEs * flips the modules which were on the back side of the board back to the back. diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index 9ecec352b3..88829775d8 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -84,6 +84,11 @@ void WinEDA_PcbFrame::ExportToSpecctra( wxCommandEvent& event ) setlocale( LC_NUMERIC, "C" ); // Switch the locale to standard C + // DSN Images (=Kicad MODULES and pads) must be presented from the + // top view. So we temporarily flip any modules which are on the back + // side of the board to the front, and record this in the MODULE's flag field. + db.FlipMODULEs( m_Pcb ); + try { db.FromBOARD( m_Pcb ); @@ -102,8 +107,7 @@ void WinEDA_PcbFrame::ExportToSpecctra( wxCommandEvent& event ) setlocale( LC_NUMERIC, "" ); // revert to the current locale - // this is called in FromBOARD() too, but if it throws an exception, that call - // does not happen, so call it again just in case here. + // done assuredly, even if an exception was thrown and caught. db.RevertMODULEs( m_Pcb ); @@ -538,7 +542,7 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad ) } -/// data type used to ensure unique-ness of pin names +/// data type used to ensure unique-ness of pin names, holding (wxString and int) typedef std::map PINMAP; @@ -672,19 +676,26 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, MODULE* aModule ) path->SetAperture( scale( graphic->m_Width ) ); path->SetLayerId( "signal" ); - double radius = hypot( scale( graphic->m_Start.x - graphic->m_End.x ), - scale( graphic->m_Start.y - graphic->m_End.y ) ); + // Do the math using Kicad units, that way we stay out of the + // scientific notation range of floating point numbers in the + // DSN file. We do not parse scientific notation in our own + // lexer/beautifier, and the spec is not clear that this is + // required. Fixed point floats are all that should be needed. - POINT offset = mapPt( graphic->m_Start0 ); + double radius = hypot( double( graphic->m_Start.x - graphic->m_End.x ), + double( graphic->m_Start.y - graphic->m_End.y ) ); // better if evenly divisible into 360 const int DEGREE_INTERVAL = 18; // 18 means 20 line segments for( double radians = 0.0; radians < 2*M_PI; radians += DEGREE_INTERVAL * M_PI / 180.0 ) { - POINT point( radius*cos( radians ), radius*sin( radians ) ); - point += offset; - path->AppendPoint( point ); + wxPoint point( int( radius * cos( radians ) ), + int( radius * sin( radians ) ) ); + + point += graphic->m_Start0; // an offset + + path->AppendPoint( mapPt(point) ); } } break; @@ -749,8 +760,7 @@ PADSTACK* SPECCTRA_DB::makeVia( const SEGVIA* aVia ) return makeVia( aVia->m_Width, aVia->GetDrillValue(), topLayer, botLayer ); } - -typedef std::set STRINGSET; +typedef std::set STRINGSET; typedef std::pair STRINGSET_PAIR; @@ -785,7 +795,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) } // if we cannot insert OK, that means the reference has been seen before. - STRINGSET_PAIR refpair = refs.insert( module->GetReference() ); + STRINGSET_PAIR refpair = refs.insert( CONV_TO_UTF8( module->GetReference() ) ); if( !refpair.second ) // insert failed { ThrowIOError( _("Multiple components have identical reference IDs of \"%s\"."), @@ -797,11 +807,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) if( !pcb ) pcb = SPECCTRA_DB::MakePCB(); - // DSN Images (=Kicad MODULES and pads) must be presented from the - // top view. So we temporarily flip any modules which are on the back - // side of the board to the front, and record this in the MODULE's flag field. - flipMODULEs( aBoard ); - //---------------------------------------------------- { // specctra wants top physical layer first, then going down to the @@ -938,10 +943,11 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) int curTrackWidth = aBoard->m_BoardSettings->m_CurrentTrackWidth; int curTrackClear = aBoard->m_BoardSettings->m_TrackClearence; - // The +5 is to give freerouter a little extra room, this is 0.5 mils. + // The +1 is to give freerouter a little extra room, this is 0.1 mils. // If we export without this, then on import freerouter violates our - // DRC checks with track to via spacing. - double clearance = scale(curTrackClear+5); + // DRC checks with track to via spacing, although this could be a + // result of > testing vs. >= testing in PCBNEW's DRC. + double clearance = scale(curTrackClear+1); STRINGS& rules = pcb->structure->rules->rules; @@ -996,15 +1002,14 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) plane->name = CONV_TO_UTF8( item->m_Netname ); - wxString layerName = aBoard->GetLayerName( item->GetLayer() ); - polygon->layer_id = CONV_TO_UTF8( layerName ); + polygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; int count = item->m_Poly->corner.size(); for( int j=0; jm_Poly->corner[j].x, item->m_Poly->corner[j].y ); - polygon->points.push_back( mapPt(point) ); + polygon->AppendPoint( mapPt(point) ); } pcb->structure->planes.push_back( plane ); @@ -1015,6 +1020,9 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) //---------------------------- { + PIN_REF empty( pcb->network ); + std::string componentId; + // find the highest numbered netCode within the board. int highestNetCode = -1; for( EQUIPOT* equipot = aBoard->m_Equipots; equipot; equipot = equipot->Next() ) @@ -1046,15 +1054,17 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) IMAGE* image = makeIMAGE( aBoard, module ); + componentId = CONV_TO_UTF8( module->GetReference() ); + // create a net list entry for all the actual pins in the image // for the current module. location of this code is critical // because we fabricated some pin names to ensure unique-ness - // of pin names within a module, do not move this code. The + // of pin names within a module, do not move this code because + // the life of this 'IMAGE* image' is not necessarily long. The // exported netlist will have some fabricated pin names in it. // If you don't like fabricated pin names, then make sure all pads // within your MODULEs are uniquely named! - PIN_REF empty( pcb->network ); for( unsigned p=0; ppins.size(); ++p ) { PIN* pin = &image->pins[p]; @@ -1068,7 +1078,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) PIN_REF& pin_ref = net->pins.back(); - pin_ref.component_id = CONV_TO_UTF8( module->GetReference() ); + pin_ref.component_id = componentId; pin_ref.pin_id = pin->pin_id; } } @@ -1090,7 +1100,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) place->SetRotation( module->m_Orient/10.0 ); place->SetVertex( mapPt( module->m_Pos ) ); - place->component_id = CONV_TO_UTF8( module->GetReference() ); + place->component_id = componentId; place->part_number = CONV_TO_UTF8( module->GetValue() ); // module is flipped from bottom side, set side to T_back @@ -1115,21 +1125,11 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) pcb->library->AddPadstack( padstack ); } - D(std::string component = "U1";) - // copy our SPECCTRA_DB::nets to the pcb->network for( unsigned n=1; npins.size() - -#if defined(DEBUG) - // experimenting with exporting a subset of all the nets - // and with incremental, iterative autorouting. - && net->FindPIN_REF( component ) >= 0 -#endif - - ) + if( net->pins.size() ) { // give ownership to pcb->network pcb->network->nets.push_back( net ); @@ -1146,7 +1146,10 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) // Next we add the via's which may be used. int defaultViaSize = aBoard->m_BoardSettings->m_CurrentViaSize; + + /* I need at least one via for the (class...) scope below if( defaultViaSize ) + */ { PADSTACK* padstack = makeVia( defaultViaSize, g_DesignSettings.m_ViaDrill, 0, aBoard->GetCopperLayerCount()-1 ); @@ -1196,10 +1199,12 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) { TRACK* track = (TRACK*) items[i]; - if( track->GetNet() == 0 ) + int netcode = track->GetNet(); + + if( netcode == 0 ) continue; - if( old_netcode != track->GetNet() + if( old_netcode != netcode || old_width != track->m_Width || old_layer != track->GetLayer() || (path && path->points.back() != mapPt(track->m_Start) ) @@ -1208,10 +1213,10 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) old_width = track->m_Width; old_layer = track->GetLayer(); - if( old_netcode != track->GetNet() ) + if( old_netcode != netcode ) { - old_netcode = track->GetNet(); - EQUIPOT* equipot = aBoard->FindNet( track->GetNet() ); + old_netcode = netcode; + EQUIPOT* equipot = aBoard->FindNet( netcode ); wxASSERT( equipot ); netname = CONV_TO_UTF8( equipot->m_Netname ); } @@ -1290,21 +1295,65 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) if( viaNdx != -1 ) { +#if 1 for( ; viaNdx < (int)padstacks.size(); ++viaNdx ) { vias->AppendVia( padstacks[viaNdx].padstack_id.c_str() ); } +#else + // output only the default via. Then use class_descriptors to + // override the default. No, this causes free router not to + // output the unmentioned vias into the session file. + vias->AppendVia( padstacks[viaNdx].padstack_id.c_str() ); +#endif } } - //--------------------------------------------------- + //------- + { + char text[80]; + STRINGSET netIds; // sort the net names in here - RevertMODULEs( aBoard ); + CLASS* clazz = new CLASS( pcb->network ); + pcb->network->classes.push_back( clazz ); + + // freerouter creates a class named 'default' anyway, and if we + // try and use that, we end up with two 'default' via rules so use + // something else as the name of our default class. Someday we may support + // additional classes. Until then the user can text edit the exported + // DSN file and use this class as a template, copying it and giving the + // copy a different class_id and splitting out some of the nets. + clazz->class_id = "kicad_default"; + + // Insert all the net_ids into the set. They are unique, but even if + // they were not the duplicated name is not our error, but the BOARD's. + // A duplicate would be removed here. + NETS& nets = pcb->network->nets; + for( NETS::iterator i=nets.begin(); i!=nets.end(); ++i ) + netIds.insert( i->net_id ); + + // netIds is now sorted, put them into clazz->net_ids + for( STRINGSET::iterator i=netIds.begin(); i!=netIds.end(); ++i ) + clazz->net_ids.push_back( *i ); + + // output the via and track dimensions, the whole reason for this scope. + int curTrackWidth = aBoard->m_BoardSettings->m_CurrentTrackWidth; + + clazz->rules = new RULE( clazz, T_rule ); + + sprintf( text, "(width %.6g)", scale( curTrackWidth ) ); + clazz->rules->rules.push_back( text ); + + int viaNdx = pcb->library->via_start_index; + + sprintf( text, "(use_via %s)", pcb->library->padstacks[viaNdx].padstack_id.c_str() ); + clazz->circuit.push_back( text ); + } } -void SPECCTRA_DB::flipMODULEs( BOARD* aBoard ) +void SPECCTRA_DB::FlipMODULEs( BOARD* aBoard ) { for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { diff --git a/pcbnew/specctra_import.cpp b/pcbnew/specctra_import.cpp index 687ed579b6..92c0b43928 100644 --- a/pcbnew/specctra_import.cpp +++ b/pcbnew/specctra_import.cpp @@ -167,7 +167,7 @@ static int scale( double distance, UNIT_RES* aResolution ) static wxPoint mapPt( const POINT& aPoint, UNIT_RES* aResolution ) { wxPoint ret( scale( aPoint.x, aResolution ), - -scale( aPoint.y, aResolution )); // negate y + -scale( aPoint.y, aResolution ) ); // negate y return ret; } @@ -347,15 +347,18 @@ void SPECCTRA_DB::FromSESSION( BOARD* aBoard ) throw( IOError ) if( !session->route->library ) ThrowIOError( _("Session file is missing the \"library_out\" section") ); +#if 1 // delete all the old tracks and vias aBoard->m_Track->DeleteStructList(); aBoard->m_Track = NULL; aBoard->m_NbSegmTrack = 0; +#endif aBoard->DeleteMARKERs(); buildLayerMaps( aBoard ); +#if 1 // Walk the PLACEMENT object's COMPONENTs list, and for each PLACE within // each COMPONENT, reposition and re-orient each component and put on // correct side of the board. @@ -413,6 +416,7 @@ void SPECCTRA_DB::FromSESSION( BOARD* aBoard ) throw( IOError ) } } } +#endif routeResolution = session->route->GetUnits(); @@ -430,8 +434,10 @@ void SPECCTRA_DB::FromSESSION( BOARD* aBoard ) throw( IOError ) EQUIPOT* equipot = aBoard->FindNet( netName ); if( equipot ) netCode = equipot->GetNet(); - - // else netCode remains 0 + else // else netCode remains 0 + { + // int breakhere = 1; + } } WIRES& wires = net->wires; @@ -442,19 +448,36 @@ void SPECCTRA_DB::FromSESSION( BOARD* aBoard ) throw( IOError ) if( shape != T_path ) { + /* shape == T_polygon is expected from freerouter if you have + a zone on a non "power" type layer, i.e. a T_signal layer + and the design does a round trip back in as session here. + We kept our own zones in the BOARD, so ignore this so called + 'wire'. + wxString netId = CONV_FROM_UTF8( wire->net_id.c_str() ); ThrowIOError( _("Unsupported wire shape: \"%s\" for net: \"%s\""), LEXER::GetTokenString(shape).GetData(), netId.GetData() ); + */ } - - PATH* path = (PATH*) wire->shape; - for( unsigned pt=0; ptpoints.size()-1; ++pt ) + else { - TRACK* track = makeTRACK( path, pt, netCode ); - aBoard->Add( track ); + PATH* path = (PATH*) wire->shape; + for( unsigned pt=0; ptpoints.size()-1; ++pt ) + { + /* a debugging aid, may come in handy + if( path->points[pt].x == 547800 + && path->points[pt].y == -380250 ) + { + int breakhere = 1; + } + */ + + TRACK* track = makeTRACK( path, pt, netCode ); + aBoard->Add( track ); + } } }