From f0364c9da62607ef2f70451e654ac3a3a0caf6da Mon Sep 17 00:00:00 2001
From: jean-pierre charras <jp.charras@wanadoo.fr>
Date: Mon, 25 Oct 2010 09:43:50 +0200
Subject: [PATCH] Gerbview: support for plot lines (currently: not arcs) with a
 rectangular pen (Yes, it exists!). pcbnew: try to fix an issue when exporting
 arcs in gencad.

---
 gerbview/class_gerber_draw_item.cpp           | 107 ++++++++++++++++--
 gerbview/class_gerber_draw_item.h             |   9 +-
 .../test_line_with_rect_aperture.gbr          |  16 +++
 gerbview/rs274d.cpp                           |  23 ++--
 pcbnew/class_drawsegment.cpp                  |   2 +
 pcbnew/class_drawsegment.h                    |   4 +-
 pcbnew/class_edge_mod.cpp                     |   1 +
 pcbnew/export_gencad.cpp                      |  49 ++++----
 8 files changed, 165 insertions(+), 46 deletions(-)
 create mode 100644 gerbview/gerber_test_files/test_line_with_rect_aperture.gbr

diff --git a/gerbview/class_gerber_draw_item.cpp b/gerbview/class_gerber_draw_item.cpp
index b34aaef67a..0bfd7da252 100644
--- a/gerbview/class_gerber_draw_item.cpp
+++ b/gerbview/class_gerber_draw_item.cpp
@@ -88,8 +88,7 @@ GERBER_DRAW_ITEM::GERBER_DRAW_ITEM( const GERBER_DRAW_ITEM& aSource ) :
     m_mirrorA       = aSource.m_mirrorA;
     m_mirrorB       = aSource.m_mirrorB;
     m_layerOffset   = aSource.m_layerOffset;
-    m_drawScale.x   = aSource.m_drawScale.x;
-    m_drawScale.y   = aSource.m_drawScale.y;
+    m_drawScale     = aSource.m_drawScale;
     m_lyrRotation   = aSource.m_lyrRotation;
 }
 
@@ -222,7 +221,13 @@ wxString GERBER_DRAW_ITEM::ShowGBRShape()
         return wxT( "polygon" );
 
     case GBR_SPOT_MACRO:
-        return wxT( "apt_macro" );  // TODO: add aperture macro name
+    {
+        wxString name = wxT( "apt_macro" );
+        D_CODE* dcode = GetDcodeDescr();
+        if( dcode && dcode->GetMacro() )
+            name << wxT(" ") << dcode->GetMacro()->name;
+        return name;
+    }
 
     default:
         return wxT( "??" );
@@ -363,8 +368,8 @@ void GERBER_DRAW_ITEM::Draw( WinEDA_DrawPanel* aPanel, wxDC* aDC, int aDrawMode,
         break;
 
     case GBR_CIRCLE:
-        radius = (int) hypot( (double) ( m_End.x - m_Start.x ),
-                             (double) ( m_End.y - m_Start.y ) );
+        radius = wxRound(hypot( (double) ( m_End.x - m_Start.x ),
+                             (double) ( m_End.y - m_Start.y ) ));
 
         halfPenWidth = m_Size.x >> 1;
 
@@ -384,6 +389,8 @@ void GERBER_DRAW_ITEM::Draw( WinEDA_DrawPanel* aPanel, wxDC* aDC, int aDrawMode,
         break;
 
     case GBR_ARC:
+        // Currently, arcs plotted witha rectangular aperture are not supported.
+        // a round pen only is expected.
 #if 0     // for arc debug only
         GRLine( &aPanel->m_ClipBox, aDC, GetABPosition( m_Start ),
                 GetABPosition( m_ArcCentre ), 0, color );
@@ -415,12 +422,30 @@ void GERBER_DRAW_ITEM::Draw( WinEDA_DrawPanel* aPanel, wxDC* aDC, int aDrawMode,
         break;
 
     case GBR_SEGMENT:
-        if( !isFilled )
-            GRCSegm( &aPanel->m_ClipBox, aDC, GetABPosition( m_Start ),
-                     GetABPosition( m_End ), m_Size.x, color );
+        /* Plot a line from m_Start to m_End.
+         * Usually, a round pen is used, but some gerber files use a rectangular pen
+         * In fact, any aperture can be used to plot a line.
+         * currently: only a square pen is handled (I believe using a polygon gives a strange plot).
+         */
+        if( d_codeDescr->m_Shape == APT_RECT )
+        {
+            if( m_PolyCorners.size() == 0 )
+                ConvertSegmentToPolygon( );
+            DrawGbrPoly( &aPanel->m_ClipBox, aDC, color, aOffset, isFilled );
+        }
         else
-            GRFilledSegment( &aPanel->m_ClipBox, aDC, GetABPosition( m_Start ),
+        {
+            if( !isFilled )
+            {
+                    GRCSegm( &aPanel->m_ClipBox, aDC, GetABPosition( m_Start ),
                              GetABPosition( m_End ), m_Size.x, color );
+            }
+            else
+            {
+                GRFilledSegment( &aPanel->m_ClipBox, aDC, GetABPosition( m_Start ),
+                                 GetABPosition( m_End ), m_Size.x, color );
+            }
+        }
         break;
 
     default:
@@ -433,9 +458,71 @@ void GERBER_DRAW_ITEM::Draw( WinEDA_DrawPanel* aPanel, wxDC* aDC, int aDrawMode,
     }
 }
 
+/** function ConvertSegmentToPolygon
+ * convert a line to an equivalent polygon.
+ * Useful when a line is plotted using a rectangular pen.
+ * In this case, the usual segment plot cannot be used
+ * The equivalent polygon is the area paint by the rectancular pen
+ * from m_Start to m_End.
+ */
+void GERBER_DRAW_ITEM::ConvertSegmentToPolygon( )
+{
+    m_PolyCorners.clear();
+    m_PolyCorners.reserve(6);
+
+    wxPoint start = m_Start;
+    wxPoint end = m_End;
+    // make calculations more easy if ensure start.x < end.x
+    // (only 2 quadrants to consider)
+    if( start.x > end.x )
+        EXCHG( start, end );
+
+    // calculate values relative to start point:
+    wxPoint delta = end - start;
+    // calculate corners for the first quadrant only (delta.x and delta.y > 0 )
+    // currently, delta.x already is > 0.
+    // make delta.y > 0
+    bool change = delta.y < 0;
+    if( change )
+        NEGATE( delta.y);
+    // Now create the full polygon.
+    // Due to previous chnages, the shape is always something like
+    //          3 4
+    // 2          5
+    // 1 6
+    wxPoint corner;
+    corner.x -= m_Size.x/2;
+    corner.y -= m_Size.y/2;
+    m_PolyCorners.push_back( corner );  // Lower left corner, start point (1)
+    corner.y += m_Size.y;
+    m_PolyCorners.push_back( corner );  // upper left corner, start point (2)
+    if( delta.x || delta.y)
+    {
+        corner += delta;
+        m_PolyCorners.push_back( corner );  // upper left corner, end point (3)
+    }
+    corner.x += m_Size.x;
+    m_PolyCorners.push_back( corner );  // upper right corner, end point (4)
+    corner.y -= m_Size.y;
+    m_PolyCorners.push_back( corner );  // lower right corner, end point (5)
+    if( delta.x || delta.y )
+    {
+        corner -= delta;
+        m_PolyCorners.push_back( corner );  // lower left corner, start point (6)
+    }
+
+    // Create final polygon:
+    for( unsigned ii = 0; ii < m_PolyCorners.size(); ii++ )
+    {
+        if( change )
+            NEGATE( m_PolyCorners[ii].y);
+         m_PolyCorners[ii] += start;
+    }
+}
+
 
 /** function DrawGbrPoly
- * a helper function used id ::Draw to draw the polygon stored ion m_PolyCorners
+ * a helper function used id ::Draw to draw the polygon stored in m_PolyCorners
  * Draw filled polygons
  */
 void GERBER_DRAW_ITEM::DrawGbrPoly( EDA_Rect*      aClipBox,
diff --git a/gerbview/class_gerber_draw_item.h b/gerbview/class_gerber_draw_item.h
index c59bc654d0..d9f172fd15 100644
--- a/gerbview/class_gerber_draw_item.h
+++ b/gerbview/class_gerber_draw_item.h
@@ -194,8 +194,15 @@ public:
                    int               aDrawMode,
                    const wxPoint&    aOffset = ZeroOffset );
 
+    /** function ConvertSegmentToPolygon
+     * convert a line to an equivalent polygon.
+     * Useful when a line is plotted using a rectangular pen.
+     * In this case, the usual segment plot function cannot be used
+     */
+    void ConvertSegmentToPolygon( );
+
     /** function DrawGbrPoly
-     * a helper function used id ::Draw to draw the polygon stored ion m_PolyCorners
+     * a helper function used id ::Draw to draw the polygon stored in m_PolyCorners
      */
     void DrawGbrPoly( EDA_Rect* aClipBox,
                       wxDC* aDC, int aColor,
diff --git a/gerbview/gerber_test_files/test_line_with_rect_aperture.gbr b/gerbview/gerber_test_files/test_line_with_rect_aperture.gbr
new file mode 100644
index 0000000000..b93e1c9fab
--- /dev/null
+++ b/gerbview/gerber_test_files/test_line_with_rect_aperture.gbr
@@ -0,0 +1,16 @@
+*
+%FSLAX23Y23*%
+%MOIN*%
+%ADD10R,0.025X0.025*%
+%ADD11R,0.03X0.06*%
+%IPPOS*%
+%LNtest_rect.gbr*%
+%LPD*%
+G75*
+G54D10*
+X04000Y00100D02*
+X04400Y00140D01*
+G54D11*
+X03000Y00100D02*
+X02400Y-00340D01*
+M02*
diff --git a/gerbview/rs274d.cpp b/gerbview/rs274d.cpp
index e9f9d4960a..2c0651436f 100644
--- a/gerbview/rs274d.cpp
+++ b/gerbview/rs274d.cpp
@@ -137,23 +137,23 @@ static void fillFlashedGBRITEM(  GERBER_DRAW_ITEM* aGbrItem,
  * @param aGbrItem The GERBER_DRAW_ITEM to fill in.
  * @param Dcode_index The DCODE value, like D14
  * @param aLayer The layer index to set into the GBRITEM
- * @param aPos The center point of the flash
- * @param aDiameter The diameter of the round flash
+ * @param aStart The starting point of the line
+ * @param aEnd The ending point of the line
+ * @param aPenSize The size of the flash. Note rectangular shapes are legal.
  * @param aLayerNegative = true if the current layer is negative
- * @param aImageNegative = true if the current image is negative
  */
 static void fillLineGBRITEM(  GERBER_DRAW_ITEM* aGbrItem,
                               int               Dcode_index,
                               int               aLayer,
                               const wxPoint&    aStart,
                               const wxPoint&    aEnd,
-                              int               aWidth,
+                              wxSize            aPenSize,
                               bool              aLayerNegative  )
 {
     aGbrItem->SetLayer( aLayer );
     aGbrItem->m_Flashed = false;
 
-    aGbrItem->m_Size.x = aGbrItem->m_Size.y = aWidth;
+    aGbrItem->m_Size = aPenSize;
 
     aGbrItem->m_Start = aStart;
     aGbrItem->m_End   = aEnd;
@@ -188,13 +188,12 @@ static void fillLineGBRITEM(  GERBER_DRAW_ITEM* aGbrItem,
  *   must be calculated from the previously given constraint: arc only in the
  * same quadrant.
  * @param aDiameter The diameter of the round flash
- * @param aWidth is the pen width.
+ * @param aPenSize The size of the flash. Note rectangular shapes are legal.
  * @param aLayerNegative = true if the current layer is negative
- * @param aImageNegative = true if the current image is negative
  */
 static void fillArcGBRITEM(  GERBER_DRAW_ITEM* aGbrItem, int Dcode_index, int aLayer,
                              const wxPoint& aStart, const wxPoint& aEnd,
-                             const wxPoint& aRelCenter, int aWidth,
+                             const wxPoint& aRelCenter, wxSize aPenSize,
                              bool aClockwise, bool aMultiquadrant,
                              bool aLayerNegative  )
 {
@@ -202,7 +201,7 @@ static void fillArcGBRITEM(  GERBER_DRAW_ITEM* aGbrItem, int Dcode_index, int aL
 
     aGbrItem->m_Shape = GBR_ARC;
     aGbrItem->SetLayer( aLayer );
-    aGbrItem->m_Size.x  = aGbrItem->m_Size.y = aWidth;
+    aGbrItem->m_Size  = aPenSize;
     aGbrItem->m_Flashed = false;
 
 
@@ -333,7 +332,7 @@ static void fillArcPOLY(  BOARD* aPcb, GERBER_DRAW_ITEM* aGbrItem,
     aGbrItem->SetLayerPolarity( aLayerNegative );
 
     fillArcGBRITEM(  &dummyGbrItem, 0, 0,
-                     aStart, aEnd, rel_center, 0,
+                     aStart, aEnd, rel_center, wxSize(0, 0),
                      clockwise, multiquadrant, aLayerNegative );
 
     wxPoint   center;
@@ -688,7 +687,7 @@ bool GERBER_IMAGE::Execute_DCODE_Command( char*& text, int D_commande )
 //                           m_PreviousPos.x, m_PreviousPos.y,
 //                            m_CurrentPos.x, m_CurrentPos.y ); )
                 fillLineGBRITEM( gbritem, dcode, activeLayer, m_PreviousPos,
-                                 m_CurrentPos, size.x, GetLayerParams().m_LayerNegative );
+                                 m_CurrentPos, size, GetLayerParams().m_LayerNegative );
                 StepAndRepeatItem( *gbritem );
                 break;
 
@@ -708,7 +707,7 @@ bool GERBER_IMAGE::Execute_DCODE_Command( char*& text, int D_commande )
 //                           m_CurrentPos.y, m_IJPos.x,
 //                            m_IJPos.y, m_Iterpolation, m_360Arc_enbl ); )
                 fillArcGBRITEM( gbritem, dcode, activeLayer, m_PreviousPos,
-                                m_CurrentPos, m_IJPos, size.x,
+                                m_CurrentPos, m_IJPos, size,
                                 ( m_Iterpolation == GERB_INTERPOL_ARC_NEG ) ?
                                 false : true, m_360Arc_enbl, GetLayerParams().m_LayerNegative );
                 StepAndRepeatItem( *gbritem );
diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp
index 6cf79668e7..d146be4aab 100644
--- a/pcbnew/class_drawsegment.cpp
+++ b/pcbnew/class_drawsegment.cpp
@@ -245,9 +245,11 @@ void DRAWSEGMENT::Draw( WinEDA_DrawPanel* panel, wxDC* DC,
     GRSetDrawMode( DC, draw_mode );
     l_piste = m_Width >> 1;  /* half trace width */
 
+    // Line start point or Circle and Arc center
     ux0 = m_Start.x;
     uy0 = m_Start.y;
 
+    // Line end point or circle and arc start point
     dx = m_End.x;
     dy = m_End.y;
 
diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h
index 1a7f4c9504..92a4b1abd2 100644
--- a/pcbnew/class_drawsegment.h
+++ b/pcbnew/class_drawsegment.h
@@ -10,8 +10,8 @@ class DRAWSEGMENT : public BOARD_ITEM
 {
 public:
     int     m_Width;            // thickness of lines ...
-    wxPoint m_Start;            // Line start point
-    wxPoint m_End;              // Line end point
+    wxPoint m_Start;            // Line start point or Circle and Arc center
+    wxPoint m_End;              // Line end point or circle and arc start point
 
     int     m_Shape;            // Shape: line, Circle, Arc
     int     m_Type;             // Used in complex associations ( Dimensions.. )
diff --git a/pcbnew/class_edge_mod.cpp b/pcbnew/class_edge_mod.cpp
index d2e58be86b..2cf70819e8 100644
--- a/pcbnew/class_edge_mod.cpp
+++ b/pcbnew/class_edge_mod.cpp
@@ -421,6 +421,7 @@ int EDGE_MODULE::ReadDescr( char* Line, FILE* File,
                 &m_Start0.x, &m_Start0.y,
                 &m_End0.x, &m_End0.y,
                 &m_Angle, &m_Width, &m_Layer );
+        NORMALIZE_ANGLE( m_Angle );
         break;
 
     case S_SEGMENT:
diff --git a/pcbnew/export_gencad.cpp b/pcbnew/export_gencad.cpp
index 03012ee950..af03bddc53 100644
--- a/pcbnew/export_gencad.cpp
+++ b/pcbnew/export_gencad.cpp
@@ -818,9 +818,9 @@ void CreateTracksInfoData( FILE* file, BOARD* pcb )
  */
 void FootprintWriteShape( FILE* file, MODULE* module )
 {
-    EDGE_MODULE*    PtEdge;
-    EDA_BaseStruct* PtStruct;
-    int             Yaxis_sign = -1; // Control Y axis change sign (as normal
+    EDGE_MODULE*    edge;
+    EDA_BaseStruct* item;
+    int             y_axis_sign = -1; // Control Y axis change sign (as normal
                                      // module / mirror axis and conventions)
 
     /* creates header: */
@@ -840,46 +840,53 @@ void FootprintWriteShape( FILE* file, MODULE* module )
     }
 
     /* creates Drawing */
-    PtStruct = module->m_Drawings;
-    for( ; PtStruct != NULL; PtStruct = PtStruct->Next() )
+    item = module->m_Drawings;
+    for( ; item != NULL; item = item->Next() )
     {
-        switch( PtStruct->Type() )
+        switch( item->Type() )
         {
         case TYPE_TEXTE_MODULE:
             break;
 
         case TYPE_EDGE_MODULE:
-            PtEdge = (EDGE_MODULE*) PtStruct;
+            edge = (EDGE_MODULE*) item;
 
-            switch( PtEdge->m_Shape )
+            switch( edge->m_Shape )
             {
             case S_SEGMENT:
                 fprintf( file, "LINE %d %d %d %d\n",
-                         PtEdge->m_Start0.x, Yaxis_sign * PtEdge->m_Start0.y,
-                         PtEdge->m_End0.x, Yaxis_sign * PtEdge->m_End0.y );
+                         edge->m_Start0.x, y_axis_sign * edge->m_Start0.y,
+                         edge->m_End0.x, y_axis_sign * edge->m_End0.y );
                 break;
 
             case S_CIRCLE:
             {
                 int rayon = (int) hypot(
-                    (double) ( PtEdge->m_End0.x - PtEdge->m_Start0.x ),
-                    (double) ( PtEdge->m_End0.y - PtEdge->m_Start0.y ) );
+                    (double) ( edge->m_End0.x - edge->m_Start0.x ),
+                    (double) ( edge->m_End0.y - edge->m_Start0.y ) );
                 fprintf( file, "CIRCLE %d %d %d\n",
-                         PtEdge->m_Start0.x, Yaxis_sign * PtEdge->m_Start0.y,
+                         edge->m_Start0.x, y_axis_sign * edge->m_Start0.y,
                          rayon );
                 break;
             }
 
             case S_ARC:         /* print ARC x,y start x,y end x,y center */
-            {
-                int arcendx, arcendy;
-                arcendx = PtEdge->m_Start0.x;
-                arcendy = PtEdge->m_Start0.y;
-                RotatePoint( &arcendx, &arcendy, PtEdge->m_Angle );
+            {   // Arcs are defined counter clockwise (positive trigonometric)
+                // from the start point to the end point (0 to 360 degrees)
+                wxPoint arcStart, arcEnd;
+                // edge->m_Start0 is the arc center relative to the shape position
+                // edge->m_End0 is the arc start point relative to the shape position
+                arcStart = edge->m_End0;
+                // calculate arcEnd arc end point relative to the shape position, in pcbnew coordinates
+                arcEnd = arcStart;
+                RotatePoint( &arcEnd, edge->m_Start0, -edge->m_Angle );
+                // due to difference between pcbnew and gencad, swap arc start and arc end
+                EXCHG(arcEnd, arcStart);
+                // print arc shape:
                 fprintf( file, "ARC %d %d %d %d %d %d\n",
-                         PtEdge->m_End0.x, Yaxis_sign * PtEdge->m_End0.y,
-                         arcendx, Yaxis_sign * arcendy,
-                         PtEdge->m_Start0.x, Yaxis_sign * PtEdge->m_Start0.y );
+                         arcStart.x, y_axis_sign * arcStart.y,   // Start point
+                         arcEnd.x, y_axis_sign * arcEnd.y,               // End point
+                         edge->m_Start0.x, y_axis_sign * edge->m_Start0.y );
                 break;
             }