/********************************************************/ /**** Routine de lecture et visu d'un fichier GERBER ****/ /********************************************************/ #include "fctsys.h" #include "common.h" #include "gerbview.h" #include "pcbplot.h" #include "protos.h" #define IsNumber(x) ( ( ((x) >= '0') && ((x) <='9') ) ||\ ((x) == '-') || ((x) == '+') || ((x) == '.')) /* Format Gerber : NOTES : Fonctions preparatoires: Gn = G01 interpolation lineaire ( trace de droites ) G02,G20,G21 Interpolation circulaire , sens trigo < 0 G03,G30,G31 Interpolation circulaire , sens trigo > 0 G04 commentaire G06 Interpolation parabolique G07 Interpolation cubique G10 interpolation lineaire ( echelle 10x ) G11 interpolation lineaire ( echelle 0.1x ) G12 interpolation lineaire ( echelle 0.01x ) G52 plot symbole reference par Dnn code G53 plot symbole reference par Dnn ; symbole tourne de -90 degres G54 Selection d'outil G55 Mode exposition photo G56 plot symbole reference par Dnn A code G57 affiche le symbole reference sur la console G58 plot et affiche le symbole reference sur la console G60 interpolation lineaire ( echelle 100x ) G70 Unites = Inches G71 Unites = Millimetres G74 supprime interpolation circulaire sur 360 degre, revient a G01 G75 Active interpolation circulaire sur 360 degre G90 Mode Coordonnees absolues G91 Mode Coordonnees Relatives Coordonnees X,Y X,Y sont suivies de + ou - et de m+n chiffres (non separes) m = partie entiere n = partie apres la virgule formats classiques : m = 2, n = 3 (format 2.3) m = 3, n = 4 (format 3.4) ex: G__ X00345Y-06123 D__* Outils et D_CODES numero d'outil ( identification des formes ) 1 a 99 (classique) 1 a 999 D_CODES: D01 ... D9 = codes d'action: D01 = activation de lumiere (baisser de plume) lors du déplacement D02 = extinction de lumiere (lever de plume) lors du déplacement D03 = Flash D09 = VAPE Flash D51 = precede par G54 -> Select VAPE D10 ... D255 = Indentification d'outils ( d'ouvertures ) Ne sont pas tj dans l'ordre ( voir tableau dans PCBPLOT.H) */ // Type d'action du phototraceur: #define GERB_ACTIVE_DRAW 1 // activation de lumiere ( baisser de plume) #define GERB_STOP_DRAW 2 // extinction de lumiere ( lever de plume) #define GERB_FLASH 3 // Flash #define NEGATE(nb) (nb) = -(nb) /* Variables locales : */ static wxPoint LastPosition; /* Routines Locales */ static void Append_1_Line_GERBER(int Dcode_index, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & startpoint,const wxPoint & endpoint, int largeur); static void Append_1_Flash_GERBER(int Dcode_index, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & pos,const wxSize & size, int forme); static void Append_1_Flash_ROND_GERBER(int Dcode_index, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & pos, int diametre); static void Append_1_SEG_ARC_GERBER(int Dcode_index, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & startpoint, const wxPoint & endpoint, const wxPoint & rel_center, int largeur, bool trigo_sens, bool multiquadrant); /****************************************************************/ static void Append_1_Flash_ROND_GERBER(int Dcode_tool, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & pos,int diametre) /****************************************************************/ /* Trace 1 flash ROND en position pos */ { TRACK * track; track = new TRACK(frame->m_Pcb); track->Insert(frame->m_Pcb, NULL); track->SetLayer( frame->GetScreen()->m_Active_Layer ); track->m_Width = diametre ; track->m_Start = track->m_End = pos; NEGATE(track->m_Start.y); NEGATE(track->m_End.y); track->m_NetCode = Dcode_tool; track->m_Shape = S_SPOT_CIRCLE; Trace_Segment(frame->DrawPanel, DC, track, GR_OR) ; } /**********************************************************************/ static void Append_1_Flash_GERBER(int Dcode_index, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & pos,const wxSize & size,int forme) /*********************************************************************/ /* Trace 1 flash rectangulaire ou ovale vertical ou horizontal donne par son centre et ses dimensions X et Y */ { TRACK * track; int width, len; width = MIN( size.x, size.y ); len = MAX( size.x, size.y ) - width; track = new TRACK(frame->m_Pcb); track->Insert(frame->m_Pcb, NULL); track->SetLayer( frame->GetScreen()->m_Active_Layer ); track->m_Width = width; track->m_Start = track->m_End = pos; NEGATE(track->m_Start.y); NEGATE(track->m_End.y); track->m_NetCode = Dcode_index; if ( forme == OVALE ) track->m_Shape = S_SPOT_OVALE; else track->m_Shape = S_SPOT_RECT; // donc rectangle ou carré len >>= 1; if ( size.x > size.y ) // ovale / rect horizontal { track->m_Start.x -= len ; track->m_End.x += len; } else // ovale / rect vertical { track->m_Start.y -= len; track->m_End.y += len; } Trace_Segment(frame->DrawPanel, DC, track, GR_OR) ; } /******************************************************************/ static void Append_1_Line_GERBER(int Dcode_index, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & startpoint, const wxPoint & endpoint, int largeur) /********************************************************************/ { TRACK * track; track = new TRACK( frame->m_Pcb ); track->Insert(frame->m_Pcb, NULL); track->SetLayer( frame->GetScreen()->m_Active_Layer ); track->m_Width = largeur ; track->m_Start = startpoint; NEGATE(track->m_Start.y); track->m_End = endpoint; NEGATE(track->m_End.y); track->m_NetCode = Dcode_index; Trace_Segment(frame->DrawPanel, DC, track,GR_OR) ; } /*****************************************************************/ static void Append_1_SEG_ARC_GERBER(int Dcode_index, WinEDA_GerberFrame * frame, wxDC * DC, const wxPoint & startpoint, const wxPoint & endpoint, const wxPoint & rel_center, int largeur, bool trigo_sens, bool multiquadrant) /*****************************************************************/ /* creation d'un arc: si multiquadrant == TRUE arc de 0 a 360 degres et rel_center est la coordonnée du centre relativement au startpoint si multiquadrant == FALSE arc de 0 à 90 entierement contenu dans le meme quadrant et rel_center est la coordonnée du centre relativement au startpoint, mais en VALEUR ABSOLUE et le signe des valeurs x et y de rel_center doit etre deduit de cette limite de 90 degres */ { TRACK * track; wxPoint center, delta; track = new TRACK( frame->m_Pcb ); track->Insert(frame->m_Pcb, NULL); track->m_Shape = S_ARC; track->SetLayer( frame->GetScreen()->m_Active_Layer ); track->m_Width = largeur ; if ( multiquadrant ) { center.x = startpoint.x + rel_center.x; center.y = startpoint.y - rel_center.y; if ( !trigo_sens ) { track->m_Start = startpoint; track->m_End = endpoint; } else { track->m_Start = endpoint; track->m_End = startpoint; } } else { center = rel_center; delta.x = endpoint.x - startpoint.x; delta.y = endpoint.y - startpoint.y; // il faut corriger de signe de rel_center.x et rel_center.y // selon le quadrant ou on se trouve if ( (delta.x >= 0) && (delta.y >= 0) ) // 1er quadrant { center.x = - center.x; } else if ( (delta.x < 0) && (delta.y >= 0) ) // 2eme quadrant { center.x = - center.x; center.y = - center.y; } else if ( (delta.x < 0) && (delta.y < 0) ) // 3eme quadrant { center.y = - center.y; } else // 4eme qadrant: les 2 coord sont >= 0! { } center.x += startpoint.x; center.y = startpoint.y + center.y ; if ( trigo_sens ) { track->m_Start = startpoint; track->m_End = endpoint; } else { track->m_Start = endpoint; track->m_End = startpoint; } } track->m_NetCode = Dcode_index; track->m_Param = center.x; track->m_Sous_Netcode = center.y; NEGATE(track->m_Start.y); NEGATE(track->m_End.y); NEGATE(track->m_Sous_Netcode); Trace_Segment(frame->DrawPanel, DC, track,GR_OR); } /**************************************************/ /* Routines utilisées en lecture de ficher gerber */ /**************************************************/ /* ces routines lisent la chaine de texte pointée par Text. Apres appel, Text pointe le debut de la sequence non lue */ /***********************************************/ wxPoint GERBER_Descr::ReadXYCoord(char * &Text) /***********************************************/ /* Retourne la coord courante pointee par Text (XnnnnYmmmm) */ { wxPoint pos = m_CurrentPos; int type_coord = 0, current_coord, nbchar; bool is_float = FALSE; char * text; char line[256]; if ( m_Relative ) pos.x = pos.y = 0; else pos = m_CurrentPos; if ( Text == NULL ) return pos; text = line; while ( * Text ) { if ( (* Text == 'X') || (* Text == 'Y') ) { type_coord = * Text; Text++; text = line; nbchar = 0; while( IsNumber(*Text) ) { if ( *Text == '.' ) is_float = TRUE; *(text++) = *(Text++); if ( (*Text >= '0') && (*Text <='9') ) nbchar++; } *text = 0; if ( is_float ) { if ( m_GerbMetric ) current_coord = (int) round(atof(line) / 0.00254); else current_coord = (int) round(atof(line) * PCB_INTERNAL_UNIT); } else { int fmt_scale = (type_coord == 'X') ? m_FmtScale.x : m_FmtScale.y; if ( m_NoTrailingZeros ) { int min_digit = (type_coord == 'X') ? m_FmtLen.x : m_FmtLen.y; while (nbchar < min_digit) { *(text++) ='0'; nbchar++; } *text=0; } current_coord = atoi(line); double real_scale = 1.0; switch( fmt_scale) { case 0: real_scale = 10000.0; break; case 1: real_scale = 1000.0; break; case 2: real_scale = 100.0; break; case 3: real_scale = 10.0; break; case 4: break; case 5: real_scale = 0.1; break; case 6: real_scale = 0.01; break; case 7: real_scale = 0.001; break; case 8: real_scale = 0.0001; break; case 9: real_scale = 0.00001; break; } if ( m_GerbMetric ) real_scale = real_scale / 25.4; current_coord = (int) round(current_coord * real_scale); } if ( type_coord == 'X' ) pos.x = current_coord; else if ( type_coord == 'Y' ) pos.y = current_coord; continue; } else break; } if ( m_Relative ) { pos.x += m_CurrentPos.x; pos.y += m_CurrentPos.y; } m_CurrentPos = pos; return pos; } /************************************************/ wxPoint GERBER_Descr::ReadIJCoord(char * &Text) /************************************************/ /* Retourne la coord type InnJnn courante pointee par Text (InnnnJmmmm) Ces coordonnées sont relatives, donc si une coord est absente, sa valeur par defaut est 0 */ { wxPoint pos(0,0); int type_coord = 0, current_coord, nbchar; bool is_float = FALSE; char * text; char line[256]; if ( Text == NULL ) return pos; text = line; while ( * Text ) { if ( (* Text == 'I') || (* Text == 'J') ) { type_coord = * Text; Text++; text = line; nbchar = 0; while( IsNumber(*Text) ) { if ( *Text == '.' ) is_float = TRUE; *(text++) = *(Text++); if ( (*Text >= '0') && (*Text <='9') ) nbchar++; } *text = 0; if ( is_float ) { if ( m_GerbMetric ) current_coord = (int) round(atof(line) / 0.00254); else current_coord = (int) round(atof(line) * PCB_INTERNAL_UNIT); } else { int fmt_scale = (type_coord == 'I') ? m_FmtScale.x : m_FmtScale.y; if ( m_NoTrailingZeros ) { int min_digit = (type_coord == 'I') ? m_FmtLen.x : m_FmtLen.y; while (nbchar < min_digit) { *(text++) ='0'; nbchar++; } *text=0; } current_coord = atoi(line); double real_scale = 1.0; switch( fmt_scale) { case 0: real_scale = 10000.0; break; case 1: real_scale = 1000.0; break; case 2: real_scale = 100.0; break; case 3: real_scale = 10.0; break; case 4: break; case 5: real_scale = 0.1; break; case 6: real_scale = 0.01; break; case 7: real_scale = 0.001; break; case 8: real_scale = 0.0001; break; case 9: real_scale = 0.00001; break; } if ( m_GerbMetric ) real_scale = real_scale / 25.4; current_coord = (int) round(current_coord * real_scale); } if ( type_coord == 'I' ) pos.x = current_coord; else if ( type_coord == 'J' ) pos.y = current_coord; continue; } else break; } m_IJPos = pos; return pos; } /*****************************************************/ int GERBER_Descr::ReturnGCodeNumber(char * &Text) /*****************************************************/ /* Lit la sequence Gnn et retourne la valeur nn */ { int ii = 0; char * text; char line[1024]; if ( Text == NULL ) return 0; Text++; text = line; while( IsNumber(*Text) ) { *(text++) = *(Text++); } *text = 0; ii = atoi(line); return ii; } /**************************************************/ int GERBER_Descr::ReturnDCodeNumber(char * &Text) /**************************************************/ /* Lit la sequence Dnn et retourne la valeur nn */ { int ii = 0; char * text; char line[1024]; if ( Text == NULL ) return 0; Text ++; text = line; while( IsNumber(*Text) ) *(text++) = *(Text++); *text = 0; ii = atoi(line); return ii; } /******************************************************************/ bool GERBER_Descr::Execute_G_Command(char * &text, int G_commande) /******************************************************************/ { switch (G_commande) { case GC_LINEAR_INTERPOL_1X: m_Iterpolation = GERB_INTERPOL_LINEAR_1X; break; case GC_CIRCLE_NEG_INTERPOL: m_Iterpolation = GERB_INTERPOL_ARC_NEG; break; case GC_CIRCLE_POS_INTERPOL: m_Iterpolation = GERB_INTERPOL_ARC_POS; break; case GC_COMMENT: text = NULL; break ; case GC_LINEAR_INTERPOL_10X: m_Iterpolation = GERB_INTERPOL_LINEAR_10X; break; case GC_LINEAR_INTERPOL_0P1X: m_Iterpolation = GERB_INTERPOL_LINEAR_01X; break; case GC_LINEAR_INTERPOL_0P01X: m_Iterpolation = GERB_INTERPOL_LINEAR_001X; break; case GC_SELECT_TOOL: { int D_commande = ReturnDCodeNumber(text); if ( D_commande < FIRST_DCODE) return FALSE; if (D_commande > (MAX_TOOLS-1)) D_commande = MAX_TOOLS-1; m_Current_Tool = D_commande; D_CODE * pt_Dcode = ReturnToolDescr( m_Layer, D_commande ); if ( pt_Dcode ) pt_Dcode->m_InUse = TRUE; break; } case GC_SPECIFY_INCHES: m_GerbMetric = FALSE; // FALSE = Inches, TRUE = metric break; case GC_SPECIFY_MILLIMETERS: m_GerbMetric = TRUE; // FALSE = Inches, TRUE = metric break; case GC_TURN_OFF_360_INTERPOL: m_360Arc_enbl = FALSE; break; case GC_TURN_ON_360_INTERPOL: m_360Arc_enbl = TRUE; break; case GC_SPECIFY_ABSOLUES_COORD: m_Relative = FALSE; // FALSE = absolute Coord, RUE = relative Coord break; case GC_SPECIFY_RELATIVEES_COORD: m_Relative = TRUE; // FALSE = absolute Coord, RUE = relative Coord break; case GC_TURN_ON_POLY_FILL: m_PolygonFillMode = TRUE; break; case GC_TURN_OFF_POLY_FILL: m_PolygonFillMode = FALSE; m_PolygonFillModeState = 0; break; case GC_MOVE: // Non existant default: { wxString msg; msg.Printf( wxT("G%.2d command not handled"),G_commande); DisplayError(NULL, msg); return FALSE; } } return TRUE; } /*****************************************************************************/ bool GERBER_Descr::Execute_DCODE_Command(WinEDA_GerberFrame * frame, wxDC * DC, char * &text, int D_commande) /*****************************************************************************/ { wxSize size(15, 15); int shape = 1, dcode = 0; D_CODE * pt_Tool = NULL; wxString msg; if(D_commande >= FIRST_DCODE ) // This is a "Set tool" command { if (D_commande > (MAX_TOOLS-1)) D_commande = MAX_TOOLS-1; m_Current_Tool = D_commande; D_CODE * pt_Dcode = ReturnToolDescr(m_Layer, D_commande); if ( pt_Dcode ) pt_Dcode->m_InUse = TRUE; return TRUE; } else // D_commande = 0..9: this is a pen command (usualy D1, D2 or D3) { m_Last_Pen_Command = D_commande; } if ( m_PolygonFillMode ) // Enter a polygon description: { switch ( D_commande ) { case 1: //code D01 Draw line, exposure ON { SEGZONE * edge_poly, *last; edge_poly = new SEGZONE( frame->m_Pcb ); last = (SEGZONE*)frame->m_Pcb->m_Zone; if ( last ) while (last->Pnext ) last = (SEGZONE*)last->Pnext; edge_poly->Insert(frame->m_Pcb, last); edge_poly->SetLayer( frame->GetScreen()->m_Active_Layer ); edge_poly->m_Width = 1; edge_poly->m_Start = m_PreviousPos; NEGATE(edge_poly->m_Start.y); edge_poly->m_End = m_CurrentPos; NEGATE(edge_poly->m_End.y); edge_poly->m_NetCode = m_PolygonFillModeState; m_PreviousPos = m_CurrentPos; m_PolygonFillModeState = 1; break; } case 2: //code D2: exposure OFF (i.e. "move to") m_PreviousPos = m_CurrentPos; m_PolygonFillModeState = 0; break; default: return FALSE; } } else switch ( D_commande ) { case 1: //code D01 Draw line, exposure ON pt_Tool = ReturnToolDescr(m_Layer, m_Current_Tool); if ( pt_Tool ) { size = pt_Tool->m_Size; dcode = pt_Tool->m_Num_Dcode; shape = pt_Tool->m_Shape; } switch ( m_Iterpolation ) { case GERB_INTERPOL_LINEAR_1X: Append_1_Line_GERBER(dcode, frame, DC, m_PreviousPos, m_CurrentPos, size.x); break; case GERB_INTERPOL_LINEAR_01X: case GERB_INTERPOL_LINEAR_001X: case GERB_INTERPOL_LINEAR_10X: wxBell(); break; case GERB_INTERPOL_ARC_NEG: Append_1_SEG_ARC_GERBER(dcode, frame, DC, m_PreviousPos, m_CurrentPos, m_IJPos, size.x, FALSE, m_360Arc_enbl); break; case GERB_INTERPOL_ARC_POS: Append_1_SEG_ARC_GERBER(dcode, frame, DC, m_PreviousPos, m_CurrentPos, m_IJPos, size.x, TRUE, m_360Arc_enbl); break; default: msg.Printf( wxT("Execute_DCODE_Command: interpol error (type %X)"), m_Iterpolation); DisplayError(frame, msg); break; } m_PreviousPos = m_CurrentPos; break; case 2: //code D2: exposure OFF (i.e. "move to") m_PreviousPos = m_CurrentPos; break; case 3: // code D3: flash aperture pt_Tool = ReturnToolDescr(m_Layer, m_Current_Tool); if ( pt_Tool ) { size = pt_Tool->m_Size; dcode = pt_Tool->m_Num_Dcode; shape = pt_Tool->m_Shape; } switch (shape) { case GERB_LINE: case GERB_CIRCLE : Append_1_Flash_ROND_GERBER(dcode, frame, DC, m_CurrentPos, size.x); break ; case GERB_OVALE : Append_1_Flash_GERBER( dcode, frame, DC, m_CurrentPos, size, OVALE); break ; case GERB_RECT: Append_1_Flash_GERBER( dcode, frame, DC, m_CurrentPos, size, RECT); break ; default: // Special (Macro) : Non implanté break; } m_PreviousPos = m_CurrentPos; break; default: return FALSE; } return TRUE; }