KiCad PCB EDA Suite
Loading...
Searching...
No Matches
constraint_overlay.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <algorithm>
23#include <cmath>
24#include <limits>
25#include <ranges>
26#include <set>
27#include <unordered_set>
28
29#include <wx/image.h>
30
31#include <board.h>
32#include <footprint.h>
33#include <pcb_dimension.h>
34#include <pcb_shape.h>
35#include <view/view.h>
36#include <view/view_overlay.h>
37#include <gal/color4d.h>
39#include <gal/painter.h>
40#include <render_settings.h>
41#include <base_units.h>
42#include <bitmap_base.h>
43#include <bitmaps.h>
44#include <math/util.h>
45#include <geometry/eda_angle.h>
48#include <tool/edit_points.h>
49
53
54using KIGFX::COLOR4D;
55
56
58 VIEW_OVERLAY_HOLDER( aView ),
59 m_board( aBoard ),
62{
63 m_view->Add( &m_badgeItem );
64}
65
66
68{
69 // Remove the badge item before the base removes the tint overlay. Each is removed exactly once.
70 m_view->Remove( &m_badgeItem );
71}
72
73
75{
76 if( !m_overlay )
77 return;
78
80 m_previewAnchor.reset();
81
82 m_overlay->Clear();
83 m_view->Update( m_overlay.get() );
84
85 // Drop the badges from both the draw item and Badges(), so a hidden overlay is not still
86 // clickable through hitTestBadge.
87 m_badges.clear();
88 m_badgeItem.SetBadges( {}, niluuid );
89 m_view->Update( &m_badgeItem );
90 m_view->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
91}
92
93
95{
96 return 14.0; // In screen pixels. The caller converts to world units with the view scale.
97}
98
99
101{
102 // Up-right of the anchor, in screen pixels, so the glyph and its hit disc sit clear of the
103 // geometry and a click on the shape at the anchor still selects the shape.
104 return VECTOR2D( 16.0, -16.0 );
105}
106
107
108double CONSTRAINT_OVERLAY::BadgeWorldPerPixel( double aWorldScale )
109{
110 // Capped so a zoomed-out glyph shrinks instead of dominating the board. Shared by the drawing
111 // and the hit-test so the clickable disc always matches the visible glyph.
112 const double maxWorldPerPx = pcbIUScale.mmToIU( 3.0 ) / 16.0;
113
114 return aWorldScale > 0.0 ? std::min( 1.0 / aWorldScale, maxWorldPerPx ) : maxWorldPerPx;
115}
116
117
118std::vector<VECTOR2D> CONSTRAINT_OVERLAY::LayoutBadges( const std::vector<CONSTRAINT_BADGE>& aBadges,
119 double aWorldPerPx )
120{
121 // Screen pixels scaled to world, so placement holds its look at any zoom.
122 const double discPx = 11.0; // chip radius
123 const double marginPx = 3.0; // gap the chip keeps from geometry and other badges
124 const VECTOR2D preferred = BadgeScreenOffset();
125 const double baseMag = preferred.EuclideanNorm();
126 const double baseAngle = atan2( preferred.y, preferred.x );
127
128 const double shapeClear = ( discPx + marginPx ) * aWorldPerPx;
129 const double badgeClear = ( 2.0 * discPx + marginPx ) * aWorldPerPx;
130
131 // Fan the direction out from the default, then push farther if every direction is blocked.
132 static const double dirSpread[] = { 0, 30, -30, 60, -60, 90, -90, 120, -120, 150, -150, 180 };
133 static const double magSteps[] = { 1.0, 1.6, 2.3 };
134
135 std::vector<VECTOR2D> positions;
136 positions.reserve( aBadges.size() );
137
138 for( const CONSTRAINT_BADGE& badge : aBadges )
139 {
140 VECTOR2D anchor( badge.pos );
141
142 // Clearance to the nearest obstacle.
143 auto clearance = [&]( const VECTOR2D& aPos ) -> double
144 {
145 double d = std::numeric_limits<double>::max();
146 VECTOR2I p( KiROUND( aPos.x ), KiROUND( aPos.y ) );
147
148 for( const SEG& seg : badge.avoid )
149 d = std::min( d, seg.Distance( p ) - shapeClear );
150
151 for( const VECTOR2D& placed : positions )
152 d = std::min( d, ( placed - aPos ).EuclideanNorm() - badgeClear );
153
154 return d;
155 };
156
157 VECTOR2D best = anchor + preferred * aWorldPerPx;
158 double bestScore = -std::numeric_limits<double>::max();
159 bool placed = false;
160
161 for( double mag : magSteps )
162 {
163 for( double spread : dirSpread )
164 {
165 double a = baseAngle + spread * M_PI / 180.0;
166 VECTOR2D cand = anchor + VECTOR2D( cos( a ), sin( a ) ) * ( baseMag * mag * aWorldPerPx );
167 double score = clearance( cand );
168
169 if( score >= 0.0 )
170 {
171 best = cand;
172 placed = true;
173 break;
174 }
175
176 if( score > bestScore )
177 {
178 bestScore = score;
179 best = cand;
180 }
181 }
182
183 if( placed )
184 break;
185 }
186
187 positions.push_back( best );
188 }
189
190 return positions;
191}
192
193
194bool CONSTRAINT_OVERLAY::SetSelected( const KIID& aConstraint )
195{
196 if( m_selected == aConstraint )
197 return false;
198
199 m_selected = aConstraint;
200 return true;
201}
202
203
204bool CONSTRAINT_OVERLAY::SetIsolated( const KIID& aConstraint )
205{
206 if( m_isolated == aConstraint )
207 return false;
208
209 m_isolated = aConstraint;
210 return true;
211}
212
213
215{
216 if( m_mode == aMode )
217 return false;
218
219 m_mode = aMode;
220 return true;
221}
222
223
225{
226 if( m_hoverShape == aShape )
227 return false;
228
229 m_hoverShape = aShape;
230 return true;
231}
232
233
234bool CONSTRAINT_OVERLAY::SetPickPreview( const KIID& aElement, bool aWholeElement,
235 const std::optional<VECTOR2I>& aAnchor )
236{
237 if( m_previewElement == aElement && m_previewWhole == aWholeElement && m_previewAnchor == aAnchor )
238 return false;
239
240 m_previewElement = aElement;
241 m_previewWhole = aWholeElement;
242 m_previewAnchor = aAnchor;
243
244 render();
245 return true;
246}
247
248
250{
251 SetPickPreview( niluuid, true, std::nullopt );
252}
253
254
256{
257 if( !m_overlay || !m_board )
258 return;
259
260 m_lastDiag = aDiag;
261 render();
262}
263
264
266{
267 // Only the selected-badge highlight changed; redraw from the cached diagnosis without the
268 // expensive board-wide re-solve. render() no-ops when the overlay is not ready.
269 render();
270}
271
272
274{
275 if( !m_overlay || !m_board )
276 return;
277
278 m_overlay->Clear();
279 m_badges.clear();
280
281 // State tints are theme colors read from active color settings so they can be rethemed
282 // Literals are shipped defaults used as fallback when no painter attached such as headless tests
283 COLOR4D underColor( 0.80, 0.42, 0.02, 0.9 ); // amber, free DOF remain
284 COLOR4D wellColor( 0.18, 0.72, 0.30, 0.9 ); // green, fully constrained
285 COLOR4D overColor( 0.92, 0.18, 0.18, 0.9 ); // red, conflicting / errored
286
287 if( KIGFX::PAINTER* painter = m_view->GetPainter() )
288 {
289 if( KIGFX::RENDER_SETTINGS* settings = painter->GetSettings() )
290 {
291 underColor = settings->GetLayerColor( LAYER_CONSTRAINT_UNDER );
292 wellColor = settings->GetLayerColor( LAYER_CONSTRAINT_WELL );
293 overColor = settings->GetLayerColor( LAYER_CONSTRAINT_OVER );
294 }
295 }
296
297 const double lineWidth = pcbIUScale.mmToIU( 0.15 );
298
299 auto colorForState =
300 [&]( CONSTRAINT_STATE aState ) -> COLOR4D
301 {
302 switch( aState )
303 {
304 case CONSTRAINT_STATE::WELL_CONSTRAINED: return wellColor;
305 case CONSTRAINT_STATE::OVER_CONSTRAINED: return overColor;
307 default: return underColor;
308 }
309 };
310
311 auto setStroke =
312 [&]( const COLOR4D& aColor )
313 {
314 m_overlay->SetIsFill( false );
315 m_overlay->SetIsStroke( true );
316 m_overlay->SetStrokeColor( aColor );
317 m_overlay->SetLineWidth( lineWidth );
318 };
319
320 auto outlineShape =
321 [&]( const PCB_SHAPE* aShape, const COLOR4D& aColor )
322 {
323 setStroke( aColor );
324
325 if( aShape->GetShape() == SHAPE_T::CIRCLE )
326 {
327 m_overlay->Circle( aShape->GetCenter(), aShape->GetRadius() );
328 }
329 else if( aShape->GetShape() == SHAPE_T::SEGMENT )
330 {
331 m_overlay->Segment( aShape->GetStart(), aShape->GetEnd(), lineWidth );
332 }
333 else if( aShape->GetShape() == SHAPE_T::ARC )
334 {
335 EDA_ANGLE startAngle( aShape->GetStart() - aShape->GetCenter() );
336 m_overlay->Arc( aShape->GetCenter(), aShape->GetRadius(), startAngle,
337 startAngle + aShape->GetArcAngle() );
338 }
339 else if( aShape->GetShape() == SHAPE_T::ELLIPSE )
340 {
341 SHAPE_ELLIPSE ellipse( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(),
342 aShape->GetEllipseMinorRadius(), aShape->GetEllipseRotation() );
343 m_overlay->Polyline( ellipse.ConvertToPolyline( aShape->GetMaxError() ) );
344 }
345 else if( aShape->GetShape() == SHAPE_T::ELLIPSE_ARC )
346 {
347 SHAPE_ELLIPSE ellipse( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(),
348 aShape->GetEllipseMinorRadius(), aShape->GetEllipseRotation(),
349 aShape->GetEllipseStartAngle(), aShape->GetEllipseEndAngle() );
350 m_overlay->Polyline( ellipse.ConvertToPolyline( aShape->GetMaxError() ) );
351 }
352 else if( aShape->GetShape() == SHAPE_T::BEZIER )
353 {
354 // Stroke flattened curve fall back to chord if untessellated
355 const std::vector<VECTOR2I>& pts = aShape->GetBezierPoints();
356
357 if( pts.size() >= 2 )
358 m_overlay->Polyline( SHAPE_LINE_CHAIN( pts ) );
359 else
360 m_overlay->Segment( aShape->GetStart(), aShape->GetEnd(), lineWidth );
361 }
362 };
363
365
366 // Decide what is shown. A panel-row isolation wins; otherwise ALWAYS shows everything and HOVER
367 // shows only the hovered shape's constraints (or nothing when nothing is hovered).
368 bool showAll = false;
369 KIID focusConstraint = niluuid; // isolation: show only this constraint
370 KIID focusShape = niluuid; // hover: show only constraints referencing this shape
371
372 auto referencesShape = []( const PCB_CONSTRAINT* aConstraint, const KIID& aShape )
373 {
374 return std::ranges::any_of( aConstraint->GetMembers(),
375 [&]( const CONSTRAINT_MEMBER& m ) { return m.m_item == aShape; } );
376 };
377
378 if( m_isolated != niluuid )
379 {
380 if( dynamic_cast<PCB_CONSTRAINT*>( m_board->ResolveItem( m_isolated, true ) ) )
381 focusConstraint = m_isolated;
382 else
383 m_isolated = niluuid; // the isolated constraint is gone; fall through to the mode
384 }
385
386 if( m_isolated == niluuid )
387 {
389 showAll = true;
390 else if( m_hoverShape != niluuid )
391 focusShape = m_hoverShape;
392 // else HOVER with nothing hovered leaves showAll false so only always on badges show none focused
393 }
394
395 auto constraintShown =
396 [&]( PCB_CONSTRAINT* aConstraint ) -> bool
397 {
398 if( focusConstraint != niluuid )
399 return aConstraint->m_Uuid == focusConstraint;
400
401 if( showAll )
402 return true;
403
404 if( focusShape == niluuid )
405 return false;
406
407 return referencesShape( aConstraint, focusShape );
408 };
409
410 // Constrained shapes are marked by cached under shadow layer not an on top tint
411 // Overlay only draws badges and while authoring the pick preview
412 std::set<KIID> erroredIds( diag.errored.begin(), diag.errored.end() );
413 std::set<KIID> conflictingIds( diag.conflicting.begin(), diag.conflicting.end() );
414
415 // Gathered on first use so a render showing no badges skips the board walk
416 std::optional<std::unordered_set<KIID>> onBoardIds;
417
418 auto onBoard =
419 [&]( const KIID& aId )
420 {
421 if( !onBoardIds )
422 onBoardIds = CollectMemberCandidateIds( m_board );
423
424 return onBoardIds->contains( aId );
425 };
426
427 auto badgePosition =
428 [&]( const CONSTRAINT_MEMBER& aFirst ) -> std::optional<VECTOR2I>
429 {
430 if( !onBoard( aFirst.m_item ) )
431 return std::nullopt;
432
433 if( std::optional<VECTOR2I> pos = ConstraintAnchorPosition( m_board, aFirst ) )
434 return pos;
435
436 // A WHOLE member (segment/line/circle) has no single anchor; label its centre.
437 if( PCB_SHAPE* shape =
438 dynamic_cast<PCB_SHAPE*>( m_board->ResolveItem( aFirst.m_item, true ) ) )
439 {
440 return shape->GetBoundingBox().Centre();
441 }
442
443 return std::nullopt;
444 };
445
446 // A constrained shape as world segments, for the badge placement search.
447 auto appendShapeSegs = [&]( const PCB_SHAPE* aShape, std::vector<SEG>& aOut )
448 {
449 switch( aShape->GetShape() )
450 {
451 case SHAPE_T::SEGMENT: aOut.emplace_back( aShape->GetStart(), aShape->GetEnd() ); break;
452
453 case SHAPE_T::CIRCLE:
454 case SHAPE_T::ARC:
455 {
456 VECTOR2I center = aShape->GetCenter();
457 double radius = aShape->GetRadius();
458 EDA_ANGLE start = aShape->GetShape() == SHAPE_T::ARC ? EDA_ANGLE( aShape->GetStart() - center ) : ANGLE_0;
459 EDA_ANGLE sweep = aShape->GetShape() == SHAPE_T::ARC ? aShape->GetArcAngle() : ANGLE_360;
460 int steps = std::max( 2, KiROUND( std::abs( sweep.AsDegrees() ) / 30.0 ) );
461 VECTOR2I prev;
462
463 for( int i = 0; i <= steps; ++i )
464 {
465 EDA_ANGLE a = start + sweep * ( double( i ) / steps );
466 VECTOR2I p( center.x + KiROUND( radius * a.Cos() ), center.y + KiROUND( radius * a.Sin() ) );
467
468 if( i > 0 )
469 aOut.emplace_back( prev, p );
470
471 prev = p;
472 }
473
474 break;
475 }
476
477 case SHAPE_T::BEZIER:
478 {
479 const std::vector<VECTOR2I>& pts = aShape->GetBezierPoints();
480
481 for( size_t i = 1; i < pts.size(); ++i )
482 aOut.emplace_back( pts[i - 1], pts[i] );
483
484 break;
485 }
486
487 default: break;
488 }
489 };
490
491 // Selected badge draws enlarged and ringed
492 // Badge colour reflects constraint solve state no on top tint
493 auto walkConstraints =
494 [&]( const CONSTRAINTS& aConstraints )
495 {
496 for( PCB_CONSTRAINT* constraint : aConstraints )
497 {
498 const std::vector<CONSTRAINT_MEMBER>& members = constraint->GetMembers();
499
500 if( members.empty() || !constraintShown( constraint ) )
501 continue;
502
503 bool errored = erroredIds.contains( constraint->m_Uuid );
504 bool conflicting = conflictingIds.contains( constraint->m_Uuid );
505
506 const CONSTRAINT_MEMBER& first = members.front();
507 std::optional<VECTOR2I> pos = badgePosition( first );
508
509 if( !pos )
510 continue;
511
512 COLOR4D color;
513
514 if( errored || conflicting )
515 {
516 color = overColor;
517 }
518 else if( auto it = diag.shapeStates.find( first.m_item );
519 it != diag.shapeStates.end() )
520 {
521 color = colorForState( it->second );
522 }
523 else
524 {
525 color = underColor; // undiagnosed (e.g. an unsupported cluster); not "well"
526 }
527
528 std::vector<SEG> avoid;
529
530 for( const CONSTRAINT_MEMBER& member : members )
531 {
532 if( !onBoard( member.m_item ) )
533 continue;
534
535 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( m_board->ResolveItem( member.m_item, true ) ) )
536 {
537 appendShapeSegs( shape, avoid );
538 }
539 }
540
541 m_badges.push_back(
542 { *pos, constraint->m_Uuid, constraint->GetConstraintType(), color, std::move( avoid ) } );
543 }
544 };
545
546 walkConstraints( m_board->Constraints() );
547
548 for( FOOTPRINT* footprint : m_board->Footprints() )
549 walkConstraints( footprint->Constraints() );
550
551 // Pick preview outline draws last on top of diagnostics tint in a hue distinct from state colours
552 // Anchor point drawn by badge item at constant screen size like an edit handle not scaled with zoom
554 {
555 const COLOR4D previewColor( 0.10, 0.85, 0.95, 0.9 ); // cyan, distinct from the state tints
556
557 BOARD_ITEM* previewItem = m_board->ResolveItem( m_previewElement, true );
558
559 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( previewItem ) )
560 {
561 outlineShape( shape, previewColor );
562 }
563 else if( PCB_DIMENSION_BASE* dimension = dynamic_cast<PCB_DIMENSION_BASE*>( previewItem ) )
564 {
565 // Dimension has no outline to stroke so mark bindable feature points like diagnostics render
566 // keeps whole element visible while picking
567 setStroke( previewColor );
568
569 for( const CONSTRAINT_ANCHOR_POINT& anchor : ConstraintItemAnchors( dimension ) )
570 m_overlay->Circle( anchor.pos, 2 * lineWidth );
571 }
572 }
573
574 // Drop a selection whose constraint no longer has a badge (deleted, or its members vanished).
575 if( m_selected != niluuid
576 && std::none_of( m_badges.begin(), m_badges.end(),
577 [&]( const CONSTRAINT_BADGE& aBadge )
578 { return aBadge.constraint == m_selected; } ) )
579 {
581 }
582
583 m_badgeItem.SetBadges( m_badges, m_selected );
584
585 // Only a point pick shows the anchor marker badge item draws it at constant screen size
586 m_badgeItem.SetPreviewAnchor( m_previewWhole ? std::optional<VECTOR2I>{} : m_previewAnchor );
587
588 m_view->Update( &m_badgeItem );
589 m_view->Update( m_overlay.get() );
590 m_view->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
591}
592
593
598
599
601
602
604{
605 if( auto it = m_icons.find( aType ); it != m_icons.end() )
606 return it->second.get();
607
608 std::unique_ptr<BITMAP_BASE> base;
609
610 if( BITMAPS id = ConstraintTypeBitmap( aType ); id != BITMAPS::INVALID_BITMAP )
611 {
612 if( wxBitmap bmp = KiBitmap( id ); bmp.IsOk() )
613 {
614 base = std::make_unique<BITMAP_BASE>();
615 base->SetImage( bmp.ConvertToImage() );
616 }
617 }
618
619 return ( m_icons[aType] = std::move( base ) ).get();
620}
621
622
624{
625 uint8_t r = uint8_t( aColor.r * 255.0 );
626 uint8_t g = uint8_t( aColor.g * 255.0 );
627 uint8_t b = uint8_t( aColor.b * 255.0 );
628 uint8_t a = uint8_t( aColor.a * 255.0 );
629
630 uint32_t key = ( uint32_t( r ) << 24 ) | ( uint32_t( g ) << 16 ) | ( uint32_t( b ) << 8 ) | a;
631
632 if( auto it = m_discs.find( key ); it != m_discs.end() )
633 return it->second.get();
634
635 const int S = 96;
636 wxImage img( S, S );
637 img.InitAlpha();
638
639 const double center = S / 2.0;
640 const double radius = center - 1.0;
641
642 for( int y = 0; y < S; ++y )
643 {
644 for( int x = 0; x < S; ++x )
645 {
646 double d = std::hypot( x + 0.5 - center, y + 0.5 - center );
647 double coverage = std::clamp( radius - d + 0.5, 0.0, 1.0 );
648
649 img.SetRGB( x, y, r, g, b );
650 img.SetAlpha( x, y, uint8_t( a * coverage ) );
651 }
652 }
653
654 auto base = std::make_unique<BITMAP_BASE>();
655 base->SetImage( img );
656
657 return ( m_discs[key] = std::move( base ) ).get();
658}
659
660
661void CONSTRAINT_BADGE_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
662{
663 KIGFX::GAL* gal = aView->GetGAL();
664
665 // Returning before the scale is read keeps a retained overlay from treating this as zoom dependent
666 if( m_badges.empty() )
667 return;
668
669 const double scale = gal->GetWorldScale();
670
671 if( scale <= 0.0 )
672 return;
673
674 const double iconPx = 18.0;
675 const double bigIconPx = 26.0;
676 const double linePx = 1.5;
677
678 // Keep the badge a constant on-screen size at any zoom. A world-size floor would make it grow
679 // without bound as you keep zooming in, so there is none.
680 const double worldPerPx = CONSTRAINT_OVERLAY::BadgeWorldPerPixel( scale );
681 const double worldUnitLength = gal->GetWorldUnitLength();
682
683 // The same layout drives hit-testing (CONSTRAINT_EDIT_TOOL::hitTestBadge), so draw and click
684 // positions can never diverge. Recompute only when the zoom or badge set changed.
685 if( m_layoutScale != worldPerPx )
686 {
688 m_layoutScale = worldPerPx;
689 }
690
691 // Draw a bitmap centred on aPos, scaled so its width spans aWidth world units.
692 auto blit = [&]( BITMAP_BASE* aBmp, const VECTOR2I& aPos, double aWidth )
693 {
694 double natural = aBmp->GetSizePixels().x / ( aBmp->GetPPI() * worldUnitLength );
695
696 if( natural <= 0.0 )
697 return;
698
699 gal->Save();
700 gal->Translate( aPos );
701 gal->Scale( VECTOR2D( aWidth / natural, aWidth / natural ) );
702 gal->DrawBitmap( *aBmp, 1.0 );
703 gal->Restore();
704 };
705
706 for( size_t i = 0; i < m_badges.size(); ++i )
707 {
708 const CONSTRAINT_BADGE& badge = m_badges[i];
709 BITMAP_BASE* bitmap = icon( badge.type );
710
711 if( !bitmap )
712 continue;
713
714 bool selected = m_selected != niluuid && badge.constraint == m_selected;
715 double iconSize = ( selected ? bigIconPx : iconPx ) * worldPerPx;
716 double chipR = 0.62 * iconSize;
717 VECTOR2I pos( KiROUND( m_layout[i].x ), KiROUND( m_layout[i].y ) );
718
719 // Chip and icon are both bitmaps, so the icon always lands on top of the chip.
720 blit( disc( badge.color ), pos, 2.0 * chipR );
721
722 if( selected )
723 {
724 gal->SetIsFill( false );
725 gal->SetIsStroke( true );
726 gal->SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 0.9 ) );
727 gal->SetLineWidth( static_cast<float>( linePx * worldPerPx ) );
728 gal->DrawCircle( pos, chipR + 2.0 * linePx * worldPerPx );
729 }
730
731 blit( bitmap, pos, iconSize );
732 }
733
734 // Anchor draws here not in cached overlay so it holds constant on screen size like edit handle
735 // sized larger so candidate point stands out
736 if( m_previewAnchor )
737 {
738 const double pxToWorld = 1.0 / scale;
739 const double radius = 1.25 * EDIT_POINT::POINT_SIZE * pxToWorld;
740
741 gal->SetIsFill( true );
742 gal->SetIsStroke( true );
743 gal->SetFillColor( COLOR4D( 0.95, 0.15, 0.85, 0.95 ) ); // magenta, contrasts the outline
744 gal->SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 0.9 ) );
745 gal->SetLineWidth( static_cast<float>( linePx * pxToWorld ) );
747 }
748}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:100
BITMAPS
A list of all bitmap identifiers.
@ INVALID_BITMAP
CONSTRAINT_STATE
The constraint state of a shape, for the diagnostics overlay tint.
@ OVER_CONSTRAINED
In a cluster the solver reports as conflicting.
@ UNDER_CONSTRAINED
In a cluster with remaining free degrees of freedom.
@ WELL_CONSTRAINED
In a fully-determined cluster (zero free DOF).
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
This class handle bitmap images in KiCad.
Definition bitmap_base.h:47
VECTOR2I GetSizePixels() const
int GetPPI() const
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
int GetMaxError() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
std::optional< VECTOR2I > m_previewAnchor
std::vector< CONSTRAINT_BADGE > m_badges
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override
Draw the parts of the object belonging to layer aLayer.
std::map< uint32_t, std::unique_ptr< BITMAP_BASE > > m_discs
BITMAP_BASE * disc(const KIGFX::COLOR4D &aColor) const
A filled disc of the given colour, built and cached on first use.
~CONSTRAINT_BADGE_ITEM() override
std::map< PCB_CONSTRAINT_TYPE, std::unique_ptr< BITMAP_BASE > > m_icons
std::vector< VECTOR2D > m_layout
BITMAP_BASE * icon(PCB_CONSTRAINT_TYPE aType) const
The badge icon for a constraint type, loaded and cached on first use (nullptr for UNDEFINED).
static double BadgeHitRadius()
Click hit radius in screen pixels.
bool SetPickPreview(const KIID &aElement, bool aWholeElement, const std::optional< VECTOR2I > &aAnchor)
Preview next constraint pick target while authoring a point based constraint aElement is shape or dim...
bool SetVisibilityMode(OVERLAY_MODE aMode)
Set the visibility mode (HOVER vs ALWAYS). Returns true if it changed.
void RefreshSelection()
Redraw from the last diagnosis without re-solving (for a selection-only change).
bool SetIsolated(const KIID &aConstraint)
Show only this constraint, or niluuid to show all. Returns true if it changed.
CONSTRAINT_OVERLAY(BOARD *aBoard, KIGFX::VIEW *aView)
CONSTRAINT_BADGE_ITEM m_badgeItem
void ClearPickPreview()
Drop any pick preview.
BOARD_CONSTRAINT_DIAGNOSTICS m_lastDiag
std::vector< CONSTRAINT_BADGE > m_badges
std::optional< VECTOR2I > m_previewAnchor
static double BadgeWorldPerPixel(double aWorldScale)
World units per screen pixel for badge sizing, capped at far zoom-out.
void render()
Redraw the tint and badges from m_lastDiag (no re-solve).
void Clear()
Hide the overlay.
static VECTOR2D BadgeScreenOffset()
Screen-pixel offset from a badge's anchor to where its glyph draws and hit-tests, keeping the glyph c...
bool SetSelected(const KIID &aConstraint)
Mark a constraint as selected so its badge draws enlarged and ringed; pass niluuid to clear.
bool SetHoverShape(const KIID &aShape)
In HOVER mode, show only this shape's constraints; niluuid draws nothing.
static std::vector< VECTOR2D > LayoutBadges(const std::vector< CONSTRAINT_BADGE > &aBadges, double aWorldPerPx)
The on-screen draw position (world units) of each badge at the given scale: the anchor offset by Badg...
void Update(const BOARD_CONSTRAINT_DIAGNOSTICS &aDiag)
Redraw the tint and badges from aDiag (the caller owns the one board-wide solve).
double Sin() const
Definition eda_angle.h:177
double Cos() const
Definition eda_angle.h:196
const KIID m_Uuid
Definition eda_item.h:599
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:84
EDA_ANGLE GetArcAngle() const
int GetEllipseMinorRadius() const
Definition eda_shape.h:403
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:385
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:431
int GetEllipseMajorRadius() const
Definition eda_shape.h:394
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:412
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:177
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:333
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:283
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:422
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:505
static const int POINT_SIZE
Single point size in pixels.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:391
double g
Green component.
Definition color4d.h:392
double a
Alpha component.
Definition color4d.h:394
double b
Blue component.
Definition color4d.h:393
Abstract interface for drawing on a 2D-surface.
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
virtual void Translate(const VECTOR2D &aTranslation)
Translate the context.
virtual void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius)
Draw a circle using world coordinates.
virtual void Restore()
Restore the context.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
virtual void SetIsStroke(bool aIsStrokeEnabled)
Enable/disable stroked outlines.
double GetWorldUnitLength() const
virtual void Scale(const VECTOR2D &aScale)
Scale the context.
virtual void DrawBitmap(const BITMAP_BASE &aBitmap, double alphaBlend=1.0)
Draw a bitmap image.
virtual void Save()
Save the context.
double GetWorldScale() const
Get the world scale.
Contains all the knowledge about how to draw graphical object onto any particular output device.
Definition painter.h:55
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:68
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:224
Definition kiid.h:46
A geometric constraint between board items (issue #2329).
const std::vector< CONSTRAINT_MEMBER > & GetMembers() const
Abstract dimension API.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:80
Definition seg.h:38
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:668
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
std::shared_ptr< KIGFX::VIEW_OVERLAY > m_overlay
VIEW_OVERLAY_HOLDER(KIGFX::VIEW *aView)
std::vector< CONSTRAINT_ANCHOR_POINT > ConstraintItemAnchors(const BOARD_ITEM *aItem)
The constraint anchors an item exposes.
std::unordered_set< KIID > CollectMemberCandidateIds(const BOARD *aBoard)
The ids of every item on aBoard a constraint member can resolve to (shapes, dimensions and table cell...
std::optional< VECTOR2I > ConstraintAnchorPosition(const BOARD *aBoard, const CONSTRAINT_MEMBER &aMember)
Current location of a constraint member's anchor (its shape's START/END/CENTER, or a dimension's feat...
OVERLAY_MODE
Draws the geometric-constraint diagnostics tint onto a GAL overlay (issue #2329): every constrained s...
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ ELLIPSE_ARC
Definition eda_shape.h:63
@ S
Solder (HASL/SMOBC)
KIID niluuid(0)
@ LAYER_CONSTRAINT_UNDER
Under-constrained (free DOF remain).
Definition layer_ids.h:340
@ LAYER_CONSTRAINT_OVER
Over-constrained / conflicting.
Definition layer_ids.h:342
@ LAYER_CONSTRAINT_WELL
Fully constrained.
Definition layer_ids.h:341
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:35
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
BITMAPS ConstraintTypeBitmap(PCB_CONSTRAINT_TYPE aType)
Badge icon for a constraint type; INVALID_BITMAP for UNDEFINED.
PCB_CONSTRAINT_TYPE
The geometric relationship a PCB_CONSTRAINT enforces between its members.
std::deque< PCB_CONSTRAINT * > CONSTRAINTS
const int scale
Board-wide diagnostics for the constraint overlay and info bar.
std::map< KIID, CONSTRAINT_STATE > shapeStates
std::vector< KIID > errored
Invalid constraints (member missing, deleted, or of a kind incompatible with the type).
A selectable feature of a shape (a segment endpoint, arc centre, ...) and its location.
An on-canvas constraint badge.
KIGFX::COLOR4D color
PCB_CONSTRAINT_TYPE type
One participant in a constraint: a referenced board item plus the feature of that item that participa...
KIID m_item
Referenced board item, usually a PCB_SHAPE.
VECTOR2I center
int radius
int clearance
static double sweep(float aStart, float aEnd)
#define M_PI
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:71
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707