KiCad PCB EDA Suite
Loading...
Searching...
No Matches
ratsnest_view_item.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) 2013 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
26
28
29#include <kiface_base.h>
31#include <pcbnew_settings.h>
32#include <pcb_painter.h>
34#include <layer_ids.h>
35#include <pcb_base_frame.h>
36#include <view/view.h>
37#include <hash.h>
38
39#include <memory>
40#include <utility>
41
42
43// A zero length connection is drawn as a fixed size cross
44static constexpr int CROSS_SIZE = 200000;
45
46// How far a curved line's control point sits off the chord, as a fraction of the span
47static constexpr double CURVE_BOW = 0.1;
48
49
50// A cross on an anchor near the coordinate limit overflows an int, so keep it in doubles
51static void drawCross( KIGFX::GAL* aGal, const VECTOR2D& aPos )
52{
53 aGal->DrawLine( aPos + VECTOR2D( -CROSS_SIZE, -CROSS_SIZE ),
54 aPos + VECTOR2D( CROSS_SIZE, CROSS_SIZE ) );
55 aGal->DrawLine( aPos + VECTOR2D( -CROSS_SIZE, CROSS_SIZE ),
56 aPos + VECTOR2D( CROSS_SIZE, -CROSS_SIZE ) );
57}
58
59
60RATSNEST_VIEW_ITEM::RATSNEST_VIEW_ITEM( std::shared_ptr<CONNECTIVITY_DATA> aData, PART aPart ) :
62 m_data( std::move( aData ) ),
63 m_part( aPart )
64{
65}
66
67
69{
70 // Doubles throughout because the span between two board corners overflows an int
71 VECTOR2D span = aTarget - aSource;
72
73 return aSource + VECTOR2D( 0.5 * span.x - CURVE_BOW * span.y,
74 0.5 * span.y + CURVE_BOW * span.x );
75}
76
77
78bool RATSNEST_VIEW_ITEM::LineInViewport( const VECTOR2I& aSource, const VECTOR2I& aTarget,
79 const BOX2D& aViewport, bool aCurved )
80{
81 VECTOR2D source( aSource );
82 VECTOR2D target( aTarget );
83 BOX2D bounds( source );
84
85 if( aSource == aTarget )
86 {
87 bounds.Inflate( CROSS_SIZE );
88 }
89 else
90 {
91 bounds.Merge( target );
92
93 // A cubic stays inside the hull of its endpoints and its control points, which are both
94 // the same point here
95 if( aCurved )
96 bounds.Merge( CurveControlPoint( source, target ) );
97 }
98
99 return aViewport.Intersects( bounds );
100}
101
102
104{
105 // Make it always visible
106 BOX2I bbox;
107 bbox.SetMaximum();
108
109 return bbox;
110}
111
112
113void RATSNEST_VIEW_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
114{
115 std::unique_lock<KISPINLOCK> lock( m_data->GetLock(), std::try_to_lock );
116
117 // A cached copy drawn now would stay empty until the next update, see BoardInputsChanged()
118 m_drawSkipped = !lock;
119
120 if( !lock )
121 return;
122
123 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
124
125 if( !cfg )
126 return;
127
128 auto rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( aView->GetPainter()->GetSettings() );
129 KIGFX::GAL* gal = aView->GetGAL();
130 gal->SetIsStroke( true );
131 gal->SetIsFill( false );
132 // A pixel width the canvas resolves itself keeps the ratsnest valid across zooms
133 if( gal->SupportsStrokePixelPad() )
134 {
135 gal->SetLineWidth( 0.0f );
137 }
138 else
139 {
141 }
142
143 std::set<int> highlightedNets = rs->GetHighlightNetCodes();
144 const std::set<int>& hiddenNets = rs->GetHiddenNets();
145
146 COLOR4D defaultColor = rs->GetColor( nullptr, LAYER_RATSNEST );
147 COLOR4D color = defaultColor;
148 const bool colorByNet = rs->GetNetColorMode() != NET_COLOR_MODE::OFF;
149 const bool dimStatic = m_data->GetLocalRatsnest().size() > 0 || highlightedNets.size() > 0;
150
151 std::map<int, KIGFX::COLOR4D>& netColors = rs->GetNetColorMap();
152
153 const bool onlyVisibleLayers = cfg->m_Display.m_RatsnestMode == RATSNEST_MODE::VISIBLE;
154 LSET visibleLayers;
155
156 // If we are in "other layers off" mode, the active layer is the only visible layer
157 if( rs->m_ContrastModeDisplay == HIGH_CONTRAST_MODE::HIDDEN )
158 {
159 if( rs->GetPrimaryHighContrastLayer() > UNDEFINED_LAYER )
160 visibleLayers.set( rs->GetPrimaryHighContrastLayer() );
161 }
162 else
163 {
165 [&]( PCB_LAYER_ID layer )
166 {
167 if( aView->IsLayerVisible( layer ) )
168 visibleLayers.set( layer );
169 } );
170 }
171
172 auto adjustColor =
173 [&]( COLOR4D& color_base, double brightnessDelta, double alpha )
174 {
175 if( rs->GetColor( nullptr, LAYER_PCB_BACKGROUND ).GetBrightness() < 0.5 )
176 return color_base.Brightened( brightnessDelta ).WithAlpha( std::min( alpha, 1.0 ) );
177 else
178 return color_base.Darkened( brightnessDelta ).WithAlpha( std::min( alpha, 1.0 ) );
179 };
180
182
183 // Adaptive tessellation filter for the curved ratsnest. The ratsnest is only a visual
184 // hint, so there is no need to tessellate the Bezier to the default 10 nm precision: a
185 // polyline whose deviation from the true curve stays below line_width/2 is fully hidden
186 // by the line's own thickness. We use line_width/4 for a 2x safety margin. The filter is
187 // in world units and scales with zoom (line width is already world-scaled above), so it
188 // produces far fewer segments without any visible faceting.
189 const double widthFilter = gal->GetLineWidth() / 4.0;
190
191 // A pixel-padded stroke has no world width and is kept at every zoom, so the filter follows
192 // the curve's own span
193 auto curveFilter = [&]( const VECTOR2I& aSource, const VECTOR2I& aTarget )
194 {
195 if( widthFilter > 0.0 )
196 return widthFilter;
197
198 return ( VECTOR2D( aTarget ) - VECTOR2D( aSource ) ).EuclideanNorm() / 512.0;
199 };
200
201 // Lines are stroked, so one just off screen still reaches back into the viewport
202 BOX2D viewport = aView->GetViewport();
203 viewport.Inflate( gal->GetLineWidth() );
204
205 // A cached board ratsnest is kept through pans and zooms, so it must hold every line
206 const bool cull = m_part != PART::BOARD;
207
208 static const std::vector<RN_DYNAMIC_LINE> noLines;
209
210 // Draw the "dynamic" ratsnest (i.e. for objects that may be currently being moved)
211 for( const RN_DYNAMIC_LINE& l : m_part == PART::BOARD ? noLines : m_data->GetLocalRatsnest() )
212 {
213 if( hiddenNets.count( l.netCode ) )
214 continue;
215
216 if( cull && !LineInViewport( l.a, l.b, viewport, curved_ratsnest ) )
217 continue;
218
219 const NETCLASS* nc = nullptr;
220 const NET_SETTINGS* netSettings = m_data->GetNetSettings();
221
222 if( m_data->HasNetNameForNetCode( l.netCode ) )
223 {
224 const wxString& netName = m_data->GetNetNameForNetCode( l.netCode );
225
226 if( netSettings && netSettings->HasEffectiveNetClass( netName ) )
227 nc = netSettings->GetCachedEffectiveNetClass( netName ).get();
228 }
229
230 if( colorByNet && netColors.count( l.netCode ) )
231 color = netColors.at( l.netCode );
232 else if( colorByNet && nc && nc->HasPcbColor() )
233 color = nc->GetPcbColor();
234 else
235 color = defaultColor;
236
237 if( color == COLOR4D::UNSPECIFIED )
238 color = defaultColor;
239
240 gal->SetStrokeColor( adjustColor( color, 0.5, color.a + 0.3 ) );
241
242 if( l.a == l.b )
243 {
244 drawCross( gal, l.a );
245 }
246 else
247 {
248 if( curved_ratsnest )
249 {
250 const VECTOR2D center = CurveControlPoint( l.a, l.b );
251 gal->DrawCurve( l.a, center, center, l.b, curveFilter( l.a, l.b ) );
252 }
253 else
254 {
255 gal->DrawLine( l.a, l.b );
256 }
257 }
258 }
259
260 const int netCount = m_part == PART::LOCAL ? 0 : m_data->GetNetCount();
261
262 for( int i = 1 /* skip "No Net" at [0] */; i < netCount; ++i )
263 {
264 if( hiddenNets.count( i ) )
265 continue;
266
267 RN_NET* net = m_data->GetRatsnestForNet( i );
268
269 if( !net || m_data->GetConnectivityAlgo()->IsNetDirty( i ) )
270 continue;
271
272 const NETCLASS* nc = nullptr;
273 const NET_SETTINGS* netSettings = m_data->GetNetSettings();
274
275 if( m_data->HasNetNameForNetCode( i ) )
276 {
277 const wxString& netName = m_data->GetNetNameForNetCode( i );
278
279 if( netSettings && netSettings->HasEffectiveNetClass( netName ) )
280 nc = netSettings->GetCachedEffectiveNetClass( netName ).get();
281 }
282
283 if( colorByNet && netColors.count( i ) )
284 color = netColors.at( i );
285 else if( colorByNet && nc && nc->HasPcbColor() )
286 color = nc->GetPcbColor();
287 else
288 color = defaultColor;
289
290 if( color == COLOR4D::UNSPECIFIED )
291 color = defaultColor;
292
293 if( dimStatic )
294 color = adjustColor( color, 0.0, color.a / 2 );
295
296 // Draw the "static" ratsnest
297 if( highlightedNets.count( i ) )
298 gal->SetStrokeColor( adjustColor( color, 0.8, color.a + 0.4 ) );
299 else
300 gal->SetStrokeColor( color ); // using the default ratsnest color for not highlighted
301
302 // Every line of a net is stroked in one colour on one layer, so their order is not
303 // observable and the sort in GetEdges() would be paid on every frame
304 for( const CN_EDGE& edge : net->GetUnsortedEdges() )
305 {
306 if( !edge.IsVisible() )
307 continue;
308
309 const std::shared_ptr<const CN_ANCHOR>& sourceNode = edge.GetSourceNode();
310 const std::shared_ptr<const CN_ANCHOR>& targetNode = edge.GetTargetNode();
311
312 if( !sourceNode || sourceNode->Dirty() || !targetNode || targetNode->Dirty() )
313 continue;
314
315 const VECTOR2I source( sourceNode->Pos() );
316 const VECTOR2I target( targetNode->Pos() );
317
318 if( cull && !LineInViewport( source, target, viewport, curved_ratsnest ) )
319 continue;
320
321 bool enable = !sourceNode->GetNoLine() && !targetNode->GetNoLine();
322 bool show;
323
324 // If the global ratsnest is currently enabled, the local ratsnest should be easy to
325 // turn off, so either element can disable it.
326 // If the global ratsnest is disabled, the local ratsnest should be easy to turn on
327 // so either element can enable it.
329 {
330 show = sourceNode->Parent()->GetLocalRatsnestVisible() &&
331 targetNode->Parent()->GetLocalRatsnestVisible();
332 }
333 else
334 {
335 show = sourceNode->Parent()->GetLocalRatsnestVisible() ||
336 targetNode->Parent()->GetLocalRatsnestVisible();
337 }
338
339 if( onlyVisibleLayers && show )
340 {
341 LSET sourceLayers = sourceNode->Parent()->GetLayerSet();
342 LSET targetLayers = targetNode->Parent()->GetLayerSet();
343
344 if( !( sourceLayers & visibleLayers ).any() ||
345 !( targetLayers & visibleLayers ).any() )
346 {
347 show = false;
348 }
349 }
350
351 if ( enable && show )
352 {
353 if ( source == target )
354 {
355 drawCross( gal, source );
356 }
357 else
358 {
359 if( curved_ratsnest )
360 {
361 const VECTOR2D center = CurveControlPoint( source, target );
362 gal->DrawCurve( source, center, center, target, curveFilter( source, target ) );
363 }
364 else
365 {
366 gal->DrawLine( source, target );
367 }
368 }
369 }
370 }
371 }
372 gal->SetStrokePixelPad( 0.0 );
373}
374
375
376std::vector<int> RATSNEST_VIEW_ITEM::ViewGetLayers() const
377{
379}
380
381
383{
384 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
385
386 if( !cfg )
387 return false;
388
389 auto rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( aView->GetPainter()->GetSettings() );
390 size_t seed = 0;
391
392 hash_combine( seed, !m_data->GetLocalRatsnest().empty(), static_cast<int>( rs->GetNetColorMode() ),
393 static_cast<int>( rs->m_ContrastModeDisplay ), static_cast<int>( rs->GetPrimaryHighContrastLayer() ),
395 static_cast<int>( cfg->m_Display.m_RatsnestMode ), cfg->m_Display.m_ShowGlobalRatsnest );
396
397 for( int net : rs->GetHighlightNetCodes() )
398 hash_combine( seed, net );
399
400 // Keeps a highlighted net distinct from a hidden one with the same code
401 hash_combine( seed, -1 );
402
403 for( int net : rs->GetHiddenNets() )
404 hash_combine( seed, net );
405
407 [&]( PCB_LAYER_ID aLayer )
408 {
409 hash_combine( seed, aView->IsLayerVisible( aLayer ) );
410 } );
411
412 const bool skipped = std::exchange( m_drawSkipped, false );
413
414 return std::exchange( m_boardInputs, seed ) != seed || skipped;
415}
416
@ curved_ratsnest
@ HIDDEN
Inactive layers are hidden.
@ OFF
Net (and netclass) colors are not shown.
@ VISIBLE
Ratsnest lines are drawn to items on visible layers only.
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
BOX2< VECTOR2D > BOX2D
Definition box2.h:915
BASE_SET & set(size_t pos)
Definition base_set.h:126
constexpr void SetMaximum()
Definition box2.h:77
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:84
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:92
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:309
COLOR4D Darkened(double aFactor) const
Return a color that is darker by a given factor, without modifying object.
Definition color4d.h:279
double a
Alpha component.
Definition color4d.h:394
COLOR4D Brightened(double aFactor) const
Return a color that is brighter by a given factor, without modifying object.
Definition color4d.h:265
Abstract interface for drawing on a 2D-surface.
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
virtual bool SupportsStrokePixelPad() const
Tell whether strokes can carry part of their width in pixels, see SetStrokePixelPad().
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.
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a line.
virtual void DrawCurve(const VECTOR2D &startPoint, const VECTOR2D &controlPointA, const VECTOR2D &controlPointB, const VECTOR2D &endPoint, double aFilterValue=0.0)
Draw a cubic bezier spline.
float GetLineWidth() const
Get the line width.
double GetWorldScale() const
Get the world scale.
virtual void SetStrokePixelPad(double aPixels)
Widen every stroke, circle and pad drawn from now on by aPixels.
PCB specific render settings.
Definition pcb_painter.h:94
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:68
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:861
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:224
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:457
PAINTER * GetPainter() const
Return the painter object used by the view for drawing VIEW_ITEM objects.
Definition view.h:242
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
void RunOnLayers(const std::function< void(PCB_LAYER_ID)> &aFunction) const
Execute a function on each layer of the LSET.
Definition lset.h:275
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:44
COLOR4D GetPcbColor(bool aIsForSave=false) const
Definition netclass.h:214
bool HasPcbColor() const
Definition netclass.h:213
NET_SETTINGS stores various net-related settings in a project context.
bool HasEffectiveNetClass(const wxString &aNetName) const
Determines if an effective netclass for the given net name has been cached.
std::shared_ptr< NETCLASS > GetCachedEffectiveNetClass(const wxString &aNetName) const
Returns an already cached effective netclass for the given net name.
DISPLAY_OPTIONS m_Display
RATSNEST_VIEW_ITEM(std::shared_ptr< CONNECTIVITY_DATA > aData, PART aPart=PART::ALL)
bool BoardInputsChanged(KIGFX::VIEW *aView)
Return true when an input to the board's lines other than connectivity has changed since the last cal...
const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
PART
The lines an item draws.
static bool LineInViewport(const VECTOR2I &aSource, const VECTOR2I &aTarget, const BOX2D &aViewport, bool aCurved)
Return true when a ratsnest line between aSource and aTarget can contribute to aViewport.
static VECTOR2D CurveControlPoint(const VECTOR2D &aSource, const VECTOR2D &aTarget)
Return the doubled Bezier control point used to bow a curved ratsnest line.
std::shared_ptr< CONNECTIVITY_DATA > m_data
Object containing ratsnest data.
static constexpr int LOCAL_LAYER
The local part is drawn on a UI layer, which every canvas keeps on its overlay.
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override
Draw the parts of the object belonging to layer aLayer.
size_t m_boardInputs
see BoardInputsChanged()
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
Describe ratsnest for a single net.
const std::vector< CN_EDGE > & GetUnsortedEdges() const
Return the ratsnest edges in no particular order.
A type-safe container of any type.
Definition ki_any.h:92
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition cvpcb.cpp:165
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
Definition hash.h:28
std::string source
@ LAYER_PCB_BACKGROUND
PCB background color.
Definition layer_ids.h:277
@ LAYER_RATSNEST
Definition layer_ids.h:249
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ UNDEFINED_LAYER
Definition layer_ids.h:57
STL namespace.
Class that computes missing connections on a PCB.
static void drawCross(KIGFX::GAL *aGal, const VECTOR2D &aPos)
static constexpr double CURVE_BOW
static constexpr int CROSS_SIZE
Class that draws missing connections on a PCB.
VECTOR2I center
@ 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