/***************************/ /**** class D_CODE ****/ /***************************/ #include "fctsys.h" #include "common.h" #include "class_drawpanel.h" #include "confirm.h" #include "macros.h" #include "trigo.h" #include "gr_basic.h" #include "gerbview.h" #include "class_gerber_draw_item.h" #include "class_GERBER.h" #define DEFAULT_SIZE 100 /* Format Gerber: NOTES: * Tools and D_CODES * tool number (identification of shapes) * 1 to 999 * * D_CODES: * D01 ... D9 = command codes: * D01 = activating light (pen down) while moving * D02 = light extinction (pen up) while moving * D03 = Flash * D04 to D09 = non used * D10 ... D999 = Indentification Tool (Shape id) * * For tools defining a shape): * DCode min = D10 * DCode max = 999 */ /***************/ /* Class DCODE */ /***************/ D_CODE::D_CODE( int num_dcode ) { m_Num_Dcode = num_dcode; Clear_D_CODE_Data(); } D_CODE::~D_CODE() { } void D_CODE::Clear_D_CODE_Data() { m_Size.x = DEFAULT_SIZE; m_Size.y = DEFAULT_SIZE; m_Shape = APT_CIRCLE; m_Drill.x = m_Drill.y = 0; m_DrillShape = APT_DEF_NO_HOLE; m_InUse = FALSE; m_Defined = FALSE; m_Macro = NULL; m_Rotation = 0.0; m_EdgesCount = 0; m_PolyCorners.clear(); } const wxChar* D_CODE::ShowApertureType( APERTURE_T aType ) { const wxChar* ret; switch( aType ) { case APT_CIRCLE: ret = wxT( "Round" ); break; case APT_RECT: ret = wxT( "Rect" ); break; case APT_OVAL: ret = wxT( "Oval" ); break; case APT_POLYGON: ret = wxT( "Poly" ); break; case APT_MACRO: ret = wxT( "Macro" ); break; default: ret = wxT( "???" ); break; } return ret; } /** GetShapeDim * Calculate a value that can be used to evaluate the size of text * when displaying the D-Code of an item * due to the complexity of some shapes, * one cannot calculate the "size" of a shape (only a bounding box) * but here, the "dimension" of the shape is the diameter of the primitive * or for lines the width of the line if the shape is a line * @param aParent = the parent GERBER_DRAW_ITEM which is actually drawn * @return a dimension, or -1 if no dim to calculate */ int D_CODE::GetShapeDim( GERBER_DRAW_ITEM* aParent ) { int dim = -1; switch( m_Shape ) { case APT_CIRCLE: dim = m_Size.x; break; case APT_RECT: case APT_OVAL: dim = MIN( m_Size.x, m_Size.y ); break; case APT_POLYGON: dim = MIN( m_Size.x, m_Size.y ); break; case APT_MACRO: if( m_Macro ) dim = m_Macro->GetShapeDim( aParent ); break; default: break; } return dim; } /* * Function ReadDCodeDefinitionFile * Can be useful only with old RS274D Gerber file format. * Is not needed with RS274X files format. * These files need an auxiliary DCode file description. There is no defined file format for this. * This function read a file format I needed a long time ago. * reads in a dcode file assuming ALSPCB file format with ';' indicating comments. * Format is like CSV but with optional ';' delineated comments: * tool, Horiz, Vert, drill, speed, accel. ,Type ; [DCODE (commentaire)] * ex: 1, 12, 12, 0, 0, 0, 3 ; D10 */ int GERBVIEW_FRAME::ReadDCodeDefinitionFile( const wxString& D_Code_FullFileName ) { int current_Dcode, ii, dcode_scale; char* ptcar; int dimH, dimV, drill, dummy; float fdimH, fdimV, fdrill; char c_type_outil[256]; char line[GERBER_BUFZ]; wxString msg; D_CODE* dcode; FILE* dest; int layer = getActiveLayer(); int type_outil; if( g_GERBER_List[layer] == NULL ) g_GERBER_List[layer] = new GERBER_IMAGE( this, layer ); GERBER_IMAGE* gerber = g_GERBER_List[layer]; /* Updating gerber scale: */ dcode_scale = 10; /* By uniting dCode = mil, internal unit = 0.1 mil * -> 1 unite dcode = 10 unit PCB */ current_Dcode = 0; if( D_Code_FullFileName.IsEmpty() ) return 0; dest = wxFopen( D_Code_FullFileName, wxT( "rt" ) ); if( dest == 0 ) { msg = _( "File " ) + D_Code_FullFileName + _( " not found" ); DisplayError( this, msg, 10 ); return -1; } gerber->InitToolTable(); while( fgets( line, sizeof(line) - 1, dest ) != NULL ) { if( *line == ';' ) continue; if( strlen( line ) < 10 ) continue; /* Skip blank line. */ dcode = NULL; current_Dcode = 0; /* Determine of the type of file from D_Code. */ ptcar = line; ii = 0; while( *ptcar ) if( *(ptcar++) == ',' ) ii++; if( ii >= 6 ) /* valeurs en mils */ { sscanf( line, "%d,%d,%d,%d,%d,%d,%d", &ii, &dimH, &dimV, &drill, &dummy, &dummy, &type_outil ); dimH = wxRound( dimH * dcode_scale ); dimV = wxRound( dimV * dcode_scale ); drill = wxRound( drill * dcode_scale ); if( ii < 1 ) ii = 1; current_Dcode = ii - 1 + FIRST_DCODE; } else /* Values in inches are converted to mils. */ { fdrill = 0; current_Dcode = 0; sscanf( line, "%f,%f,%1s", &fdimV, &fdimH, c_type_outil ); ptcar = line; while( *ptcar ) { if( *ptcar == 'D' ) { sscanf( ptcar + 1, "%d,%f", ¤t_Dcode, &fdrill ); break; } else ptcar++; } dimH = wxRound( fdimH * dcode_scale * 1000 ); dimV = wxRound( fdimV * dcode_scale * 1000 ); drill = wxRound( fdrill * dcode_scale * 1000 ); if( strchr( "CLROP", c_type_outil[0] ) ) type_outil = (APERTURE_T) c_type_outil[0]; else { fclose( dest ); return -2; } } /* Update the list of d_codes if consistant. */ if( current_Dcode < FIRST_DCODE ) continue; if( current_Dcode >= TOOLS_MAX_COUNT ) continue; dcode = gerber->GetDCODE( current_Dcode ); dcode->m_Size.x = dimH; dcode->m_Size.y = dimV; dcode->m_Shape = (APERTURE_T) type_outil; dcode->m_Drill.x = dcode->m_Drill.y = drill; dcode->m_Defined = TRUE; } fclose( dest ); return 1; } /* Set Size Items (Lines, Flashes) from DCodes List */ void GERBVIEW_FRAME::CopyDCodesSizeToItems() { static D_CODE dummy( 999 ); //Used if D_CODE not found in list BOARD_ITEM* item = GetBoard()->m_Drawings; for( ; item; item = item->Next() ) { GERBER_DRAW_ITEM* gerb_item = (GERBER_DRAW_ITEM*) item; D_CODE* dcode = gerb_item->GetDcodeDescr(); wxASSERT( dcode ); if( dcode == NULL ) dcode = &dummy; dcode->m_InUse = TRUE; gerb_item->m_Size = dcode->m_Size; if( // Line Item (gerb_item->m_Shape == GBR_SEGMENT ) /* rectilinear segment */ || (gerb_item->m_Shape == GBR_ARC ) /* segment arc (rounded tips) */ || (gerb_item->m_Shape == GBR_CIRCLE ) /* segment in a circle (ring) */ ) { } else // Spots ( Flashed Items ) { switch( dcode->m_Shape ) { case APT_CIRCLE: /* spot round */ gerb_item->m_Shape = GBR_SPOT_CIRCLE; break; case APT_OVAL: /* spot oval*/ gerb_item->m_Shape = GBR_SPOT_OVAL; break; case APT_RECT: /* spot rect*/ gerb_item->m_Shape = GBR_SPOT_RECT; break; case APT_POLYGON: /* spot regular polyg 3 to 1é edges */ gerb_item->m_Shape = GBR_SPOT_POLY; break; case APT_MACRO: /* spot defined by a macro */ gerb_item->m_Shape = GBR_SPOT_MACRO; break; default: wxMessageBox( wxT( "GERBVIEW_FRAME::CopyDCodesSizeToItems() error" ) ); break; } } } } /* * Function DrawFlashedShape * Draw the dcode shape for flashed items. * When an item is flashed, the DCode shape is the shape of the item */ void D_CODE::DrawFlashedShape( GERBER_DRAW_ITEM* aParent, EDA_RECT* aClipBox, wxDC* aDC, int aColor, int aAltColor, wxPoint aShapePos, bool aFilledShape ) { int radius; switch( m_Shape ) { case APT_MACRO: GetMacro()->DrawApertureMacroShape( aParent, aClipBox, aDC, aColor, aAltColor, aShapePos, aFilledShape); break; case APT_CIRCLE: radius = m_Size.x >> 1; if( !aFilledShape ) GRCircle( aClipBox, aDC, aParent->GetABPosition(aShapePos), radius, 0, aColor ); else if( m_DrillShape == APT_DEF_NO_HOLE ) GRFilledCircle( aClipBox, aDC, aParent->GetABPosition(aShapePos), radius, aColor ); else if( APT_DEF_ROUND_HOLE == 1 ) // round hole in shape { int width = (m_Size.x - m_Drill.x ) / 2; GRCircle( aClipBox, aDC, aParent->GetABPosition(aShapePos), radius - (width / 2), width, aColor ); } else // rectangular hole { if( m_PolyCorners.size() == 0 ) ConvertShapeToPolygon(); DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos ); } break; case APT_RECT: { wxPoint start; start.x = aShapePos.x - m_Size.x / 2; start.y = aShapePos.y - m_Size.y / 2; wxPoint end = start + m_Size; start = aParent->GetABPosition( start ); end = aParent->GetABPosition( end ); if( !aFilledShape ) { GRRect( aClipBox, aDC, start.x, start.y, end.x, end.y, 0, aColor ); } else if( m_DrillShape == APT_DEF_NO_HOLE ) { GRFilledRect( aClipBox, aDC, start.x, start.y, end.x, end.y, 0, aColor, aColor ); } else { if( m_PolyCorners.size() == 0 ) ConvertShapeToPolygon(); DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos ); } } break; case APT_OVAL: { wxPoint start = aShapePos; wxPoint end = aShapePos; if( m_Size.x > m_Size.y ) // horizontal oval { int delta = (m_Size.x - m_Size.y) / 2; start.x -= delta; end.x += delta; radius = m_Size.y; } else // horizontal oval { int delta = (m_Size.y - m_Size.x) / 2; start.y -= delta; end.y += delta; radius = m_Size.x; } start = aParent->GetABPosition( start ); end = aParent->GetABPosition( end ); if( !aFilledShape ) { GRCSegm( aClipBox, aDC, start.x, start.y, end.x, end.y, radius, aColor ); } else if( m_DrillShape == APT_DEF_NO_HOLE ) { GRFillCSegm( aClipBox, aDC, start.x, start.y, end.x, end.y, radius, aColor ); } else { if( m_PolyCorners.size() == 0 ) ConvertShapeToPolygon(); DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos ); } } break; case APT_POLYGON: if( m_PolyCorners.size() == 0 ) ConvertShapeToPolygon(); DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos ); break; } } /* * Function DrawFlashedPolygon * a helper function used id ::Draw to draw the polygon stored ion m_PolyCorners * Draw some Apertures shapes when they are defined as filled polygons. * APT_POLYGON is always a polygon, but some complex shapes are also converted to * polygons (shapes with holes) */ void D_CODE::DrawFlashedPolygon( GERBER_DRAW_ITEM* aParent, EDA_RECT* aClipBox, wxDC* aDC, int aColor, bool aFilled, const wxPoint& aPosition ) { if( m_PolyCorners.size() == 0 ) return; std::vector points; points = m_PolyCorners; for( unsigned ii = 0; ii < points.size(); ii++ ) { points[ii] += aPosition; points[ii] = aParent->GetABPosition( points[ii] ); } GRClosedPoly( aClipBox, aDC, points.size(), &points[0], aFilled, aColor, aColor ); } #define SEGS_CNT 32 // number of segments to approximate a circle // A helper function for D_CODE::ConvertShapeToPolygon(). // Add a hole to a polygon static void addHoleToPolygon( std::vector& aBuffer, APERTURE_DEF_HOLETYPE aHoleShape, wxSize aSize, wxPoint aAnchorPos ); /** * Function ConvertShapeToPolygon * convert a shape to an equivalent polygon. * Arcs and circles are approximated by segments * Useful when a shape is not a graphic primitive (shape with hole, * Rotated shape ... ) and cannot be easily drawn. */ void D_CODE::ConvertShapeToPolygon() { wxPoint initialpos; wxPoint currpos; m_PolyCorners.clear(); switch( m_Shape ) { case APT_CIRCLE: // creates only a circle with rectangular hole currpos.x = m_Size.x >> 1; initialpos = currpos; for( unsigned ii = 0; ii <= SEGS_CNT; ii++ ) { currpos = initialpos; RotatePoint( &currpos, ii * 3600 / SEGS_CNT ); m_PolyCorners.push_back( currpos ); } addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos ); break; case APT_RECT: currpos.x = m_Size.x / 2; currpos.y = m_Size.y / 2; initialpos = currpos; m_PolyCorners.push_back( currpos ); currpos.x -= m_Size.x; m_PolyCorners.push_back( currpos ); currpos.y -= m_Size.y; m_PolyCorners.push_back( currpos ); currpos.x += m_Size.x; m_PolyCorners.push_back( currpos ); currpos.y += m_Size.y; m_PolyCorners.push_back( currpos ); // close polygon addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos ); break; case APT_OVAL: { int delta, radius; // we create an horizontal oval shape. then rotate if needed if( m_Size.x > m_Size.y ) // horizontal oval { delta = (m_Size.x - m_Size.y) / 2; radius = m_Size.y / 2; } else // vertical oval { delta = (m_Size.y - m_Size.x) / 2; radius = m_Size.x / 2; } currpos.y = radius; initialpos = currpos; m_PolyCorners.push_back( currpos ); // build the right arc of the shape unsigned ii = 0; for( ; ii <= SEGS_CNT / 2; ii++ ) { currpos = initialpos; RotatePoint( &currpos, ii * 3600 / SEGS_CNT ); currpos.x += delta; m_PolyCorners.push_back( currpos ); } // build the left arc of the shape for( ii = SEGS_CNT / 2; ii <= SEGS_CNT; ii++ ) { currpos = initialpos; RotatePoint( &currpos, ii * 3600 / SEGS_CNT ); currpos.x -= delta; m_PolyCorners.push_back( currpos ); } m_PolyCorners.push_back( initialpos ); // close outline if( m_Size.y > m_Size.x ) // vertical oval, rotate polygon. { for( unsigned jj = 0; jj < m_PolyCorners.size(); jj++ ) RotatePoint( &m_PolyCorners[jj], 900 ); } addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos ); } break; case APT_POLYGON: currpos.x = m_Size.x >> 1; // first point is on X axis initialpos = currpos; // rs274x said: m_EdgesCount = 3 ... 12 if( m_EdgesCount < 3 ) m_EdgesCount = 3; if( m_EdgesCount > 12 ) m_EdgesCount = 12; for( int ii = 0; ii <= m_EdgesCount; ii++ ) { currpos = initialpos; RotatePoint( &currpos, ii * 3600 / m_EdgesCount ); m_PolyCorners.push_back( currpos ); } addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos ); if( m_Rotation ) // vertical oval, rotate polygon. { int angle = wxRound( m_Rotation * 10 ); for( unsigned jj = 0; jj < m_PolyCorners.size(); jj++ ) { RotatePoint( &m_PolyCorners[jj], -angle ); } } break; case APT_MACRO: // TODO break; } } // The helper function for D_CODE::ConvertShapeToPolygon(). // Add a hole to a polygon static void addHoleToPolygon( std::vector& aBuffer, APERTURE_DEF_HOLETYPE aHoleShape, wxSize aSize, wxPoint aAnchorPos ) { wxPoint currpos; if( aHoleShape == APT_DEF_ROUND_HOLE ) // build a round hole { for( int ii = 0; ii <= SEGS_CNT; ii++ ) { currpos.x = 0; currpos.y = aSize.x / 2; // aSize.x / 2 is the radius of the hole RotatePoint( &currpos, ii * 3600 / SEGS_CNT ); aBuffer.push_back( currpos ); } aBuffer.push_back( aAnchorPos ); // link to outline } if( aHoleShape == APT_DEF_RECT_HOLE ) // Create rectangular hole { currpos.x = aSize.x / 2; currpos.y = aSize.y / 2; aBuffer.push_back( currpos ); // link to hole and begin hole currpos.x -= aSize.x; aBuffer.push_back( currpos ); currpos.y -= aSize.y; aBuffer.push_back( currpos ); currpos.x += aSize.x; aBuffer.push_back( currpos ); currpos.y += aSize.y; aBuffer.push_back( currpos ); // close hole aBuffer.push_back( aAnchorPos ); // link to outline } }