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