KiCad PCB EDA Suite
Loading...
Searching...
No Matches
view.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-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 * @author Maciej Suminski <[email protected]>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
24
25#include <layer_ids.h>
26#include <trace_helpers.h>
27#include <wx/log.h>
28
29#include <view/view.h>
30#include <view/view_group.h>
31#include <view/view_item.h>
32#include <view/view_rtree.h>
33#include <view/view_overlay.h>
34
35#include <gal/definitions.h>
37#include <gal/painter.h>
38#include <algorithm>
39#include <unordered_map>
40
41#include <core/profile.h>
42
43#ifdef KICAD_GAL_PROFILE
44#include <wx/log.h>
45#include <trace_helpers.h>
46#endif
47
48namespace KIGFX {
49
50class VIEW;
51
53{
54public:
56 m_view( nullptr ),
59 m_drawPriority( 0 ),
60 m_cachedIndex( -1 ),
61 m_groups( nullptr ),
62 m_groupsSize( 0 ) {}
63
65 {
67 }
68
69 int GetFlags() const
70 {
71 return m_flags;
72 }
73
74private:
75 friend class VIEW;
76
83 int getGroup( int aLayer ) const
84 {
85 for( int i = 0; i < m_groupsSize; ++i )
86 {
87 if( m_groups[i].first == aLayer )
88 return m_groups[i].second;
89 }
90
91 return -1;
92 }
93
100 void setGroup( int aLayer, int aGroup )
101 {
102 // Look if there is already an entry for the layer
103 for( int i = 0; i < m_groupsSize; ++i )
104 {
105 if( m_groups[i].first == aLayer )
106 {
107 m_groups[i].second = aGroup;
108 return;
109 }
110 }
111
112 // If there was no entry for the given layer - create one
113 std::pair<int, int>* newGroups = new std::pair<int, int>[m_groupsSize + 1];
114
115 if( m_groupsSize > 0 )
116 {
117 std::copy( m_groups, m_groups + m_groupsSize, newGroups );
118 delete[] m_groups;
119 }
120
121 m_groups = newGroups;
122 newGroups[m_groupsSize++] = { aLayer, aGroup };
123 }
124
125
130 {
131 delete[] m_groups;
132 m_groups = nullptr;
133 m_groupsSize = 0;
134 }
135
141 inline bool storesGroups() const
142 {
143 return m_groupsSize > 0;
144 }
145
153 void reorderGroups( std::unordered_map<int, int> aReorderMap )
154 {
155 for( int i = 0; i < m_groupsSize; ++i )
156 {
157 int orig_layer = m_groups[i].first;
158 int new_layer = orig_layer;
159
160 if( aReorderMap.count( orig_layer ) )
161 new_layer = aReorderMap.at( orig_layer );
162
163 m_groups[i].first = new_layer;
164 }
165 }
166
173 void saveLayers( const std::vector<int>& aLayers )
174 {
175 m_layers.clear();
176
177 for( int layer : aLayers )
178 {
179 wxCHECK2_MSG( layer >= 0 && layer < VIEW::VIEW_MAX_LAYERS, continue,
180 wxString::Format( wxT( "Invalid layer number: %d" ), layer ) );
181 m_layers.push_back( layer );
182 }
183 }
184
188 int requiredUpdate() const
189 {
190 return m_requiredUpdate;
191 }
192
197 {
199 }
200
204 bool isRenderable() const
205 {
206 return m_flags == VISIBLE;
207 }
208
214
215 std::pair<int, int>* m_groups;
218
219 std::vector<int> m_layers;
220
222};
223
224
226{
227 if( aItem->m_viewPrivData )
228 {
229 if( aItem->m_viewPrivData->m_view )
230 aItem->m_viewPrivData->m_view->VIEW::Remove( aItem );
231
232 delete aItem->m_viewPrivData;
233 aItem->m_viewPrivData = nullptr;
234 }
235}
236
237
239 m_enableOrderModifier( true ),
240 m_scale( 4.0 ),
241 m_minScale( 0.2 ), m_maxScale( 50000.0 ),
242 m_mirrorX( false ), m_mirrorY( false ),
245 m_painter( nullptr ),
246 m_gal( nullptr ),
247 m_useDrawPriority( false ),
249 m_reverseDrawOrder( false ),
251{
252 // Set m_boundary to define the max area size. The default area size
253 // is defined here as the max value of a int.
254 // this is a default value acceptable for Pcbnew and Gerbview, but too large for Eeschema.
255 // So in eeschema a call to SetBoundary() with a smaller value will be needed.
256 typedef std::numeric_limits<int> coord_limits;
257 double pos = coord_limits::lowest() / 2 + coord_limits::epsilon();
258 double size = coord_limits::max() - coord_limits::epsilon();
259 m_boundary.SetOrigin( pos, pos );
260 m_boundary.SetSize( size, size );
261
262 m_allItems.reset( new std::vector<VIEW_ITEM*> );
263 m_allItems->reserve( 32768 );
264
265 // Redraw everything at the beginning
266 MarkDirty();
267
268 // View uses layers to display EDA_ITEMs (item may be displayed on several layers, for example
269 // pad may be shown on pad, pad hole and solder paste layers). There are usual copper layers
270 // (eg. F.Cu, B.Cu, internal and so on) and layers for displaying objects such as texts,
271 // silkscreen, pads, vias, etc.
272 for( int ii = 0; ii < VIEW_MAX_LAYERS; ++ii )
273 {
274 auto [it, _] = m_layers.emplace( ii, VIEW_LAYER() );
275 VIEW_LAYER& l = it->second;
276
277 l.items = std::make_shared<VIEW_RTREE>();
278 l.id = ii;
279 l.renderingOrder = ii;
280 l.visible = true;
281 l.displayOnly = false;
282 l.diffLayer = false;
283 l.hasNegatives = false;
285 }
286
288
289 m_preview.reset( new KIGFX::VIEW_GROUP() );
290 Add( m_preview.get() );
291}
292
293
295{
296 Remove( m_preview.get() );
297}
298
299
300void VIEW::Add( VIEW_ITEM* aItem, int aDrawPriority )
301{
302 if( aDrawPriority < 0 )
303 aDrawPriority = m_nextDrawPriority++;
304
305 if( !aItem->m_viewPrivData )
306 aItem->m_viewPrivData = new VIEW_ITEM_DATA;
307
308 wxASSERT_MSG( aItem->m_viewPrivData->m_view == nullptr || aItem->m_viewPrivData->m_view == this,
309 wxS( "Already in a different view!" ) );
310
311 aItem->m_viewPrivData->m_view = this;
312 aItem->m_viewPrivData->m_drawPriority = aDrawPriority;
313 const BOX2I bbox = aItem->ViewBBox();
314 aItem->m_viewPrivData->m_bbox = bbox;
315 aItem->m_viewPrivData->m_cachedIndex = m_allItems->size();
316
317 std::vector<int> layers = aItem->ViewGetLayers();
318
319 std::erase_if( layers, []( int layer )
320 {
321 return layer < 0 || layer >= VIEW_MAX_LAYERS;
322 } );
323
324 if( layers.empty() )
325 return;
326
327 aItem->viewPrivData()->saveLayers( layers );
328
329 m_allItems->push_back( aItem );
330
331 for( int layer : layers )
332 {
333 VIEW_LAYER& l = m_layers[layer];
334 l.items->Insert( aItem, bbox );
336 }
337
338 SetVisible( aItem, true );
339 Update( aItem, KIGFX::INITIAL_ADD );
340}
341
342
343void VIEW::AddBatch( const std::vector<VIEW_ITEM*>& aItems )
344{
345 // Phase 1: Register all items and collect per-layer data
346 std::unordered_map<int, std::vector<std::pair<VIEW_ITEM*, BOX2I>>> layerBulk;
347
348 for( VIEW_ITEM* item : aItems )
349 {
350 if( !item )
351 continue;
352
353 int drawPriority = m_nextDrawPriority++;
354
355 if( !item->m_viewPrivData )
356 item->m_viewPrivData = new VIEW_ITEM_DATA;
357
358 item->m_viewPrivData->m_view = this;
359 item->m_viewPrivData->m_drawPriority = drawPriority;
360 const BOX2I bbox = item->ViewBBox();
361 item->m_viewPrivData->m_bbox = bbox;
362 item->m_viewPrivData->m_cachedIndex = m_allItems->size();
363
364 std::vector<int> layers = item->ViewGetLayers();
365
366 std::erase_if( layers, []( int layer )
367 {
368 return layer < 0 || layer >= VIEW_MAX_LAYERS;
369 } );
370
371 if( layers.empty() )
372 continue;
373
374 item->viewPrivData()->saveLayers( layers );
375 m_allItems->push_back( item );
376
377 for( int layer : layers )
378 layerBulk[layer].emplace_back( item, bbox );
379 }
380
381 // Phase 2: Bulk load each layer's R-tree
382 for( auto& [layerId, items] : layerBulk )
383 {
384 VIEW_LAYER& l = m_layers[layerId];
385 l.items->BulkLoad( items );
387 }
388
389 // Phase 3: Set visibility and queue initial invalidation.
390 // INITIAL_ADD is required even though VIEW_ITEM_DATA defaults VISIBLE=true (making
391 // SetVisible a no-op). Without it, items reused after VIEW::Clear() retain stale GAL
392 // cache group IDs that point to freed memory.
393 for( VIEW_ITEM* item : aItems )
394 {
395 if( !item || !item->m_viewPrivData || item->m_viewPrivData->m_view != this )
396 continue;
397
398 SetVisible( item, true );
399 Update( item, KIGFX::INITIAL_ADD );
400 }
401}
402
403
405{
406 static int s_gcCounter = 0;
407
408 if( aItem && aItem->m_viewPrivData )
409 {
410 if( aItem->m_viewPrivData->m_view != nullptr && aItem->m_viewPrivData->m_view != this )
411 {
412 wxLogDebug( wxT( "VIEW::Remove: item %s belongs to a different view" ), aItem->GetClass() );
413 return;
414 }
415
416 std::vector<VIEW_ITEM*>::iterator item = m_allItems->end();
417 int cachedIndex = aItem->m_viewPrivData->m_cachedIndex;
418
419 if( cachedIndex >= 0
420 && cachedIndex < static_cast<ssize_t>( m_allItems->size() )
421 && ( *m_allItems )[cachedIndex] == aItem )
422 {
423 item = m_allItems->begin() + cachedIndex;
424 }
425 else
426 {
427 item = std::find( m_allItems->begin(), m_allItems->end(), aItem );
428 }
429
430 if( item != m_allItems->end() )
431 {
432 *item = nullptr;
434
435 s_gcCounter++;
436
437 if( s_gcCounter > 4096 )
438 {
439 // Perform defragmentation
440 std::erase_if( *m_allItems,
441 []( VIEW_ITEM* it )
442 {
443 return it == nullptr;
444 } );
445
446 // Update cached indices
447 for( size_t idx = 0; idx < m_allItems->size(); idx++ )
448 ( *m_allItems )[idx]->m_viewPrivData->m_cachedIndex = idx;
449
450 s_gcCounter = 0;
451 }
452 }
453
454 const BOX2I* bbox = &aItem->m_viewPrivData->m_bbox;
455
456 for( int layer : aItem->m_viewPrivData->m_layers )
457 {
458 VIEW_LAYER& l = m_layers[layer];
459 l.items->Remove( aItem, bbox );
461
462 // Clear the GAL cache
463 int prevGroup = aItem->m_viewPrivData->getGroup( layer );
464
465 if( prevGroup >= 0 )
466 m_gal->DeleteGroup( prevGroup );
467 }
468
470 aItem->m_viewPrivData->m_view = nullptr;
471 }
472}
473
474
475void VIEW::SetRequired( int aLayerId, int aRequiredId, bool aRequired )
476{
477 wxCHECK( (unsigned) aLayerId < m_layers.size(), /*void*/ );
478 wxCHECK( (unsigned) aRequiredId < m_layers.size(), /*void*/ );
479
480 if( aRequired )
481 m_layers[aLayerId].requiredLayers.insert( aRequiredId );
482 else
483 m_layers[aLayerId].requiredLayers.erase( aRequired );
484}
485
486
487int VIEW::Query( const BOX2I& aRect, std::vector<LAYER_ITEM_PAIR>& aResult ) const
488{
489 if( m_orderedLayers.empty() )
490 return 0;
491
492 int layer = UNDEFINED_LAYER;
493 auto visitor =
494 [&]( VIEW_ITEM* item ) -> bool
495 {
496 aResult.push_back( VIEW::LAYER_ITEM_PAIR( item, layer ) );
497 return true;
498 };
499
500 std::vector<VIEW_LAYER*>::const_reverse_iterator i;
501
502 // execute queries in reverse direction, so that items that are on the top of
503 // the rendering stack are returned first.
504 for( i = m_orderedLayers.rbegin(); i != m_orderedLayers.rend(); ++i )
505 {
506 // ignore layers that do not contain actual items (i.e. the selection box, menus, floats)
507 if( ( *i )->displayOnly || !( *i )->visible )
508 continue;
509
510 layer = ( *i )->id;
511 ( *i )->items->Query( aRect, visitor );
512 }
513
514 return aResult.size();
515}
516
517
518void VIEW::Query( const BOX2I& aRect, const std::function<bool( VIEW_ITEM* )>& aFunc ) const
519{
520 if( m_orderedLayers.empty() )
521 return;
522
523 for( const auto& i : m_orderedLayers )
524 {
525 // ignore layers that do not contain actual items (i.e. the selection box, menus, floats)
526 if( i->displayOnly || !i->visible )
527 continue;
528
529 i->items->Query( aRect, aFunc );
530 }
531}
532
533
534VECTOR2D VIEW::ToWorld( const VECTOR2D& aCoord, bool aAbsolute ) const
535{
536 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
537
538 if( aAbsolute )
539 return VECTOR2D( matrix * aCoord );
540 else
541 return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y );
542}
543
544
545double VIEW::ToWorld( double aSize ) const
546{
547 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
548
549 return fabs( matrix.GetScale().x * aSize );
550}
551
552
553VECTOR2D VIEW::ToScreen( const VECTOR2D& aCoord, bool aAbsolute ) const
554{
555 const MATRIX3x3D& matrix = m_gal->GetWorldScreenMatrix();
556
557 if( aAbsolute )
558 return VECTOR2D( matrix * aCoord );
559 else
560 return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y );
561}
562
563
564double VIEW::ToScreen( double aSize ) const
565{
566 const MATRIX3x3D& matrix = m_gal->GetWorldScreenMatrix();
567
568 return matrix.GetScale().x * aSize;
569}
570
571
572void VIEW::CopySettings( const VIEW* aOtherView )
573{
574 wxASSERT_MSG( false, wxT( "This is not implemented" ) );
575}
576
577
578void VIEW::SetGAL( GAL* aGal )
579{
580 bool recacheGroups = ( m_gal != nullptr ); // recache groups only if GAL is reassigned
581 m_gal = aGal;
582
583 // clear group numbers, so everything is going to be recached
584 if( recacheGroups )
586
587 // every target has to be refreshed
588 MarkDirty();
589
590 // force the new GAL to display the current viewport.
592 SetScale( m_scale );
594}
595
596
598{
599 BOX2D rect;
600 VECTOR2D screenSize = m_gal->GetScreenPixelSize();
601
602 rect.SetOrigin( ToWorld( VECTOR2D( 0, 0 ) ) );
603 rect.SetEnd( ToWorld( screenSize ) );
604
605 return rect.Normalize();
606}
607
608
609void VIEW::SetViewport( const BOX2D& aViewport )
610{
611 VECTOR2D ssize = ToWorld( m_gal->GetScreenPixelSize(), false );
612
613 wxCHECK( fabs(ssize.x) > 0 && fabs(ssize.y) > 0, /*void*/ );
614
615 VECTOR2D centre = aViewport.Centre();
616 VECTOR2D vsize = aViewport.GetSize();
617 double zoom = 1.0 / std::max( fabs( vsize.x / ssize.x ), fabs( vsize.y / ssize.y ) );
618
619 SetCenter( centre );
620 SetScale( GetScale() * zoom );
621}
622
623
624void VIEW::SetMirror( bool aMirrorX, bool aMirrorY )
625{
626 wxASSERT_MSG( !aMirrorY, _( "Mirroring for Y axis is not supported yet" ) );
627
628 m_mirrorX = aMirrorX;
629 m_mirrorY = aMirrorY;
630 m_gal->SetFlip( aMirrorX, aMirrorY );
631
632 // Redraw everything
633 MarkDirty();
634}
635
636
637void VIEW::SetScale( double aScale, VECTOR2D aAnchor )
638{
639 if( aAnchor == VECTOR2D( 0, 0 ) )
640 aAnchor = m_center;
641
642 VECTOR2D a = ToScreen( aAnchor );
643
644 if( aScale < m_minScale )
646 else if( aScale > m_maxScale )
648 else
649 m_scale = aScale;
650
651 m_gal->SetZoomFactor( m_scale );
652 m_gal->ComputeWorldScreenMatrix();
653
654 VECTOR2D delta = ToWorld( a ) - aAnchor;
655
657
658 // Redraw everything after the viewport has changed
659 MarkDirty();
660}
661
662
663void VIEW::SetCenter( const VECTOR2D& aCenter )
664{
665 m_center = aCenter;
666
667 if( !m_boundary.Contains( aCenter ) )
668 {
669 if( m_center.x < m_boundary.GetLeft() )
670 m_center.x = m_boundary.GetLeft();
671 else if( aCenter.x > m_boundary.GetRight() )
672 m_center.x = m_boundary.GetRight();
673
674 if( m_center.y < m_boundary.GetTop() )
675 m_center.y = m_boundary.GetTop();
676 else if( m_center.y > m_boundary.GetBottom() )
677 m_center.y = m_boundary.GetBottom();
678 }
679
680 m_gal->SetLookAtPoint( m_center );
681 m_gal->ComputeWorldScreenMatrix();
682
683 // Redraw everything after the viewport has changed
684 MarkDirty();
685}
686
687
688void VIEW::SetCenter( const VECTOR2D& aCenter, const std::vector<BOX2D>& obscuringScreenRects )
689{
690 if( obscuringScreenRects.empty() )
691 return SetCenter( aCenter );
692
693 BOX2D screenRect( { 0, 0 }, m_gal->GetScreenPixelSize() );
694 SHAPE_POLY_SET unobscuredPoly( screenRect );
695 VECTOR2D unobscuredCenter = screenRect.Centre();
696
697 for( const BOX2D& obscuringScreenRect : obscuringScreenRects )
698 {
699 SHAPE_POLY_SET obscuringPoly( obscuringScreenRect );
700 unobscuredPoly.BooleanSubtract( obscuringPoly );
701 }
702
703 /*
704 * Perform a step-wise deflate to find the center of the largest unobscured area
705 */
706
707 BOX2I bbox = unobscuredPoly.BBox();
708 int step = std::min( bbox.GetWidth(), bbox.GetHeight() ) / 10;
709
710 if( step < 20 )
711 step = 20;
712
713 while( !unobscuredPoly.IsEmpty() )
714 {
715 unobscuredCenter = unobscuredPoly.BBox().Centre();
717 }
718
719 SetCenter( aCenter - ToWorld( unobscuredCenter - screenRect.Centre(), false ) );
720}
721
722
723void VIEW::SetLayerOrder( int aLayer, int aRenderingOrder, bool aAutoSort )
724{
725 m_layers[aLayer].renderingOrder = aRenderingOrder;
726
727 if( aAutoSort )
729}
730
731
732int VIEW::GetLayerOrder( int aLayer ) const
733{
734 return m_layers.at( aLayer ).renderingOrder;
735}
736
737
738void VIEW::SortLayers( std::vector<int>& aLayers ) const
739{
740 std::sort( aLayers.begin(), aLayers.end(),
741 [this]( int a, int b )
742 {
743 return GetLayerOrder( a ) > GetLayerOrder( b );
744 } );
745}
746
747
748void VIEW::ReorderLayerData( std::unordered_map<int, int> aReorderMap )
749{
750 std::map<int,VIEW_LAYER> new_map;
751
752 for( auto& [_, layer] : m_layers )
753 {
754 auto reorder_it = aReorderMap.find( layer.id );
755
756 // If the layer is not in the reorder map or if it is mapped to itself,
757 // just copy the layer to the new map.
758 if( reorder_it == aReorderMap.end() || reorder_it->second == layer.id )
759 {
760 new_map.emplace( layer.id, layer );
761 continue;
762 }
763
764 auto [new_it,__] = new_map.emplace( reorder_it->second, layer );
765 new_it->second.id = reorder_it->second;
766 }
767
768 // Transfer reordered data (using the copy assignment operator ):
769 m_layers = new_map;
770
772
773 for( VIEW_ITEM* item : *m_allItems )
774 {
775 if( !item )
776 continue;
777
778 VIEW_ITEM_DATA* viewData = item->viewPrivData();
779
780 if( !viewData )
781 continue;
782
783 std::vector<int> layers = item->ViewGetLayers();
784 viewData->saveLayers( layers );
785
786 viewData->reorderGroups( aReorderMap );
787
788 viewData->m_requiredUpdate |= COLOR;
790 }
791
792 UpdateItems();
793}
794
795
797{
798 UPDATE_COLOR_VISITOR( int aLayer, PAINTER* aPainter, GAL* aGal ) :
799 layer( aLayer ),
800 painter( aPainter ),
801 gal( aGal )
802 {
803 }
804
805 bool operator()( VIEW_ITEM* aItem )
806 {
807 // Obtain the color that should be used for coloring the item
808 const COLOR4D color = painter->GetSettings()->GetColor( aItem, layer );
809 int group = aItem->viewPrivData()->getGroup( layer );
810
811 if( group >= 0 )
812 gal->ChangeGroupColor( group, color );
813
814 return true;
815 }
816
817 int layer;
820};
821
822
823void VIEW::UpdateLayerColor( int aLayer )
824{
825 // There is no point in updating non-cached layers
826 if( !IsCached( aLayer ) )
827 return;
828
829 BOX2I r;
830
831 r.SetMaximum();
832
833 if( m_gal->IsVisible() )
834 {
836
837 UPDATE_COLOR_VISITOR visitor( aLayer, m_painter, m_gal );
838 m_layers[aLayer].items->Query( r, visitor );
839 MarkTargetDirty( m_layers[aLayer].target );
840 }
841}
842
843
845{
846 if( m_gal->IsVisible() )
847 {
849
850 for( VIEW_ITEM* item : *m_allItems )
851 {
852 if( !item )
853 continue;
854
855 VIEW_ITEM_DATA* viewData = item->viewPrivData();
856
857 if( !viewData )
858 continue;
859
860 for( int layer : viewData->m_layers )
861 {
862 const COLOR4D color = m_painter->GetSettings()->GetColor( item, layer );
863 int group = viewData->getGroup( layer );
864
865 if( group >= 0 )
866 m_gal->ChangeGroupColor( group, color );
867 }
868 }
869 }
870
871 MarkDirty();
872}
873
874
876{
877 UPDATE_DEPTH_VISITOR( int aLayer, int aDepth, GAL* aGal ) :
878 layer( aLayer ),
879 depth( aDepth ),
880 gal( aGal )
881 {
882 }
883
884 bool operator()( VIEW_ITEM* aItem )
885 {
886 int group = aItem->viewPrivData()->getGroup( layer );
887
888 if( group >= 0 )
889 gal->ChangeGroupDepth( group, depth );
890
891 return true;
892 }
893
896};
897
898
900{
901 if( m_topLayers.size() == 0 )
902 return 0;
903
904 return *m_topLayers.begin();
905}
906
907
908void VIEW::SetTopLayer( int aLayer, bool aEnabled )
909{
910 if( aEnabled )
911 {
912 if( m_topLayers.count( aLayer ) == 1 )
913 return;
914
915 m_topLayers.insert( aLayer );
916
917 // Move the layer closer to front
919 m_layers[aLayer].renderingOrder += TOP_LAYER_MODIFIER;
920 }
921 else
922 {
923 if( m_topLayers.count( aLayer ) == 0 )
924 return;
925
926 m_topLayers.erase( aLayer );
927
928 // Restore the previous rendering order
930 m_layers[aLayer].renderingOrder -= TOP_LAYER_MODIFIER;
931 }
932}
933
934
935void VIEW::EnableTopLayer( bool aEnable )
936{
937 if( aEnable == m_enableOrderModifier )
938 return;
939
940 m_enableOrderModifier = aEnable;
941
942 std::set<unsigned int>::iterator it;
943
944 if( aEnable )
945 {
946 for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it )
947 m_layers[*it].renderingOrder += TOP_LAYER_MODIFIER;
948 }
949 else
950 {
951 for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it )
952 m_layers[*it].renderingOrder -= TOP_LAYER_MODIFIER;
953 }
954
957}
958
959
961{
962 std::set<unsigned int>::iterator it;
963
965 {
966 // Restore the previous rendering order for layers that were marked as top
967 for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it )
968 m_layers[*it].renderingOrder -= TOP_LAYER_MODIFIER;
969 }
970
971 m_topLayers.clear();
972}
973
974
976{
978
979 if( m_gal->IsVisible() )
980 {
982
983 for( VIEW_ITEM* item : *m_allItems )
984 {
985 if( !item )
986 continue;
987
988 VIEW_ITEM_DATA* viewData = item->viewPrivData();
989
990 if( !viewData )
991 continue;
992
993 for( int layer : viewData->m_layers )
994 {
995 int group = viewData->getGroup( layer );
996
997 if( group >= 0 )
998 m_gal->ChangeGroupDepth( group, m_layers[layer].renderingOrder );
999 }
1000 }
1001 }
1002
1003 MarkDirty();
1004}
1005
1006
1008{
1009 DRAW_ITEM_VISITOR( VIEW* aView, int aLayer, bool aUseDrawPriority, bool aReverseDrawOrder ) :
1010 view( aView ),
1011 layer( aLayer ),
1012 useDrawPriority( aUseDrawPriority ),
1013 reverseDrawOrder( aReverseDrawOrder ),
1014 drawForcedTransparent( false ),
1015 foundForcedTransparent( false )
1016 {
1017 }
1018
1019 bool operator()( VIEW_ITEM* aItem )
1020 {
1021 wxCHECK( aItem->viewPrivData(), false );
1022
1023 if( aItem->m_forcedTransparency > 0 && !drawForcedTransparent )
1024 {
1026 return true;
1027 }
1028
1029 const double itemLOD = aItem->ViewGetLOD( layer, view );
1030
1031 // Conditions that have to be fulfilled for an item to be drawn
1032 bool drawCondition = aItem->viewPrivData()->isRenderable() && itemLOD < view->m_scale;
1033
1034 if( !drawCondition )
1035 return true;
1036
1037 if( useDrawPriority )
1038 drawItems.push_back( aItem );
1039 else
1040 view->draw( aItem, layer );
1041
1042 return true;
1043 }
1044
1046 {
1047 if( reverseDrawOrder )
1048 {
1049 std::sort( drawItems.begin(), drawItems.end(),
1050 []( VIEW_ITEM* a, VIEW_ITEM* b ) -> bool
1051 {
1052 return b->viewPrivData()->m_drawPriority
1053 < a->viewPrivData()->m_drawPriority;
1054 } );
1055 }
1056 else
1057 {
1058 std::sort( drawItems.begin(), drawItems.end(),
1059 []( VIEW_ITEM* a, VIEW_ITEM* b ) -> bool
1060 {
1061 return a->viewPrivData()->m_drawPriority
1062 < b->viewPrivData()->m_drawPriority;
1063 } );
1064 }
1065
1066 for( VIEW_ITEM* item : drawItems )
1067 view->draw( item, layer );
1068 }
1069
1073 std::vector<VIEW_ITEM*> drawItems;
1076};
1077
1078
1079void VIEW::redrawRect( const BOX2I& aRect )
1080{
1081
1083
1084 for( VIEW_LAYER* l : m_orderedLayers )
1085 {
1086 if( l->items->IsEmpty() )
1087 continue;
1088
1089 if( l->visible && IsTargetDirty( l->target ) && areRequiredLayersEnabled( l->id ) )
1090 {
1091 DRAW_ITEM_VISITOR drawFunc( this, l->id, m_useDrawPriority, m_reverseDrawOrder );
1092
1093 m_gal->SetTarget( l->target );
1094 m_gal->SetLayerDepth( l->renderingOrder );
1095
1096 // Differential layer also work for the negatives, since both special layer types
1097 // will composite on separate layers (at least in Cairo)
1098 if( l->diffLayer )
1099 m_gal->StartDiffLayer();
1100 else if( l->hasNegatives )
1101 m_gal->StartNegativesLayer();
1102
1103 l->items->Query( aRect, drawFunc );
1104
1105 if( m_useDrawPriority )
1106 drawFunc.deferredDraw();
1107
1108 if( l->diffLayer )
1109 m_gal->EndDiffLayer();
1110 else if( l->hasNegatives )
1111 m_gal->EndNegativesLayer();
1112
1113 if( drawFunc.foundForcedTransparent )
1114 {
1115 drawFunc.drawForcedTransparent = true;
1116
1117 m_gal->SetTarget( TARGET_NONCACHED );
1118 m_gal->EnableDepthTest( true );
1119 m_gal->SetLayerDepth( l->renderingOrder );
1120
1121 l->items->Query( aRect, drawFunc );
1122 }
1123 }
1124 }
1125}
1126
1127
1128void VIEW::draw( VIEW_ITEM* aItem, int aLayer, bool aImmediate )
1129{
1130 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1131
1132 if( !viewData )
1133 return;
1134
1135 if( m_layerCachedFlagCache[ aLayer ] && !aImmediate )
1136 {
1137 // Draw using cached information or create one
1138 int group = viewData->getGroup( aLayer );
1139
1140 if( group >= 0 )
1141 m_gal->DrawGroup( group );
1142 else
1143 Update( aItem );
1144 }
1145 else
1146 {
1147 // Immediate mode
1148 if( !m_painter->Draw( aItem, aLayer ) )
1149 aItem->ViewDraw( aLayer, this ); // Alternative drawing method
1150 }
1151}
1152
1153
1154void VIEW::draw( VIEW_ITEM* aItem, bool aImmediate )
1155{
1156 std::vector<int> layers = aItem->ViewGetLayers();
1157
1158 // Sorting is needed for drawing order dependent GALs (like Cairo)
1159 if( !m_gal || !m_gal->IsOpenGlEngine())
1160 SortLayers( layers );
1161
1162 for( int layer : layers )
1163 {
1164 auto it = m_layers.find( layer );
1165
1166 if( it == m_layers.end() )
1167 continue;
1168
1169 if( m_gal )
1170 m_gal->SetLayerDepth( it->second.renderingOrder );
1171
1172 draw( aItem, layer, aImmediate );
1173 }
1174}
1175
1176
1177void VIEW::draw( VIEW_GROUP* aGroup, bool aImmediate )
1178{
1179 for( unsigned int i = 0; i < aGroup->GetSize(); i++ )
1180 draw( aGroup->GetItem(i), aImmediate );
1181}
1182
1183
1185{
1186 RECACHE_ITEM_VISITOR( VIEW* aView, GAL* aGal, int aLayer ) :
1187 view( aView ),
1188 gal( aGal ),
1189 layer( aLayer )
1190 {
1191 }
1192
1193 bool operator()( VIEW_ITEM* aItem )
1194 {
1195 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1196
1197 if( !viewData )
1198 return false;
1199
1200 // Remove previously cached group
1201 int group = viewData->getGroup( layer );
1202
1203 if( group >= 0 )
1204 gal->DeleteGroup( group );
1205
1206 viewData->setGroup( layer, -1 );
1207 view->Update( aItem, KIGFX::REPAINT );
1208
1209 return true;
1210 }
1211
1215};
1216
1217
1219{
1220 BOX2I r;
1221 r.SetMaximum();
1222
1223 // Invalidate viewPrivData for all items before clearing. This ensures that items
1224 // which persist outside the view (like selection groups) won't have stale references
1225 // to this view, which could cause issues if they're later removed and re-added.
1226 for( VIEW_ITEM* item : *m_allItems )
1227 {
1228 if( item && item->m_viewPrivData )
1229 item->m_viewPrivData->m_view = nullptr;
1230 }
1231
1232 m_allItems->clear();
1233
1234 for( auto& [_, layer] : m_layers )
1235 layer.items->RemoveAll();
1236
1238
1239 m_gal->ClearCache();
1240}
1241
1242
1244{
1246 {
1247 // TARGET_CACHED and TARGET_NONCACHED have to be redrawn together, as they contain
1248 // layers that rely on each other (eg. netnames are noncached, but tracks - are cached)
1249 m_gal->ClearTarget( TARGET_NONCACHED );
1250 m_gal->ClearTarget( TARGET_CACHED );
1251
1252 MarkDirty();
1253 }
1254
1256 {
1257 m_gal->ClearTarget( TARGET_OVERLAY );
1258 }
1259}
1260
1261
1263{
1264#ifdef KICAD_GAL_PROFILE
1265 PROF_TIMER totalRealTime("view-redraw-total");
1266 latencyProbeZoomToRender.Checkpoint("view-redraw-start");
1267#endif /* KICAD_GAL_PROFILE */
1268
1269 VECTOR2D screenSize = m_gal->GetScreenPixelSize();
1270 BOX2D rect( ToWorld( VECTOR2D( 0, 0 ) ),
1271 ToWorld( screenSize ) - ToWorld( VECTOR2D( 0, 0 ) ) );
1272
1273 rect.Normalize();
1274 BOX2I recti = BOX2ISafe( rect );
1275
1276 redrawRect( recti );
1277
1278 // All targets were redrawn, so nothing is dirty
1279 MarkClean();
1280
1281#ifdef KICAD_GAL_PROFILE
1282 totalRealTime.Stop();
1283 wxLogTrace( traceGalProfile, wxS( "VIEW::Redraw(): %.1f ms" ), totalRealTime.msecs() );
1284 latencyProbeZoomToRender.AddTimer( totalRealTime );
1285 latencyProbeZoomToRender.Checkpoint("view-redraw-end");
1286
1287#endif /* KICAD_GAL_PROFILE */
1288}
1289
1290
1292{
1293 return m_gal->GetScreenPixelSize();
1294}
1295
1296
1298{
1300 view( aView )
1301 {
1302 }
1303
1304 bool operator()( VIEW_ITEM* aItem )
1305 {
1306 aItem->viewPrivData()->deleteGroups();
1307
1308 return true;
1309 }
1310
1312};
1313
1314
1316{
1317 BOX2I r;
1318
1319 r.SetMaximum();
1320 CLEAR_LAYER_CACHE_VISITOR visitor( this );
1321
1322 for( auto& [_, layer] : m_layers )
1323 layer.items->Query( r, visitor );
1324}
1325
1326
1327void VIEW::invalidateItem( VIEW_ITEM* aItem, int aUpdateFlags )
1328{
1329 if( aUpdateFlags & INITIAL_ADD )
1330 {
1331 // Don't update layers or bbox, since it was done in VIEW::Add()
1332 // Now that we have initialized, set flags to ALL for the code below
1333 aUpdateFlags = ALL;
1334 }
1335 else
1336 {
1337 // updateLayers updates geometry too, so we do not have to update both of them at the
1338 // same time
1339 if( aUpdateFlags & LAYERS )
1340 updateLayers( aItem );
1341 else if( aUpdateFlags & GEOMETRY )
1342 updateBbox( aItem );
1343 }
1344
1345 std::vector<int> layers = aItem->ViewGetLayers();
1346
1347 // Iterate through layers used by the item and recache it immediately
1348 for( int layer : layers )
1349 {
1350 if( IsCached( layer ) )
1351 {
1352 if( aUpdateFlags & ( GEOMETRY | LAYERS | REPAINT ) )
1353 updateItemGeometry( aItem, layer );
1354 else if( aUpdateFlags & COLOR )
1355 updateItemColor( aItem, layer );
1356 }
1357
1358 // Mark those layers as dirty, so the VIEW will be refreshed
1359 MarkTargetDirty( m_layers[layer].target );
1360 }
1361
1362 aItem->viewPrivData()->clearUpdateFlags();
1363}
1364
1365
1367{
1368 int n = 0;
1369
1370 m_orderedLayers.resize( m_layers.size() );
1371
1372 for( auto& [layer_id, layer] : m_layers )
1373 m_orderedLayers[n++] = &layer;
1374
1376
1377 MarkDirty();
1378}
1379
1380
1381void VIEW::updateItemColor( VIEW_ITEM* aItem, int aLayer )
1382{
1383 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1384 wxCHECK( IsCached( aLayer ), /*void*/ ); // This will check if the layer exists
1385
1386 if( !viewData )
1387 return;
1388
1389 // Obtain the color that should be used for coloring the item on the specific layerId
1390 const COLOR4D color = m_painter->GetSettings()->GetColor( aItem, aLayer );
1391 int group = viewData->getGroup( aLayer );
1392
1393 // Change the color, only if it has group assigned
1394 if( group >= 0 )
1395 m_gal->ChangeGroupColor( group, color );
1396}
1397
1398
1399void VIEW::updateItemGeometry( VIEW_ITEM* aItem, int aLayer )
1400{
1401 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1402
1403 if( !viewData )
1404 return;
1405
1406 auto it = m_layers.find( aLayer );
1407
1408 if( it == m_layers.end() )
1409 return;
1410
1411 VIEW_LAYER& l = it->second;
1412
1413 // Save the extra map lookup in IsCached by open coding here
1414 if( l.target != TARGET_CACHED )
1415 return;
1416
1417 m_gal->SetTarget( l.target );
1418 m_gal->SetLayerDepth( l.renderingOrder );
1419
1420 // Redraw the item from scratch
1421 int group = viewData->getGroup( aLayer );
1422
1423 if( group >= 0 )
1424 m_gal->DeleteGroup( group );
1425
1426 group = m_gal->BeginGroup();
1427 viewData->setGroup( aLayer, group );
1428
1429 if( !m_painter->Draw( aItem, aLayer ) )
1430 aItem->ViewDraw( aLayer, this ); // Alternative drawing method
1431
1432 m_gal->EndGroup();
1433}
1434
1435
1437{
1438 std::vector<int> layers = aItem->ViewGetLayers();
1439
1440 wxASSERT( aItem->m_viewPrivData ); //must have a viewPrivData
1441
1442 const BOX2I new_bbox = aItem->ViewBBox();
1443
1444 // The R-tree removal below keys on the bbox the item was inserted with, so it must be
1445 // copied before the m_bbox overwrite that follows rather than aliased to it. Otherwise
1446 // a moved item is removed with the wrong box and the R-tree falls back to a full-tree
1447 // search instead of the targeted removal.
1448 const BOX2I old_bbox = aItem->m_viewPrivData->m_bbox;
1449
1450 if( new_bbox == old_bbox )
1451 return;
1452
1453 aItem->m_viewPrivData->m_bbox = new_bbox;
1454
1455 for( int layer : layers )
1456 {
1457 auto it = m_layers.find( layer );
1458
1459 if( it == m_layers.end() )
1460 continue;
1461
1462 VIEW_LAYER& l = it->second;
1463 l.items->Remove( aItem, &old_bbox );
1464 l.items->Insert( aItem, new_bbox );
1466 }
1467}
1468
1469
1471{
1472 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1473
1474 if( !viewData )
1475 return;
1476
1477 const BOX2I new_bbox = aItem->ViewBBox();
1478 std::vector<int> newLayers = aItem->ViewGetLayers();
1479
1480 // If neither the layers nor the bbox have changed, skip the expensive R-tree operations
1481 if( newLayers == viewData->m_layers && new_bbox == viewData->m_bbox )
1482 return;
1483
1484 // Remove the item from previous layer set
1485 const BOX2I& old_bbox = aItem->m_viewPrivData->m_bbox;
1486
1487 for( int layer : aItem->m_viewPrivData->m_layers )
1488 {
1489 auto it = m_layers.find( layer );
1490
1491 if( it == m_layers.end() )
1492 continue;
1493
1494 VIEW_LAYER& l = it->second;
1495 l.items->Remove( aItem, &old_bbox );
1497
1498 if( IsCached( l.id ) )
1499 {
1500 // Redraw the item from scratch
1501 int prevGroup = viewData->getGroup( layer );
1502
1503 if( prevGroup >= 0 )
1504 {
1505 m_gal->DeleteGroup( prevGroup );
1506 viewData->setGroup( l.id, -1 );
1507 }
1508 }
1509 }
1510
1511 aItem->m_viewPrivData->m_bbox = new_bbox;
1512 viewData->saveLayers( newLayers );
1513
1514 for( int layer : newLayers )
1515 {
1516 auto it = m_layers.find( layer );
1517
1518 if( it == m_layers.end() )
1519 continue;
1520
1521 VIEW_LAYER& l = it->second;
1522 l.items->Insert( aItem, new_bbox );
1524 }
1525}
1526
1527
1528bool VIEW::areRequiredLayersEnabled( int aLayerId ) const
1529{
1530 auto it = m_layers.find( aLayerId );
1531
1532 if( it == m_layers.end() )
1533 return false;
1534
1535 for( int layer : it->second.requiredLayers )
1536 {
1537 // That is enough if just one layer is not enabled
1538
1539 auto it2 = m_layers.find( layer );
1540
1541 if( it2 == m_layers.end() || !it2->second.visible )
1542 return false;
1543
1544 if( !areRequiredLayersEnabled( layer ) )
1545 return false;
1546 }
1547
1548 return true;
1549}
1550
1551
1553{
1554 BOX2I r;
1555
1556 r.SetMaximum();
1557
1558 for( const auto& [_, l] : m_layers )
1559 {
1560 if( IsCached( l.id ) )
1561 {
1562 RECACHE_ITEM_VISITOR visitor( this, m_gal, l.id );
1563 l.items->Query( r, visitor );
1564 }
1565 }
1566}
1567
1568
1570{
1571 if( !m_gal->IsVisible() || !m_gal->IsInitialized() )
1572 return;
1573
1574#ifdef KICAD_GAL_PROFILE
1575 latencyProbeZoomToRender.Checkpoint("view-update-items");
1576#endif
1577
1579 return;
1580
1581 unsigned int cntGeomUpdate = 0;
1582 bool anyUpdated = false;
1583
1584 for( VIEW_ITEM* item : *m_allItems )
1585 {
1586 if( !item )
1587 continue;
1588
1589 auto vpd = item->viewPrivData();
1590
1591 if( !vpd )
1592 continue;
1593
1594 if( vpd->m_requiredUpdate != NONE )
1595 {
1596 anyUpdated = true;
1597
1598 if( vpd->m_requiredUpdate & ( GEOMETRY | LAYERS ) )
1599 cntGeomUpdate++;
1600 }
1601 }
1602
1603 unsigned int cntTotal = m_allItems->size();
1604
1605 double ratio = (double) cntGeomUpdate / (double) cntTotal;
1606
1607 // R*-tree individual inserts use forced reinsertion on node overflow, making them
1608 // significantly more expensive than simple R-tree inserts. At ~5% changed items
1609 // the cost of individual Remove+Insert operations exceeds a full bulk rebuild.
1610 if( ratio > 0.05 )
1611 {
1612 auto allItems = *m_allItems;
1613
1614 // Clear all R-trees
1615 for( auto& [_, layer] : m_layers )
1616 layer.items->RemoveAll();
1617
1618 // Collect items per layer for bulk loading
1619 std::unordered_map<int, std::vector<std::pair<VIEW_ITEM*, BOX2I>>> layerBulk;
1620
1621 for( VIEW_ITEM* item : allItems )
1622 {
1623 if( !item )
1624 continue;
1625
1626 const BOX2I bbox = item->ViewBBox();
1627 item->m_viewPrivData->m_bbox = bbox;
1628
1629 std::vector<int> layers = item->ViewGetLayers();
1630 item->viewPrivData()->saveLayers( layers );
1631
1632 for( int layer : layers )
1633 {
1634 auto it = m_layers.find( layer );
1635
1636 wxCHECK2_MSG( it != m_layers.end(), continue, wxS( "Invalid layer" ) );
1637 layerBulk[layer].emplace_back( item, bbox );
1638 }
1639
1640 // The bulk rebuild handled R-tree reinsertion, so clear LAYERS|GEOMETRY
1641 // to avoid redundant R-tree work in invalidateItem(). Replace with REPAINT
1642 // so items still reach updateItemGeometry() for GAL cache rebuilds.
1643 if( item->viewPrivData()->m_requiredUpdate & ( LAYERS | GEOMETRY ) )
1644 {
1645 item->viewPrivData()->m_requiredUpdate &= ~( LAYERS | GEOMETRY );
1646 item->viewPrivData()->m_requiredUpdate |= REPAINT;
1647 }
1648 }
1649
1650 // Bulk load each layer's R-tree
1651 for( auto& [layerId, items] : layerBulk )
1652 {
1653 auto it = m_layers.find( layerId );
1654
1655 if( it != m_layers.end() )
1656 {
1657 it->second.items->BulkLoad( items );
1658 MarkTargetDirty( it->second.target );
1659 }
1660 }
1661 }
1662
1663 if( anyUpdated )
1664 {
1666
1667 for( VIEW_ITEM* item : *m_allItems.get() )
1668 {
1669 if( item && item->viewPrivData() && item->viewPrivData()->m_requiredUpdate != NONE )
1670 {
1671 invalidateItem( item, item->viewPrivData()->m_requiredUpdate );
1672 item->viewPrivData()->m_requiredUpdate = NONE;
1673 }
1674 }
1675 }
1676
1677#ifdef KICAD_GAL_PROFILE
1678 wxLogTrace( traceGalProfile, wxS( "View update: total items %u, geom %u anyUpdated %u" ),
1679 cntTotal, cntGeomUpdate, (unsigned) anyUpdated );
1680#endif
1681
1683}
1684
1685
1686void VIEW::UpdateAllItems( int aUpdateFlags )
1687{
1688 if( aUpdateFlags == NONE )
1689 return;
1690
1691 for( VIEW_ITEM* item : *m_allItems )
1692 {
1693 if( item && item->viewPrivData() )
1694 {
1695 item->viewPrivData()->m_requiredUpdate |= aUpdateFlags;
1697 }
1698 }
1699}
1700
1701
1703 std::function<bool( VIEW_ITEM* )> aCondition )
1704{
1705 if( aUpdateFlags == NONE )
1706 return;
1707
1708 for( VIEW_ITEM* item : *m_allItems )
1709 {
1710 if( !item )
1711 continue;
1712
1713 if( aCondition( item ) )
1714 {
1715 if( item->viewPrivData() )
1716 {
1717 item->viewPrivData()->m_requiredUpdate |= aUpdateFlags;
1719 }
1720 }
1721 }
1722}
1723
1724
1725void VIEW::UpdateAllItemsConditionally( std::function<int( VIEW_ITEM* )> aItemFlagsProvider )
1726{
1727 for( VIEW_ITEM* item : *m_allItems )
1728 {
1729 if( !item )
1730 continue;
1731
1732 if( item->viewPrivData() )
1733 {
1734 int flags = aItemFlagsProvider( item );
1735 item->viewPrivData()->m_requiredUpdate |= flags;
1736
1737 if( flags != NONE )
1739 }
1740 }
1741}
1742
1743
1744
1745std::unique_ptr<VIEW> VIEW::DataReference() const
1746{
1747 std::unique_ptr<VIEW> ret = std::make_unique<VIEW>();
1748 ret->m_allItems = m_allItems;
1749 ret->m_layers = m_layers;
1750 ret->m_hasPendingItemUpdates = m_hasPendingItemUpdates;
1751 ret->SortOrderedLayers();
1752 return ret;
1753}
1754
1755
1756void VIEW::SetVisible( VIEW_ITEM* aItem, bool aIsVisible )
1757{
1758 if( !aItem )
1759 return;
1760
1761 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1762
1763 if( !viewData )
1764 return;
1765
1766 bool cur_visible = viewData->m_flags & VISIBLE;
1767
1768 if( cur_visible != aIsVisible )
1769 {
1770 if( aIsVisible )
1771 viewData->m_flags |= VISIBLE;
1772 else
1773 viewData->m_flags &= ~VISIBLE;
1774
1775 Update( aItem, APPEARANCE | COLOR );
1776 }
1777}
1778
1779
1780void VIEW::Hide( VIEW_ITEM* aItem, bool aHide, bool aHideOverlay )
1781{
1782 if( !aItem )
1783 return;
1784
1785 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1786
1787 if( !viewData )
1788 return;
1789
1790 if( !( viewData->m_flags & VISIBLE ) )
1791 return;
1792
1793 if( aHideOverlay )
1794 viewData->m_flags |= OVERLAY_HIDDEN;
1795
1796 if( aHide )
1797 viewData->m_flags |= HIDDEN;
1798 else
1799 viewData->m_flags &= ~( HIDDEN | OVERLAY_HIDDEN );
1800
1801 Update( aItem, APPEARANCE );
1802}
1803
1804
1805bool VIEW::IsVisible( const VIEW_ITEM* aItem ) const
1806{
1807 const VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1808
1809 return viewData && ( viewData->m_flags & VISIBLE );
1810}
1811
1812
1813bool VIEW::IsHiddenOnOverlay( const VIEW_ITEM* aItem ) const
1814{
1815 const VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1816
1817 return viewData && ( viewData->m_flags & OVERLAY_HIDDEN );
1818}
1819
1820
1821bool VIEW::HasItem( const VIEW_ITEM* aItem ) const
1822{
1823 const VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1824
1825 return viewData && viewData->m_view == this;
1826}
1827
1828
1829void VIEW::Update( const VIEW_ITEM* aItem ) const
1830{
1831 Update( aItem, ALL );
1832}
1833
1834
1835void VIEW::Update( const VIEW_ITEM* aItem, int aUpdateFlags ) const
1836{
1837 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1838
1839 if( !viewData )
1840 return;
1841
1842 assert( aUpdateFlags != NONE );
1843
1844 viewData->m_requiredUpdate |= aUpdateFlags;
1846}
1847
1848
1849std::shared_ptr<VIEW_OVERLAY> VIEW::MakeOverlay()
1850{
1851 std::shared_ptr<VIEW_OVERLAY> overlay = std::make_shared<VIEW_OVERLAY>();
1852
1853 Add( overlay.get() );
1854 return overlay;
1855}
1856
1857
1859{
1860 if( !m_preview )
1861 return;
1862
1863 m_preview->Clear();
1864
1865 for( VIEW_ITEM* item : m_ownedItems )
1866 delete item;
1867
1868 m_ownedItems.clear();
1869 Update( m_preview.get() );
1870}
1871
1872
1874{
1875 m_preview.reset( new KIGFX::VIEW_GROUP() );
1876 Add( m_preview.get() );
1877}
1878
1879
1880void VIEW::AddToPreview( VIEW_ITEM* aItem, bool aTakeOwnership )
1881{
1882 Hide( aItem, false );
1883 m_preview->Add( aItem );
1884
1885 if( aTakeOwnership )
1886 m_ownedItems.push_back( aItem );
1887
1888 SetVisible( m_preview.get(), true );
1889 Hide( m_preview.get(), false );
1890 Update( m_preview.get() );
1891}
1892
1893
1894void VIEW::ShowPreview( bool aShow )
1895{
1896 SetVisible( m_preview.get(), aShow );
1897}
1898
1900{
1901 for( const auto& layer : m_layers )
1902 {
1903 m_layerVisibilityCache[ layer.first ] = layer.second.visible;
1904 m_layerCachedFlagCache[ layer.first ] = (layer.second.target == TARGET_CACHED);
1905 }
1906}
1907
1908} // namespace KIGFX
1909
constexpr int ARC_LOW_DEF
Definition base_units.h:136
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:925
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
constexpr void SetMaximum()
Definition box2.h:76
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:233
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr const SizeVec & GetSize() const
Definition box2.h:202
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:293
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
Abstract interface for drawing on a 2D-surface.
Contains all the knowledge about how to draw graphical object onto any particular output device.
Definition painter.h:55
Extend VIEW_ITEM by possibility of grouping items into a single object.
Definition view_group.h:39
virtual unsigned int GetSize() const
Return the number of stored items.
virtual VIEW_ITEM * GetItem(unsigned int aIdx) const
bool storesGroups() const
Return information if the item uses at least one group id (ie.
Definition view.cpp:141
std::vector< int > m_layers
Definition view.cpp:219
int requiredUpdate() const
Return current update flag for an item.
Definition view.cpp:188
int m_requiredUpdate
Flag required for updating.
Definition view.cpp:211
int m_flags
Visibility flags.
Definition view.cpp:210
void reorderGroups(std::unordered_map< int, int > aReorderMap)
Reorder the stored groups (to facilitate reordering of layers).
Definition view.cpp:153
int m_cachedIndex
Cached index in m_allItems.
Definition view.cpp:213
void deleteGroups()
Remove all of the stored group ids.
Definition view.cpp:129
bool isRenderable() const
Return if the item should be drawn or not.
Definition view.cpp:204
int m_drawPriority
Order to draw this item in a layer, lowest first.
Definition view.cpp:212
friend class VIEW
Definition view.cpp:75
VIEW * m_view
Current dynamic view the item is assigned to.
Definition view.cpp:209
std::pair< int, int > * m_groups
layer_number:group_id pairs for each layer the item occupies.
Definition view.cpp:215
int getGroup(int aLayer) const
Return number of the group id for the given layer, or -1 in case it was not cached before.
Definition view.cpp:83
BOX2I m_bbox
Stores layer numbers used by the item.
Definition view.cpp:221
void setGroup(int aLayer, int aGroup)
Set a group id for the item and the layer combination.
Definition view.cpp:100
void saveLayers(const std::vector< int > &aLayers)
Save layers used by the item.
Definition view.cpp:173
int GetFlags() const
Definition view.cpp:69
void clearUpdateFlags()
Mark an item as already updated, so it is not going to be redrawn.
Definition view.cpp:196
virtual const BOX2I ViewBBox() const =0
Return the bounding box of the item covering all its layers.
double m_forcedTransparency
Additional transparency for diff'ing items.
Definition view_item.h:206
VIEW_ITEM_DATA * viewPrivData() const
Definition view_item.h:157
virtual void ViewDraw(int aLayer, VIEW *aView) const
Draw the parts of the object belonging to layer aLayer.
Definition view_item.h:123
virtual std::vector< int > ViewGetLayers() const =0
Return the all the layers within the VIEW the object is painted on.
virtual wxString GetClass() const =0
Return the class name.
virtual double ViewGetLOD(int aLayer, const VIEW *aView) const
Return the level of detail (LOD) of the item.
Definition view_item.h:151
VIEW_ITEM_DATA * m_viewPrivData
Definition view_item.h:205
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
double GetScale() const
Definition view.h:281
void SetMirror(bool aMirrorX, bool aMirrorY)
Control the mirroring of the VIEW.
Definition view.cpp:624
void ShowPreview(bool aShow=true)
Definition view.cpp:1894
double m_maxScale
Definition view.h:907
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:597
void CopySettings(const VIEW *aOtherView)
Copy layers and visibility settings from another view.
Definition view.cpp:572
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition view.cpp:637
static constexpr int TOP_LAYER_MODIFIER
Rendering order modifier for layers that are marked as top layers.
Definition view.h:774
void draw(VIEW_ITEM *aItem, int aLayer, bool aImmediate=false)
Draw an item, but on a specified layers.
Definition view.cpp:1128
bool m_reverseDrawOrder
Flag to reverse the draw order when using draw priority.
Definition view.h:930
void UpdateAllLayersOrder()
Do everything that is needed to apply the rendering order of layers.
Definition view.cpp:975
void updateItemColor(VIEW_ITEM *aItem, int aLayer)
Update colors that are used for an item to be drawn.
Definition view.cpp:1381
void SetViewport(const BOX2D &aViewport)
Set the visible area of the VIEW.
Definition view.cpp:609
void SetRequired(int aLayerId, int aRequiredId, bool aRequired=true)
Mark the aRequiredId layer as required for the aLayerId layer.
Definition view.cpp:475
VECTOR2D ToScreen(const VECTOR2D &aCoord, bool aAbsolute=true) const
Convert a world space point/vector to a point/vector in screen space coordinates.
Definition view.cpp:553
static bool compareRenderingOrder(VIEW_LAYER *aI, VIEW_LAYER *aJ)
Determine rendering order of layers. Used in display order sorting function.
Definition view.h:866
int GetLayerOrder(int aLayer) const
Return rendering order of a particular layer.
Definition view.cpp:732
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:300
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:404
void ClearTargets()
Clear targets that are marked as dirty.
Definition view.cpp:1243
virtual void EnableTopLayer(bool aEnable)
Enable or disable display of the top layer.
Definition view.cpp:935
bool m_mirrorX
Definition view.h:909
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:844
bool m_mirrorY
Definition view.h:910
std::shared_ptr< std::vector< VIEW_ITEM * > > m_allItems
Flat list of all items.
Definition view.h:896
std::shared_ptr< VIEW_OVERLAY > MakeOverlay()
Definition view.cpp:1849
void SetGAL(GAL *aGal)
Assign a rendering device for the VIEW.
Definition view.cpp:578
int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition view.cpp:487
std::vector< VIEW_ITEM * > m_ownedItems
Definition view.h:884
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition view.cpp:1835
void invalidateItem(VIEW_ITEM *aItem, int aUpdateFlags)
Manage dirty flags & redraw queuing when updating an item.
Definition view.cpp:1327
const VECTOR2I & GetScreenPixelSize() const
Return the size of the our rendering area in pixels.
Definition view.cpp:1291
bool HasItem(const VIEW_ITEM *aItem) const
Indicates whether or not the given item has been added to the view.
Definition view.cpp:1821
virtual int GetTopLayer() const
Definition view.cpp:899
PAINTER * m_painter
PAINTER contains information how do draw items.
Definition view.h:915
virtual void Redraw()
Immediately redraws the whole view.
Definition view.cpp:1262
void Clear()
Remove all items from the view.
Definition view.cpp:1218
std::set< unsigned int > m_topLayers
The set of layers that are displayed on the top.
Definition view.h:899
bool m_enableOrderModifier
Whether to use rendering order modifier or not.
Definition view.h:887
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition view.cpp:534
bool IsHiddenOnOverlay(const VIEW_ITEM *aItem) const
Definition view.cpp:1813
void ClearTopLayers()
Remove all layers from the on-the-top set (they are no longer displayed over the rest of layers).
Definition view.cpp:960
void InitPreview()
Definition view.cpp:1873
void SetLayerOrder(int aLayer, int aRenderingOrder, bool aAutoSort=true)
Set rendering order of a particular layer.
Definition view.cpp:723
void ClearPreview()
Definition view.cpp:1858
void MarkClean()
Force redraw of view on the next rendering.
Definition view.h:686
void updateItemGeometry(VIEW_ITEM *aItem, int aLayer)
Update all information needed to draw an item.
Definition view.cpp:1399
static constexpr int VIEW_MAX_LAYERS
Maximum number of layers that may be shown.
Definition view.h:771
double m_minScale
Definition view.h:906
double m_scale
Definition view.h:904
void updateLayers(VIEW_ITEM *aItem)
Update set of layers that an item occupies.
Definition view.cpp:1470
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:1552
bool areRequiredLayersEnabled(int aLayerId) const
Check if every layer required by the aLayerId layer is enabled.
Definition view.cpp:1528
bool IsTargetDirty(int aTarget) const
Return true if any of layers belonging to the target or the target itself should be redrawn.
Definition view.h:646
int m_nextDrawPriority
The next sequential drawing priority.
Definition view.h:927
bool m_useDrawPriority
Flag to respect draw priority when drawing items.
Definition view.h:924
void UpdateItems()
Iterate through the list of items that asked for updating and updates them.
Definition view.cpp:1569
bool IsCached(int aLayer) const
Return true if the layer is cached.
Definition view.h:664
void AddBatch(const std::vector< VIEW_ITEM * > &aItems)
Add a batch of items to the view, using bulk-loaded R-trees for initial population.
Definition view.cpp:343
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition view.h:67
void SortLayers(std::vector< int > &aLayers) const
Change the order of given layer ids, so after sorting the order corresponds to layers rendering order...
Definition view.cpp:738
BASE_SET m_layerVisibilityCache
Definition view.h:911
void UpdateAllItems(int aUpdateFlags)
Update all items in the view according to the given flags.
Definition view.cpp:1686
bool m_hasPendingItemUpdates
True when at least one item has deferred update flags that still need processing.
Definition view.h:933
std::unique_ptr< KIGFX::VIEW_GROUP > m_preview
Definition view.h:883
static void OnDestroy(VIEW_ITEM *aItem)
Nasty hack, invoked by the destructor of VIEW_ITEM to auto-remove the item from the owning VIEW if th...
Definition view.cpp:225
virtual ~VIEW()
Definition view.cpp:294
GAL * m_gal
Interface to PAINTER that is used to draw items.
Definition view.h:918
std::map< int, VIEW_LAYER > m_layers
The set of possible displayed layers and its properties.
Definition view.h:890
std::unique_ptr< VIEW > DataReference() const
Return a new VIEW object that shares the same set of VIEW_ITEMs and LAYERs.
Definition view.cpp:1745
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1780
virtual void SetTopLayer(int aLayer, bool aEnabled=true)
Set given layer to be displayed on the top or sets back the default order of layers.
Definition view.cpp:908
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition view.cpp:1880
void UpdateLayerColor(int aLayer)
Apply the new coloring scheme held by RENDER_SETTINGS in case that it has changed.
Definition view.cpp:823
void syncLayerVisibilityCache()
Definition view.cpp:1899
void MarkDirty()
Force redraw of view on the next rendering.
Definition view.h:677
friend class VIEW_ITEM
Definition view.h:65
BOX2D m_boundary
Definition view.h:905
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition view.cpp:663
std::vector< VIEW_LAYER * > m_orderedLayers
Sorted list of pointers to members of m_layers.
Definition view.h:893
void clearGroupCache()
Clear cached GAL group numbers (ONLY numbers stored in VIEW_ITEMs, not group objects used by GAL).
Definition view.cpp:1315
VECTOR2D m_center
Center point of the VIEW (the point at which we are looking at).
Definition view.h:902
void SortOrderedLayers()
Sorts m_orderedLayers after layer rendering order has changed.
Definition view.cpp:1366
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition view.h:657
void updateBbox(VIEW_ITEM *aItem)
Update bounding box of an item.
Definition view.cpp:1436
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition view.cpp:1805
BASE_SET m_layerCachedFlagCache
Definition view.h:912
void UpdateAllItemsConditionally(int aUpdateFlags, std::function< bool(VIEW_ITEM *)> aCondition)
Update items in the view according to the given flags and condition.
Definition view.cpp:1702
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1756
void ReorderLayerData(std::unordered_map< int, int > aReorderMap)
Remap the data between layer ids without invalidating that data.
Definition view.cpp:748
void redrawRect(const BOX2I &aRect)
Redraw contents within rectangle aRect.
Definition view.cpp:1079
VECTOR2< T > GetScale() const
Get the scale components of the matrix.
Definition matrix3x3.h:291
A small class to help profiling.
Definition profile.h:46
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:86
double msecs(bool aSinceLast=false)
Definition profile.h:147
Represent a set of closed polygons.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
#define _(s)
const wxChar *const traceGalProfile
Flag to enable debug output of GAL performance profiling.
@ UNDEFINED_LAYER
Definition layer_ids.h:57
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:469
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:29
@ COLOR
Color has changed.
Definition view_item.h:50
@ INITIAL_ADD
Item is being added to the view.
Definition view_item.h:53
@ NONE
No updates are required.
Definition view_item.h:48
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:54
@ APPEARANCE
Visibility flag has changed.
Definition view_item.h:49
@ GEOMETRY
Position or shape has changed.
Definition view_item.h:51
@ LAYERS
Layers have changed.
Definition view_item.h:52
@ ALL
All except INITIAL_ADD.
Definition view_item.h:55
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition definitions.h:34
@ TARGET_CACHED
Main rendering target (cached)
Definition definitions.h:33
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:35
@ HIDDEN
Item is temporarily hidden (usually in favor of a being drawn from an overlay, such as a SELECTION).
Definition view_item.h:66
@ OVERLAY_HIDDEN
Item is temporarily hidden from being drawn on an overlay.
Definition view_item.h:67
@ VISIBLE
Item is visible (in general)
Definition view_item.h:62
std::shared_ptr< PNS_LOG_VIEWER_OVERLAY > overlay
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:1304
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:1019
DRAW_ITEM_VISITOR(VIEW *aView, int aLayer, bool aUseDrawPriority, bool aReverseDrawOrder)
Definition view.cpp:1009
int layers[VIEW_MAX_LAYERS]
Definition view.cpp:1071
std::vector< VIEW_ITEM * > drawItems
Definition view.cpp:1073
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:1193
RECACHE_ITEM_VISITOR(VIEW *aView, GAL *aGal, int aLayer)
Definition view.cpp:1186
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:805
UPDATE_COLOR_VISITOR(int aLayer, PAINTER *aPainter, GAL *aGal)
Definition view.cpp:798
UPDATE_DEPTH_VISITOR(int aLayer, int aDepth, GAL *aGal)
Definition view.cpp:877
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:884
bool diffLayer
Layer should be drawn differentially over lower layers.
Definition view.h:783
int renderingOrder
Rendering order of this layer.
Definition view.h:788
bool hasNegatives
Layer should be drawn separately to not delete lower layers.
Definition view.h:786
bool visible
Is the layer to be rendered?
Definition view.h:779
bool displayOnly
Is the layer display only?
Definition view.h:780
std::shared_ptr< VIEW_RTREE > items
R-tree indexing all items on this layer.
Definition view.h:787
RENDER_TARGET target
Where the layer should be rendered.
Definition view.h:790
int id
Layer ID.
Definition view.h:789
int delta
wxLogTrace helper definitions.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682