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