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>
39#include <algorithm>
40#include <array>
41#include <chrono>
42#include <cinttypes>
43#include <cmath>
44#include <limits>
45#include <tuple>
46#include <unordered_map>
47#include <unordered_set>
48
49#include <core/profile.h>
50
51
52#ifdef KICAD_GAL_PROFILE
53#include <wx/log.h>
54#include <trace_helpers.h>
55#endif
56
57namespace KIGFX {
58
59class VIEW;
60
62{
63public:
65 m_view( nullptr ),
68 m_pendingIndex( -1 ),
69 m_drawPriority( 0 ),
70 m_cachedIndex( -1 ),
71 m_groups( nullptr ),
72 m_groupsSize( 0 ) {}
73
75 {
77 }
78
79 int GetFlags() const
80 {
81 return m_flags;
82 }
83
84private:
85 friend class VIEW;
86
93 int getGroup( int aLayer ) const
94 {
95 for( int i = 0; i < m_groupsSize; ++i )
96 {
97 if( m_groups[i].first == aLayer )
98 return m_groups[i].second;
99 }
100
101 return -1;
102 }
103
110 void setGroup( int aLayer, int aGroup )
111 {
112 // Look if there is already an entry for the layer
113 for( int i = 0; i < m_groupsSize; ++i )
114 {
115 if( m_groups[i].first == aLayer )
116 {
117 m_groups[i].second = aGroup;
118 return;
119 }
120 }
121
122 // If there was no entry for the given layer - create one
123 std::pair<int, int>* newGroups = new std::pair<int, int>[m_groupsSize + 1];
124
125 if( m_groupsSize > 0 )
126 {
127 std::copy( m_groups, m_groups + m_groupsSize, newGroups );
128 delete[] m_groups;
129 }
130
131 m_groups = newGroups;
132 newGroups[m_groupsSize++] = { aLayer, aGroup };
133 }
134
135
140 {
141 delete[] m_groups;
142 m_groups = nullptr;
143 m_groupsSize = 0;
144 }
145
151 inline bool storesGroups() const
152 {
153 return m_groupsSize > 0;
154 }
155
163 void reorderGroups( const std::unordered_map<int, int>& aReorderMap )
164 {
165 for( int i = 0; i < m_groupsSize; ++i )
166 {
167 auto it = aReorderMap.find( m_groups[i].first );
168
169 if( it != aReorderMap.end() )
170 m_groups[i].first = it->second;
171 }
172 }
173
179 void saveLayers( const std::vector<int>& aLayers )
180 {
181 m_layers.clear();
182
183 for( int layer : aLayers )
184 {
185 wxCHECK2_MSG( layer >= 0 && layer < VIEW::VIEW_MAX_LAYERS, continue,
186 wxString::Format( wxT( "Invalid layer number: %d" ), layer ) );
187 m_layers.push_back( layer );
188 }
189 }
190
194 int requiredUpdate() const
195 {
196 return m_requiredUpdate;
197 }
198
203 {
205 }
206
210 bool isRenderable() const
211 {
212 return m_flags == VISIBLE;
213 }
214
218 int m_pendingIndex; // Place in the view's pending list, or -1
221
222 std::pair<int, int>* m_groups;
225
226 std::vector<int> m_layers;
227
229};
230
231
239
240
241static RETAINED_GROUP_ORDER retainedOrderKey( VIEW_ITEM* aItem, int aPriority, const BOX2I& aBBox,
242 bool aUseDrawPriority, bool aReverseDrawOrder,
243 double aLodScale )
244{
246 key.priority = aUseDrawPriority ? ( aReverseDrawOrder ? -int64_t( aPriority ) : int64_t( aPriority ) ) : 0;
247
248 key.bbox = { aBBox.GetLeft(), aBBox.GetTop(), aBBox.GetRight(), aBBox.GetBottom() };
249 key.className = aItem->GetClass();
250 key.lodScale = std::isnan( aLodScale ) ? std::numeric_limits<double>::infinity() : aLodScale;
251
252 return key;
253}
254
255
256template <typename T>
257static void sortRetainedOrder( VIEW* aView, std::vector<T>& aEntries )
258{
259 std::sort( aEntries.begin(), aEntries.end(), []( const T& a, const T& b )
260 {
261 return a.order < b.order;
262 } );
263
264 for( size_t first = 0; first < aEntries.size(); )
265 {
266 size_t last = first + 1;
267
268 while( last < aEntries.size() && aEntries[first].order.SamePrefix( aEntries[last].order ) )
269 ++last;
270
271 if( last - first > 1 )
272 {
273 for( size_t i = first; i < last; ++i )
274 {
275 T& entry = aEntries[i];
276 entry.order.contentHash = aView->GetGAL()->GroupContentFingerprint( entry.group );
277 entry.order.contentTie = true;
278 }
279
280 std::sort( aEntries.begin() + first, aEntries.begin() + last,
281 []( const T& a, const T& b ) { return a.order < b.order; } );
282 }
283
284 first = last;
285 }
286}
287
288
290{
291 if( aItem->m_viewPrivData )
292 {
293 if( aItem->m_viewPrivData->m_view )
294 aItem->m_viewPrivData->m_view->unlinkItem( aItem );
295
296 delete aItem->m_viewPrivData;
297 aItem->m_viewPrivData = nullptr;
298 }
299}
300
301
303 m_enableOrderModifier( true ),
304 m_scale( 4.0 ),
305 m_minScale( 0.2 ), m_maxScale( 50000.0 ),
306 m_mirrorX( false ), m_mirrorY( false ),
309 m_painter( nullptr ),
310 m_gal( nullptr ),
311 m_useDrawPriority( false ),
313 m_reverseDrawOrder( false ),
315 m_cameraDirtyOnly( false )
316{
317 // Set m_boundary to define the max area size. The default area size
318 // is defined here as the max value of a int.
319 // this is a default value acceptable for Pcbnew and Gerbview, but too large for Eeschema.
320 // So in eeschema a call to SetBoundary() with a smaller value will be needed.
321 typedef std::numeric_limits<int> coord_limits;
322 double pos = coord_limits::lowest() / 2 + coord_limits::epsilon();
323 double size = coord_limits::max() - coord_limits::epsilon();
324 m_boundary.SetOrigin( pos, pos );
325 m_boundary.SetSize( size, size );
326
327 m_allItems.reset( new std::vector<VIEW_ITEM*> );
328 m_allItems->reserve( 32768 );
329
330 // Redraw everything at the beginning
331 MarkDirty();
332
333 // View uses layers to display EDA_ITEMs (item may be displayed on several layers, for example
334 // pad may be shown on pad, pad hole and solder paste layers). There are usual copper layers
335 // (eg. F.Cu, B.Cu, internal and so on) and layers for displaying objects such as texts,
336 // silkscreen, pads, vias, etc.
337 for( int ii = 0; ii < VIEW_MAX_LAYERS; ++ii )
338 {
339 auto [it, _] = m_layers.emplace( ii, VIEW_LAYER() );
340 VIEW_LAYER& l = it->second;
341
342 l.items = std::make_shared<VIEW_RTREE>();
343 l.id = ii;
344 l.renderingOrder = ii;
345 l.visible = true;
346 l.displayOnly = false;
347 l.diffLayer = false;
348 l.hasNegatives = false;
350 }
351
353
354 m_preview.reset( new KIGFX::VIEW_GROUP() );
355 Add( m_preview.get() );
356}
357
358
360{
361 Remove( m_preview.get() );
362}
363
364
365void VIEW::Add( VIEW_ITEM* aItem, int aDrawPriority )
366{
367 if( aDrawPriority < 0 )
368 aDrawPriority = m_nextDrawPriority++;
369
370 if( !aItem->m_viewPrivData )
371 aItem->m_viewPrivData = new VIEW_ITEM_DATA;
372
373 // One view pointer and one index per item, so re-registering strands the first entry to
374 // dangle at free time
375 if( VIEW* previous = aItem->m_viewPrivData->m_view )
376 previous->unlinkItem( aItem );
377
378 aItem->m_viewPrivData->m_view = this;
379 aItem->m_viewPrivData->m_pendingIndex = -1;
380 aItem->m_viewPrivData->m_drawPriority = aDrawPriority;
381 const BOX2I bbox = aItem->ViewBBox();
382 aItem->m_viewPrivData->m_bbox = bbox;
383 aItem->m_viewPrivData->m_cachedIndex = m_allItems->size();
384
385 std::vector<int> layers = aItem->ViewGetLayers();
386
387 std::erase_if( layers, []( int layer )
388 {
389 return layer < 0 || layer >= VIEW_MAX_LAYERS;
390 } );
391
392 if( layers.empty() )
393 return;
394
395 aItem->viewPrivData()->saveLayers( layers );
396
397 m_allItems->push_back( aItem );
398
399 for( int layer : layers )
400 {
401 VIEW_LAYER& l = m_layers[layer];
402 l.items->Insert( aItem, bbox );
404 }
405
406 // The camera-reuse path would replay a scene without this item.
408
409 SetVisible( aItem, true );
410 Update( aItem, KIGFX::INITIAL_ADD );
411}
412
413
414void VIEW::AddBatch( const std::vector<VIEW_ITEM*>& aItems )
415{
416 // Phase 1: Register all items and collect per-layer data
417 std::unordered_map<int, std::vector<std::pair<VIEW_ITEM*, BOX2I>>> layerBulk;
418 std::unordered_set<VIEW_ITEM*> seen;
419 std::vector<VIEW_ITEM*> registered;
420
421 seen.reserve( aItems.size() );
422 registered.reserve( aItems.size() );
423
424 for( VIEW_ITEM* item : aItems )
425 {
426 // A repeat within the batch cannot be detached below because its layer entries are not
427 // inserted until phase 2, so it would reach BulkLoad twice
428 if( !item || !seen.insert( item ).second )
429 continue;
430
431 int drawPriority = m_nextDrawPriority++;
432
433 if( !item->m_viewPrivData )
434 item->m_viewPrivData = new VIEW_ITEM_DATA;
435
436 // Same single-registration invariant as Add()
437 if( VIEW* previous = item->m_viewPrivData->m_view )
438 previous->unlinkItem( item );
439
440 item->m_viewPrivData->m_view = this;
441 item->m_viewPrivData->m_pendingIndex = -1;
442 item->m_viewPrivData->m_drawPriority = drawPriority;
443 const BOX2I bbox = item->ViewBBox();
444 item->m_viewPrivData->m_bbox = bbox;
445 item->m_viewPrivData->m_cachedIndex = m_allItems->size();
446
447 std::vector<int> layers = item->ViewGetLayers();
448
449 std::erase_if( layers, []( int layer )
450 {
451 return layer < 0 || layer >= VIEW_MAX_LAYERS;
452 } );
453
454 if( layers.empty() )
455 continue;
456
457 item->viewPrivData()->saveLayers( layers );
458 m_allItems->push_back( item );
459 registered.push_back( item );
460
461 for( int layer : layers )
462 layerBulk[layer].emplace_back( item, bbox );
463 }
464
465 // Phase 2: Bulk load each layer's R-tree
466 for( auto& [layerId, items] : layerBulk )
467 {
468 VIEW_LAYER& l = m_layers[layerId];
469 l.items->BulkLoad( items );
471 }
472
473 if( !aItems.empty() )
475
476 // Phase 3: Set visibility and queue initial invalidation.
477 // INITIAL_ADD is required even though VIEW_ITEM_DATA defaults VISIBLE=true (making
478 // SetVisible a no-op). Without it, items reused after VIEW::Clear() retain stale GAL
479 // cache group IDs that point to freed memory.
480 for( VIEW_ITEM* item : registered )
481 {
482 SetVisible( item, true );
483 Update( item, KIGFX::INITIAL_ADD );
484 }
485}
486
487
489{
490 unlinkItem( aItem );
491}
492
493
495{
496 static int s_gcCounter = 0;
497
498 if( aItem && aItem->m_viewPrivData )
499 {
500 if( aItem->m_viewPrivData->m_view != nullptr && aItem->m_viewPrivData->m_view != this )
501 {
502 return;
503 }
504
505 std::vector<VIEW_ITEM*>::iterator item = m_allItems->end();
506 int cachedIndex = aItem->m_viewPrivData->m_cachedIndex;
507
508 if( cachedIndex >= 0
509 && cachedIndex < static_cast<ssize_t>( m_allItems->size() )
510 && ( *m_allItems )[cachedIndex] == aItem )
511 {
512 item = m_allItems->begin() + cachedIndex;
513 }
514 else
515 {
516 item = std::find( m_allItems->begin(), m_allItems->end(), aItem );
517 }
518
519 if( aItem->m_viewPrivData->m_pendingIndex >= 0 )
520 {
522 aItem->m_viewPrivData->m_pendingIndex = -1;
523 }
524
525 if( item != m_allItems->end() )
526 {
527 *item = nullptr;
529
530 s_gcCounter++;
531
532 if( s_gcCounter > 4096 )
533 {
534 // Perform defragmentation
535 std::erase_if( *m_allItems,
536 []( VIEW_ITEM* it )
537 {
538 return it == nullptr;
539 } );
540
541 // Update cached indices
542 for( size_t idx = 0; idx < m_allItems->size(); idx++ )
543 ( *m_allItems )[idx]->m_viewPrivData->m_cachedIndex = idx;
544
545 s_gcCounter = 0;
546 }
547 }
548
549 const BOX2I* bbox = &aItem->m_viewPrivData->m_bbox;
550
551 for( int layer : aItem->m_viewPrivData->m_layers )
552 {
553 VIEW_LAYER& l = m_layers[layer];
554 l.items->Remove( aItem, bbox );
556
557 // Clear the GAL cache
558 int prevGroup = aItem->m_viewPrivData->getGroup( layer );
559
560 if( prevGroup >= 0 )
561 m_gal->DeleteGroup( prevGroup );
562 }
563
565 aItem->m_viewPrivData->m_view = nullptr;
566
567 // The camera-reuse path would still draw the removed item.
569 }
570}
571
572
573void VIEW::SetRequired( int aLayerId, int aRequiredId, bool aRequired )
574{
575 wxCHECK( (unsigned) aLayerId < m_layers.size(), /*void*/ );
576 wxCHECK( (unsigned) aRequiredId < m_layers.size(), /*void*/ );
577
578 if( aRequired )
579 m_layers[aLayerId].requiredLayers.insert( aRequiredId );
580 else
581 m_layers[aLayerId].requiredLayers.erase( aRequiredId );
582
583 // redrawRect() skips layers whose required layers are hidden, so the cached output is stale.
584 layerStateChanged( aLayerId );
585}
586
587
588void VIEW::layerStateChanged( int aLayer )
589{
590 if( !m_gal || !m_gal->HasRetainedScene() )
591 {
592 if( IsCached( aLayer ) )
593 {
596 }
597
598 return;
599 }
600
601 m_sceneLayersStale = true;
602 m_lodRefreshLayers.push_back( aLayer );
603
604 // Items of the kept non-cached targets can read the layer's visibility as well
609}
610
611
612void VIEW::noteLodRead( int aLayer ) const
613{
614 if( aLayer < 0 || aLayer >= VIEW_MAX_LAYERS )
615 return;
616
617 std::vector<bool>& readers = m_lodReads[aLayer];
618
619 if( readers.empty() )
620 readers.resize( VIEW_MAX_LAYERS );
621
622 readers[m_lodProbeLayer] = true;
623}
624
625
627{
628 if( !m_gal || !m_gal->HasRetainedScene() )
629 return;
630
631 m_gal->SetSceneScale( m_scale );
632 m_gal->SetSceneDrawOrdered( m_useDrawPriority );
633
635 {
636 for( const auto& [id, l] : m_layers )
637 {
638 const bool drawn = l.target == TARGET_CACHED && l.visible && areRequiredLayersEnabled( id );
639
640 m_gal->SetSceneLayerState( id, l.renderingOrder, drawn, l.hasNegatives );
641
642 COLOR4D toward;
643 double keep = 1.0;
644
645 if( l.target == TARGET_CACHED && m_painter && m_painter->GetSettings()->GetLayerDim( id, toward, keep ) )
646 m_gal->SetSceneLayerDim( id, toward, keep );
647 else
648 m_gal->SetSceneLayerDim( id, COLOR4D::BLACK, 1.0 );
649 }
650
651 m_sceneLayersStale = false;
652 }
653
654 if( m_lodRefreshLayers.empty() )
655 return;
656
657 // A rebuild asks every item anyway
658 if( m_gal->HasStaticRebuildPending() || m_lodReads.empty() )
659 {
660 m_lodRefreshLayers.clear();
661 return;
662 }
663
664 if( !m_gal->IsVisible() || !m_gal->IsInitialized() )
665 return;
666
668
669 if( !ctx.IsUpdating() )
670 return;
671
673
674 std::vector<bool> readers( VIEW_MAX_LAYERS );
675
676 for( int read : m_lodRefreshLayers )
677 {
678 auto it = m_lodReads.find( read );
679
680 if( it == m_lodReads.end() )
681 continue;
682
683 for( int reader = 0; reader < VIEW_MAX_LAYERS; ++reader )
684 {
685 if( it->second[reader] )
686 readers[reader] = true;
687 }
688 }
689
690 m_lodRefreshLayers.clear();
691
692 BOX2I everywhere;
693 everywhere.SetMaximum();
694
695 for( auto& [id, l] : m_layers )
696 {
697 if( !readers[id] || l.target != TARGET_CACHED )
698 continue;
699
700 const int layer = id;
701
702 auto place = [&]( VIEW_ITEM* aItem ) -> bool
703 {
704 placeCachedGroup( aItem, layer );
705 return true;
706 };
707
708 l.items->Query( everywhere, place );
709 }
710}
711
712
714{
715 if( !m_gal )
716 return;
717
718 m_gal->MarkStaticDirty();
719
720 // Whatever rebuilds the cached scene, such as showing a layer, can also change what the kept
721 // non-cached targets draw
722 if( m_gal->HasRetainedScene() )
723 {
726 m_sceneLayersStale = true;
727 }
728}
729
730
732{
733 // Slots are handed out in arrival order, which cannot follow a draw priority
734 if( !m_gal || !m_gal->HasRetainedScene() || m_useDrawPriority )
735 {
737 return;
738 }
739
740 m_cameraDirtyOnly = false;
741}
742
743
744int VIEW::Query( const BOX2I& aRect, std::vector<LAYER_ITEM_PAIR>& aResult ) const
745{
746 if( m_orderedLayers.empty() )
747 return 0;
748
749 int layer = UNDEFINED_LAYER;
750 auto visitor =
751 [&]( VIEW_ITEM* item ) -> bool
752 {
753 aResult.push_back( VIEW::LAYER_ITEM_PAIR( item, layer ) );
754 return true;
755 };
756
757 std::vector<VIEW_LAYER*>::const_reverse_iterator i;
758
759 // execute queries in reverse direction, so that items that are on the top of
760 // the rendering stack are returned first.
761 for( i = m_orderedLayers.rbegin(); i != m_orderedLayers.rend(); ++i )
762 {
763 // ignore layers that do not contain actual items (i.e. the selection box, menus, floats)
764 if( ( *i )->displayOnly || !( *i )->visible )
765 continue;
766
767 layer = ( *i )->id;
768 ( *i )->items->Query( aRect, visitor );
769 }
770
771 return aResult.size();
772}
773
774
775void VIEW::Query( const BOX2I& aRect, const std::function<bool( VIEW_ITEM* )>& aFunc ) const
776{
777 if( m_orderedLayers.empty() )
778 return;
779
780 for( const auto& i : m_orderedLayers )
781 {
782 // ignore layers that do not contain actual items (i.e. the selection box, menus, floats)
783 if( i->displayOnly || !i->visible )
784 continue;
785
786 i->items->Query( aRect, aFunc );
787 }
788}
789
790
791VECTOR2D VIEW::ToWorld( const VECTOR2D& aCoord, bool aAbsolute ) const
792{
793 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
794
795 if( aAbsolute )
796 return VECTOR2D( matrix * aCoord );
797 else
798 return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y );
799}
800
801
802double VIEW::ToWorld( double aSize ) const
803{
804 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
805
806 return fabs( matrix.GetScale().x * aSize );
807}
808
809
810VECTOR2D VIEW::ToScreen( const VECTOR2D& aCoord, bool aAbsolute ) const
811{
812 const MATRIX3x3D& matrix = m_gal->GetWorldScreenMatrix();
813
814 if( aAbsolute )
815 return VECTOR2D( matrix * aCoord );
816 else
817 return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y );
818}
819
820
821double VIEW::ToScreen( double aSize ) const
822{
823 const MATRIX3x3D& matrix = m_gal->GetWorldScreenMatrix();
824
825 return matrix.GetScale().x * aSize;
826}
827
828
829void VIEW::CopySettings( const VIEW* aOtherView )
830{
831 wxASSERT_MSG( false, wxT( "This is not implemented" ) );
832}
833
834
835void VIEW::SetGAL( GAL* aGal )
836{
837 bool recacheGroups = ( m_gal != nullptr ); // recache groups only if GAL is reassigned
838 m_gal = aGal;
839
840 // A new retained GAL knows no layer state and would show every layer
841 m_sceneLayersStale = true;
842
843 // clear group numbers, so everything is going to be recached
844 if( recacheGroups )
845 {
848 }
849
850 // every target has to be refreshed
851 MarkDirty();
852
853 // SetScale() would derive the centre from a GAL still at its default zoom, shifting the view a
854 // little on every backend switch.
856 m_gal->SetZoomFactor( m_scale );
858}
859
860
862{
863 BOX2D rect;
864 VECTOR2D screenSize = m_gal->GetScreenPixelSize();
865
866 rect.SetOrigin( ToWorld( VECTOR2D( 0, 0 ) ) );
867 rect.SetEnd( ToWorld( screenSize ) );
868
869 return rect.Normalize();
870}
871
872
873void VIEW::SetViewport( const BOX2D& aViewport )
874{
875 VECTOR2D ssize = ToWorld( m_gal->GetScreenPixelSize(), false );
876
877 wxCHECK( fabs(ssize.x) > 0 && fabs(ssize.y) > 0, /*void*/ );
878
879 VECTOR2D centre = aViewport.Centre();
880 VECTOR2D vsize = aViewport.GetSize();
881 double zoom = 1.0 / std::max( fabs( vsize.x / ssize.x ), fabs( vsize.y / ssize.y ) );
882
883 SetCenter( centre );
884 SetScale( GetScale() * zoom );
885}
886
887
888void VIEW::SetMirror( bool aMirrorX, bool aMirrorY )
889{
890 wxASSERT_MSG( !aMirrorY, _( "Mirroring for Y axis is not supported yet" ) );
891
892 m_mirrorX = aMirrorX;
893 m_mirrorY = aMirrorY;
894 m_gal->SetFlip( aMirrorX, aMirrorY );
895
896 // Redraw everything
897 MarkDirty();
898}
899
900
901void VIEW::SetScale( double aScale, VECTOR2D aAnchor )
902{
903 if( aAnchor == VECTOR2D( 0, 0 ) )
904 aAnchor = m_center;
905
906 VECTOR2D a = ToScreen( aAnchor );
907 const double oldScale = m_scale;
908
909 if( aScale < m_minScale )
911 else if( aScale > m_maxScale )
913 else
914 m_scale = aScale;
915
916 m_gal->SetZoomFactor( m_scale );
917 m_gal->ComputeWorldScreenMatrix();
918
919 VECTOR2D delta = ToWorld( a ) - aAnchor;
920
922
923 // Redraw everything after the viewport has changed
925
926 if( m_scale != oldScale )
928}
929
930
931bool VIEW::IsShownAtScale( double aLod ) const
932{
933 noteKeptLod( aLod );
934
935 return aLod < m_scale;
936}
937
938
939void VIEW::noteKeptLod( double aLod ) const
940{
941 // A kept target has to be emitted again once the scale crosses one of these
942 if( !m_keptScaleUse || aLod == VIEW_ITEM::LOD_SHOW || aLod == VIEW_ITEM::LOD_HIDE )
943 return;
944
945 if( aLod < m_scale )
946 m_keptScaleUse->lodBelow = std::max( m_keptScaleUse->lodBelow, aLod );
947 else
948 m_keptScaleUse->lodAbove = std::min( m_keptScaleUse->lodAbove, aLod );
949}
950
951
953{
954 if( !m_gal || !m_gal->HasRetainedScene() )
955 return;
956
957 for( int target : { TARGET_NONCACHED, TARGET_OVERLAY } )
958 {
959 const SCALE_USE& use = m_scaleUse[target];
960
961 // A pan reuses the target, and so does a zoom that changes nothing it holds
963 {
964 MarkTargetDirty( target );
965 m_cameraDirtyOnly = false;
966 }
967 }
968}
969
970
971void VIEW::SetCenter( const VECTOR2D& aCenter )
972{
973 m_center = aCenter;
974
975 if( !m_boundary.Contains( aCenter ) )
976 {
977 if( m_center.x < m_boundary.GetLeft() )
978 m_center.x = m_boundary.GetLeft();
979 else if( aCenter.x > m_boundary.GetRight() )
980 m_center.x = m_boundary.GetRight();
981
982 if( m_center.y < m_boundary.GetTop() )
983 m_center.y = m_boundary.GetTop();
984 else if( m_center.y > m_boundary.GetBottom() )
985 m_center.y = m_boundary.GetBottom();
986 }
987
988 m_gal->SetLookAtPoint( m_center );
989 m_gal->ComputeWorldScreenMatrix();
990
991 // Redraw everything after the viewport has changed
993}
994
995
996void VIEW::SetCenter( const VECTOR2D& aCenter, const std::vector<BOX2D>& obscuringScreenRects )
997{
998 if( obscuringScreenRects.empty() )
999 return SetCenter( aCenter );
1000
1001 BOX2D screenRect( { 0, 0 }, m_gal->GetScreenPixelSize() );
1002 SHAPE_POLY_SET unobscuredPoly( screenRect );
1003 VECTOR2D unobscuredCenter = screenRect.Centre();
1004
1005 for( const BOX2D& obscuringScreenRect : obscuringScreenRects )
1006 {
1007 SHAPE_POLY_SET obscuringPoly( obscuringScreenRect );
1008 unobscuredPoly.BooleanSubtract( obscuringPoly );
1009 }
1010
1011 /*
1012 * Perform a step-wise deflate to find the center of the largest unobscured area
1013 */
1014
1015 BOX2I bbox = unobscuredPoly.BBox();
1016 int step = std::min( bbox.GetWidth(), bbox.GetHeight() ) / 10;
1017
1018 if( step < 20 )
1019 step = 20;
1020
1021 while( !unobscuredPoly.IsEmpty() )
1022 {
1023 unobscuredCenter = unobscuredPoly.BBox().Centre();
1025 }
1026
1027 SetCenter( aCenter - ToWorld( unobscuredCenter - screenRect.Centre(), false ) );
1028}
1029
1030
1031void VIEW::SetLayerOrder( int aLayer, int aRenderingOrder, bool aAutoSort )
1032{
1033 m_layers[aLayer].renderingOrder = aRenderingOrder;
1034
1035 if( aAutoSort )
1037}
1038
1039
1040int VIEW::GetLayerOrder( int aLayer ) const
1041{
1042 return m_layers.at( aLayer ).renderingOrder;
1043}
1044
1045
1046void VIEW::SortLayers( std::vector<int>& aLayers ) const
1047{
1048 std::sort( aLayers.begin(), aLayers.end(),
1049 [this]( int a, int b )
1050 {
1051 return GetLayerOrder( a ) > GetLayerOrder( b );
1052 } );
1053}
1054
1055
1056void VIEW::ReorderLayerData( std::unordered_map<int, int> aReorderMap )
1057{
1058 // GerbView's remapping is not a permutation once an image is deleted, so permute in place.
1059 // Rebuilding the map can drop an id, and m_layers[] then default-constructs a null R-tree
1060 std::map<int, VIEW_LAYER> moved;
1061 std::unordered_set<int> destinations;
1062
1063 for( const auto& [from, to] : aReorderMap )
1064 {
1065 if( from == to )
1066 continue;
1067
1068 auto source = m_layers.find( from );
1069
1070 if( source == m_layers.end() || !m_layers.count( to ) )
1071 continue;
1072
1073 moved.emplace( from, source->second );
1074 destinations.insert( to );
1075 }
1076
1077 // Every mover was copied before any of them was overwritten, so these can run in any order
1078 for( const auto& [from, layer] : moved )
1079 {
1080 auto destination = m_layers.find( aReorderMap.at( from ) );
1081
1082 destination->second = layer;
1083 destination->second.id = destination->first;
1084 }
1085
1086 // A source nothing moves onto keeps its settings but must own an empty tree rather than the
1087 // one it just handed to its destination
1088 for( const auto& [from, _] : moved )
1089 {
1090 if( !destinations.count( from ) )
1091 m_layers.find( from )->second.items = std::make_shared<VIEW_RTREE>();
1092 }
1093
1094 // Visibility and targets moved between ids
1095 m_layerCachesStale = true;
1096
1098
1099 // A recolor leaves each retained group on the scene layer of its old id
1100 if( m_gal && m_gal->HasRetainedScene() )
1102
1103 for( VIEW_ITEM* item : *m_allItems )
1104 {
1105 if( !item )
1106 continue;
1107
1108 VIEW_ITEM_DATA* viewData = item->viewPrivData();
1109
1110 if( !viewData )
1111 continue;
1112
1113 std::vector<int> layers = item->ViewGetLayers();
1114 viewData->saveLayers( layers );
1115
1116 viewData->reorderGroups( aReorderMap );
1117
1118 viewData->m_requiredUpdate |= COLOR;
1121 }
1122
1123 UpdateItems();
1124}
1125
1126
1127void VIEW::UpdateLayerColor( int aLayer )
1128{
1129 // There is no point in updating non-cached layers
1130 if( !IsCached( aLayer ) )
1131 return;
1132
1133 // The layer's fade can follow the same settings as its colours
1134 m_sceneLayersStale = true;
1135
1136 BOX2I r;
1137
1138 r.SetMaximum();
1139
1140 if( m_gal->IsVisible() )
1141 {
1143
1144 if( ctx.IsUpdating() )
1145 {
1146 std::vector<std::pair<VIEW_ITEM*, int>> groups;
1147
1148 auto collect = [&]( VIEW_ITEM* aItem )
1149 {
1150 if( int group = aItem->viewPrivData()->getGroup( aLayer ); group >= 0 )
1151 groups.emplace_back( aItem, group );
1152
1153 return true;
1154 };
1155
1156 m_layers[aLayer].items->Query( r, collect );
1157
1158 // Items without a uniform color are drawn again, as in recolorAllLayers()
1159 for( const auto& [item, group] : groups )
1160 recolorGroup( item, aLayer, group );
1161
1162 MarkTargetDirty( m_layers[aLayer].target );
1163
1164 // A retained scene's ChangeGroupColor() does not re-upload the static buffer
1167 }
1168 }
1169}
1170
1171
1173{
1174 recolorAllLayers( false );
1175}
1176
1177
1179{
1180 recolorAllLayers( m_gal->HasRetainedScene() );
1181}
1182
1183
1184void VIEW::UpdateLayersColor( bool aVisibility, const std::function<bool( int )>& aLayers )
1185{
1186 recolorAllLayers( aVisibility && m_gal->HasRetainedScene(), aLayers );
1187}
1188
1189
1190void VIEW::recolorAllLayers( bool aReplace, const std::function<bool( int )>& aLayers )
1191{
1192 // A layer's fade can follow the same settings as its colours
1193 m_sceneLayersStale = true;
1194
1195 if( aReplace )
1196 {
1198 m_gal->SetSceneScale( m_scale );
1200 }
1201
1202 if( m_gal->IsVisible() )
1203 {
1205
1206 if( ctx.IsUpdating() )
1207 {
1208 for( VIEW_ITEM* item : *m_allItems )
1209 {
1210 if( !item )
1211 continue;
1212
1213 VIEW_ITEM_DATA* viewData = item->viewPrivData();
1214
1215 if( !viewData )
1216 continue;
1217
1218 for( int layer : viewData->m_layers )
1219 {
1220 if( aLayers && !aLayers( layer ) )
1221 continue;
1222
1223 int group = viewData->getGroup( layer );
1224
1225 if( group >= 0 )
1226 recolorGroup( item, layer, group );
1227
1228 if( group >= 0 && aReplace )
1229 placeCachedGroup( item, layer );
1230 }
1231 }
1232 }
1233 }
1234
1235 MarkDirty();
1236}
1237
1238
1240{
1241 if( m_topLayers.size() == 0 )
1242 return 0;
1243
1244 return *m_topLayers.begin();
1245}
1246
1247
1248void VIEW::SetTopLayer( int aLayer, bool aEnabled )
1249{
1250 if( aEnabled )
1251 {
1252 if( m_topLayers.count( aLayer ) == 1 )
1253 return;
1254
1255 m_topLayers.insert( aLayer );
1256
1257 // Move the layer closer to front
1259 m_layers[aLayer].renderingOrder += TOP_LAYER_MODIFIER;
1260 }
1261 else
1262 {
1263 if( m_topLayers.count( aLayer ) == 0 )
1264 return;
1265
1266 m_topLayers.erase( aLayer );
1267
1268 // Restore the previous rendering order
1270 m_layers[aLayer].renderingOrder -= TOP_LAYER_MODIFIER;
1271 }
1272}
1273
1274
1275void VIEW::EnableTopLayer( bool aEnable )
1276{
1277 if( aEnable == m_enableOrderModifier )
1278 return;
1279
1280 m_enableOrderModifier = aEnable;
1281
1282 std::set<unsigned int>::iterator it;
1283
1284 if( aEnable )
1285 {
1286 for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it )
1287 m_layers[*it].renderingOrder += TOP_LAYER_MODIFIER;
1288 }
1289 else
1290 {
1291 for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it )
1292 m_layers[*it].renderingOrder -= TOP_LAYER_MODIFIER;
1293 }
1294
1297}
1298
1299
1301{
1302 std::set<unsigned int>::iterator it;
1303
1305 {
1306 // Restore the previous rendering order for layers that were marked as top
1307 for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it )
1308 m_layers[*it].renderingOrder -= TOP_LAYER_MODIFIER;
1309 }
1310
1311 m_topLayers.clear();
1312}
1313
1314
1316{
1318
1319 // A retained scene keeps depth per layer, not per group
1320 if( m_gal->IsVisible() && !m_gal->HasRetainedScene() )
1321 {
1323
1324 if( ctx.IsUpdating() )
1325 {
1326 for( VIEW_ITEM* item : *m_allItems )
1327 {
1328 if( !item )
1329 continue;
1330
1331 VIEW_ITEM_DATA* viewData = item->viewPrivData();
1332
1333 if( !viewData )
1334 continue;
1335
1336 for( int layer : viewData->m_layers )
1337 {
1338 int group = viewData->getGroup( layer );
1339
1340 if( group >= 0 )
1341 m_gal->ChangeGroupDepth( group, m_layers[layer].renderingOrder );
1342 }
1343 }
1344 }
1345 }
1346
1347 MarkDirty();
1348}
1349
1350
1352{
1353 DRAW_ITEM_VISITOR( VIEW* aView, int aLayer, bool aUseDrawPriority, bool aReverseDrawOrder ) :
1354 view( aView ),
1355 layer( aLayer ),
1356 useDrawPriority( aUseDrawPriority ),
1357 reverseDrawOrder( aReverseDrawOrder ),
1358 drawForcedTransparent( false ),
1359 foundForcedTransparent( false ),
1360 placing( false )
1361 {
1362 }
1363
1364 bool operator()( VIEW_ITEM* aItem )
1365 {
1366 wxCHECK( aItem->viewPrivData(), false );
1367
1368 if( aItem->m_forcedTransparency > 0 && !drawForcedTransparent )
1369 {
1371
1372 if( placing )
1373 drawItems.push_back( aItem );
1374
1375 return true;
1376 }
1377
1378 // A retained scene decides what is drawn itself, so it is told about every item
1380 {
1381 drawItems.push_back( aItem );
1382
1383 return true;
1384 }
1385
1386 const double itemLOD = aItem->ViewGetLOD( layer, view );
1387
1388 view->noteKeptLod( itemLOD );
1389
1390 // Conditions that have to be fulfilled for an item to be drawn
1391 bool drawCondition = aItem->viewPrivData()->isRenderable() && view->IsShownAtScale( itemLOD );
1392
1393 if( !drawCondition )
1394 return true;
1395
1396 if( useDrawPriority )
1397 drawItems.push_back( aItem );
1398 else
1399 view->draw( aItem, layer );
1400
1401 return true;
1402 }
1403
1405 {
1406 if( placing )
1407 {
1408 std::vector<VIEW::LAYER_ITEM_PAIR> items;
1409 items.reserve( drawItems.size() );
1410
1411 for( VIEW_ITEM* item : drawItems )
1412 items.emplace_back( item, layer );
1413
1414 view->placeRetainedInOrder( items, false );
1415 return;
1416 }
1417
1418 if( reverseDrawOrder )
1419 {
1420 std::sort( drawItems.begin(), drawItems.end(),
1421 []( VIEW_ITEM* a, VIEW_ITEM* b ) -> bool
1422 {
1423 return b->viewPrivData()->m_drawPriority
1424 < a->viewPrivData()->m_drawPriority;
1425 } );
1426 }
1427 else
1428 {
1429 std::sort( drawItems.begin(), drawItems.end(),
1430 []( VIEW_ITEM* a, VIEW_ITEM* b ) -> bool
1431 {
1432 return a->viewPrivData()->m_drawPriority
1433 < b->viewPrivData()->m_drawPriority;
1434 } );
1435 }
1436
1437 for( VIEW_ITEM* item : drawItems )
1438 view->draw( item, layer );
1439 }
1440
1444 std::vector<VIEW_ITEM*> drawItems;
1448};
1449
1450
1451void VIEW::placeRetainedInOrder( const std::vector<LAYER_ITEM_PAIR>& aItems, bool aReplacing )
1452{
1453 std::vector<RETAINED_ORDER_ITEM> ordered;
1454 ordered.reserve( aItems.size() );
1455
1456 for( const auto& [item, layer] : aItems )
1457 {
1458 VIEW_ITEM_DATA* data = item->viewPrivData();
1459
1460 if( !data || !IsCached( layer ) )
1461 continue;
1462
1463 if( data->getGroup( layer ) < 0 )
1464 updateItemGeometry( item, layer, false );
1465
1466 ordered.push_back( { item, layer, data->getGroup( layer ),
1468 m_reverseDrawOrder, cachedGroupLOD( item, layer ) ) } );
1469 }
1470
1471 sortRetainedOrder( this, ordered );
1472
1473 for( const RETAINED_ORDER_ITEM& entry : ordered )
1474 {
1475 if( aReplacing )
1476 releaseReplacedGroup( entry.item, entry.layer );
1477
1478 placeCachedGroup( entry.item, entry.layer, &entry.order );
1479 }
1480
1481 if( aReplacing )
1483}
1484
1485
1486void VIEW::placeCachedGroup( VIEW_ITEM* aItem, int aLayer,
1487 const RETAINED_GROUP_ORDER* aOrder )
1488{
1489 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1490
1491 if( !viewData )
1492 return;
1493
1495 {
1496 m_deferredRetainedPlacements.emplace_back( aItem, aLayer );
1497 return;
1498 }
1499
1500 if( viewData->getGroup( aLayer ) < 0 )
1501 updateItemGeometry( aItem, aLayer );
1502
1503 int group = viewData->getGroup( aLayer );
1504
1505 if( group < 0 )
1506 return;
1507
1508 // cachedGroupLOD() records which other layers the item reads, so showing or hiding one of them
1509 // places it again. The layer's own visibility is layer state.
1510 const double lod = aOrder ? aOrder->lodScale : cachedGroupLOD( aItem, aLayer );
1511 RETAINED_GROUP_ORDER order = aOrder ? *aOrder
1512 : retainedOrderKey( aItem, viewData->m_drawPriority, viewData->m_bbox,
1514 m_reverseDrawOrder, lod );
1515
1516 if( !aOrder )
1517 {
1518 order.contentHash = m_gal->GroupContentFingerprint( group );
1519 order.contentTie = true;
1520 }
1521
1522 m_gal->SetGroupPlacement( group, aLayer, lod,
1523 viewData->isRenderable() && aItem->m_forcedTransparency <= 0, order );
1524}
1525
1526
1527double VIEW::cachedGroupLOD( VIEW_ITEM* aItem, int aLayer )
1528{
1529 m_lodProbeLayer = aLayer;
1530 const double lod = aItem->ViewGetLOD( aLayer, this );
1531 m_lodProbeLayer = -1;
1532 return lod;
1533}
1534
1535
1536void VIEW::redrawRect( const BOX2I& aRect )
1537{
1539
1540 const bool retained = m_gal && m_gal->HasRetainedScene();
1541
1543
1544 if( retained )
1545 {
1546 for( int target : { TARGET_NONCACHED, TARGET_OVERLAY } )
1547 {
1548 if( IsTargetDirty( target ) || m_gal->RefillsNonCachedTargets() )
1549 m_scaleUse[target] = SCALE_USE();
1550 }
1551 }
1552
1553 for( VIEW_LAYER* l : m_orderedLayers )
1554 {
1555 if( l->items->IsEmpty() )
1556 continue;
1557
1558 const bool placing = retained && l->target == TARGET_CACHED;
1559
1560 // A backend that empties its non-cached targets on each drawn frame needs a clean
1561 // target emitted along with the dirty one, or it goes missing
1562 const bool refilled = l->target != TARGET_CACHED && m_gal && m_gal->RefillsNonCachedTargets();
1563
1564 // A retained scene keeps its cached layers until the backend asks for all of them again. A
1565 // pending rebuild empties the static pool even when TARGET_CACHED is clean.
1566 const bool targetDirty = placing ? m_gal->HasStaticRebuildPending()
1567 : ( IsTargetDirty( l->target ) || refilled
1568 || ( l->target == TARGET_CACHED && m_gal
1569 && m_gal->HasStaticRebuildPending() ) );
1570
1571 // A rebuild places the groups of hidden layers too, as the scene leaves those layers out
1572 if( targetDirty && ( placing || ( l->visible && areRequiredLayersEnabled( l->id ) ) ) )
1573 {
1574 DRAW_ITEM_VISITOR drawFunc( this, l->id, m_useDrawPriority, m_reverseDrawOrder );
1575
1576 m_gal->SetTarget( l->target );
1577 m_gal->SetLayerDepth( l->renderingOrder );
1578
1579 // Differential layer also work for the negatives, since both special layer types
1580 // will composite on separate layers (at least in Cairo)
1581 if( l->diffLayer )
1582 m_gal->StartDiffLayer();
1583 else if( l->hasNegatives )
1584 m_gal->StartNegativesLayer();
1585
1586 if( placing )
1587 {
1588 drawFunc.placing = true;
1589 m_gal->BeginSceneLayer( l->id );
1590 }
1591
1592 const bool kept = retained && l->target != TARGET_CACHED;
1593
1594 if( kept )
1595 {
1596 m_keptScaleUse = &m_scaleUse[l->target];
1597 m_scaleRead = false;
1598 m_gal->TakeWorldScaleRead();
1599
1600 double below, above;
1601 m_gal->TakeWorldScaleLimits( below, above );
1602 }
1603
1604 l->items->Query( aRect, drawFunc );
1605
1606 if( m_useDrawPriority || placing )
1607 drawFunc.deferredDraw();
1608
1609 m_keptScaleUse = nullptr;
1610
1611 if( kept && ( m_gal->TakeWorldScaleRead() || m_scaleRead ) )
1612 m_scaleUse[l->target].read = true;
1613
1614 // Limits on the world scale become limits on the view's, which is proportional
1615 if( kept && m_gal->GetWorldScaleForScreenSpace() > 0.0 )
1616 {
1617 const double toView = m_scale / m_gal->GetWorldScaleForScreenSpace();
1618 double below, above;
1619
1620 m_gal->TakeWorldScaleLimits( below, above );
1621
1622 SCALE_USE& use = m_scaleUse[l->target];
1623 use.lodBelow = std::max( use.lodBelow, below * toView );
1624
1625 if( above < std::numeric_limits<double>::max() )
1626 use.lodAbove = std::min( use.lodAbove, above * toView );
1627 }
1628
1629 if( l->diffLayer )
1630 m_gal->EndDiffLayer();
1631 else if( l->hasNegatives )
1632 m_gal->EndNegativesLayer();
1633
1634 if( drawFunc.foundForcedTransparent )
1635 {
1636 drawFunc.drawForcedTransparent = true;
1637
1638 m_gal->SetTarget( TARGET_NONCACHED );
1639 m_gal->EnableDepthTest( true );
1640 m_gal->SetLayerDepth( l->renderingOrder );
1641
1642 l->items->Query( aRect, drawFunc );
1643 }
1644 }
1645 }
1646}
1647
1648
1649void VIEW::draw( VIEW_ITEM* aItem, int aLayer, bool aImmediate )
1650{
1651 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1652
1653 if( !viewData )
1654 return;
1655
1656 if( m_layerCachedFlagCache[ aLayer ] && !aImmediate )
1657 {
1658 int group = viewData->getGroup( aLayer );
1659
1660 if( group >= 0 )
1661 {
1662 m_gal->DrawGroup( group );
1663 }
1664 else
1665 {
1666 wxLogTrace( traceRhi, wxS( "ViewCache: miss layer=%d item=%p drawingMode=%d" ),
1667 aLayer, static_cast<const void*>( aItem ), m_gal->IsDrawingMode() ? 1 : 0 );
1668
1669 // Only a retained scene builds a missed group mid-frame. Other backends wait for the next
1670 // update, since opening a group in Cairo ends its drawing surface.
1671 if( !m_gal->HasRetainedScene() )
1672 {
1673 Update( aItem );
1674 return;
1675 }
1676
1677 if( m_gal->IsDrawingMode() )
1678 {
1679 updateItemGeometry( aItem, aLayer, false );
1680 group = viewData->getGroup( aLayer );
1681
1682 if( group >= 0 )
1683 {
1684 m_gal->DrawGroup( group );
1685 return;
1686 }
1687 }
1688
1689 // Outside drawing mode there is no open group to draw into, so rebuild on the next pass
1690 wxLogTrace( traceRhi, wxS( "ViewCache: defer-build layer=%d item=%p" ),
1691 aLayer, static_cast<const void*>( aItem ) );
1692
1693 Update( aItem, REPAINT );
1694 }
1695 }
1696 else
1697 {
1698 // Immediate mode
1699 if( !m_painter->Draw( aItem, aLayer ) )
1700 aItem->ViewDraw( aLayer, this );
1701 }
1702}
1703
1704
1705void VIEW::draw( VIEW_ITEM* aItem, bool aImmediate )
1706{
1707 std::vector<int> layers = aItem->ViewGetLayers();
1708
1709 // Sorting is needed for drawing order dependent GALs (like Cairo)
1710 if( !m_gal || !m_gal->IsOpenGlEngine())
1711 SortLayers( layers );
1712
1713 for( int layer : layers )
1714 {
1715 auto it = m_layers.find( layer );
1716
1717 if( it == m_layers.end() )
1718 continue;
1719
1720 if( m_gal )
1721 m_gal->SetLayerDepth( it->second.renderingOrder );
1722
1723 draw( aItem, layer, aImmediate );
1724 }
1725}
1726
1727
1728void VIEW::draw( VIEW_GROUP* aGroup, bool aImmediate )
1729{
1730 for( unsigned int i = 0; i < aGroup->GetSize(); i++ )
1731 draw( aGroup->GetItem(i), aImmediate );
1732}
1733
1734
1736{
1737 RECACHE_ITEM_VISITOR( VIEW* aView, GAL* aGal, int aLayer ) :
1738 view( aView ),
1739 gal( aGal ),
1740 layer( aLayer )
1741 {
1742 }
1743
1744 bool operator()( VIEW_ITEM* aItem )
1745 {
1746 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
1747
1748 if( !viewData )
1749 return false;
1750
1751 // A retained scene keeps the cached group until the item is drawn again, so its replacement
1752 // takes the same place in the draw order, see updateItemGeometry()
1753 if( !gal->HasRetainedScene() )
1754 {
1755 int group = viewData->getGroup( layer );
1756
1757 if( group >= 0 )
1758 gal->DeleteGroup( group );
1759
1760 viewData->setGroup( layer, -1 );
1761 }
1762
1763 view->Update( aItem, KIGFX::REPAINT );
1764
1765 return true;
1766 }
1767
1771};
1772
1773
1775{
1776 BOX2I r;
1777 r.SetMaximum();
1778
1779 // Invalidate viewPrivData for all items before clearing. This ensures that items
1780 // which persist outside the view (like selection groups) won't have stale references
1781 // to this view, which could cause issues if they're later removed and re-added.
1783
1784 for( VIEW_ITEM* item : *m_allItems )
1785 {
1786 if( item && item->m_viewPrivData )
1787 item->m_viewPrivData->m_view = nullptr;
1788 }
1789
1790 m_allItems->clear();
1791
1792 for( auto& [_, layer] : m_layers )
1793 layer.items->RemoveAll();
1794
1796
1797 m_gal->ClearCache();
1798}
1799
1800
1802{
1803 // A kept target that only the camera moved still holds valid content
1804 if( m_gal && m_gal->HasRetainedScene() && !m_gal->RefillsNonCachedTargets() )
1805 {
1806 for( int target : { TARGET_NONCACHED, TARGET_OVERLAY } )
1807 {
1808 if( !m_targetEdited[target] )
1809 m_dirtyTargets[target] = false;
1810 }
1811 }
1812
1813 // Without a retained scene the cached and non-cached targets share surfaces, so they clear
1814 // together
1815 if( m_gal && !m_gal->HasRetainedScene() )
1816 {
1818 {
1819 m_gal->ClearTarget( TARGET_NONCACHED );
1820 m_gal->ClearTarget( TARGET_CACHED );
1821
1822 MarkDirty();
1823 }
1824 }
1825 else
1826 {
1828 m_gal->ClearTarget( TARGET_CACHED );
1829
1831 m_gal->ClearTarget( TARGET_NONCACHED );
1832 }
1833
1835 {
1836 m_gal->ClearTarget( TARGET_OVERLAY );
1837 }
1838}
1839
1840
1842{
1843#ifdef KICAD_GAL_PROFILE
1844 PROF_TIMER totalRealTime("view-redraw-total");
1845 latencyProbeZoomToRender.Checkpoint("view-redraw-start");
1846#endif /* KICAD_GAL_PROFILE */
1847
1848 BOX2I recti;
1849
1850 // A retained scene holds every item, not only the visible ones, so a full redraw visits them all
1851 if( m_gal && m_gal->HasRetainedScene() && m_gal->IsInitialized() )
1852 {
1853 recti.SetMaximum();
1854 }
1855 else
1856 {
1857 VECTOR2D screenSize = m_gal->GetScreenPixelSize();
1858 BOX2D rect( ToWorld( VECTOR2D( 0, 0 ) ),
1859 ToWorld( screenSize ) - ToWorld( VECTOR2D( 0, 0 ) ) );
1860
1861 rect.Normalize();
1862 recti = BOX2ISafe( rect );
1863 }
1864
1865 redrawRect( recti );
1866
1867 // All targets were redrawn, so nothing is dirty
1868 MarkClean();
1869
1870#ifdef KICAD_GAL_PROFILE
1871 totalRealTime.Stop();
1872 wxLogTrace( traceGalProfile, wxS( "VIEW::Redraw(): %.1f ms" ), totalRealTime.msecs() );
1873 latencyProbeZoomToRender.AddTimer( totalRealTime );
1874 latencyProbeZoomToRender.Checkpoint("view-redraw-end");
1875
1876#endif /* KICAD_GAL_PROFILE */
1877}
1878
1879
1881{
1882 return m_gal->GetScreenPixelSize();
1883}
1884
1885
1887{
1889 view( aView )
1890 {
1891 }
1892
1893 bool operator()( VIEW_ITEM* aItem )
1894 {
1895 aItem->viewPrivData()->deleteGroups();
1896
1897 return true;
1898 }
1899
1901};
1902
1903
1905{
1906 BOX2I r;
1907
1908 r.SetMaximum();
1909 CLEAR_LAYER_CACHE_VISITOR visitor( this );
1910
1911 for( auto& [_, layer] : m_layers )
1912 layer.items->Query( r, visitor );
1913}
1914
1915
1916void VIEW::invalidateItem( VIEW_ITEM* aItem, int aUpdateFlags )
1917{
1918 // updateLayers updates geometry too, so we do not have to update both of them at the
1919 // same time
1920 if( aUpdateFlags & LAYERS )
1921 updateLayers( aItem );
1922 else if( aUpdateFlags & GEOMETRY )
1923 updateBbox( aItem );
1924
1925 // Now that we have initialized, set flags to ALL for the code below
1926 if( aUpdateFlags & INITIAL_ADD )
1927 aUpdateFlags = ALL;
1928
1929 std::vector<int> layers = aItem->ViewGetLayers();
1930 bool cachedMutation = false;
1931 const bool retained = m_gal && m_gal->HasRetainedScene();
1932
1933 // Iterate through layers used by the item and recache it immediately
1934 for( int layer : layers )
1935 {
1936 if( IsCached( layer ) )
1937 {
1938 cachedMutation = true;
1939
1940 if( aUpdateFlags & ( GEOMETRY | LAYERS | REPAINT ) )
1941 updateItemGeometry( aItem, layer );
1942 else if( aUpdateFlags & COLOR )
1943 updateItemColor( aItem, layer );
1944
1945 // Visibility is a placement, the group itself is unchanged
1946 if( retained && ( aUpdateFlags & APPEARANCE ) )
1947 placeCachedGroup( aItem, layer );
1948 }
1949
1950 // Mark those layers as dirty, so the VIEW will be refreshed
1951 MarkTargetDirty( m_layers[layer].target );
1952 }
1953
1954 if( retained && !m_useDrawPriority && aItem->m_forcedTransparency <= 0 )
1955 {
1956 // The groups above are already patched into the scene, but a non-cached layer still needs
1957 // the frame off the camera-reuse path
1959 }
1960 else if( aUpdateFlags & ( GEOMETRY | LAYERS | REPAINT ) )
1961 {
1963 }
1964 else if( cachedMutation && ( aUpdateFlags & ( APPEARANCE | COLOR ) ) )
1965 {
1966 // A retained scene patches a color change into the group. Visibility changes cannot be
1967 // patched that way and rebuild.
1968 const bool colorOnlyPatchable = ( aUpdateFlags & COLOR ) && !( aUpdateFlags & APPEARANCE )
1969 && m_gal && m_gal->HasRetainedScene();
1970
1971 if( !colorOnlyPatchable )
1973 }
1974
1975 aItem->viewPrivData()->clearUpdateFlags();
1976}
1977
1978
1980{
1981 int n = 0;
1982
1983 m_orderedLayers.resize( m_layers.size() );
1984
1985 for( auto& [layer_id, layer] : m_layers )
1986 m_orderedLayers[n++] = &layer;
1987
1989
1990 // A retained scene takes the new order as layer state
1991 m_sceneLayersStale = true;
1992
1993 MarkDirty();
1994}
1995
1996
1997void VIEW::recolorGroup( VIEW_ITEM* aItem, int aLayer, int aGroup )
1998{
1999 if( m_painter->HasUniformColor( aItem, aLayer ) )
2000 {
2001 m_gal->ChangeGroupColor( aGroup, m_painter->GetSettings()->GetColor( aItem, aLayer ) );
2002 markDimExemption( aItem, aGroup );
2003 }
2004 else
2005 {
2006 updateItemGeometry( aItem, aLayer );
2007 }
2008}
2009
2010
2011void VIEW::markDimExemption( const VIEW_ITEM* aItem, int aGroup )
2012{
2013 if( m_gal->HasRetainedScene() )
2014 m_gal->ChangeGroupUndimmed( aGroup, m_painter->GetSettings()->IsDimExempt( aItem ) );
2015}
2016
2017
2018void VIEW::updateItemColor( VIEW_ITEM* aItem, int aLayer )
2019{
2020 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2021 wxCHECK( IsCached( aLayer ), /*void*/ ); // This will check if the layer exists
2022
2023 if( !viewData )
2024 return;
2025
2026 int group = viewData->getGroup( aLayer );
2027
2028 if( group >= 0 )
2029 recolorGroup( aItem, aLayer, group );
2030}
2031
2032
2033void VIEW::updateItemGeometry( VIEW_ITEM* aItem, int aLayer, bool aPlace )
2034{
2035 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2036
2037 if( !viewData )
2038 return;
2039
2040 auto it = m_layers.find( aLayer );
2041
2042 if( it == m_layers.end() )
2043 return;
2044
2045 VIEW_LAYER& l = it->second;
2046
2047 // Save the extra map lookup in IsCached by open coding here
2048 if( l.target != TARGET_CACHED )
2049 return;
2050
2051 int group = viewData->getGroup( aLayer );
2052
2053 m_gal->SetTarget( l.target );
2054 m_gal->SetLayerDepth( l.renderingOrder );
2055
2056 // A batch draws all of its items again before it places any. Deleting each old group here
2057 // would hand the freed slots back in reverse, so the old group waits for its replacement.
2058 const bool keepForPlacement = group >= 0 && aPlace && m_deferRetainedPlacement && m_gal->HasRetainedScene()
2059 && m_replacedRetainedGroups.emplace( std::make_pair( aItem, aLayer ), group ).second;
2060
2061 if( group >= 0 && !keepForPlacement )
2062 m_gal->DeleteGroup( group );
2063
2064 group = m_gal->BeginGroup();
2065 viewData->setGroup( aLayer, group );
2066
2067 wxLogTrace( traceGalGroups, wxS( "updateItemGeometry() item=%p layer=%d newGroup=%d" ),
2068 static_cast<void*>( aItem ), aLayer, group );
2069
2070 if( !m_painter->Draw( aItem, aLayer ) )
2071 aItem->ViewDraw( aLayer, this );
2072
2073 m_gal->EndGroup();
2074 markDimExemption( aItem, group );
2075
2076 if( aPlace && m_gal->HasRetainedScene() )
2077 placeCachedGroup( aItem, aLayer );
2078}
2079
2080
2081void VIEW::releaseReplacedGroup( VIEW_ITEM* aItem, int aLayer )
2082{
2083 auto it = m_replacedRetainedGroups.find( std::make_pair( aItem, aLayer ) );
2084
2085 if( it == m_replacedRetainedGroups.end() )
2086 return;
2087
2088 m_gal->DeleteGroup( it->second );
2089 m_replacedRetainedGroups.erase( it );
2090}
2091
2092
2094{
2095 for( const auto& [key, group] : m_replacedRetainedGroups )
2096 m_gal->DeleteGroup( group );
2097
2099}
2100
2101
2103{
2104 std::vector<int> layers = aItem->ViewGetLayers();
2105
2106 wxASSERT( aItem->m_viewPrivData ); //must have a viewPrivData
2107
2108 const BOX2I new_bbox = aItem->ViewBBox();
2109
2110 // The R-tree removal below keys on the bbox the item was inserted with, so it must be
2111 // copied before the m_bbox overwrite that follows rather than aliased to it. Otherwise
2112 // a moved item is removed with the wrong box and the R-tree falls back to a full-tree
2113 // search instead of the targeted removal.
2114 const BOX2I old_bbox = aItem->m_viewPrivData->m_bbox;
2115
2116 if( new_bbox == old_bbox )
2117 return;
2118
2119 aItem->m_viewPrivData->m_bbox = new_bbox;
2120
2121 for( int layer : layers )
2122 {
2123 auto it = m_layers.find( layer );
2124
2125 if( it == m_layers.end() )
2126 continue;
2127
2128 VIEW_LAYER& l = it->second;
2129 l.items->Remove( aItem, &old_bbox );
2130 l.items->Insert( aItem, new_bbox );
2132 }
2133
2135}
2136
2137
2139{
2140 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2141
2142 if( !viewData )
2143 return;
2144
2145 const BOX2I new_bbox = aItem->ViewBBox();
2146 std::vector<int> newLayers = aItem->ViewGetLayers();
2147
2148 // If neither the layers nor the bbox have changed, skip the expensive R-tree operations
2149 if( newLayers == viewData->m_layers && new_bbox == viewData->m_bbox )
2150 return;
2151
2152 // Remove the item from previous layer set
2153 const BOX2I& old_bbox = aItem->m_viewPrivData->m_bbox;
2154
2155 for( int layer : aItem->m_viewPrivData->m_layers )
2156 {
2157 auto it = m_layers.find( layer );
2158
2159 if( it == m_layers.end() )
2160 continue;
2161
2162 VIEW_LAYER& l = it->second;
2163 l.items->Remove( aItem, &old_bbox );
2165
2166 if( IsCached( l.id ) )
2167 {
2168 // Redraw the item from scratch
2169 int prevGroup = viewData->getGroup( layer );
2170
2171 if( prevGroup >= 0 )
2172 {
2173 m_gal->DeleteGroup( prevGroup );
2174 viewData->setGroup( l.id, -1 );
2175 }
2176 }
2177 }
2178
2179 aItem->m_viewPrivData->m_bbox = new_bbox;
2180 viewData->saveLayers( newLayers );
2181
2182 for( int layer : newLayers )
2183 {
2184 auto it = m_layers.find( layer );
2185
2186 if( it == m_layers.end() )
2187 continue;
2188
2189 VIEW_LAYER& l = it->second;
2190 l.items->Insert( aItem, new_bbox );
2192 }
2193
2195}
2196
2197
2198bool VIEW::areRequiredLayersEnabled( int aLayerId ) const
2199{
2200 auto it = m_layers.find( aLayerId );
2201
2202 if( it == m_layers.end() )
2203 return false;
2204
2205 for( int layer : it->second.requiredLayers )
2206 {
2207 // That is enough if just one layer is not enabled
2208
2209 auto it2 = m_layers.find( layer );
2210
2211 if( it2 == m_layers.end() || !it2->second.visible )
2212 return false;
2213
2214 if( !areRequiredLayersEnabled( layer ) )
2215 return false;
2216 }
2217
2218 return true;
2219}
2220
2221
2223{
2224 BOX2I r;
2225
2226 r.SetMaximum();
2227
2228 for( const auto& [_, l] : m_layers )
2229 {
2230 if( IsCached( l.id ) )
2231 {
2232 RECACHE_ITEM_VISITOR visitor( this, m_gal, l.id );
2233 l.items->Query( r, visitor );
2234 }
2235 }
2236}
2237
2238
2240{
2241 if( !m_gal->IsVisible() || !m_gal->IsInitialized() )
2242 {
2243 wxLogTrace( traceGalGroups,
2244 wxS( "UpdateItems() SKIP: visible=%d initialized=%d" ),
2245 m_gal->IsVisible(), m_gal->IsInitialized() );
2246 return;
2247 }
2248
2249#ifdef KICAD_GAL_PROFILE
2250 latencyProbeZoomToRender.Checkpoint("view-update-items");
2251#endif
2252
2254 return;
2255
2256 // Groups cached below are placed against the scale they will be drawn at
2257 if( m_gal->HasRetainedScene() )
2258 m_gal->SetSceneScale( m_scale );
2259
2260 unsigned int cntGeomUpdate = 0;
2261 bool anyUpdated = false;
2262
2263 // Other backends scan every item, since the shape of their R-trees depends on that order
2264 const bool retained = m_gal->HasRetainedScene();
2265 const bool listed = retained && !m_pendingListIncomplete;
2266
2267 const std::vector<VIEW_ITEM*>& flagged = listed ? m_pendingItems : *m_allItems;
2268
2269 for( VIEW_ITEM* item : flagged )
2270 {
2271 if( !item )
2272 continue;
2273
2274 auto vpd = item->viewPrivData();
2275
2276 if( !vpd )
2277 continue;
2278
2279 if( vpd->m_requiredUpdate != NONE )
2280 {
2281 anyUpdated = true;
2282
2283 if( vpd->m_requiredUpdate & ( GEOMETRY | LAYERS ) )
2284 cntGeomUpdate++;
2285 }
2286 }
2287
2288 unsigned int cntTotal = m_allItems->size();
2289
2290 double ratio = (double) cntGeomUpdate / (double) cntTotal;
2291
2292 // R*-tree individual inserts use forced reinsertion on node overflow, making them
2293 // significantly more expensive than simple R-tree inserts. At ~5% changed items
2294 // the cost of individual Remove+Insert operations exceeds a full bulk rebuild.
2295 if( ratio > 0.05 )
2296 {
2297 auto allItems = *m_allItems;
2298
2299 // Clear all R-trees
2300 for( auto& [_, layer] : m_layers )
2301 layer.items->RemoveAll();
2302
2303 // Collect items per layer for bulk loading
2304 std::unordered_map<int, std::vector<std::pair<VIEW_ITEM*, BOX2I>>> layerBulk;
2305
2306 for( VIEW_ITEM* item : allItems )
2307 {
2308 if( !item )
2309 continue;
2310
2311 const BOX2I bbox = item->ViewBBox();
2312 item->m_viewPrivData->m_bbox = bbox;
2313
2314 std::vector<int> layers = item->ViewGetLayers();
2315 item->viewPrivData()->saveLayers( layers );
2316
2317 for( int layer : layers )
2318 {
2319 auto it = m_layers.find( layer );
2320
2321 wxCHECK2_MSG( it != m_layers.end(), continue, wxS( "Invalid layer" ) );
2322 layerBulk[layer].emplace_back( item, bbox );
2323 }
2324
2325 // The bulk rebuild handled R-tree reinsertion, so clear LAYERS|GEOMETRY
2326 // to avoid redundant R-tree work in invalidateItem(). Replace with REPAINT
2327 // so items still reach updateItemGeometry() for GAL cache rebuilds.
2328 if( item->viewPrivData()->m_requiredUpdate & ( LAYERS | GEOMETRY ) )
2329 {
2330 item->viewPrivData()->m_requiredUpdate &= ~( LAYERS | GEOMETRY );
2331 item->viewPrivData()->m_requiredUpdate |= REPAINT;
2332 }
2333 }
2334
2335 // Bulk load each layer's R-tree
2336 for( auto& [layerId, items] : layerBulk )
2337 {
2338 auto it = m_layers.find( layerId );
2339
2340 if( it != m_layers.end() )
2341 {
2342 it->second.items->BulkLoad( items );
2343 MarkTargetDirty( it->second.target );
2344 }
2345 }
2346
2348 }
2349
2350 // Skipping the loop below leaves m_requiredUpdate and m_hasPendingItemUpdates set so the
2351 // work is redone once the canvas has a valid context again
2352 bool updatesProcessed = true;
2353
2354 if( anyUpdated )
2355 {
2356 unsigned int cntInvalidated = 0;
2358
2359 if( ctx.IsUpdating() )
2360 {
2361 auto invalidate = [&]( VIEW_ITEM* item )
2362 {
2363 if( item && item->viewPrivData() && item->viewPrivData()->m_requiredUpdate != NONE )
2364 {
2365 cntInvalidated++;
2366 invalidateItem( item, item->viewPrivData()->m_requiredUpdate );
2367 item->viewPrivData()->m_requiredUpdate = NONE;
2368 }
2369 };
2370
2371 if( retained )
2372 {
2373 if( m_gal->HasStaticRebuildPending() )
2374 {
2375 // The full redraw places every cached group, including those hidden at this scale
2377
2378 for( size_t next = 0; next < flagged.size(); ++next )
2379 invalidate( flagged[next] );
2380
2384 }
2385 else
2386 {
2387 size_t next = 0;
2388
2389 // An invalidation may queue another item, so sort each newly queued batch.
2390 while( next < flagged.size() )
2391 {
2392 std::vector<RETAINED_ORDER_ITEM> batch;
2393
2394 while( next < flagged.size() )
2395 {
2396 VIEW_ITEM* item = flagged[next++];
2397
2398 if( !item || !item->viewPrivData() || item->viewPrivData()->m_requiredUpdate == NONE )
2399 continue;
2400
2401 const VIEW_ITEM_DATA* data = item->viewPrivData();
2402 const int front = data->m_layers.empty() ? -1 : data->m_layers.front();
2403
2404 batch.push_back(
2405 { item, front, front < 0 ? -1 : data->getGroup( front ),
2408 data->m_layers.empty()
2409 ? 0.0
2410 : cachedGroupLOD( item, data->m_layers.front() ) ) } );
2411 }
2412
2413 std::sort( batch.begin(), batch.end(),
2414 []( const RETAINED_ORDER_ITEM& a, const RETAINED_ORDER_ITEM& b )
2415 {
2416 return a.order < b.order;
2417 } );
2418
2420
2421 for( const RETAINED_ORDER_ITEM& entry : batch )
2422 invalidate( entry.item );
2423
2425
2426 placeRetainedInOrder( std::exchange( m_deferredRetainedPlacements, {} ), true );
2427 }
2428 }
2429 }
2430 else
2431 {
2432 // Invalidating an item can flag another, which grows the list being walked.
2433 for( size_t ii = 0; ii < flagged.size(); ++ii )
2434 invalidate( flagged[ii] );
2435 }
2436 }
2437 else
2438 {
2439 updatesProcessed = false;
2440 }
2441
2442 wxLogTrace( traceGalGroups, wxS( "UpdateItems() invalidated %u items" ), cntInvalidated );
2443 }
2444
2445 wxLogTrace( traceGalGroups,
2446 wxS( "UpdateItems() total=%u geomUpdate=%u anyUpdated=%d bulkRebuild=%d" ),
2447 cntTotal, cntGeomUpdate, anyUpdated, ratio > 0.05 );
2448
2449#ifdef KICAD_GAL_PROFILE
2450 wxLogTrace( traceGalProfile, wxS( "View update: total items %u, geom %u anyUpdated %u" ),
2451 cntTotal, cntGeomUpdate, (unsigned) anyUpdated );
2452#endif
2453
2454 if( updatesProcessed )
2455 {
2459 }
2460}
2461
2462
2464{
2465 for( VIEW_ITEM* item : m_pendingItems )
2466 {
2467 if( item && item->viewPrivData() )
2468 item->viewPrivData()->m_pendingIndex = -1;
2469 }
2470
2471 m_pendingItems.clear();
2472}
2473
2474
2475void VIEW::UpdateAllItems( int aUpdateFlags )
2476{
2477 if( aUpdateFlags == NONE )
2478 return;
2479
2480 for( VIEW_ITEM* item : *m_allItems )
2481 {
2482 if( item && item->viewPrivData() )
2483 {
2484 item->viewPrivData()->m_requiredUpdate |= aUpdateFlags;
2487 }
2488 }
2489}
2490
2491
2493 std::function<bool( VIEW_ITEM* )> aCondition )
2494{
2495 if( aUpdateFlags == NONE )
2496 return;
2497
2498 for( VIEW_ITEM* item : *m_allItems )
2499 {
2500 if( !item )
2501 continue;
2502
2503 if( aCondition( item ) )
2504 {
2505 if( item->viewPrivData() )
2506 {
2507 item->viewPrivData()->m_requiredUpdate |= aUpdateFlags;
2510 }
2511 }
2512 }
2513}
2514
2515
2516void VIEW::UpdateAllItemsConditionally( std::function<int( VIEW_ITEM* )> aItemFlagsProvider )
2517{
2518 for( VIEW_ITEM* item : *m_allItems )
2519 {
2520 if( !item )
2521 continue;
2522
2523 if( item->viewPrivData() )
2524 {
2525 int flags = aItemFlagsProvider( item );
2526 item->viewPrivData()->m_requiredUpdate |= flags;
2527
2528 if( flags != NONE )
2529 {
2532 }
2533 }
2534 }
2535}
2536
2537
2538
2539std::unique_ptr<VIEW> VIEW::DataReference() const
2540{
2541 std::unique_ptr<VIEW> ret = std::make_unique<VIEW>();
2542 ret->m_allItems = m_allItems;
2543 ret->m_layers = m_layers;
2544 ret->m_hasPendingItemUpdates = m_hasPendingItemUpdates;
2545 ret->m_pendingListIncomplete = true;
2546 ret->m_cameraDirtyOnly = m_cameraDirtyOnly;
2547 ret->SortOrderedLayers();
2548 return ret;
2549}
2550
2551
2552void VIEW::SetVisible( VIEW_ITEM* aItem, bool aIsVisible )
2553{
2554 if( !aItem )
2555 return;
2556
2557 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2558
2559 if( !viewData )
2560 return;
2561
2562 bool cur_visible = viewData->m_flags & VISIBLE;
2563
2564 if( cur_visible != aIsVisible )
2565 {
2566 if( aIsVisible )
2567 viewData->m_flags |= VISIBLE;
2568 else
2569 viewData->m_flags &= ~VISIBLE;
2570
2571 Update( aItem, APPEARANCE | COLOR );
2572 }
2573}
2574
2575
2576void VIEW::Hide( VIEW_ITEM* aItem, bool aHide, bool aHideOverlay )
2577{
2578 if( !aItem )
2579 return;
2580
2581 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2582
2583 if( !viewData )
2584 return;
2585
2586 if( !( viewData->m_flags & VISIBLE ) )
2587 return;
2588
2589 if( aHideOverlay )
2590 viewData->m_flags |= OVERLAY_HIDDEN;
2591
2592 if( aHide )
2593 viewData->m_flags |= HIDDEN;
2594 else
2595 viewData->m_flags &= ~( HIDDEN | OVERLAY_HIDDEN );
2596
2597 Update( aItem, APPEARANCE );
2598}
2599
2600
2601bool VIEW::IsVisible( const VIEW_ITEM* aItem ) const
2602{
2603 const VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2604
2605 return viewData && ( viewData->m_flags & VISIBLE );
2606}
2607
2608
2609bool VIEW::IsHiddenOnOverlay( const VIEW_ITEM* aItem ) const
2610{
2611 const VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2612
2613 return viewData && ( viewData->m_flags & OVERLAY_HIDDEN );
2614}
2615
2616
2617bool VIEW::HasItem( const VIEW_ITEM* aItem ) const
2618{
2619 const VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2620
2621 return viewData && viewData->m_view == this;
2622}
2623
2624
2625void VIEW::Update( const VIEW_ITEM* aItem ) const
2626{
2627 Update( aItem, ALL );
2628}
2629
2630
2631void VIEW::Update( const VIEW_ITEM* aItem, int aUpdateFlags ) const
2632{
2633 VIEW_ITEM_DATA* viewData = aItem->viewPrivData();
2634
2635 if( !viewData )
2636 return;
2637
2638 assert( aUpdateFlags != NONE );
2639
2640 viewData->m_requiredUpdate |= aUpdateFlags;
2642
2643 // An item records a single list position, so only this view's own items are listed
2644 if( viewData->m_view != this )
2645 {
2647 }
2648 else if( viewData->m_pendingIndex < 0 )
2649 {
2650 viewData->m_pendingIndex = static_cast<int>( m_pendingItems.size() );
2651 m_pendingItems.push_back( const_cast<VIEW_ITEM*>( aItem ) );
2652 }
2653}
2654
2655
2656void VIEW::RepaintLayers( const BOX2I& aRect, const std::function<bool( int )>& aLayerFilter,
2657 const std::function<bool( VIEW_ITEM*, int )>& aItemFilter )
2658{
2659 if( !m_gal->IsVisible() || !m_gal->IsInitialized() )
2660 return;
2661
2662 std::vector<LAYER_ITEM_PAIR> stale;
2663
2664 for( VIEW_LAYER* l : m_orderedLayers )
2665 {
2666 if( l->target != TARGET_CACHED || !l->visible || !aLayerFilter( l->id ) )
2667 continue;
2668
2669 auto collect = [&]( VIEW_ITEM* aItem ) -> bool
2670 {
2671 if( aItemFilter( aItem, l->id ) )
2672 stale.emplace_back( aItem, l->id );
2673
2674 return true;
2675 };
2676
2677 l->items->Query( aRect, collect );
2678 }
2679
2680 if( stale.empty() )
2681 return;
2682
2684
2685 if( !ctx.IsUpdating() )
2686 return;
2687
2688 if( m_gal->HasRetainedScene() )
2689 m_gal->SetSceneScale( m_scale );
2690
2691 for( const LAYER_ITEM_PAIR& pair : stale )
2692 updateItemGeometry( pair.first, pair.second );
2693
2694 if( m_gal->HasRetainedScene() && !m_useDrawPriority )
2695 {
2696 // The camera path uploads the already placed groups, so a zoom and the net names it redraws
2697 // still go out as one camera frame
2698 const bool cameraOnly = m_cameraDirtyOnly || !IsDirty();
2699
2701 m_cameraDirtyOnly = cameraOnly;
2702 return;
2703 }
2704
2707}
2708
2709
2710std::shared_ptr<VIEW_OVERLAY> VIEW::MakeOverlay()
2711{
2712 std::shared_ptr<VIEW_OVERLAY> overlay = std::make_shared<VIEW_OVERLAY>();
2713
2714 Add( overlay.get() );
2715 return overlay;
2716}
2717
2718
2720{
2721 if( !m_preview )
2722 return;
2723
2724 m_preview->Clear();
2725
2726 for( VIEW_ITEM* item : m_ownedItems )
2727 delete item;
2728
2729 m_ownedItems.clear();
2730 Update( m_preview.get() );
2731}
2732
2733
2735{
2736 m_preview.reset( new KIGFX::VIEW_GROUP() );
2737 Add( m_preview.get() );
2738}
2739
2740
2741void VIEW::AddToPreview( VIEW_ITEM* aItem, bool aTakeOwnership )
2742{
2743 Hide( aItem, false );
2744 m_preview->Add( aItem );
2745
2746 if( aTakeOwnership )
2747 m_ownedItems.push_back( aItem );
2748
2749 SetVisible( m_preview.get(), true );
2750 Hide( m_preview.get(), false );
2751 Update( m_preview.get() );
2752}
2753
2754
2755void VIEW::ShowPreview( bool aShow )
2756{
2757 SetVisible( m_preview.get(), aShow );
2758}
2759
2760
2762{
2763 // Tools ask for this once per item they look at, far more often than a layer changes
2764 if( !m_layerCachesStale )
2765 return;
2766
2767 for( const auto& layer : m_layers )
2768 {
2769 m_layerVisibilityCache[ layer.first ] = layer.second.visible;
2770 m_layerCachedFlagCache[ layer.first ] = (layer.second.target == TARGET_CACHED);
2771 }
2772
2773 m_layerCachesStale = false;
2774}
2775
2776} // namespace KIGFX
constexpr int ARC_LOW_DEF
Definition base_units.h:143
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:921
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
BOX2< VECTOR2D > BOX2D
Definition box2.h:915
constexpr void SetMaximum()
Definition box2.h:77
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:234
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:143
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr const SizeVec & GetSize() const
Definition box2.h:203
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:294
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
static const COLOR4D BLACK
Definition color4d.h:404
Abstract interface for drawing on a 2D-surface.
virtual std::array< uint64_t, 2 > GroupContentFingerprint(int aGroupNumber) const
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:151
std::vector< int > m_layers
Definition view.cpp:226
int requiredUpdate() const
Return current update flag for an item.
Definition view.cpp:194
int m_requiredUpdate
Flag required for updating.
Definition view.cpp:217
int m_flags
Visibility flags.
Definition view.cpp:216
int m_cachedIndex
Cached index in m_allItems.
Definition view.cpp:220
void deleteGroups()
Remove all of the stored group ids.
Definition view.cpp:139
bool isRenderable() const
Return if the item should be drawn or not.
Definition view.cpp:210
int m_drawPriority
Order to draw this item in a layer, lowest first.
Definition view.cpp:219
friend class VIEW
Definition view.cpp:85
VIEW * m_view
Current dynamic view the item is assigned to.
Definition view.cpp:215
std::pair< int, int > * m_groups
layer_number:group_id pairs for each layer the item occupies.
Definition view.cpp:222
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:93
void reorderGroups(const std::unordered_map< int, int > &aReorderMap)
Reorder the stored groups (to facilitate reordering of layers).
Definition view.cpp:163
BOX2I m_bbox
Stores layer numbers used by the item.
Definition view.cpp:228
void setGroup(int aLayer, int aGroup)
Set a group id for the item and the layer combination.
Definition view.cpp:110
void saveLayers(const std::vector< int > &aLayers)
Save layers used by the item.
Definition view.cpp:179
int GetFlags() const
Definition view.cpp:79
void clearUpdateFlags()
Mark an item as already updated, so it is not going to be redrawn.
Definition view.cpp:202
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
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.
static constexpr double LOD_HIDE
Return this constant from ViewGetLOD() to hide the item unconditionally.
Definition view_item.h:176
static constexpr double LOD_SHOW
Return this constant from ViewGetLOD() to show the item unconditionally.
Definition view_item.h:181
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:68
double GetScale() const
Definition view.h:298
void SetMirror(bool aMirrorX, bool aMirrorY)
Control the mirroring of the VIEW.
Definition view.cpp:888
bool m_layerCachesStale
A layer's visibility or target changed since SyncLayerVisibilityCache() last copied them.
Definition view.h:1103
void ShowPreview(bool aShow=true)
Definition view.cpp:2755
void markSceneEdited()
Note that items of the cached scene were added, removed or re-cached.
Definition view.cpp:731
std::vector< int > m_lodRefreshLayers
Layers shown or hidden since the last sync, whose readers have to be placed again.
Definition view.h:1184
bool m_cameraDirtyOnly
The current dirty state is due only to camera changes.
Definition view.h:1205
double m_maxScale
Definition view.h:1093
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:861
void releaseReplacedGroup(VIEW_ITEM *aItem, int aLayer)
Delete the group a deferred placement of an item on a layer replaces, if it is still kept.
Definition view.cpp:2081
void CopySettings(const VIEW *aOtherView)
Copy layers and visibility settings from another view.
Definition view.cpp:829
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition view.cpp:901
static constexpr int TOP_LAYER_MODIFIER
Rendering order modifier for layers that are marked as top layers.
Definition view.h:902
void draw(VIEW_ITEM *aItem, int aLayer, bool aImmediate=false)
Draw an item, but on a specified layers.
Definition view.cpp:1649
SCALE_USE * m_keptScaleUse
The SCALE_USE of the kept target being drawn, updated by each level of detail drawn.
Definition view.h:1172
bool m_reverseDrawOrder
Flag to reverse the draw order when using draw priority.
Definition view.h:1127
std::unordered_map< int, std::vector< bool > > m_lodReads
For each layer whose visibility an item asked about, the cached layers those items were placed on.
Definition view.h:1195
bool m_targetEdited[TARGETS_NUMBER]
The target's content changed, not just the camera.
Definition view.h:1118
void UpdateAllLayersOrder()
Do everything that is needed to apply the rendering order of layers.
Definition view.cpp:1315
int m_lodProbeLayer
Cached layer being placed while an item is asked for its level of detail, else -1.
Definition view.h:1189
void placeCachedGroup(VIEW_ITEM *aItem, int aLayer, const RETAINED_GROUP_ORDER *aOrder=nullptr)
Tell a retained scene whether the cached group of aItem on aLayer belongs in it, caching the group fi...
Definition view.cpp:1486
void updateItemColor(VIEW_ITEM *aItem, int aLayer)
Update colors that are used for an item to be drawn.
Definition view.cpp:2018
void SetViewport(const BOX2D &aViewport)
Set the visible area of the VIEW.
Definition view.cpp:873
void SetRequired(int aLayerId, int aRequiredId, bool aRequired=true)
Mark the aRequiredId layer as required for the aLayerId layer.
Definition view.cpp:573
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:810
void recolorGroup(VIEW_ITEM *aItem, int aLayer, int aGroup)
Definition view.cpp:1997
void markDimExemption(const VIEW_ITEM *aItem, int aGroup)
Keep a group of a retained scene out of its layer's fade if the painter exempts its item.
Definition view.cpp:2011
double cachedGroupLOD(VIEW_ITEM *aItem, int aLayer)
Definition view.cpp:1527
static bool compareRenderingOrder(VIEW_LAYER *aI, VIEW_LAYER *aJ)
Determine rendering order of layers. Used in display order sorting function.
Definition view.h:1056
int GetLayerOrder(int aLayer) const
Return rendering order of a particular layer.
Definition view.cpp:1040
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:365
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:488
void ClearTargets()
Clear targets that are marked as dirty.
Definition view.cpp:1801
virtual void EnableTopLayer(bool aEnable)
Enable or disable display of the top layer.
Definition view.cpp:1275
void updateItemGeometry(VIEW_ITEM *aItem, int aLayer, bool aPlace=true)
Update all information needed to draw an item.
Definition view.cpp:2033
bool m_mirrorX
Definition view.h:1095
std::vector< std::pair< VIEW_ITEM *, int > > m_deferredRetainedPlacements
Definition view.h:1138
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:1172
void MarkStaticDirty()
Flag the GAL's static geometry pool as dirty so the next full redraw rebuilds it.
Definition view.cpp:713
bool m_deferRetainedPlacement
Definition view.h:1137
bool m_mirrorY
Definition view.h:1096
std::shared_ptr< std::vector< VIEW_ITEM * > > m_allItems
Flat list of all items.
Definition view.h:1082
std::shared_ptr< VIEW_OVERLAY > MakeOverlay()
Definition view.cpp:2710
void MarkGeometryDirty()
Signal a change to cached geometry so the next paint leaves the camera-reuse path and rebuilds the st...
Definition view.h:741
void rescaleKeptTargets()
Mark the kept targets whose content the new scale changes, so they are emitted again.
Definition view.cpp:952
void SetGAL(GAL *aGal)
Assign a rendering device for the VIEW.
Definition view.cpp:835
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:744
std::vector< VIEW_ITEM * > m_ownedItems
Definition view.h:1070
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:2631
void invalidateItem(VIEW_ITEM *aItem, int aUpdateFlags)
Manage dirty flags & redraw queuing when updating an item.
Definition view.cpp:1916
const VECTOR2I & GetScreenPixelSize() const
Return the size of the our rendering area in pixels.
Definition view.cpp:1880
bool HasItem(const VIEW_ITEM *aItem) const
Indicates whether or not the given item has been added to the view.
Definition view.cpp:2617
virtual int GetTopLayer() const
Definition view.cpp:1239
PAINTER * m_painter
PAINTER contains information how do draw items.
Definition view.h:1106
virtual void Redraw()
Immediately redraws the whole view.
Definition view.cpp:1841
void Clear()
Remove all items from the view.
Definition view.cpp:1774
std::set< unsigned int > m_topLayers
The set of layers that are displayed on the top.
Definition view.h:1085
bool m_enableOrderModifier
Whether to use rendering order modifier or not.
Definition view.h:1073
SCALE_USE m_scaleUse[TARGETS_NUMBER]
Definition view.h:1166
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:224
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:791
void UpdateAllLayersColorAndVisibility()
Apply the new coloring scheme to all layers after a render settings change that can also hide items,...
Definition view.cpp:1178
bool IsHiddenOnOverlay(const VIEW_ITEM *aItem) const
Definition view.cpp:2609
void ClearTopLayers()
Remove all layers from the on-the-top set (they are no longer displayed over the rest of layers).
Definition view.cpp:1300
void InitPreview()
Definition view.cpp:2734
bool m_dirtyTargets[TARGETS_NUMBER]
Flag to mark targets as dirty so they have to be redrawn on the next refresh event.
Definition view.h:1112
void SetLayerOrder(int aLayer, int aRenderingOrder, bool aAutoSort=true)
Set rendering order of a particular layer.
Definition view.cpp:1031
void ClearPreview()
Definition view.cpp:2719
void MarkClean()
Force redraw of view on the next rendering.
Definition view.h:804
bool IsShownAtScale(double aLod) const
Return true if something with level of detail aLod is drawn at the current scale.
Definition view.cpp:931
static constexpr int VIEW_MAX_LAYERS
Maximum number of layers that may be shown.
Definition view.h:899
double m_minScale
Definition view.h:1092
double m_scale
Definition view.h:1090
void layerStateChanged(int aLayer)
React to a change of a layer's visibility, order, required layers or negatives.
Definition view.cpp:588
void SyncRetainedScene()
Bring a retained scene up to date with the view scale and layer state, and place again the items whos...
Definition view.cpp:626
void updateLayers(VIEW_ITEM *aItem)
Update set of layers that an item occupies.
Definition view.cpp:2138
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:2222
bool areRequiredLayersEnabled(int aLayerId) const
Check if every layer required by the aLayerId layer is enabled.
Definition view.cpp:2198
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:718
bool m_pendingListIncomplete
Items were flagged without being listed, so UpdateItems() has to scan them all.
Definition view.h:1149
int m_nextDrawPriority
The next sequential drawing priority.
Definition view.h:1124
bool m_scaleRead
GetScale() was called, see SCALE_USE::read.
Definition view.h:1167
bool m_useDrawPriority
Flag to respect draw priority when drawing items.
Definition view.h:1121
void UpdateItems()
Iterate through the list of items that asked for updating and updates them.
Definition view.cpp:2239
void MarkViewportDirty()
Force a redraw for a camera change alone, which lets the paint reuse the cached scene.
Definition view.h:793
void SyncLayerVisibilityCache()
Definition view.cpp:2761
bool IsCached(int aLayer) const
Return true if the layer is cached.
Definition view.h:766
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:414
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition view.h:72
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:1046
BASE_SET m_layerVisibilityCache
Definition view.h:1097
void UpdateAllItems(int aUpdateFlags)
Update all items in the view according to the given flags.
Definition view.cpp:2475
bool m_hasPendingItemUpdates
True when at least one item has deferred update flags that still need processing.
Definition view.h:1130
std::unique_ptr< KIGFX::VIEW_GROUP > m_preview
Definition view.h:1069
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:289
void clearPendingItems()
Empty m_pendingItems and forget each listed item's place in it.
Definition view.cpp:2463
bool m_sceneLayersStale
A layer's order, visibility, required layers or negatives changed since the last sync.
Definition view.h:1154
std::vector< VIEW_ITEM * > m_pendingItems
Items flagged through Update() since the last UpdateItems(), so a frame that changed a few items need...
Definition view.h:1136
virtual ~VIEW()
Definition view.cpp:359
GAL * m_gal
Interface to PAINTER that is used to draw items.
Definition view.h:1109
bool IsDirty() const
Return true if any of the VIEW layers needs to be refreshened.
Definition view.h:701
void placeRetainedInOrder(const std::vector< LAYER_ITEM_PAIR > &aItems, bool aReplacing)
Cache the missing groups of these items, then place them all in retained draw order.
Definition view.cpp:1451
std::map< int, VIEW_LAYER > m_layers
The set of possible displayed layers and its properties.
Definition view.h:1076
void RepaintLayers(const BOX2I &aRect, const std::function< bool(int)> &aLayerFilter, const std::function< bool(VIEW_ITEM *, int)> &aItemFilter)
Re-cache only some layers of the items in aRect.
Definition view.cpp:2656
std::unique_ptr< VIEW > DataReference() const
Return a new VIEW object that shares the same set of VIEW_ITEMs and LAYERs.
Definition view.cpp:2539
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:2576
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:1248
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition view.cpp:2741
void UpdateLayerColor(int aLayer)
Apply the new coloring scheme held by RENDER_SETTINGS in case that it has changed.
Definition view.cpp:1127
void MarkDirty()
Force redraw of view on the next rendering.
Definition view.h:779
friend class VIEW_ITEM
Definition view.h:70
BOX2D m_boundary
Definition view.h:1091
void noteLodRead(int aLayer) const
Record that the item being placed asked whether aLayer is visible.
Definition view.cpp:612
void recolorAllLayers(bool aReplace, const std::function< bool(int)> &aLayers={})
Recolor every cached group on the layers aLayers accepts, or all when empty, and place it again in a ...
Definition view.cpp:1190
void noteKeptLod(double aLod) const
Definition view.cpp:939
std::map< std::pair< VIEW_ITEM *, int >, int > m_replacedRetainedGroups
Groups replaced by items redrawn during a deferred placement.
Definition view.h:1144
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition view.cpp:971
std::vector< VIEW_LAYER * > m_orderedLayers
Sorted list of pointers to members of m_layers.
Definition view.h:1079
void clearGroupCache()
Clear cached GAL group numbers (ONLY numbers stored in VIEW_ITEMs, not group objects used by GAL).
Definition view.cpp:1904
VECTOR2D m_center
Center point of the VIEW (the point at which we are looking at).
Definition view.h:1088
void SortOrderedLayers()
Sorts m_orderedLayers after layer rendering order has changed.
Definition view.cpp:1979
void unlinkItem(VIEW_ITEM *aItem)
Detach a single VIEW_ITEM from this view.
Definition view.cpp:494
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition view.h:729
void UpdateLayersColor(bool aVisibility, const std::function< bool(int)> &aLayers)
Recolor only the layers aLayers accepts, along with the fade the canvas may apply to them.
Definition view.cpp:1184
void updateBbox(VIEW_ITEM *aItem)
Update bounding box of an item.
Definition view.cpp:2102
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition view.cpp:2601
void releaseReplacedGroups()
Delete every group still kept for a deferred placement that did not happen.
Definition view.cpp:2093
BASE_SET m_layerCachedFlagCache
Definition view.h:1098
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:2492
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:2552
void ReorderLayerData(std::unordered_map< int, int > aReorderMap)
Remap the data between layer ids without invalidating that data.
Definition view.cpp:1056
void redrawRect(const BOX2I &aRect)
Redraw contents within rectangle aRect.
Definition view.cpp:1536
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:148
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
static bool registered
#define _(s)
const wxChar *const traceGalGroups
Flag to enable debug output of GAL cached group creation and drawing.
const wxChar *const traceRhi
Flag to enable debug output of the RHI rendering backend.
const wxChar *const traceGalProfile
Flag to enable debug output of GAL performance profiling.
std::string source
@ 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:30
static RETAINED_GROUP_ORDER retainedOrderKey(VIEW_ITEM *aItem, int aPriority, const BOX2I &aBBox, bool aUseDrawPriority, bool aReverseDrawOrder, double aLodScale)
Definition view.cpp:241
static void sortRetainedOrder(VIEW *aView, std::vector< T > &aEntries)
Definition view.cpp:257
@ 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
CITER next(CITER it)
Definition ptree.cpp:120
std::array< uint64_t, 2 > contentHash
RETAINED_GROUP_ORDER order
Definition view.cpp:237
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:1893
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:1364
DRAW_ITEM_VISITOR(VIEW *aView, int aLayer, bool aUseDrawPriority, bool aReverseDrawOrder)
Definition view.cpp:1353
int layers[VIEW_MAX_LAYERS]
Definition view.cpp:1442
std::vector< VIEW_ITEM * > drawItems
Definition view.cpp:1444
bool operator()(VIEW_ITEM *aItem)
Definition view.cpp:1744
RECACHE_ITEM_VISITOR(VIEW *aView, GAL *aGal, int aLayer)
Definition view.cpp:1737
How the content of a kept target depends on the scale it was emitted at.
Definition view.h:1160
bool read
something was sized from the scale
Definition view.h:1161
double lodAbove
nearest threshold above it
Definition view.h:1163
double lodBelow
nearest threshold under that scale
Definition view.h:1162
bool diffLayer
Layer should be drawn differentially over lower layers.
Definition view.h:911
int renderingOrder
Rendering order of this layer.
Definition view.h:916
bool hasNegatives
Layer should be drawn separately to not delete lower layers.
Definition view.h:914
bool visible
Is the layer to be rendered?
Definition view.h:907
bool displayOnly
Is the layer display only?
Definition view.h:908
std::shared_ptr< VIEW_RTREE > items
R-tree indexing all items on this layer.
Definition view.h:915
RENDER_TARGET target
Where the layer should be rendered.
Definition view.h:918
int id
Layer ID.
Definition view.h:917
bool moved
int delta
wxLogTrace helper definitions.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707