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