← Back to team overview

kicad-developers team mailing list archive

[PATCH] Fix sloppy polygon tracing for board outline (RESUBMIT)

 

Attached a patch with a fix for the 'sloppy' board outline tracing that was used in pcbnew/specctra_export.cpp

I submitted a similar patch earlier but that has not been merged yet. That previous patch may also have been a bit more messy and in some spots even violated the coding rules.

So with this patch consider the previous similar patch NULL and void.

I also revisited this code in light of the recent DXF closed polygon fix. The existing code in specctra_export for the outline tracing had a few problems when the segment size became quite small and selecting between the begin and end of a next segment. In some case the 0.01 limit used was also too small.

The updated algorithm explicitlty chooses the closest end of a new segment to be appended. It also only terminates the path tracing when there really are no more segments available within the 0.05 mm limit distance. The previous code was testing to close the polygon before it checking to see if there were more close segments and hence could prematurely exit the path tracing which would leave some segments dangling.

I hope this can still be considered for the upcoming 'stable' release.

Cheers,

Marco

pcbnew/specctra_export.cpp | 123 +++++++++++++++++++++++++++++++--------------
 1 file changed, 86 insertions(+), 37 deletions(-)

diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp
index fa88078..6dfccce 100644
--- a/pcbnew/specctra_export.cpp
+++ b/pcbnew/specctra_export.cpp
@@ -82,7 +82,7 @@ static const double safetyMargin = 0.1;
 static unsigned close_ness(  const wxPoint& aLeft, const wxPoint& aRight )
 {
     // Don't need an accurate distance calculation, just something
-    // approximating it, for relative orering.
+    // approximating it, for relative ordering.
return unsigned( abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
 }

@@ -104,6 +104,23 @@ inline bool close_enough( const wxPoint& aLeft, const wxPoint& aRight, unsigned
 }


+/**
+ * Function close_st
+ * is a local method of qualifying if either the start of end point of a segment is closest to a point.
+ *
+ * @param aReference is the reference point
+ * @param aFirst is the first point
+ * @param aSecond is the second point
+ * @return bool - true if the the first point is closest to the reference, otherwise false.
+ */
+inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wxPoint& aSecond )
+{
+    // We don't use an accurate distance calculation, just something
+    // approximating to find the closest to the reference.
+ return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
+}
+
+
 // see wxPcbStruct.h
 void PCB_EDIT_FRAME::ExportToSpecctra( wxCommandEvent& event )
 {
@@ -955,7 +972,7 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
                 break;

             case S_ARC:
-                // freerouter does not yet understand arcs, so approximate
+                // Freerouter does not yet understand arcs, so approximate
                 // an arc with a series of short lines and put those
                 // line segments into the !same! PATH.
                 {
@@ -1024,7 +1041,7 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )

// Set maximum proximity threshold for point to point nearness metric for
         // board perimeter only, not interior keepouts yet.
- prox = Millimeter2iu( 0.01 ); // should be enough to fix rounding issues + prox = Millimeter2iu( 0.01 ); // should be enough to fix rounding issues // is arc start and end point calculations

         // Output the Edge.Cuts perimeter as circle or polygon.
@@ -1034,8 +1051,10 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
         }
         else
         {
+            // Polygon start point. Arbitrarily chosen end of the
+            // segment and build the poly from here.
+
             wxPoint startPt = wxPoint( graphic->GetEnd() );
-
             prevPt = graphic->GetEnd();
             path->AppendPoint( mapPt( prevPt ) );

@@ -1050,15 +1069,17 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
                     {
                         wxPoint  nextPt;

- if( !close_enough( prevPt, graphic->GetStart(), prox ) ) + // Use the line segment end point furthest away from + // prevPt as we assume the other end to be ON prevPt or
+                        // very close to it.
+
+ if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
                         {
- wxASSERT( close_enough( prevPt, graphic->GetEnd(), prox ) );
-                            nextPt = graphic->GetStart();
+                            nextPt = graphic->GetEnd();
                         }
                         else
                         {
- wxASSERT( close_enough( prevPt, graphic->GetStart(), prox ) );
-                            nextPt = graphic->GetEnd();
+                            nextPt = graphic->GetStart();
                         }

                         path->AppendPoint( mapPt( nextPt ) );
@@ -1115,27 +1136,39 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
                     break;
                 }

- if( close_enough( startPt, prevPt, prox ) ) // the polygon is closed.
-                    break;
+                // Get next closest segment.

                 graphic = findPoint( prevPt, &items, prox );

-                if( !graphic )
+                // If there are no more close segments, check if the board
+                // outline polygon can be closed.
+
+                if( !graphic )
                 {
-                    wxString error = wxString::Format(
- _( "Unable to find the next segment with an endpoint of (%s mm, %s mm).\n" - "Edit Edge.Cuts perimeter graphics, making them contiguous polygons each." ), - GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ), - GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
+                    if( close_enough( startPt, prevPt, prox ) )
+                    {
+                        // Close the polygon back to start point
+                        path->AppendPoint( mapPt( startPt ) );
+                    }
+                    else
+                    {
+                        wxString error = wxString::Format(
+ _( "Unable to find the next boundary segment with an endpoint of (%s mm, %s mm).\n" + "Edit Edge.Cuts perimeter graphics, making them contiguous polygons each." ), + GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ), + GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
                         );
-                    ThrowIOError( error );
-                }
+                        ThrowIOError( error );
+                    }
+                    break;
+                }
             }
         }

- // Output the interior Edge.Cuts graphics as keepouts, using nearness metric
-        // for sloppy graphical items.
-        prox = Millimeter2iu( 0.25 );
+ // Output the interior Edge.Cuts graphics as keepouts, using same nearness + // metric as the board edge as otherwise we have trouble completing complex
+        // polygons.
+        prox = Millimeter2iu( 0.05 );

         while( items.GetCount() )
         {
@@ -1155,6 +1188,9 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
             }
             else
             {
+                // Polygon start point. Arbitrarily chosen end of the
+                // segment and build the poly from here.
+
                 wxPoint startPt( graphic->GetEnd() );
                 prevPt = graphic->GetEnd();
                 poly_ko->AppendPoint( mapPt( prevPt ) );
@@ -1168,15 +1204,17 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
                         {
                             wxPoint nextPt;

- if( !close_enough( prevPt, graphic->GetStart(), prox ) ) + // Use the line segment end point furthest away from + // prevPt as we assume the other end to be ON prevPt or
+                            // very close to it.
+
+ if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
                             {
- wxASSERT( close_enough( prevPt, graphic->GetEnd(), prox ) );
-                                nextPt = graphic->GetStart();
+                                nextPt = graphic->GetEnd();
                             }
                             else
                             {
- wxASSERT( close_enough( prevPt, graphic->GetStart(), prox ) );
-                                nextPt = graphic->GetEnd();
+                                nextPt = graphic->GetStart();
                             }

                             prevPt = nextPt;
@@ -1185,7 +1223,7 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
                         break;

                     case S_ARC:
- // freerouter does not yet understand arcs, so approximate + // Freerouter does not yet understand arcs, so approximate // an arc with a series of short lines and put those
                         // line segments into the !same! PATH.
                         {
@@ -1234,21 +1272,32 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
                         break;
                     }

-                    if( close_enough( startPt, prevPt, prox ) )
-                        break;
-
+                    // Get next closest segment.
+
                     graphic = findPoint( prevPt, &items, prox );

+ // If there are no more close segments, check if polygon
+                    // can be closed.
+
                     if( !graphic )
                     {
-                        wxString error = wxString::Format(
- _( "Unable to find the next segment with an endpoint of (%s mm, %s mm).\n" - "Edit Edge.Cuts interior graphics, making them contiguous polygons each." ), - GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ), - GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
+                        if( close_enough( startPt, prevPt, prox ) )
+                        {
+                            // Close the polygon back to start point
+                            poly_ko->AppendPoint( mapPt( startPt ) );
+                        }
+                        else
+                        {
+                            wxString error = wxString::Format(
+ _( "Unable to find the next keepout segment with an endpoint of (%s mm, %s mm).\n" + "Edit Edge.Cuts interior graphics, making them contiguous polygons each." ), + GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ), + GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
                             );

-                        ThrowIOError( error );
+                            ThrowIOError( error );
+                        }
+                        break;
                     }
                 }
             }



Follow ups