KiCad PCB EDA Suite
Loading...
Searching...
No Matches
teardrop_utils.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2021 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25/*
26 * Some calculations (mainly computeCurvedForRoundShape) are derived from
27 * https://github.com/NilujePerchut/kicad_scripts/tree/master/teardrops
28 */
29
31#include <pcb_track.h>
32#include <pad.h>
33#include <zone_filler.h>
34#include <board_commit.h>
35#include <drc/drc_rtree.h>
36#include <trigo.h>
37
38#include "teardrop.h"
42#include <bezier_curves.h>
43
44#include <wx/log.h>
45
46
47void TRACK_BUFFER::AddTrack( PCB_TRACK* aTrack, int aLayer, int aNetcode )
48{
49 auto item = m_map_tracks.find( idxFromLayNet( aLayer, aNetcode ) );
50 std::vector<PCB_TRACK*>* buffer;
51
52 if( item == m_map_tracks.end() )
53 {
54 buffer = new std::vector<PCB_TRACK*>;
55 m_map_tracks[idxFromLayNet( aLayer, aNetcode )] = buffer;
56 }
57 else
58 {
59 buffer = (*item).second;
60 }
61
62 buffer->push_back( aTrack );
63}
64
65
67{
68 if( aItem->Type() == PCB_VIA_T )
69 {
70 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
71 return via->GetWidth( aLayer );
72 }
73 else if( aItem->Type() == PCB_PAD_T )
74 {
75 PAD* pad = static_cast<PAD*>( aItem );
76 return std::min( pad->GetSize( aLayer ).x, pad->GetSize( aLayer ).y );
77 }
78 else if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
79 {
80 PCB_TRACK* track = static_cast<PCB_TRACK*>( aItem );
81 return track->GetWidth();
82 }
83
84 return 0;
85}
86
87
89{
90 if( aItem->Type() == PCB_PAD_T )
91 {
92 PAD* pad = static_cast<PAD*>( aItem );
93
94 return pad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE
95 || ( pad->GetShape( aLayer ) == PAD_SHAPE::OVAL
96 && pad->GetSize( aLayer ).x
97 == pad->GetSize( aLayer ).y );
98 }
99
100 return true;
101}
102
103
105{
106 for( PCB_TRACK* track : m_board->Tracks() )
107 {
108 if( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T )
109 {
110 m_tracksRTree.Insert( track, track->GetLayer() );
111 m_trackLookupList.AddTrack( track, track->GetLayer(), track->GetNetCode() );
112 }
113 }
114
115 m_tracksRTree.Build();
116}
117
118
120{
121 PCB_LAYER_ID layer = aTrack->GetLayer();
122
123 for( ZONE* zone : m_board->Zones() )
124 {
125 // Skip teardrops
126 if( zone->IsTeardropArea() )
127 continue;
128
129 // Only consider zones on the same layer as the track
130 if( !zone->IsOnLayer( layer ) )
131 continue;
132
133 if( zone->GetNetCode() != aTrack->GetNetCode() )
134 continue;
135
136 // The zone must have filled copper on this layer to provide a connection
137 if( !zone->HasFilledPolysForLayer( layer ) )
138 continue;
139
140 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
141
142 if( !fill || fill->IsEmpty() )
143 continue;
144
145 // Check if the zone's filled copper actually contains both the pad/via and the track.
146 // The zone outline might contain these items, but the actual fill might not reach them
147 // due to thermal settings, minimum width, island removal, etc.
148 VECTOR2I padPos( aPadOrVia->GetPosition() );
149
150 if( !fill->Contains( padPos ) )
151 continue;
152
153 // Also verify the track is within the filled zone (check both endpoints)
154 if( !fill->Contains( aTrack->GetStart() ) && !fill->Contains( aTrack->GetEnd() ) )
155 continue;
156
157 // If the first item is a pad, ensure it can be connected to the zone
158 if( aPadOrVia->Type() == PCB_PAD_T )
159 {
160 PAD* pad = static_cast<PAD*>( aPadOrVia );
161
162 if( zone->GetPadConnection() == ZONE_CONNECTION::NONE
163 || pad->GetZoneConnectionOverrides( nullptr ) == ZONE_CONNECTION::NONE )
164 {
165 return false;
166 }
167 }
168
169 return true;
170 }
171
172 return false;
173}
174
175
177 const VECTOR2I& aEndPoint ) const
178{
179 int matches = 0; // Count of candidates: only 1 is acceptable
180 PCB_TRACK* candidate = nullptr; // a reference to the track connected
181
182 m_tracksRTree.QueryColliding( aTrackRef, aTrackRef->GetLayer(), aTrackRef->GetLayer(),
183 // Filter:
184 [&]( BOARD_ITEM* trackItem ) -> bool
185 {
186 return trackItem != aTrackRef;
187 },
188 // Visitor
189 [&]( BOARD_ITEM* trackItem ) -> bool
190 {
191 PCB_TRACK* curr_track = static_cast<PCB_TRACK*>( trackItem );
192
193 // IsPointOnEnds() returns 0, EDA_ITEM_FLAGS::STARTPOINT or EDA_ITEM_FLAGS::ENDPOINT
194 if( EDA_ITEM_FLAGS match = curr_track->IsPointOnEnds( aEndPoint, m_tolerance ) )
195 {
196 // if faced with a Y junction, choose the track longest segment as candidate
197 matches++;
198
199 if( matches > 1 )
200 {
201 double previous_len = candidate->GetLength();
202 double curr_len = curr_track->GetLength();
203
204 if( previous_len >= curr_len )
205 return true;
206 }
207
208 aMatchType = match;
209 candidate = curr_track;
210 }
211
212 return true;
213 },
214 0 );
215
216 return candidate;
217}
218
219
223static VECTOR2D NormalizeVector( const VECTOR2I& aVector )
224{
225 VECTOR2D vect( aVector );
226 double norm = vect.EuclideanNorm();
227 return vect / norm;
228}
229
230
231/*
232 * Compute the curve part points for teardrops connected to a round shape
233 * The Bezier curve control points are optimized for a round pad/via shape,
234 * and do not give a good curve shape for other pad shapes.
235 *
236 * For large circles where the teardrop width is constrained, the anchor points
237 * are projected onto the circle edge to ensure proper tangent calculation.
238 */
240 std::vector<VECTOR2I>& aPoly,
241 PCB_LAYER_ID aLayer,
242 int aTrackHalfWidth, const VECTOR2D& aTrackDir,
243 BOARD_ITEM* aOther, const VECTOR2I& aOtherPos,
244 std::vector<VECTOR2I>& pts ) const
245{
246 int maxError = m_board->GetDesignSettings().m_MaxError;
247
248 // in pts:
249 // A and B are points on the track ( pts[0] and pts[1] )
250 // C and E are points on the aViaPad ( pts[2] and pts[4] )
251 // D is the aViaPad centre ( pts[3] )
252 double Vpercent = aParams.m_BestWidthRatio;
253 int td_height = KiROUND( GetWidth( aOther, aLayer ) * Vpercent );
254
255 // First, calculate a aVpercent equivalent to the td_height clamped by aTdMaxHeight
256 // We cannot use the initial aVpercent because it gives bad shape with points
257 // on aViaPad calculated for a clamped aViaPad size
258 if( aParams.m_TdMaxWidth > 0 && aParams.m_TdMaxWidth < td_height )
259 Vpercent *= (double) aParams.m_TdMaxWidth / td_height;
260
261 int radius = GetWidth( aOther, aLayer ) / 2;
262
263 // Don't divide by zero. No good can come of that.
264 wxCHECK2( radius != 0, radius = 1 );
265
266 double minVpercent = double( aTrackHalfWidth ) / radius;
267 double weaken = (Vpercent - minVpercent) / ( 1 - minVpercent ) / radius;
268
269 // For large circles where teardrop width is constrained, the anchor points from the
270 // convex hull may not be exactly on the circle. Project them onto the circle edge
271 // to ensure proper tangent calculation for smooth curves.
272 VECTOR2I vecC = pts[2] - aOtherPos;
273 double distC = vecC.EuclideanNorm();
274
275 if( distC > 0 && std::abs( distC - radius ) > maxError )
276 {
277 // Point is not on the circle - project it to the circle edge
278 pts[2] = aOtherPos + vecC.Resize( radius );
279 vecC = pts[2] - aOtherPos;
280 }
281
282 VECTOR2I vecE = pts[4] - aOtherPos;
283 double distE = vecE.EuclideanNorm();
284
285 if( distE > 0 && std::abs( distE - radius ) > maxError )
286 {
287 // Point is not on the circle - project it to the circle edge
288 pts[4] = aOtherPos + vecE.Resize( radius );
289 vecE = pts[4] - aOtherPos;
290 }
291
292 double biasBC = 0.5 * SEG( pts[1], pts[2] ).Length();
293 double biasAE = 0.5 * SEG( pts[4], pts[0] ).Length();
294
295 VECTOR2I tangentC = VECTOR2I( pts[2].x - vecC.y * biasBC * weaken,
296 pts[2].y + vecC.x * biasBC * weaken );
297 VECTOR2I tangentE = VECTOR2I( pts[4].x + vecE.y * biasAE * weaken,
298 pts[4].y - vecE.x * biasAE * weaken );
299
300 VECTOR2I tangentB = VECTOR2I( pts[1].x - aTrackDir.x * biasBC, pts[1].y - aTrackDir.y * biasBC );
301 VECTOR2I tangentA = VECTOR2I( pts[0].x - aTrackDir.x * biasAE, pts[0].y - aTrackDir.y * biasAE );
302
303 std::vector<VECTOR2I> curve_pts;
304 BEZIER_POLY( pts[1], tangentB, tangentC, pts[2] ).GetPoly( curve_pts, maxError );
305
306 for( VECTOR2I& corner: curve_pts )
307 aPoly.push_back( corner );
308
309 aPoly.push_back( pts[3] );
310
311 curve_pts.clear();
312 BEZIER_POLY( pts[4], tangentE, tangentA, pts[0] ).GetPoly( curve_pts, maxError );
313
314 for( VECTOR2I& corner: curve_pts )
315 aPoly.push_back( corner );
316}
317
318
331 const VECTOR2I& aCornerCenter,
332 double aBias,
333 const VECTOR2I& aDesiredDir )
334{
335 VECTOR2I radial = aAnchor - aCornerCenter;
336
337 if( radial.EuclideanNorm() == 0 )
338 return aAnchor;
339
340 // Tangent is perpendicular to the radius. There are two perpendicular directions:
341 // (radial.y, -radial.x) and (-radial.y, radial.x)
342 // Choose the one that best aligns with the desired direction (toward the track)
343 VECTOR2I tangent1( radial.y, -radial.x );
344 VECTOR2I tangent2( -radial.y, radial.x );
345
346 // Use dot product to determine which tangent direction aligns better with desired direction
347 int64_t dot1 = static_cast<int64_t>( tangent1.x ) * aDesiredDir.x
348 + static_cast<int64_t>( tangent1.y ) * aDesiredDir.y;
349 int64_t dot2 = static_cast<int64_t>( tangent2.x ) * aDesiredDir.x
350 + static_cast<int64_t>( tangent2.y ) * aDesiredDir.y;
351
352 VECTOR2I tangent = ( dot1 > dot2 ) ? tangent1 : tangent2;
353
354 return aAnchor + tangent.Resize( KiROUND( aBias ) );
355}
356
357
369static bool isPointOnOvalEnd( const VECTOR2I& aPoint, const VECTOR2I& aPadPos,
370 const VECTOR2I& aPadSize, const EDA_ANGLE& aRotation,
371 VECTOR2I& aArcCenter )
372{
373 // Transform point to pad-local coordinates (unrotated)
374 VECTOR2I localPt = aPoint - aPadPos;
375 RotatePoint( localPt, aRotation );
376
377 int halfW = aPadSize.x / 2;
378 int halfH = aPadSize.y / 2;
379
380 // Oval geometry: semicircle radius is min dimension / 2
381 // The semicircle centers are offset along the major axis
382 int radius = std::min( halfW, halfH );
383 bool isHorizontal = halfW > halfH;
384
385 if( isHorizontal )
386 {
387 // Semicircles at left and right ends
388 int centerOffset = halfW - radius;
389
390 // Check if point is in the curved region (beyond the straight sides)
391 if( std::abs( localPt.x ) <= centerOffset )
392 return false;
393
394 // Determine which end
395 int centerX = ( localPt.x > 0 ) ? centerOffset : -centerOffset;
396 aArcCenter = VECTOR2I( centerX, 0 );
397 }
398 else
399 {
400 // Semicircles at top and bottom ends
401 int centerOffset = halfH - radius;
402
403 // Check if point is in the curved region (beyond the straight sides)
404 if( std::abs( localPt.y ) <= centerOffset )
405 return false;
406
407 // Determine which end
408 int centerY = ( localPt.y > 0 ) ? centerOffset : -centerOffset;
409 aArcCenter = VECTOR2I( 0, centerY );
410 }
411
412 // Transform arc center back to board coordinates
413 RotatePoint( aArcCenter, -aRotation );
414 aArcCenter += aPadPos;
415
416 return true;
417}
418
419
432static bool isPointOnRoundedCorner( const VECTOR2I& aPoint, const VECTOR2I& aPadPos,
433 const VECTOR2I& aPadSize, int aCornerRadius,
434 const EDA_ANGLE& aRotation, VECTOR2I& aCornerCenter )
435{
436 // Transform point to pad-local coordinates (unrotated)
437 VECTOR2I localPt = aPoint - aPadPos;
438 RotatePoint( localPt, aRotation );
439
440 // Half-sizes minus corner radius define the inner rectangle
441 int halfW = aPadSize.x / 2;
442 int halfH = aPadSize.y / 2;
443 int innerHalfW = halfW - aCornerRadius;
444 int innerHalfH = halfH - aCornerRadius;
445
446 // Point is in corner region if it's outside the inner rectangle in both dimensions
447 bool inCornerX = std::abs( localPt.x ) > innerHalfW;
448 bool inCornerY = std::abs( localPt.y ) > innerHalfH;
449
450 if( !inCornerX || !inCornerY )
451 return false;
452
453 // Determine which corner
454 int cornerX = ( localPt.x > 0 ) ? innerHalfW : -innerHalfW;
455 int cornerY = ( localPt.y > 0 ) ? innerHalfH : -innerHalfH;
456
457 aCornerCenter = VECTOR2I( cornerX, cornerY );
458
459 // Transform corner center back to board coordinates
460 RotatePoint( aCornerCenter, -aRotation );
461 aCornerCenter += aPadPos;
462
463 return true;
464}
465
466
467/*
468 * Compute the curve part points for teardrops connected to a rectangular/polygonal shape.
469 * For rounded rectangles, control points are computed to be tangent to corner arcs,
470 * preventing the teardrop curve from intersecting the pad's corner radius.
471 */
473 std::vector<VECTOR2I>& aPoly, int aTdWidth,
474 int aTrackHalfWidth,
475 std::vector<VECTOR2I>& aPts,
476 const VECTOR2I& aIntersection,
477 BOARD_ITEM* aOther,
478 const VECTOR2I& aOtherPos,
479 PCB_LAYER_ID aLayer ) const
480{
481 int maxError = m_board->GetDesignSettings().m_MaxError;
482
483 // in aPts:
484 // A and B are points on the track ( pts[0] and pts[1] )
485 // C and E are points on the pad/via ( pts[2] and pts[4] )
486 // D is the aViaPad centre ( pts[3] )
487
488 // side1 is( aPts[1], aPts[2] ); from track to via
489 VECTOR2I side1( aPts[2] - aPts[1] ); // vector from track to via
490 // side2 is ( aPts[4], aPts[0] ); from via to track
491 VECTOR2I side2( aPts[4] - aPts[0] ); // vector from track to via
492
493 VECTOR2I trackDir( aIntersection - ( aPts[0] + aPts[1] ) / 2 );
494
495 // Check if this is a rounded rectangle or oval pad (both have curved regions)
496 bool isRoundRect = false;
497 bool isOval = false;
498 int cornerRadius = 0;
499 VECTOR2I padSize;
500 EDA_ANGLE padRotation;
501
502 if( aOther && aOther->Type() == PCB_PAD_T )
503 {
504 PAD* pad = static_cast<PAD*>( aOther );
505 PAD_SHAPE shape = pad->GetShape( aLayer );
506
507 if( shape == PAD_SHAPE::ROUNDRECT )
508 {
509 isRoundRect = true;
510 cornerRadius = pad->GetRoundRectCornerRadius( aLayer );
511 padSize = pad->GetSize( aLayer );
512 padRotation = pad->GetOrientation();
513 }
514 else if( shape == PAD_SHAPE::OVAL )
515 {
516 isOval = true;
517 padSize = pad->GetSize( aLayer );
518 padRotation = pad->GetOrientation();
519 }
520 }
521
522 std::vector<VECTOR2I> curve_pts;
523
524 // Compute control points for the first Bezier curve (track point B to pad point C)
525 VECTOR2I ctrl1 = aPts[1] + trackDir.Resize( side1.EuclideanNorm() / 4 );
526 VECTOR2I ctrl2;
527
528 // Direction from pad anchor toward track (opposite of trackDir which goes pad-ward)
529 VECTOR2I towardTrack = -trackDir;
530
531 // Default control point - midpoint approach
532 ctrl2 = ( aPts[2] + aIntersection ) / 2;
533
534 if( isRoundRect && cornerRadius > 0 )
535 {
536 VECTOR2I cornerCenter;
537
538 if( isPointOnRoundedCorner( aPts[2], aOtherPos, padSize, cornerRadius,
539 padRotation, cornerCenter ) )
540 {
541 // Anchor is on a corner arc - use tangent-based control point
542 double bias = 0.5 * side1.EuclideanNorm();
543 ctrl2 = computeCornerTangentControlPoint( aPts[2], cornerCenter, bias, towardTrack );
544 }
545 }
546 else if( isOval )
547 {
548 VECTOR2I arcCenter;
549
550 if( isPointOnOvalEnd( aPts[2], aOtherPos, padSize, padRotation, arcCenter ) )
551 {
552 // Anchor is on a curved end - use tangent-based control point
553 double bias = 0.5 * side1.EuclideanNorm();
554 ctrl2 = computeCornerTangentControlPoint( aPts[2], arcCenter, bias, towardTrack );
555 }
556 }
557
558 BEZIER_POLY( aPts[1], ctrl1, ctrl2, aPts[2] ).GetPoly( curve_pts, maxError );
559
560 for( VECTOR2I& corner: curve_pts )
561 aPoly.push_back( corner );
562
563 aPoly.push_back( aPts[3] );
564
565 // Compute control points for second Bezier curve (pad point E to track point A)
566 curve_pts.clear();
567
568 // Default control point - midpoint approach
569 ctrl1 = ( aPts[4] + aIntersection ) / 2;
570
571 if( isRoundRect && cornerRadius > 0 )
572 {
573 VECTOR2I cornerCenter;
574
575 if( isPointOnRoundedCorner( aPts[4], aOtherPos, padSize, cornerRadius,
576 padRotation, cornerCenter ) )
577 {
578 // Anchor is on a corner arc - use tangent-based control point
579 double bias = 0.5 * side2.EuclideanNorm();
580 ctrl1 = computeCornerTangentControlPoint( aPts[4], cornerCenter, bias, towardTrack );
581 }
582 }
583 else if( isOval )
584 {
585 VECTOR2I arcCenter;
586
587 if( isPointOnOvalEnd( aPts[4], aOtherPos, padSize, padRotation, arcCenter ) )
588 {
589 // Anchor is on a curved end - use tangent-based control point
590 double bias = 0.5 * side2.EuclideanNorm();
591 ctrl1 = computeCornerTangentControlPoint( aPts[4], arcCenter, bias, towardTrack );
592 }
593 }
594
595 ctrl2 = aPts[0] + trackDir.Resize( side2.EuclideanNorm() / 4 );
596
597 BEZIER_POLY( aPts[4], ctrl1, ctrl2, aPts[0] ).GetPoly( curve_pts, maxError );
598
599 for( VECTOR2I& corner: curve_pts )
600 aPoly.push_back( corner );
601}
602
603
605 BOARD_ITEM* aItem, const VECTOR2I& aPos,
606 std::vector<VECTOR2I>& aPts ) const
607{
608 int maxError = m_board->GetDesignSettings().m_MaxError;
609
610 // Compute the 2 anchor points on pad/via/track of the teardrop shape
611
612 SHAPE_POLY_SET c_buffer;
613
614 // m_BestWidthRatio is the factor to calculate the teardrop preferred width.
615 // teardrop width = pad, via or track size * m_BestWidthRatio (m_BestWidthRatio <= 1.0)
616 // For rectangular (and similar) shapes, the preferred_width is calculated from the min
617 // dim of the rectangle
618
619 int preferred_width = KiROUND( GetWidth( aItem, aLayer ) * aParams.m_BestWidthRatio );
620
621 // force_clip = true to force the pad/via/track polygon to be clipped to follow
622 // constraints
623 // Clipping is also needed for rectangular shapes, because the teardrop shape is restricted
624 // to a polygonal area smaller than the pad area (the teardrop height use the smaller value
625 // of X and Y sizes).
626 bool force_clip = aParams.m_BestWidthRatio < 1.0;
627
628 // To find the anchor points on the pad/via/track shape, we build the polygonal shape, and
629 // clip the polygon to the max size (preferred_width or m_TdMaxWidth) by a rectangle
630 // centered on the axis of the expected teardrop shape.
631 // (only reduce the size of polygonal shape does not give good anchor points)
632 if( IsRound( aItem, aLayer ) )
633 {
634 TransformCircleToPolygon( c_buffer, aPos, GetWidth( aItem, aLayer ) / 2, maxError,
635 ERROR_INSIDE, 16 );
636 }
637 else // Only PADS can have a not round shape
638 {
639 wxCHECK_MSG( aItem->Type() == PCB_PAD_T, false, wxT( "Expected non-round item to be PAD" ) );
640 PAD* pad = static_cast<PAD*>( aItem );
641
642 force_clip = true;
643
644 preferred_width = KiROUND( GetWidth( pad, aLayer ) * aParams.m_BestWidthRatio );
645 pad->TransformShapeToPolygon( c_buffer, aLayer, 0, maxError, ERROR_INSIDE );
646 }
647
648 // Clip the pad/via/track shape to match the m_TdMaxWidth constraint, and for non-round pads,
649 // clip the shape to the smallest of size.x and size.y values.
650 if( force_clip || ( aParams.m_TdMaxWidth > 0 && aParams.m_TdMaxWidth < preferred_width ) )
651 {
652 int halfsize = std::min( aParams.m_TdMaxWidth, preferred_width )/2;
653
654 // teardrop_axis is the line from anchor point on the track and the end point
655 // of the teardrop in the pad/via
656 // this is the teardrop_axis of the teardrop shape to build
657 VECTOR2I ref_on_track = ( aPts[0] + aPts[1] ) / 2;
658 VECTOR2I teardrop_axis( aPts[3] - ref_on_track );
659
660 EDA_ANGLE orient( teardrop_axis );
661 int len = teardrop_axis.EuclideanNorm();
662
663 // Build the constraint polygon: a rectangle with
664 // length = dist between the point on track and the pad/via pos
665 // height = m_TdMaxWidth or aViaPad.m_Width
666 SHAPE_POLY_SET clipping_rect;
667 clipping_rect.NewOutline();
668
669 // Build a horizontal rect: it will be rotated later
670 clipping_rect.Append( 0, - halfsize );
671 clipping_rect.Append( 0, halfsize );
672 clipping_rect.Append( len, halfsize );
673 clipping_rect.Append( len, - halfsize );
674
675 clipping_rect.Rotate( -orient );
676 clipping_rect.Move( ref_on_track );
677
678 // Clip the shape to the max allowed teadrop area
679 c_buffer.BooleanIntersection( clipping_rect );
680 }
681
682 /* in aPts:
683 * A and B are points on the track ( aPts[0] and aPts[1] )
684 * C and E are points on the aViaPad ( aPts[2] and aPts[4] )
685 * D is midpoint behind the aViaPad centre ( aPts[3] )
686 */
687
688 SHAPE_LINE_CHAIN& padpoly = c_buffer.Outline(0);
689 std::vector<VECTOR2I> points = padpoly.CPoints();
690
691 std::vector<VECTOR2I> initialPoints;
692 initialPoints.push_back( aPts[0] );
693 initialPoints.push_back( aPts[1] );
694
695 for( const VECTOR2I& pt: points )
696 initialPoints.emplace_back( pt.x, pt.y );
697
698 std::vector<VECTOR2I> hull;
699 BuildConvexHull( hull, initialPoints );
700
701 // Search for end points of segments starting at aPts[0] or aPts[1]
702 // In some cases, in convex hull, only one point (aPts[0] or aPts[1]) is still in list
703 VECTOR2I PointC;
704 VECTOR2I PointE;
705 int found_start = -1; // 2 points (one start and one end) should be found
706 int found_end = -1;
707
708 VECTOR2I start = aPts[0];
709 VECTOR2I pend = aPts[1];
710
711 for( unsigned ii = 0, jj = 0; jj < hull.size(); ii++, jj++ )
712 {
713 unsigned next = ii+ 1;
714
715 if( next >= hull.size() )
716 next = 0;
717
718 int prev = ii -1;
719
720 if( prev < 0 )
721 prev = hull.size()-1;
722
723 if( hull[ii] == start )
724 {
725 // the previous or the next point is candidate:
726 if( hull[next] != pend )
727 PointE = hull[next];
728 else
729 PointE = hull[prev];
730
731 found_start = ii;
732 }
733
734 if( hull[ii] == pend )
735 {
736 if( hull[next] != start )
737 PointC = hull[next];
738 else
739 PointC = hull[prev];
740
741 found_end = ii;
742 }
743 }
744
745 if( found_start < 0 ) // PointE was not initialized, because start point does not exit
746 {
747 int ii = found_end-1;
748
749 if( ii < 0 )
750 ii = hull.size()-1;
751
752 PointE = hull[ii];
753 }
754
755 if( found_end < 0 ) // PointC was not initialized, because end point does not exit
756 {
757 int ii = found_start-1;
758
759 if( ii < 0 )
760 ii = hull.size()-1;
761
762 PointC = hull[ii];
763 }
764
765 aPts[2] = PointC;
766 aPts[4] = PointE;
767
768 // Now we have to know if the choice aPts[2] = PointC is the best, or if
769 // aPts[2] = PointE is better.
770 // A criteria is to calculate the polygon area in these 2 cases, and choose the case
771 // that gives the bigger area, because the segments starting at PointC and PointE
772 // maximize their distance.
773 SHAPE_LINE_CHAIN dummy1( aPts, true );
774 double area1 = dummy1.Area();
775
776 std::swap( aPts[2], aPts[4] );
777 SHAPE_LINE_CHAIN dummy2( aPts, true );
778 double area2 = dummy2.Area();
779
780 if( area1 > area2 ) // The first choice (without swapping) is the better.
781 std::swap( aPts[2], aPts[4] );
782
783 return true;
784}
785
786
788 VECTOR2I& aStartPoint, VECTOR2I& aEndPoint,
789 VECTOR2I& aIntersection, PCB_TRACK*& aTrack,
790 BOARD_ITEM* aOther, const VECTOR2I& aOtherPos,
791 int* aEffectiveTeardropLen ) const
792{
793 bool found = true;
794 VECTOR2I start = aTrack->GetStart(); // one reference point on the track, inside teardrop
795 VECTOR2I end = aTrack->GetEnd(); // the second reference point on the track, outside teardrop
796 PCB_LAYER_ID layer = aTrack->GetLayer();
797 int radius = GetWidth( aOther, layer ) / 2;
798 int maxError = m_board->GetDesignSettings().m_MaxError;
799
800 // Requested length of the teardrop:
801 int targetLength = KiROUND( GetWidth( aOther, layer ) * aParams.m_BestLengthRatio );
802
803 if( aParams.m_TdMaxLen > 0 )
804 targetLength = std::min( aParams.m_TdMaxLen, targetLength );
805
806 // actualTdLen is the distance between start and the teardrop point on the segment from start to end
807 int actualTdLen;
808 bool need_swap = false; // true if the start and end points of the current track are swapped
809
810 // aTrack is expected to have one end inside the via/pad and the other end outside
811 // so ensure the start point is inside the via/pad
812 if( !aOther->HitTest( start, 0 ) )
813 {
814 std::swap( start, end );
815 need_swap = true;
816 }
817
818 SHAPE_POLY_SET shapebuffer;
819
820 if( IsRound( aOther, layer ) )
821 {
822 TransformCircleToPolygon( shapebuffer, aOtherPos, radius, maxError, ERROR_INSIDE, 16 );
823 }
824 else
825 {
826 wxCHECK_MSG( aOther->Type() == PCB_PAD_T, false, wxT( "Expected non-round item to be PAD" ) );
827 static_cast<PAD*>( aOther )->TransformShapeToPolygon( shapebuffer, aTrack->GetLayer(), 0,
828 maxError, ERROR_INSIDE );
829 }
830
831 SHAPE_LINE_CHAIN& outline = shapebuffer.Outline(0);
832 outline.SetClosed( true );
833
834 // Search the intersection point between the pad/via shape and the current track
835 // This this the starting point to define the teardrop length
837 int pt_count;
838
839 if( aTrack->Type() == PCB_ARC_T )
840 {
841 // To find the starting point we convert the arc to a polyline
842 // and compute the intersection point with the pad/via shape
843 SHAPE_ARC arc( aTrack->GetStart(), static_cast<PCB_ARC*>( aTrack )->GetMid(),
844 aTrack->GetEnd(), aTrack->GetWidth() );
845
846 SHAPE_LINE_CHAIN poly = arc.ConvertToPolyline( maxError );
847 pt_count = outline.Intersect( poly, pts );
848 }
849 else
850 {
851 pt_count = outline.Intersect( SEG( start, end ), pts );
852 }
853
854 // Ensure a intersection point was found, otherwise we cannot built the teardrop
855 // using this track (it is fully outside or inside the pad/via shape)
856 if( pt_count < 1 )
857 return false;
858
859 aIntersection = pts[0].p;
860 start = aIntersection; // This is currently the reference point of the teardrop length
861
862 // actualTdLen for now the distance between start and the teardrop point on the (start end)segment
863 // It cannot be bigger than the lenght of this segment
864 actualTdLen = std::min( targetLength, SEG( start, end ).Length() );
865 VECTOR2I ref_lenght_point = start; // the reference point of actualTdLen
866
867 // If the first track is too short to allow a teardrop having the requested length
868 // explore the connected track(s), and try to find a anchor point at targetLength from initial start
869 if( actualTdLen < targetLength && aParams.m_AllowUseTwoTracks )
870 {
871 int consumed = 0;
872
873 while( actualTdLen + consumed < targetLength )
874 {
875 EDA_ITEM_FLAGS matchType;
876
877 PCB_TRACK* connected_track = findTouchingTrack( matchType, aTrack, end );
878
879 if( connected_track == nullptr )
880 break;
881
882 // TODO: stop if angle between old and new segment is > 45 deg to avoid bad shape
883 consumed += actualTdLen;
884 // actualTdLen is the new distance from new start point and the teardrop anchor point
885 actualTdLen = std::min( targetLength-consumed, int( connected_track->GetLength() ) );
886 aTrack = connected_track;
887 end = connected_track->GetEnd();
888 start = connected_track->GetStart();
889 need_swap = false;
890
891 if( matchType != STARTPOINT )
892 {
893 std::swap( start, end );
894 need_swap = true;
895 }
896
897 // If we do not want to explore more than one connected track, stop search here
898 break;
899 }
900 }
901
902 // if aTrack is an arc, find the best teardrop end point on the arc
903 // It is currently on the segment from arc start point to arc end point,
904 // therefore not really on the arc, because we have used only the track end points.
905 if( aTrack->Type() == PCB_ARC_T )
906 {
907 // To find the best start and end points to build the teardrop shape, we convert
908 // the arc to segments, and search for the segment having its start point at a dist
909 // < actualTdLen, and its end point at adist > actualTdLen:
910 SHAPE_ARC arc( aTrack->GetStart(), static_cast<PCB_ARC*>( aTrack )->GetMid(),
911 aTrack->GetEnd(), aTrack->GetWidth() );
912
913 if( need_swap )
914 arc.Reverse();
915
916 SHAPE_LINE_CHAIN poly = arc.ConvertToPolyline( maxError );
917
918 // Now, find the segment of the arc at a distance < actualTdLen from ref_lenght_point.
919 // We just search for the first segment (starting from the farest segment) with its
920 // start point at a distance < actualTdLen dist
921 // This is basic, but it is probably enough.
922 if( poly.PointCount() > 2 )
923 {
924 // Note: the first point is inside or near the pad/via shape
925 // The last point is outside and the farest from the ref_lenght_point
926 // So we explore segments from the last to the first
927 for( int ii = poly.PointCount()-1; ii >= 0 ; ii-- )
928 {
929 int dist_from_start = ( poly.CPoint( ii ) - start ).EuclideanNorm();
930
931 // The first segment at a distance of the reference point < actualTdLen is OK
932 // and is suitable to define the reference segment of the teardrop anchor.
933 if( dist_from_start < actualTdLen || ii == 0 )
934 {
935 start = poly.CPoint( ii );
936
937 if( ii < poly.PointCount()-1 )
938 end = poly.CPoint( ii+1 );
939
940 // actualTdLen is the distance between start (the reference segment start point)
941 // and the point on track of the teardrop.
942 // This is the difference between the initial actualTdLen value and the
943 // distance between start and ref_lenght_point.
944 actualTdLen -= (start - ref_lenght_point).EuclideanNorm();
945
946 // Ensure validity of actualTdLen: >= 0, and <= segment lenght
947 if( actualTdLen < 0 ) // should not happen, but...
948 actualTdLen = 0;
949
950 actualTdLen = std::min( actualTdLen, (end - start).EuclideanNorm() );
951
952 break;
953 }
954 }
955 }
956 }
957
958 // aStartPoint and aEndPoint will define later a segment to build the 2 anchors points
959 // of the teardrop on the aTrack shape.
960 // they are two points (both outside the pad/via shape) of aTrack if aTrack is a segment,
961 // or a small segment on aTrack if aTrack is an ARC
962 aStartPoint = start;
963 aEndPoint = end;
964
965 *aEffectiveTeardropLen = actualTdLen;
966 return found;
967}
968
969
971 std::vector<VECTOR2I>& aCorners, PCB_TRACK* aTrack,
972 BOARD_ITEM* aOther, const VECTOR2I& aOtherPos ) const
973{
974 VECTOR2I start, end; // Start and end points of the track anchor of the teardrop
975 // the start point is inside the teardrop shape
976 // the end point is outside.
977 VECTOR2I intersection; // Where the track centerline intersects the pad/via edge
978 int track_stub_len; // the dist between the start point and the anchor point
979 // on the track
980
981 // Note: aTrack can be modified if the initial track is too short
982 if( !findAnchorPointsOnTrack( aParams, start, end, intersection, aTrack, aOther, aOtherPos,
983 &track_stub_len ) )
984 {
985 return false;
986 }
987
988 // The start and end points must be different to calculate a valid polygon shape
989 if( start == end )
990 return false;
991
992 VECTOR2D vecT = NormalizeVector(end - start);
993
994 // find the 2 points on the track, sharp end of the teardrop
995 int track_halfwidth = aTrack->GetWidth() / 2;
996 VECTOR2I pointB = start + VECTOR2I( vecT.x * track_stub_len + vecT.y * track_halfwidth,
997 vecT.y * track_stub_len - vecT.x * track_halfwidth );
998 VECTOR2I pointA = start + VECTOR2I( vecT.x * track_stub_len - vecT.y * track_halfwidth,
999 vecT.y * track_stub_len + vecT.x * track_halfwidth );
1000
1001 PCB_LAYER_ID layer = aTrack->GetLayer();
1002
1003 // To build a polygonal valid shape pointA and point B must be outside the pad
1004 // It can be inside with some pad shapes having very different X and X sizes
1005 if( !IsRound( aOther, layer ) )
1006 {
1007 PAD* pad = static_cast<PAD*>( aOther );
1008
1009 if( pad->HitTest( pointA, 0, layer ) )
1010 return false;
1011
1012 if( pad->HitTest( pointB, 0, layer ) )
1013 return false;
1014 }
1015
1016 // Compute pointD, the "back" point of the teardrop behind the pad/via center.
1017 // For off-center track connections (where the track doesn't pass through the pad center),
1018 // we project the pad center onto the track axis so the teardrop is built symmetrically
1019 // about the track rather than being skewed toward the pad center.
1020 int padRadius = GetWidth( aOther, layer ) / 2;
1021 VECTOR2D intToPad = VECTOR2D( aOtherPos - intersection );
1022 double projOnTrack = -( intToPad.x * vecT.x + intToPad.y * vecT.y );
1023 double effectiveDist = std::max( projOnTrack, static_cast<double>( padRadius ) );
1024 int offset = pcbIUScale.mmToIU( 0.001 );
1025 VECTOR2I pointD = intersection + VECTOR2I( KiROUND( -vecT.x * ( effectiveDist + offset ) ),
1026 KiROUND( -vecT.y * ( effectiveDist + offset ) ) );
1027
1028 VECTOR2I pointC, pointE; // Point on pad/via outlines
1029 std::vector<VECTOR2I> pts = { pointA, pointB, pointC, pointD, pointE };
1030
1031 computeAnchorPoints( aParams, aTrack->GetLayer(), aOther, aOtherPos, pts );
1032
1033 // For off-center track connections, the convex hull produces asymmetric anchor points
1034 // (C and E at different distances from the track axis). Recompute them to be symmetric
1035 // so the teardrop flares out evenly from the track on both sides.
1036 if( IsRound( aOther, layer ) )
1037 {
1038 VECTOR2D perpT( -vecT.y, vecT.x );
1039
1040 // Perpendicular distance from pad center to the track axis
1041 VECTOR2D padOffset = VECTOR2D( aOtherPos - intersection );
1042 double perpDistToCenter = padOffset.x * perpT.x + padOffset.y * perpT.y;
1043
1044 // Only apply the symmetric adjustment when the track is significantly off-center.
1045 if( std::abs( perpDistToCenter ) > padRadius * 0.1 )
1046 {
1047 double d = std::abs( perpDistToCenter );
1048
1049 if( d < padRadius )
1050 {
1051 // The maximum symmetric half-width is limited by the shorter side, which is
1052 // the distance from the track axis to the nearest circle edge (R - d).
1053 double maxSymmetric = static_cast<double>( padRadius ) - d;
1054
1055 // Apply the configured width ratio and max width constraints
1056 int preferred_width = KiROUND( GetWidth( aOther, layer ) * aParams.m_BestWidthRatio );
1057 int maxHalfWidth = preferred_width / 2;
1058
1059 if( aParams.m_TdMaxWidth > 0 )
1060 maxHalfWidth = std::min( maxHalfWidth, aParams.m_TdMaxWidth / 2 );
1061
1062 double symHalfWidth = std::min( maxSymmetric,
1063 static_cast<double>( maxHalfWidth ) );
1064
1065 if( symHalfWidth > track_halfwidth )
1066 {
1067 VECTOR2D center = VECTOR2D( aOtherPos );
1068 double R = static_cast<double>( padRadius );
1069
1070 // Find C on the circle at perpendicular distance +symHalfWidth from track.
1071 // Line: p = (perpFoot + perpT*symHalfWidth) + t * vecT
1072 // Intersect with circle (center, R) and pick the point closest to
1073 // the intersection point (track entry side).
1074 auto findCircleLineIntersection =
1075 [&]( double perpDist ) -> VECTOR2I
1076 {
1077 double projAlongTrack = padOffset.x * vecT.x
1078 + padOffset.y * vecT.y;
1079 VECTOR2D lineOrigin = VECTOR2D( intersection )
1080 + vecT * projAlongTrack
1081 + perpT * perpDist;
1082
1083 VECTOR2D oc = lineOrigin - center;
1084 // a_coeff = 1.0 since vecT is unit length
1085 double b_coeff = oc.x * vecT.x + oc.y * vecT.y;
1086 double c_coeff = oc.x * oc.x + oc.y * oc.y - R * R;
1087 double disc = b_coeff * b_coeff - c_coeff;
1088
1089 if( disc < 0 )
1090 return VECTOR2I( KiROUND( lineOrigin.x ),
1091 KiROUND( lineOrigin.y ) );
1092
1093 double sqrtDisc = std::sqrt( disc );
1094 double t1 = -b_coeff - sqrtDisc;
1095 double t2 = -b_coeff + sqrtDisc;
1096
1097 // Pick the point on the intersection side (closer to the track entry)
1098 VECTOR2D p1 = lineOrigin + vecT * t1;
1099 VECTOR2D p2 = lineOrigin + vecT * t2;
1100 VECTOR2D intPt = VECTOR2D( intersection );
1101
1102 if( ( p1 - intPt ).EuclideanNorm()
1103 < ( p2 - intPt ).EuclideanNorm() )
1104 {
1105 return VECTOR2I( KiROUND( p1.x ), KiROUND( p1.y ) );
1106 }
1107
1108 return VECTOR2I( KiROUND( p2.x ), KiROUND( p2.y ) );
1109 };
1110
1111 pts[2] = findCircleLineIntersection( symHalfWidth );
1112 pts[4] = findCircleLineIntersection( -symHalfWidth );
1113 }
1114 }
1115 }
1116 }
1117
1118 if( !aParams.m_CurvedEdges )
1119 {
1120 aCorners = std::move( pts );
1121 return true;
1122 }
1123
1124 // See if we can use curved teardrop shape
1125 if( IsRound( aOther, layer ) )
1126 {
1127 computeCurvedForRoundShape( aParams, aCorners, layer, track_halfwidth, vecT, aOther, aOtherPos, pts );
1128 }
1129 else
1130 {
1131 int td_width = KiROUND( GetWidth( aOther, layer ) * aParams.m_BestWidthRatio );
1132
1133 if( aParams.m_TdMaxWidth > 0 && aParams.m_TdMaxWidth < td_width )
1134 td_width = aParams.m_TdMaxWidth;
1135
1136 computeCurvedForRectShape( aParams, aCorners, td_width, track_halfwidth, pts, intersection,
1137 aOther, aOtherPos, layer );
1138 }
1139
1140 return true;
1141}
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
Bezier curves to polygon converter.
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual VECTOR2I GetPosition() const
Definition eda_item.h:279
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition eda_item.h:240
Definition pad.h:55
const VECTOR2I & GetMid() const
Definition pcb_track.h:290
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
const VECTOR2I & GetStart() const
Definition pcb_track.h:97
const VECTOR2I & GetEnd() const
Definition pcb_track.h:94
virtual int GetWidth() const
Definition pcb_track.h:91
Definition seg.h:42
int Length() const
Return the length (this).
Definition seg.h:343
const SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError=DefaultAccuracyForPCB(), int *aActualError=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
void Reverse()
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Find all intersection points between our line chain and the segment aSeg.
int PointCount() const
Return the number of points (vertices) in this line chain.
double Area(bool aAbsolute=true) const
Return the area of this chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
std::vector< INTERSECTION > INTERSECTIONS
const std::vector< VECTOR2I > & CPoints() const
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
void Move(const VECTOR2I &aVector) override
BOARD * m_board
Definition teardrop.h:269
static bool IsRound(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
bool computeAnchorPoints(const TEARDROP_PARAMETERS &aParams, PCB_LAYER_ID aLayer, BOARD_ITEM *aItem, const VECTOR2I &aPos, std::vector< VECTOR2I > &aPts) const
Compute the 2 points on pad/via of the teardrop shape.
static int GetWidth(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
bool computeTeardropPolygon(const TEARDROP_PARAMETERS &aParams, std::vector< VECTOR2I > &aCorners, PCB_TRACK *aTrack, BOARD_ITEM *aOther, const VECTOR2I &aOtherPos) const
Compute all teardrop points of the polygon shape.
void computeCurvedForRectShape(const TEARDROP_PARAMETERS &aParams, std::vector< VECTOR2I > &aPoly, int aTdWidth, int aTrackHalfWidth, std::vector< VECTOR2I > &aPts, const VECTOR2I &aIntersection, BOARD_ITEM *aOther, const VECTOR2I &aOtherPos, PCB_LAYER_ID aLayer) const
Compute the curve part points for teardrops connected to a rectangular/polygonal shape The Bezier cur...
void computeCurvedForRoundShape(const TEARDROP_PARAMETERS &aParams, std::vector< VECTOR2I > &aPoly, PCB_LAYER_ID aLayer, int aTrackHalfWidth, const VECTOR2D &aTrackDir, BOARD_ITEM *aOther, const VECTOR2I &aOtherPos, std::vector< VECTOR2I > &aPts) const
Compute the curve part points for teardrops connected to a round shape The Bezier curve control point...
PCB_TRACK * findTouchingTrack(EDA_ITEM_FLAGS &aMatchType, PCB_TRACK *aTrackRef, const VECTOR2I &aEndPoint) const
Find a track connected to the end of another track.
TRACK_BUFFER m_trackLookupList
Definition teardrop.h:274
bool areItemsInSameZone(BOARD_ITEM *aPadOrVia, PCB_TRACK *aTrack) const
DRC_RTREE m_tracksRTree
Definition teardrop.h:273
friend class TEARDROP_PARAMETERS
Definition teardrop.h:95
bool findAnchorPointsOnTrack(const TEARDROP_PARAMETERS &aParams, VECTOR2I &aStartPoint, VECTOR2I &aEndPoint, VECTOR2I &aIntersection, PCB_TRACK *&aTrack, BOARD_ITEM *aOther, const VECTOR2I &aOtherPos, int *aEffectiveTeardropLen) const
double m_BestWidthRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxLen
max allowed length for teardrops in IU. <= 0 to disable
bool m_AllowUseTwoTracks
True to create teardrops using 2 track segments if the first in too small.
int m_TdMaxWidth
max allowed height for teardrops in IU. <= 0 to disable
double m_BestLengthRatio
The length of a teardrop as ratio between length and size of pad/via.
bool m_CurvedEdges
True if the teardrop should be curved.
int idxFromLayNet(int aLayer, int aNetcode) const
Definition teardrop.h:69
void AddTrack(PCB_TRACK *aTrack, int aLayer, int aNetcode)
Add a track in buffer, in space grouping tracks having the same netcode and the same layer.
std::map< int, std::vector< PCB_TRACK * > * > m_map_tracks
Definition teardrop.h:75
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
std::uint32_t EDA_ITEM_FLAGS
#define STARTPOINT
When a line is selected, these flags indicate which.
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition padstack.h:52
@ ROUNDRECT
Definition padstack.h:57
CITER next(CITER it)
Definition ptree.cpp:124
static bool isPointOnRoundedCorner(const VECTOR2I &aPoint, const VECTOR2I &aPadPos, const VECTOR2I &aPadSize, int aCornerRadius, const EDA_ANGLE &aRotation, VECTOR2I &aCornerCenter)
Check if a point is within a rounded corner region of a rounded rectangle pad.
static bool isPointOnOvalEnd(const VECTOR2I &aPoint, const VECTOR2I &aPadPos, const VECTOR2I &aPadSize, const EDA_ANGLE &aRotation, VECTOR2I &aArcCenter)
Check if a point is on the curved (semicircular) end of an oval pad.
static VECTOR2D NormalizeVector(const VECTOR2I &aVector)
static VECTOR2I computeCornerTangentControlPoint(const VECTOR2I &aAnchor, const VECTOR2I &aCornerCenter, double aBias, const VECTOR2I &aDesiredDir)
Helper to compute a control point for a teardrop anchor on a rounded rectangle corner.
VECTOR2I center
int radius
VECTOR2I end
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686
@ NONE
Pads are not covered.
Definition zones.h:49