1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
5  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
7  *
8  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, you may find one here:
22  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23  * or you may search the http://www.gnu.org website for the version 2 license,
24  * or you may write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
26  */
27 
28 #include <confirm.h>
29 #include <pcbnew.h>
30 #include <pcb_edit_frame.h>
31 #include <widgets/msgpanel.h>
32 #include <board.h>
33 #include <footprint.h>
34 #include <pcb_shape.h>
35 #include <pad.h>
36 #include <board_commit.h>
37 #include <connectivity/connectivity_data.h>
38 #include <progress_reporter.h>
39 
40 #include "ar_autoplacer.h"
41 #include "ar_matrix.h"
42 #include <memory>
43 #include <ratsnest/ratsnest_data.h>
44 
45 #define AR_GAIN            16
46 #define AR_KEEPOUT_MARGIN  500
47 #define AR_ABORT_PLACEMENT -1
48 
49 #define STEP_AR_MM 1.0
50 
51 /* Bits characterizing cell */
52 #define CELL_IS_EMPTY  0x00
53 #define CELL_IS_HOLE   0x01   /* a conducting hole or obstacle */
54 #define CELL_IS_MODULE 0x02   /* auto placement occupied by a footprint */
55 #define CELL_IS_EDGE   0x20   /* Area and auto-placement: limiting cell contour (Board, Zone) */
56 #define CELL_IS_FRIEND 0x40   /* Area and auto-placement: cell part of the net */
57 #define CELL_IS_ZONE   0x80   /* Area and auto-placement: cell available */
58 
59 
60 /* Penalty (cost) for CntRot90 and CntRot180:
61  * CntRot90 and CntRot180 are from 0 (rotation allowed) to 10 (rotation not allowed)
62  */
63 static const double OrientationPenalty[11] =
64 {
65     2.0,        // CntRot = 0 rotation prohibited
66     1.9,        // CntRot = 1
67     1.8,        // CntRot = 2
68     1.7,        // CntRot = 3
69     1.6,        // CntRot = 4
70     1.5,        // CntRot = 5
71     1.4,        // CntRot = 5
72     1.3,        // CntRot = 7
73     1.2,        // CntRot = 8
74     1.1,        // CntRot = 9
75     1.0         // CntRot = 10 rotation authorized, no penalty
76 };
77 
78 
AR_AUTOPLACER(BOARD * aBoard)79 AR_AUTOPLACER::AR_AUTOPLACER( BOARD* aBoard )
80 {
81     m_board = aBoard;
82     m_connectivity = std::make_unique<CONNECTIVITY_DATA>( );
83 
84     for( FOOTPRINT* footprint : m_board->Footprints() )
85         m_connectivity->Add( footprint );
86 
87     m_gridSize = Millimeter2iu( STEP_AR_MM );
88     m_progressReporter = nullptr;
89     m_refreshCallback = nullptr;
90     m_minCost = 0.0;
91 }
92 
93 
placeFootprint(FOOTPRINT * aFootprint,bool aDoNotRecreateRatsnest,const wxPoint & aPos)94 void AR_AUTOPLACER::placeFootprint( FOOTPRINT* aFootprint, bool aDoNotRecreateRatsnest,
95                                     const wxPoint& aPos )
96 {
97     if( !aFootprint )
98         return;
99 
100     aFootprint->SetPosition( aPos );
101     m_connectivity->Update( aFootprint );
102 }
103 
104 
genPlacementRoutingMatrix()105 int AR_AUTOPLACER::genPlacementRoutingMatrix()
106 {
107     m_matrix.UnInitRoutingMatrix();
108 
109     EDA_RECT bbox = m_board->GetBoardEdgesBoundingBox();
110 
111     if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 )
112         return 0;
113 
114     // Build the board shape
115     m_board->GetBoardPolygonOutlines( m_boardShape );
116     m_topFreeArea = m_boardShape;
117     m_bottomFreeArea = m_boardShape;
118 
119     m_matrix.ComputeMatrixSize( bbox );
120     int nbCells = m_matrix.m_Ncols * m_matrix.m_Nrows;
121 
122     // Choose the number of board sides.
123     m_matrix.m_RoutingLayersCount = 2;
124     m_matrix.InitRoutingMatrix();
125     m_matrix.m_routeLayerBottom = B_Cu;
126     m_matrix.m_routeLayerTop = F_Cu;
127 
128     // Fill (mark) the cells inside the board:
129     fillMatrix();
130 
131     // Other obstacles can be added here:
132     for( auto drawing : m_board->Drawings() )
133     {
134         switch( drawing->Type() )
135         {
136         case PCB_SHAPE_T:
137             if( drawing->GetLayer() != Edge_Cuts )
138             {
139                 m_matrix.TraceSegmentPcb( (PCB_SHAPE*) drawing, CELL_IS_HOLE | CELL_IS_EDGE,
140                                           m_matrix.m_GridRouting, AR_MATRIX::WRITE_CELL );
141             }
142 
143             break;
144 
145         default:
146             break;
147         }
148     }
149 
150     // Initialize top layer. to the same value as the bottom layer
151     if( m_matrix.m_BoardSide[AR_SIDE_TOP] )
152         memcpy( m_matrix.m_BoardSide[AR_SIDE_TOP], m_matrix.m_BoardSide[AR_SIDE_BOTTOM],
153                 nbCells * sizeof(AR_MATRIX::MATRIX_CELL) );
154 
155     return 1;
156 }
157 
158 
fillMatrix()159 bool AR_AUTOPLACER::fillMatrix()
160 {
161     std::vector <int> x_coordinates;
162     bool success = true;
163     int step = m_matrix.m_GridRouting;
164     wxPoint coord_orgin = m_matrix.GetBrdCoordOrigin(); // Board coordinate of matruix cell (0,0)
165 
166     // Create a single board outline:
167     SHAPE_POLY_SET brd_shape = m_boardShape;
168     brd_shape.Fracture( SHAPE_POLY_SET::PM_FAST );
169     const SHAPE_LINE_CHAIN& outline = brd_shape.Outline(0);
170     const BOX2I& rect = outline.BBox();
171 
172     // Creates the horizontal segments
173     // Calculate the y limits of the area
174     for( int refy = rect.GetY(), endy = rect.GetBottom(); refy < endy; refy += step )
175     {
176         // The row index (vertical position) of current line scan inside the placement matrix
177         int idy = (refy - coord_orgin.y) / step;
178 
179         // Ensure we are still inside the placement matrix
180         if( idy >= m_matrix.m_Nrows )
181             break;
182 
183         // Ensure we are inside the placement matrix
184         if( idy <= 0 )
185             continue;
186 
187         // find all intersection points of an infinite line with polyline sides
188         x_coordinates.clear();
189 
190         for( int v = 0; v < outline.PointCount(); v++ )
191         {
192             int seg_startX = outline.CPoint( v ).x;
193             int seg_startY = outline.CPoint( v ).y;
194             int seg_endX   = outline.CPoint( v + 1 ).x;
195             int seg_endY   = outline.CPoint( v + 1 ).y;
196 
197             /* Trivial cases: skip if ref above or below the segment to test */
198             if( ( seg_startY > refy ) && ( seg_endY > refy ) )
199                 continue;
200 
201             // segment below ref point, or its Y end pos on Y coordinate ref point: skip
202             if( ( seg_startY <= refy ) && (seg_endY <= refy ) )
203                 continue;
204 
205             /* at this point refy is between seg_startY and seg_endY
206              * see if an horizontal line at Y = refy is intersecting this segment
207              */
208             // calculate the x position of the intersection of this segment and the
209             // infinite line this is more easier if we move the X,Y axis origin to
210             // the segment start point:
211 
212             seg_endX -= seg_startX;
213             seg_endY -= seg_startY;
214             double newrefy = (double) ( refy - seg_startY );
215             double intersec_x;
216 
217             if ( seg_endY == 0 )    // horizontal segment on the same line: skip
218                 continue;
219 
220             // Now calculate the x intersection coordinate of the horizontal line at
221             // y = newrefy and the segment from (0,0) to (seg_endX,seg_endY) with the
222             // horizontal line at the new refy position the line slope is:
223             // slope = seg_endY/seg_endX; and inv_slope = seg_endX/seg_endY
224             // and the x pos relative to the new origin is:
225             // intersec_x = refy/slope = refy * inv_slope
226             // Note: because horizontal segments are already tested and skipped, slope
227             // exists (seg_end_y not O)
228             double inv_slope = (double) seg_endX / seg_endY;
229             intersec_x = newrefy * inv_slope;
230             x_coordinates.push_back( (int) intersec_x + seg_startX );
231         }
232 
233         // A line scan is finished: build list of segments
234 
235         // Sort intersection points by increasing x value:
236         // So 2 consecutive points are the ends of a segment
237         std::sort( x_coordinates.begin(), x_coordinates.end() );
238 
239         // An even number of coordinates is expected, because a segment has 2 ends.
240         // An if this algorithm always works, it must always find an even count.
241         if( ( x_coordinates.size() & 1 ) != 0 )
242         {
243             success = false;
244             break;
245         }
246 
247         // Fill cells having the same Y coordinate
248         int iimax = x_coordinates.size() - 1;
249 
250         for( int ii = 0; ii < iimax; ii += 2 )
251         {
252             int seg_start_x = x_coordinates[ii] - coord_orgin.x;
253             int seg_end_x = x_coordinates[ii + 1] - coord_orgin.x;
254 
255             // Fill cells at y coord = idy,
256             // and at x cood >= seg_start_x and <= seg_end_x
257 
258             for( int idx = seg_start_x / step; idx < m_matrix.m_Ncols; idx++ )
259             {
260                 if( idx * step > seg_end_x )
261                     break;
262 
263                 if( idx * step >= seg_start_x )
264                     m_matrix.SetCell( idy, idx, AR_SIDE_BOTTOM, CELL_IS_ZONE );
265             }
266         }
267     }   // End examine segments in one area
268 
269     return success;
270 }
271 
272 
rotateFootprint(FOOTPRINT * aFootprint,double angle,bool incremental)273 void AR_AUTOPLACER::rotateFootprint( FOOTPRINT* aFootprint, double angle, bool incremental )
274 {
275     if( aFootprint == nullptr )
276         return;
277 
278     if( incremental )
279         aFootprint->SetOrientation( aFootprint->GetOrientation() + angle );
280     else
281         aFootprint->SetOrientation( angle );
282 
283 
284     m_board->GetConnectivity()->Update( aFootprint );
285 }
286 
287 
addFpBody(const wxPoint & aStart,const wxPoint & aEnd,LSET aLayerMask)288 void AR_AUTOPLACER::addFpBody( const wxPoint& aStart, const wxPoint& aEnd, LSET aLayerMask )
289 {
290     // Add a polygonal shape (rectangle) to m_fpAreaFront and/or m_fpAreaBack
291     if( aLayerMask[ F_Cu ] )
292     {
293         m_fpAreaTop.NewOutline();
294         m_fpAreaTop.Append( aStart.x, aStart.y );
295         m_fpAreaTop.Append( aEnd.x, aStart.y );
296         m_fpAreaTop.Append( aEnd.x, aEnd.y );
297         m_fpAreaTop.Append( aStart.x, aEnd.y );
298     }
299 
300     if( aLayerMask[ B_Cu ] )
301     {
302         m_fpAreaBottom.NewOutline();
303         m_fpAreaBottom.Append( aStart.x, aStart.y );
304         m_fpAreaBottom.Append( aEnd.x, aStart.y );
305         m_fpAreaBottom.Append( aEnd.x, aEnd.y );
306         m_fpAreaBottom.Append( aStart.x, aEnd.y );
307     }
308 }
309 
310 
addPad(PAD * aPad,int aClearance)311 void AR_AUTOPLACER::addPad( PAD* aPad, int aClearance )
312 {
313     // Add a polygonal shape (rectangle) to m_fpAreaFront and/or m_fpAreaBack
314     EDA_RECT bbox = aPad->GetBoundingBox();
315     bbox.Inflate( aClearance );
316 
317     if( aPad->IsOnLayer( F_Cu ) )
318     {
319         m_fpAreaTop.NewOutline();
320         m_fpAreaTop.Append( bbox.GetLeft(), bbox.GetTop() );
321         m_fpAreaTop.Append( bbox.GetRight(), bbox.GetTop() );
322         m_fpAreaTop.Append( bbox.GetRight(), bbox.GetBottom() );
323         m_fpAreaTop.Append( bbox.GetLeft(), bbox.GetBottom() );
324     }
325 
326     if( aPad->IsOnLayer( B_Cu ) )
327     {
328         m_fpAreaBottom.NewOutline();
329         m_fpAreaBottom.Append( bbox.GetLeft(), bbox.GetTop() );
330         m_fpAreaBottom.Append( bbox.GetRight(), bbox.GetTop() );
331         m_fpAreaBottom.Append( bbox.GetRight(), bbox.GetBottom() );
332         m_fpAreaBottom.Append( bbox.GetLeft(), bbox.GetBottom() );
333     }
334 }
335 
336 
buildFpAreas(FOOTPRINT * aFootprint,int aFpClearance)337 void AR_AUTOPLACER::buildFpAreas( FOOTPRINT* aFootprint, int aFpClearance )
338 {
339     m_fpAreaTop.RemoveAllContours();
340     m_fpAreaBottom.RemoveAllContours();
341 
342     aFootprint->BuildPolyCourtyards();
343     m_fpAreaTop = aFootprint->GetPolyCourtyard( F_CrtYd );
344     m_fpAreaBottom = aFootprint->GetPolyCourtyard( B_CrtYd );
345 
346     LSET layerMask;
347 
348     if( aFootprint->GetLayer() == F_Cu )
349         layerMask.set( F_Cu );
350 
351     if( aFootprint->GetLayer() == B_Cu )
352         layerMask.set( B_Cu );
353 
354     EDA_RECT fpBBox = aFootprint->GetBoundingBox();
355 
356     fpBBox.Inflate( ( m_matrix.m_GridRouting / 2 ) + aFpClearance );
357 
358     // Add a minimal area to the fp area:
359     addFpBody( fpBBox.GetOrigin(), fpBBox.GetEnd(), layerMask );
360 
361     // Trace pads + clearance areas.
362     for( PAD* pad : aFootprint->Pads() )
363     {
364         int margin = (m_matrix.m_GridRouting / 2) + pad->GetOwnClearance( pad->GetLayer() );
365         addPad( pad, margin );
366     }
367 }
368 
369 
genModuleOnRoutingMatrix(FOOTPRINT * Module)370 void AR_AUTOPLACER::genModuleOnRoutingMatrix( FOOTPRINT* Module )
371 {
372     int         ox, oy, fx, fy;
373     LSET        layerMask;
374     EDA_RECT    fpBBox = Module->GetBoundingBox();
375 
376     fpBBox.Inflate( m_matrix.m_GridRouting / 2 );
377     ox  = fpBBox.GetX();
378     fx  = fpBBox.GetRight();
379     oy  = fpBBox.GetY();
380     fy  = fpBBox.GetBottom();
381 
382     if( ox < m_matrix.m_BrdBox.GetX() )
383         ox = m_matrix.m_BrdBox.GetX();
384 
385     if( ox > m_matrix.m_BrdBox.GetRight() )
386         ox = m_matrix.m_BrdBox.GetRight();
387 
388     if( fx < m_matrix.m_BrdBox.GetX() )
389         fx = m_matrix.m_BrdBox.GetX();
390 
391     if( fx > m_matrix.m_BrdBox.GetRight() )
392         fx = m_matrix.m_BrdBox.GetRight();
393 
394     if( oy < m_matrix.m_BrdBox.GetY() )
395         oy = m_matrix.m_BrdBox.GetY();
396 
397     if( oy > m_matrix.m_BrdBox.GetBottom() )
398         oy = m_matrix.m_BrdBox.GetBottom();
399 
400     if( fy < m_matrix.m_BrdBox.GetY() )
401         fy = m_matrix.m_BrdBox.GetY();
402 
403     if( fy > m_matrix.m_BrdBox.GetBottom() )
404         fy = m_matrix.m_BrdBox.GetBottom();
405 
406     if( Module->GetLayer() == F_Cu )
407         layerMask.set( F_Cu );
408 
409     if( Module->GetLayer() == B_Cu )
410         layerMask.set( B_Cu );
411 
412     m_matrix.TraceFilledRectangle( ox, oy, fx, fy, layerMask,
413                                    CELL_IS_MODULE, AR_MATRIX::WRITE_OR_CELL );
414 
415     // Trace pads + clearance areas.
416     for( PAD* pad : Module->Pads() )
417     {
418         int margin = (m_matrix.m_GridRouting / 2) + pad->GetOwnClearance( pad->GetLayer() );
419         m_matrix.PlacePad( pad, CELL_IS_MODULE, margin, AR_MATRIX::WRITE_OR_CELL );
420     }
421 
422     // Trace clearance.
423     int margin = ( m_matrix.m_GridRouting * Module->GetPadCount() ) / AR_GAIN;
424     m_matrix.CreateKeepOutRectangle( ox, oy, fx, fy, margin, AR_KEEPOUT_MARGIN , layerMask );
425 
426     // Build the footprint courtyard
427     buildFpAreas( Module, margin );
428 
429     // Substract the shape to free areas
430     m_topFreeArea.BooleanSubtract( m_fpAreaTop, SHAPE_POLY_SET::PM_FAST );
431     m_bottomFreeArea.BooleanSubtract( m_fpAreaBottom, SHAPE_POLY_SET::PM_FAST );
432 }
433 
434 
testRectangle(const EDA_RECT & aRect,int side)435 int AR_AUTOPLACER::testRectangle( const EDA_RECT& aRect, int side )
436 {
437     EDA_RECT rect = aRect;
438 
439     rect.Inflate( m_matrix.m_GridRouting / 2 );
440 
441     wxPoint start   = rect.GetOrigin();
442     wxPoint end     = rect.GetEnd();
443 
444     start   -= m_matrix.m_BrdBox.GetOrigin();
445     end     -= m_matrix.m_BrdBox.GetOrigin();
446 
447     int row_min = start.y / m_matrix.m_GridRouting;
448     int row_max = end.y / m_matrix.m_GridRouting;
449     int col_min = start.x / m_matrix.m_GridRouting;
450     int col_max = end.x / m_matrix.m_GridRouting;
451 
452     if( start.y > row_min * m_matrix.m_GridRouting )
453         row_min++;
454 
455     if( start.x > col_min * m_matrix.m_GridRouting )
456         col_min++;
457 
458     if( row_min < 0 )
459         row_min = 0;
460 
461     if( row_max >= ( m_matrix.m_Nrows - 1 ) )
462         row_max = m_matrix.m_Nrows - 1;
463 
464     if( col_min < 0 )
465         col_min = 0;
466 
467     if( col_max >= ( m_matrix.m_Ncols - 1 ) )
468         col_max = m_matrix.m_Ncols - 1;
469 
470     for( int row = row_min; row <= row_max; row++ )
471     {
472         for( int col = col_min; col <= col_max; col++ )
473         {
474             unsigned int data = m_matrix.GetCell( row, col, side );
475 
476             if( ( data & CELL_IS_ZONE ) == 0 )
477                 return AR_OUT_OF_BOARD;
478 
479             if( (data & CELL_IS_MODULE) )
480                 return AR_OCCUIPED_BY_MODULE;
481         }
482     }
483 
484     return AR_FREE_CELL;
485 }
486 
487 
calculateKeepOutArea(const EDA_RECT & aRect,int side)488 unsigned int AR_AUTOPLACER::calculateKeepOutArea( const EDA_RECT& aRect, int side )
489 {
490     wxPoint start   = aRect.GetOrigin();
491     wxPoint end     = aRect.GetEnd();
492 
493     start   -= m_matrix.m_BrdBox.GetOrigin();
494     end     -= m_matrix.m_BrdBox.GetOrigin();
495 
496     int row_min = start.y / m_matrix.m_GridRouting;
497     int row_max = end.y / m_matrix.m_GridRouting;
498     int col_min = start.x / m_matrix.m_GridRouting;
499     int col_max = end.x / m_matrix.m_GridRouting;
500 
501     if( start.y > row_min * m_matrix.m_GridRouting )
502         row_min++;
503 
504     if( start.x > col_min * m_matrix.m_GridRouting )
505         col_min++;
506 
507     if( row_min < 0 )
508         row_min = 0;
509 
510     if( row_max >= ( m_matrix.m_Nrows - 1 ) )
511         row_max = m_matrix.m_Nrows - 1;
512 
513     if( col_min < 0 )
514         col_min = 0;
515 
516     if( col_max >= ( m_matrix.m_Ncols - 1 ) )
517         col_max = m_matrix.m_Ncols - 1;
518 
519     unsigned int keepOutCost = 0;
520 
521     for( int row = row_min; row <= row_max; row++ )
522     {
523         for( int col = col_min; col <= col_max; col++ )
524         {
525             // m_matrix.GetDist returns the "cost" of the cell
526             // at position (row, col)
527             // in autoplace this is the cost of the cell, if it is
528             // inside aRect
529             keepOutCost += m_matrix.GetDist( row, col, side );
530         }
531     }
532 
533     return keepOutCost;
534 }
535 
536 
testFootprintOnBoard(FOOTPRINT * aFootprint,bool TstOtherSide,const wxPoint & aOffset)537 int AR_AUTOPLACER::testFootprintOnBoard( FOOTPRINT* aFootprint, bool TstOtherSide,
538                                          const wxPoint& aOffset )
539 {
540     int side = AR_SIDE_TOP;
541     int otherside = AR_SIDE_BOTTOM;
542 
543     if( aFootprint->GetLayer() == B_Cu )
544     {
545         side = AR_SIDE_BOTTOM; otherside = AR_SIDE_TOP;
546     }
547 
548     EDA_RECT    fpBBox = aFootprint->GetBoundingBox( false, false );
549     fpBBox.Move( -aOffset );
550 
551     buildFpAreas( aFootprint, 0 );
552 
553     int diag = //testModuleByPolygon( aFootprint, side, aOffset );
554         testRectangle( fpBBox, side );
555 
556     if( diag != AR_FREE_CELL )
557         return diag;
558 
559     if( TstOtherSide )
560     {
561         diag = //testModuleByPolygon( aFootprint, otherside, aOffset );
562                 testRectangle( fpBBox, otherside );
563 
564         if( diag != AR_FREE_CELL )
565             return diag;
566     }
567 
568     int marge = ( m_matrix.m_GridRouting * aFootprint->GetPadCount() ) / AR_GAIN;
569 
570     fpBBox.Inflate( marge );
571     return calculateKeepOutArea( fpBBox, side );
572 }
573 
574 
getOptimalFPPlacement(FOOTPRINT * aFootprint)575 int AR_AUTOPLACER::getOptimalFPPlacement( FOOTPRINT* aFootprint )
576 {
577     int     error = 1;
578     wxPoint lastPosOK;
579     double  min_cost, curr_cost, Score;
580     bool    testOtherSide;
581 
582     lastPosOK = m_matrix.m_BrdBox.GetOrigin();
583 
584     wxPoint  fpPos = aFootprint->GetPosition();
585     EDA_RECT fpBBox  = aFootprint->GetBoundingBox( false, false );
586 
587     // Move fpBBox to have the footprint position at (0,0)
588     fpBBox.Move( -fpPos );
589     wxPoint fpBBoxOrg = fpBBox.GetOrigin();
590 
591     // Calculate the limit of the footprint position, relative to the routing matrix area
592     wxPoint xylimit = m_matrix.m_BrdBox.GetEnd() - fpBBox.GetEnd();
593 
594     wxPoint initialPos = m_matrix.m_BrdBox.GetOrigin() - fpBBoxOrg;
595 
596     // Stay on grid.
597     initialPos.x    -= initialPos.x % m_matrix.m_GridRouting;
598     initialPos.y    -= initialPos.y % m_matrix.m_GridRouting;
599 
600     m_curPosition = initialPos;
601     wxPoint fpOffset = fpPos - m_curPosition;
602 
603     // Examine pads, and set testOtherSide to true if a footprint has at least 1 pad through.
604     testOtherSide = false;
605 
606     if( m_matrix.m_RoutingLayersCount > 1 )
607     {
608         LSET other( aFootprint->GetLayer() == B_Cu ? F_Cu : B_Cu );
609 
610         for( PAD* pad : aFootprint->Pads() )
611         {
612             if( !( pad->GetLayerSet() & other ).any() )
613                 continue;
614 
615             testOtherSide = true;
616             break;
617         }
618     }
619 
620     fpBBox.SetOrigin( fpBBoxOrg + m_curPosition );
621 
622     min_cost = -1.0;
623 //    m_frame->SetStatusText( wxT( "Score ??, pos ??" ) );
624 
625 
626     for( ; m_curPosition.x < xylimit.x; m_curPosition.x += m_matrix.m_GridRouting )
627     {
628         m_curPosition.y = initialPos.y;
629 
630         for( ; m_curPosition.y < xylimit.y; m_curPosition.y += m_matrix.m_GridRouting )
631         {
632 
633             fpBBox.SetOrigin( fpBBoxOrg + m_curPosition );
634             fpOffset = fpPos - m_curPosition;
635             int keepOutCost = testFootprintOnBoard( aFootprint, testOtherSide, fpOffset );
636 
637             if( keepOutCost >= 0 )    // i.e. if the footprint can be put here
638             {
639                 error = 0;
640                 // m_frame->build_ratsnest_footprint( aFootprint ); // fixme
641                 curr_cost   = computePlacementRatsnestCost( aFootprint, fpOffset );
642                 Score       = curr_cost + keepOutCost;
643 
644                 if( (min_cost >= Score ) || (min_cost < 0 ) )
645                 {
646                     lastPosOK   = m_curPosition;
647                     min_cost    = Score;
648                     wxString msg;
649 /*                    msg.Printf( wxT( "Score %g, pos %s, %s" ),
650                                 min_cost,
651                                 GetChars( ::CoordinateToString( LastPosOK.x ) ),
652                                 GetChars( ::CoordinateToString( LastPosOK.y ) ) );
653                     m_frame->SetStatusText( msg );*/
654                 }
655             }
656         }
657     }
658 
659     // Regeneration of the modified variable.
660     m_curPosition = lastPosOK;
661 
662     m_minCost = min_cost;
663     return error;
664 }
665 
666 
nearestPad(FOOTPRINT * aRefFP,PAD * aRefPad,const wxPoint & aOffset)667 const PAD* AR_AUTOPLACER::nearestPad( FOOTPRINT *aRefFP, PAD* aRefPad, const wxPoint& aOffset)
668 {
669     const PAD* nearest = nullptr;
670     int64_t    nearestDist = INT64_MAX;
671 
672     for( FOOTPRINT* footprint : m_board->Footprints() )
673     {
674         if ( footprint == aRefFP )
675             continue;
676 
677         if( !m_matrix.m_BrdBox.Contains( footprint->GetPosition() ) )
678             continue;
679 
680         for( PAD* pad: footprint->Pads() )
681         {
682             if( pad->GetNetCode() != aRefPad->GetNetCode() || pad->GetNetCode() <= 0 )
683                 continue;
684 
685             auto dist = ( VECTOR2I( aRefPad->GetPosition() - aOffset ) -
686                           VECTOR2I( pad->GetPosition() ) ).EuclideanNorm();
687 
688             if ( dist < nearestDist )
689             {
690                 nearestDist = dist;
691                 nearest = pad;
692             }
693         }
694     }
695 
696     return nearest;
697 }
698 
699 
computePlacementRatsnestCost(FOOTPRINT * aFootprint,const wxPoint & aOffset)700 double AR_AUTOPLACER::computePlacementRatsnestCost( FOOTPRINT *aFootprint, const wxPoint& aOffset )
701 {
702     double  curr_cost;
703     VECTOR2I start;      // start point of a ratsnest
704     VECTOR2I end;        // end point of a ratsnest
705     int     dx, dy;
706 
707     curr_cost = 0;
708 
709     for( PAD* pad : aFootprint->Pads() )
710     {
711         const PAD* nearest = nearestPad( aFootprint, pad, aOffset );
712 
713         if( !nearest )
714             continue;
715 
716         start   = VECTOR2I( pad->GetPosition() ) - VECTOR2I(aOffset);
717         end     = VECTOR2I( nearest->GetPosition() );
718 
719         //m_overlay->SetIsStroke( true );
720         //m_overlay->SetStrokeColor( COLOR4D(0.0, 1.0, 0.0, 1.0) );
721         //m_overlay->Line( start, end );
722 
723         // Cost of the ratsnest.
724         dx  = end.x - start.x;
725         dy  = end.y - start.y;
726 
727         dx  = abs( dx );
728         dy  = abs( dy );
729 
730         // ttry to have always dx >= dy to calculate the cost of the ratsnest
731         if( dx < dy )
732             std::swap( dx, dy );
733 
734         // Cost of the connection = length + penalty due to the slope
735         // dx is the biggest length relative to the X or Y axis
736         // the penalty is max for 45 degrees ratsnests,
737         // and 0 for horizontal or vertical ratsnests.
738         // For Horizontal and Vertical ratsnests, dy = 0;
739         double conn_cost = hypot( dx, dy * 2.0 );
740         curr_cost += conn_cost;    // Total cost = sum of costs of each connection
741     }
742 
743     return curr_cost;
744 }
745 
746 
747 // Sort routines
sortFootprintsByComplexity(FOOTPRINT * ref,FOOTPRINT * compare)748 static bool sortFootprintsByComplexity( FOOTPRINT* ref, FOOTPRINT* compare )
749 {
750     double ff1, ff2;
751 
752     ff1 = ref->GetArea() * ref->GetPadCount();
753     ff2 = compare->GetArea() * compare->GetPadCount();
754 
755     return ff2 < ff1;
756 }
757 
758 
sortFootprintsByRatsnestSize(FOOTPRINT * ref,FOOTPRINT * compare)759 static bool sortFootprintsByRatsnestSize( FOOTPRINT* ref, FOOTPRINT* compare )
760 {
761     double ff1, ff2;
762 
763     ff1 = ref->GetArea() * ref->GetFlag();
764     ff2 = compare->GetArea() * compare->GetFlag();
765     return ff2 < ff1;
766 }
767 
768 
pickFootprint()769 FOOTPRINT* AR_AUTOPLACER::pickFootprint()
770 {
771     std::vector<FOOTPRINT*> fpList;
772 
773 
774     for( FOOTPRINT* footprint : m_board->Footprints() )
775         fpList.push_back( footprint );
776 
777     sort( fpList.begin(), fpList.end(), sortFootprintsByComplexity );
778 
779     for( unsigned kk = 0; kk < fpList.size(); kk++ )
780     {
781         FOOTPRINT* footprint = fpList[kk];
782         footprint->SetFlag( 0 );
783 
784         if( !footprint->NeedsPlaced() )
785             continue;
786 
787         m_connectivity->Update( footprint );
788     }
789 
790     m_connectivity->RecalculateRatsnest();
791 
792     for( unsigned kk = 0; kk < fpList.size(); kk++ )
793     {
794         FOOTPRINT* footprint = fpList[kk];
795 
796         auto edges = m_connectivity->GetRatsnestForComponent( footprint, true );
797 
798         footprint->SetFlag( edges.size() ) ;
799     }
800 
801     sort( fpList.begin(), fpList.end(), sortFootprintsByRatsnestSize );
802 
803     // Search for "best" footprint.
804     FOOTPRINT* bestFootprint  = nullptr;
805     FOOTPRINT* altFootprint   = nullptr;
806 
807     for( unsigned ii = 0; ii < fpList.size(); ii++ )
808     {
809         FOOTPRINT* footprint = fpList[ii];
810 
811         if( !footprint->NeedsPlaced() )
812             continue;
813 
814         altFootprint = footprint;
815 
816         if( footprint->GetFlag() == 0 )
817             continue;
818 
819         bestFootprint = footprint;
820         break;
821     }
822 
823     if( bestFootprint )
824         return bestFootprint;
825     else
826         return altFootprint;
827 }
828 
829 
drawPlacementRoutingMatrix()830 void AR_AUTOPLACER::drawPlacementRoutingMatrix( )
831 {
832     // Draw the board free area
833     m_overlay->Clear();
834     m_overlay->SetIsFill( true );
835     m_overlay->SetIsStroke( false );
836 
837     SHAPE_POLY_SET freeArea = m_topFreeArea;
838     freeArea.Fracture( SHAPE_POLY_SET::PM_FAST );
839 
840     // Draw the free polygon areas, top side:
841     if( freeArea.OutlineCount() > 0 )
842     {
843         m_overlay->SetIsFill( true );
844         m_overlay->SetIsStroke( false );
845         m_overlay->SetFillColor( COLOR4D(0.7, 0.0, 0.1, 0.2) );
846         m_overlay->Polygon( freeArea );
847     }
848 
849     freeArea = m_bottomFreeArea;
850     freeArea.Fracture( SHAPE_POLY_SET::PM_FAST );
851 
852     // Draw the free polygon areas, bottom side:
853     if( freeArea.OutlineCount() > 0 )
854     {
855         m_overlay->SetFillColor( COLOR4D(0.0, 0.7, 0.0, 0.2) );
856         m_overlay->Polygon( freeArea );
857     }
858 }
859 
860 
AutoplaceFootprints(std::vector<FOOTPRINT * > & aFootprints,BOARD_COMMIT * aCommit,bool aPlaceOffboardModules)861 AR_RESULT AR_AUTOPLACER::AutoplaceFootprints( std::vector<FOOTPRINT*>& aFootprints,
862                                               BOARD_COMMIT* aCommit,
863                                               bool aPlaceOffboardModules )
864 {
865     wxPoint memopos;
866     int     error;
867     bool    cancelled = false;
868 
869     memopos = m_curPosition;
870 
871     m_matrix.m_GridRouting = m_gridSize; //(int) m_frame->GetScreen()->GetGridSize().x;
872 
873     // Ensure Board.m_GridRouting has a reasonable value:
874     if( m_matrix.m_GridRouting < Millimeter2iu( 0.25 ) )
875         m_matrix.m_GridRouting = Millimeter2iu( 0.25 );
876 
877     // Compute footprint parameters used in autoplace
878     if( genPlacementRoutingMatrix( ) == 0 )
879         return AR_FAILURE;
880 
881     int placedCount = 0;
882 
883     for( FOOTPRINT* footprint : m_board->Footprints() )
884         footprint->SetNeedsPlaced( false );
885 
886     std::vector<FOOTPRINT*> offboardMods;
887 
888     if( aPlaceOffboardModules )
889     {
890         for( FOOTPRINT* footprint : m_board->Footprints() )
891         {
892             if( !m_matrix.m_BrdBox.Contains( footprint->GetPosition() ) )
893                 offboardMods.push_back( footprint );
894         }
895     }
896 
897     for( FOOTPRINT* footprint : aFootprints )
898     {
899         footprint->SetNeedsPlaced( true );
900         aCommit->Modify( footprint );
901     }
902 
903     for( FOOTPRINT* footprint : offboardMods )
904     {
905         footprint->SetNeedsPlaced( true );
906         aCommit->Modify( footprint );
907     }
908 
909     for( FOOTPRINT* footprint : m_board->Footprints() )
910     {
911         if( footprint->NeedsPlaced() )    // Erase from screen
912             placedCount++;
913         else
914             genModuleOnRoutingMatrix( footprint );
915     }
916 
917 
918     int         cnt = 0;
919     wxString    msg;
920 
921     if( m_progressReporter )
922     {
923         m_progressReporter->Report( _( "Autoplacing components..." ) );
924         m_progressReporter->SetMaxProgress( placedCount );
925     }
926 
927     drawPlacementRoutingMatrix();
928 
929     if( m_refreshCallback )
930         m_refreshCallback( nullptr );
931 
932     FOOTPRINT* footprint;
933 
934     while( ( footprint = pickFootprint() ) != nullptr )
935     {
936         // Display some info about activity, footprint placement can take a while:
937         //m_frame->SetStatusText( msg );
938 
939         if( m_progressReporter )
940             m_progressReporter->SetTitle( wxString::Format( _( "Autoplacing %s" ),
941                                                             footprint->GetReference() ) );
942 
943         double initialOrient = footprint->GetOrientation();
944 
945         error = getOptimalFPPlacement( footprint );
946         double bestScore = m_minCost;
947         double bestRotation = 0.0;
948         int rotAllowed;
949 
950         if( error == AR_ABORT_PLACEMENT )
951             goto end_of_tst;
952 
953         // Try orientations 90, 180, 270 degrees from initial orientation
954         rotAllowed = footprint->GetPlacementCost180();
955 
956         if( rotAllowed != 0 )
957         {
958             rotateFootprint( footprint, 1800.0, true );
959             error   = getOptimalFPPlacement( footprint );
960             m_minCost *= OrientationPenalty[rotAllowed];
961 
962             if( bestScore > m_minCost )    // This orientation is better.
963             {
964                 bestScore   = m_minCost;
965                 bestRotation = 1800.0;
966             }
967             else
968             {
969                 rotateFootprint( footprint, initialOrient, false );
970             }
971 
972             if( error == AR_ABORT_PLACEMENT )
973                 goto end_of_tst;
974         }
975 
976         // Determine if the best orientation of a footprint is 90.
977         rotAllowed = footprint->GetPlacementCost90();
978 
979         if( rotAllowed != 0 )
980         {
981             rotateFootprint( footprint, 900.0, true );
982             error   = getOptimalFPPlacement( footprint );
983             m_minCost *= OrientationPenalty[rotAllowed];
984 
985             if( bestScore > m_minCost )    // This orientation is better.
986             {
987                 bestScore   = m_minCost;
988                 bestRotation = 900.0;
989             }
990             else
991             {
992                 rotateFootprint( footprint, initialOrient, false );
993             }
994 
995             if( error == AR_ABORT_PLACEMENT )
996                 goto end_of_tst;
997         }
998 
999         // Determine if the best orientation of a footprint is -90.
1000         if( rotAllowed != 0 )
1001         {
1002             rotateFootprint( footprint, 2700.0, true );
1003             error = getOptimalFPPlacement( footprint );
1004             m_minCost *= OrientationPenalty[rotAllowed];
1005 
1006             if( bestScore > m_minCost )    // This orientation is better.
1007             {
1008                 bestScore   = m_minCost;
1009                 bestRotation = 2700.0;
1010             }
1011             else
1012             {
1013                 rotateFootprint( footprint, initialOrient, false );
1014             }
1015 
1016             if( error == AR_ABORT_PLACEMENT )
1017                 goto end_of_tst;
1018         }
1019 
1020 end_of_tst:
1021 
1022         if( error == AR_ABORT_PLACEMENT )
1023             break;
1024 
1025         bestRotation += initialOrient;
1026 
1027         if( bestRotation != footprint->GetOrientation() )
1028         {
1029             rotateFootprint( footprint, bestRotation, false );
1030         }
1031 
1032         // Place footprint.
1033         placeFootprint( footprint, true, m_curPosition );
1034 
1035         genModuleOnRoutingMatrix( footprint );
1036         footprint->SetIsPlaced( true );
1037         footprint->SetNeedsPlaced( false );
1038         drawPlacementRoutingMatrix();
1039 
1040         if( m_refreshCallback )
1041             m_refreshCallback( footprint );
1042 
1043         if( m_progressReporter )
1044         {
1045             m_progressReporter->AdvanceProgress();
1046 
1047             if ( !m_progressReporter->KeepRefreshing( false ) )
1048             {
1049                 cancelled = true;
1050                 break;
1051             }
1052         }
1053 
1054         cnt++;
1055     }
1056 
1057     m_curPosition = memopos;
1058 
1059     m_matrix.UnInitRoutingMatrix();
1060 
1061     return cancelled ? AR_CANCELLED : AR_COMPLETED;
1062 }
1063