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