KiCad PCB EDA Suite
Loading...
Searching...
No Matches
mathplot.cpp
Go to the documentation of this file.
1
2// Name: mathplot.cpp
3// Purpose: Framework for plotting in wxWindows
4// Original Author: David Schalig
5// Maintainer: Davide Rondini
6// Contributors: Jose Luis Blanco, Val Greene, Maciej Suminski, Tomasz Wlostowski
7// Created: 21/07/2003
8// Last edit: 2024
9// Copyright: (c) David Schalig, Davide Rondini
10// Copyright (c) 2021-2024 KiCad Developers, see AUTHORS.txt for contributors.
11// Licence: wxWindows licence
13
14#include <wx/window.h>
15
16// Comment out for release operation:
17// (Added by J.L.Blanco, Aug 2007)
18//#define MATHPLOT_DO_LOGGING
19
20#ifdef __BORLANDC__
21#pragma hdrstop
22#endif
23
24#ifndef WX_PRECOMP
25#include "wx/object.h"
26#include "wx/font.h"
27#include "wx/colour.h"
28#include "wx/sizer.h"
29#include "wx/intl.h"
30#include "wx/dcclient.h"
31#include "wx/cursor.h"
32#include "gal/cursors.h"
33#endif
34
35#include <widgets/mathplot.h>
36#include <wx/graphics.h>
37#include <wx/image.h>
38
39#include <cmath>
40#include <cstdio> // used only for debug
41#include <ctime> // used for representation of x axes involving date
42#include <set>
43
44// Memory leak debugging
45#ifdef _DEBUG
46#define new DEBUG_NEW
47#endif
48
49// Legend margins
50#define mpLEGEND_MARGIN 5
51#define mpLEGEND_LINEWIDTH 10
52
53// See doxygen comments.
55
56// -----------------------------------------------------------------------------
57// mpLayer
58// -----------------------------------------------------------------------------
59
60IMPLEMENT_ABSTRACT_CLASS( mpLayer, wxObject )
61
63 m_type( mpLAYER_UNDEF )
64{
65 SetPen( (wxPen&) *wxBLACK_PEN );
66 SetFont( (wxFont&) *wxNORMAL_FONT );
67 m_continuous = false; // Default
68 m_showName = true; // Default
69 m_visible = true;
70}
71
72
73// -----------------------------------------------------------------------------
74// mpInfoLayer
75// -----------------------------------------------------------------------------
76IMPLEMENT_DYNAMIC_CLASS( mpInfoLayer, mpLayer )
77
79{
80 m_dim = wxRect( 0, 0, 1, 1 );
81 m_brush = *wxTRANSPARENT_BRUSH;
82 m_reference.x = 0; m_reference.y = 0;
83 m_winX = 1; // parent->GetScrX();
84 m_winY = 1; // parent->GetScrY();
85 m_type = mpLAYER_INFO;
86}
87
88
89mpInfoLayer::mpInfoLayer( wxRect rect, const wxBrush* brush ) :
90 m_dim( rect )
91{
92 m_brush = *brush;
93 m_reference.x = rect.x;
94 m_reference.y = rect.y;
95 m_winX = 1; // parent->GetScrX();
96 m_winY = 1; // parent->GetScrY();
98}
99
100
102{
103}
104
105
106bool mpInfoLayer::Inside( const wxPoint& point ) const
107{
108 return m_dim.Contains( point );
109}
110
111
113{
114 m_dim.SetX( m_reference.x + delta.x );
115 m_dim.SetY( m_reference.y + delta.y );
116}
117
118
120{
121 m_reference.x = m_dim.x;
122 m_reference.y = m_dim.y;
123}
124
125
126void mpInfoLayer::Plot( wxDC& dc, mpWindow& w )
127{
128 if( m_visible )
129 {
130 // Adjust relative position inside the window
131 int scrx = w.GetScrX();
132 int scry = w.GetScrY();
133
134 // Avoid dividing by 0
135 if( scrx == 0 )
136 scrx = 1;
137
138 if( scry == 0 )
139 scry = 1;
140
141 if( (m_winX != scrx) || (m_winY != scry) )
142 {
143 if( m_winX > 1 )
144 m_dim.x = (int) floor( (double) (m_dim.x * scrx / m_winX) );
145
146 if( m_winY > 1 )
147 {
148 m_dim.y = (int) floor( (double) (m_dim.y * scry / m_winY) );
150 }
151
152 // Finally update window size
153 m_winX = scrx;
154 m_winY = scry;
155 }
156
157 dc.SetPen( m_pen );
158 dc.SetBrush( m_brush );
159 dc.DrawRectangle( m_dim.x, m_dim.y, m_dim.width, m_dim.height );
160 }
161}
162
163
165{
166 return m_dim.GetPosition();
167}
168
169
171{
172 return m_dim.GetSize();
173}
174
175
178{
179}
180
181
182mpInfoLegend::mpInfoLegend( wxRect rect, const wxBrush* brush ) :
183 mpInfoLayer( rect, brush )
184{
185}
186
187
189{
190}
191
192
193void mpInfoLegend::Plot( wxDC& dc, mpWindow& w )
194{
195 if( m_visible )
196 {
197 // Adjust relative position inside the window
198 int scrx = w.GetScrX();
199 int scry = w.GetScrY();
200
201 if( m_winX != scrx || m_winY != scry )
202 {
203 if( m_winX > 1 )
204 m_dim.x = (int) floor( (double) (m_dim.x * scrx / m_winX) );
205
206 if( m_winY > 1 )
207 {
208 m_dim.y = (int) floor( (double) (m_dim.y * scry / m_winY) );
210 }
211
212 // Finally update window size
213 m_winX = scrx;
214 m_winY = scry;
215 }
216
217 dc.SetBrush( m_brush );
218 dc.SetFont( m_font );
219
220 const int baseWidth = mpLEGEND_MARGIN * 2 + mpLEGEND_LINEWIDTH;
221 int textX = baseWidth, textY = mpLEGEND_MARGIN;
222 int plotCount = 0;
223 int posY = 0;
224 int tmpX = 0;
225 int tmpY = 0;
226 mpLayer* layer = nullptr;
227 wxPen lpen;
228 wxString label;
229
230 for( unsigned int p = 0; p < w.CountAllLayers(); p++ )
231 {
232 layer = w.GetLayer( p );
233
234 if( layer->GetLayerType() == mpLAYER_PLOT && layer->IsVisible() )
235 {
236 label = layer->GetDisplayName();
237 dc.GetTextExtent( label, &tmpX, &tmpY );
238 textX = ( textX > tmpX + baseWidth ) ? textX : tmpX + baseWidth + mpLEGEND_MARGIN;
239 textY += tmpY;
240 }
241 }
242
243 dc.SetPen( m_pen );
244 dc.SetBrush( m_brush );
245 m_dim.width = textX;
246
247 if( textY != mpLEGEND_MARGIN ) // Don't draw any thing if there are no visible layers
248 {
249 textY += mpLEGEND_MARGIN;
250 m_dim.height = textY;
251 dc.DrawRectangle( m_dim.x, m_dim.y, m_dim.width, m_dim.height );
252
253 for( unsigned int p2 = 0; p2 < w.CountAllLayers(); p2++ )
254 {
255 layer = w.GetLayer( p2 );
256
257 if( layer->GetLayerType() == mpLAYER_PLOT && layer->IsVisible() )
258 {
259 label = layer->GetDisplayName();
260 lpen = layer->GetPen();
261 dc.GetTextExtent( label, &tmpX, &tmpY );
262 dc.SetPen( lpen );
263 posY = m_dim.y + mpLEGEND_MARGIN + plotCount * tmpY + (tmpY >> 1);
264 dc.DrawLine( m_dim.x + mpLEGEND_MARGIN, // X start coord
265 posY, // Y start coord
266 m_dim.x + mpLEGEND_LINEWIDTH + mpLEGEND_MARGIN, // X end coord
267 posY );
268 dc.DrawText( label,
269 m_dim.x + baseWidth,
270 m_dim.y + mpLEGEND_MARGIN + plotCount * tmpY );
271 plotCount++;
272 }
273 }
274 }
275 }
276}
277
278
279// -----------------------------------------------------------------------------
280// mpLayer implementations - functions
281// -----------------------------------------------------------------------------
282
283IMPLEMENT_ABSTRACT_CLASS( mpFX, mpLayer )
284
285mpFX::mpFX( const wxString& name, int flags )
286{
287 SetName( name );
288 m_flags = flags;
289 m_type = mpLAYER_PLOT;
290}
291
292
293void mpFX::Plot( wxDC& dc, mpWindow& w )
294{
295 if( m_visible )
296 {
297 dc.SetPen( m_pen );
298
299 wxCoord startPx = w.GetMarginLeft();
300 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
301 wxCoord minYpx = w.GetMarginTop();
302 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
303
304 wxCoord iy = 0;
305
306 if( m_pen.GetWidth() <= 1 )
307 {
308 for( wxCoord i = startPx; i < endPx; ++i )
309 {
310 iy = w.y2p( GetY( w.p2x( i ) ) );
311
312 // Draw the point only if you can draw outside margins or if the point is inside margins
313 if( (iy >= minYpx) && (iy <= maxYpx) )
314 dc.DrawPoint( i, iy );
315 }
316 }
317 else
318 {
319 for( wxCoord i = startPx; i < endPx; ++i )
320 {
321 iy = w.y2p( GetY( w.p2x( i ) ) );
322
323 // Draw the point only if you can draw outside margins or if the point is inside margins
324 if( iy >= minYpx && iy <= maxYpx )
325 dc.DrawLine( i, iy, i, iy );
326 }
327 }
328
329 if( !m_name.IsEmpty() && m_showName )
330 {
331 dc.SetFont( m_font );
332
333 wxCoord tx, ty;
334 dc.GetTextExtent( m_name, &tx, &ty );
335
337 tx = (w.GetScrX() - tx) - w.GetMarginRight() - 8;
338 else if( (m_flags & mpALIGNMASK) == mpALIGN_CENTER )
339 tx = ( (w.GetScrX() - w.GetMarginRight() - w.GetMarginLeft() - tx) / 2 ) + w.GetMarginLeft();
340 else
341 tx = w.GetMarginLeft() + 8;
342
343 dc.DrawText( m_name, tx, w.y2p( GetY( w.p2x( tx ) ) ) );
344 }
345 }
346}
347
348
349IMPLEMENT_ABSTRACT_CLASS( mpFY, mpLayer )
350
351mpFY::mpFY( const wxString& name, int flags )
352{
353 SetName( name );
354 m_flags = flags;
355 m_type = mpLAYER_PLOT;
356}
357
358
359void mpFY::Plot( wxDC& dc, mpWindow& w )
360{
361 if( m_visible )
362 {
363 dc.SetPen( m_pen );
364
365 wxCoord i, ix;
366
367 wxCoord startPx = w.GetMarginLeft();
368 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
369 wxCoord minYpx = w.GetMarginTop();
370 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
371
372 if( m_pen.GetWidth() <= 1 )
373 {
374 for( i = minYpx; i < maxYpx; ++i )
375 {
376 ix = w.x2p( GetX( w.p2y( i ) ) );
377
378 if( (ix >= startPx) && (ix <= endPx) )
379 dc.DrawPoint( ix, i );
380 }
381 }
382 else
383 {
384 for( i = 0; i< w.GetScrY(); ++i )
385 {
386 ix = w.x2p( GetX( w.p2y( i ) ) );
387
388 if( (ix >= startPx) && (ix <= endPx) )
389 dc.DrawLine( ix, i, ix, i );
390 }
391 }
392
393 if( !m_name.IsEmpty() && m_showName )
394 {
395 dc.SetFont( m_font );
396
397 wxCoord tx, ty;
398 dc.GetTextExtent( m_name, &tx, &ty );
399
400 if( (m_flags & mpALIGNMASK) == mpALIGN_TOP )
401 ty = w.GetMarginTop() + 8;
402 else if( (m_flags & mpALIGNMASK) == mpALIGN_CENTER )
403 ty = ( ( w.GetScrY() - w.GetMarginTop() - w.GetMarginBottom() - ty) / 2 ) + w.GetMarginTop();
404 else
405 ty = w.GetScrY() - 8 - ty - w.GetMarginBottom();
406
407 dc.DrawText( m_name, w.x2p( GetX( w.p2y( ty ) ) ), ty );
408 }
409 }
410}
411
412
413IMPLEMENT_ABSTRACT_CLASS( mpFXY, mpLayer )
414
415mpFXY::mpFXY( const wxString& name, int flags )
416{
417 SetName( name );
418 m_flags = flags;
419 m_type = mpLAYER_PLOT;
420 m_scaleX = nullptr;
421 m_scaleY = nullptr;
422
423 // Avoid not initialized members:
424 maxDrawX = minDrawX = maxDrawY = minDrawY = 0;
425}
426
427
428void mpFXY::UpdateViewBoundary( wxCoord xnew, wxCoord ynew )
429{
430 // Keep track of how many points have been drawn and the bounding box
431 maxDrawX = (xnew > maxDrawX) ? xnew : maxDrawX;
432 minDrawX = (xnew < minDrawX) ? xnew : minDrawX;
433 maxDrawY = (maxDrawY > ynew) ? maxDrawY : ynew;
434 minDrawY = (minDrawY < ynew) ? minDrawY : ynew;
435 // drawnPoints++;
436}
437
438
439void mpFXY::Plot( wxDC& dc, mpWindow& w )
440{
441 // If trace doesn't have any data yet then it won't have any scale set. In any case, there's
442 // nothing to plot.
443 if( !GetCount() )
444 return;
445
446 wxCHECK_RET( m_scaleX, wxS( "X scale was not set" ) );
447 wxCHECK_RET( m_scaleY, wxS( "Y scale was not set" ) );
448
449 if( !m_visible )
450 return;
451
452 wxCoord startPx = w.GetMarginLeft();
453 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
454 wxCoord minYpx = w.GetMarginTop();
455 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
456
457 // Check for a collapsed window before we try to allocate a negative number of points
458 if( endPx <= startPx || minYpx >= maxYpx )
459 return;
460
461 dc.SetPen( m_pen );
462
463 double x, y;
464 // Do this to reset the counters to evaluate bounding box for label positioning
465 Rewind();
466 GetNextXY( x, y );
467 maxDrawX = x;
468 minDrawX = x;
469 maxDrawY = y;
470 minDrawY = y;
471 // drawnPoints = 0;
472 Rewind();
473
474 dc.SetClippingRegion( startPx, minYpx, endPx - startPx + 1, maxYpx - minYpx + 1 );
475
476 if( !m_continuous )
477 {
478 bool first = true;
479 wxCoord ix = 0;
480 std::set<wxCoord> ys;
481
482 while( GetNextXY( x, y ) )
483 {
484 double px = m_scaleX->TransformToPlot( x );
485 double py = m_scaleY->TransformToPlot( y );
486 wxCoord newX = w.x2p( px );
487
488 if( first )
489 {
490 ix = newX;
491 first = false;
492 }
493
494 if( newX == ix ) // continue until a new X coordinate is reached
495 {
496 // collect all unique points
497 ys.insert( w.y2p( py ) );
498 continue;
499 }
500
501 for( auto& iy: ys )
502 {
503 if( (ix >= startPx) && (ix <= endPx) && (iy >= minYpx) && (iy <= maxYpx) )
504 {
505 // for some reason DrawPoint does not use the current pen, so we use
506 // DrawLine for fat pens
507 if( m_pen.GetWidth() <= 1 )
508 dc.DrawPoint( ix, iy );
509 else
510 dc.DrawLine( ix, iy, ix, iy );
511
512 UpdateViewBoundary( ix, iy );
513 }
514 }
515
516 ys.clear();
517 ix = newX;
518 ys.insert( w.y2p( py ) );
519 }
520 }
521 else for( int sweep = 0; sweep < GetSweepCount(); ++sweep )
522 {
523 SetSweepWindow( sweep );
524
525 int count = 0;
526 int x0 = 0; // X position of merged current vertical line
527 int ymin0 = 0; // y min coord of merged current vertical line
528 int ymax0 = 0; // y max coord of merged current vertical line
529 int dupx0 = 0; // count of currently merged vertical lines
530 wxPoint line_start; // starting point of the current line to draw
531
532 // A buffer to store coordinates of lines to draw
533 std::vector<wxPoint>pointList;
534 pointList.reserve( ( endPx - startPx ) * 2 );
535
536 double nextX;
537 double nextY;
538 bool hasNext = GetNextXY( nextX, nextY );
539 bool offRight = false;
540
541 // Note: we can use dc.DrawLines() only for a reasonable number or points (<10,000),
542 // because at least on Windows dc.DrawLines() can hang for a lot of points. Note that
543 // this includes the intermediate points when drawing dotted lines.
544
545 // Our first-pass optimization is to exclude points outside the view, and aggregate all
546 // contiguous y values found at a single x value into a vertical line.
547 while( hasNext )
548 {
549 x = nextX;
550 y = nextY;
551 hasNext = GetNextXY( nextX, nextY );
552
553 double px = m_scaleX->TransformToPlot( x );
554 double py = m_scaleY->TransformToPlot( y );
555
556 wxCoord x1 = w.x2p( px );
557 wxCoord y1 = w.y2p( py );
558
559 // Note that we can't start *right* at the edge of the view because we need to
560 // interpolate between two points, one of which might be outside the view.
561 // Note: x1 is a value truncated from px by w.x2p(). So to be sure the first point
562 // is drawn, the x1 low limit is startPx-1 in plot coordinates
563 if( x1 < startPx-1 )
564 {
565 wxCoord nextX1 = w.x2p( m_scaleX->TransformToPlot( nextX ) );
566
567 if( nextX1 < startPx-1 )
568 continue;
569 }
570 else if( x1 > endPx )
571 {
572 if( offRight )
573 continue;
574 else
575 offRight = true;
576 }
577
578 if( !count || line_start.x != x1 )
579 {
580 // We've aggregated a bunch of y values with a shared x value, so we need to draw
581 // a vertical line. However, short vertical segments spoil anti-aliasing on
582 // Retina displays, so only draw them if they're "significant" (the user should
583 // zoom in if they need a more accurate picture).
584 if( count && dupx0 > 1 && abs( ymax0 - ymin0 ) > 2 )
585 dc.DrawLine( x0, ymin0, x0, ymax0 );
586
587 x0 = x1;
588 ymin0 = ymax0 = y1;
589 dupx0 = 0;
590
591 pointList.emplace_back( wxPoint( x1, y1 ) );
592
593 line_start.x = x1;
594 line_start.y = y1;
595 count++;
596 }
597 else
598 {
599 ymin0 = std::min( ymin0, y1 );
600 ymax0 = std::max( ymax0, y1 );
601 x0 = x1;
602 dupx0++;
603 }
604 }
605
606 if( pointList.size() > 1 )
607 {
608 // Second pass optimization is to merge horizontal segments. This improves the look
609 // of dotted lines, keeps the point count down, and it's easy.
610 //
611 // This pass also includes a final protection to keep MSW from hanging by chunking to
612 // a size it can handle.
613 std::vector<wxPoint> drawPoints;
614 drawPoints.reserve( ( endPx - startPx ) * 2 );
615
616#ifdef __WXMSW__
617 int chunkSize = 10000;
618#else
619 int chunkSize = 100000;
620#endif
621 if( dc.GetPen().GetStyle() == wxPENSTYLE_DOT )
622 chunkSize /= 500;
623
624 drawPoints.push_back( pointList[0] ); // push the first point in list
625
626 for( size_t ii = 1; ii < pointList.size()-1; ii++ )
627 {
628 // Skip intermediate points between the first point and the last point of the
629 // segment candidate
630 if( drawPoints.back().y == pointList[ii].y &&
631 drawPoints.back().y == pointList[ii+1].y )
632 {
633 continue;
634 }
635 else
636 {
637 drawPoints.push_back( pointList[ii] );
638
639 if( (int) drawPoints.size() > chunkSize )
640 {
641 dc.DrawLines( (int) drawPoints.size(), &drawPoints[0] );
642 drawPoints.clear();
643
644 // Restart the line with the current point
645 drawPoints.push_back( pointList[ii] );
646 }
647 }
648 }
649
650 // push the last point to draw in list
651 if( drawPoints.back() != pointList.back() )
652 drawPoints.push_back( pointList.back() );
653
654 dc.DrawLines( (int) drawPoints.size(), &drawPoints[0] );
655 }
656 }
657
658 if( !m_name.IsEmpty() && m_showName )
659 {
660 dc.SetFont( m_font );
661
662 wxCoord tx, ty;
663 dc.GetTextExtent( m_name, &tx, &ty );
664
665 if( (m_flags & mpALIGNMASK) == mpALIGN_NW )
666 {
667 tx = minDrawX + 8;
668 ty = maxDrawY + 8;
669 }
670 else if( (m_flags & mpALIGNMASK) == mpALIGN_NE )
671 {
672 tx = maxDrawX - tx - 8;
673 ty = maxDrawY + 8;
674 }
675 else if( (m_flags & mpALIGNMASK) == mpALIGN_SE )
676 {
677 tx = maxDrawX - tx - 8;
678 ty = minDrawY - ty - 8;
679 }
680 else
681 {
682 // mpALIGN_SW
683 tx = minDrawX + 8;
684 ty = minDrawY - ty - 8;
685 }
686
687 dc.DrawText( m_name, tx, ty );
688 }
689
690 dc.DestroyClippingRegion();
691}
692
693
694// -----------------------------------------------------------------------------
695// mpLayer implementations - furniture (scales, ...)
696// -----------------------------------------------------------------------------
697
698#define mpLN10 2.3025850929940456840179914546844
699
701{
702 double minV, maxV, minVvis, maxVvis;
703
704 GetDataRange( minV, maxV );
705 getVisibleDataRange( w, minVvis, maxVvis );
706
707 m_absVisibleMaxV = std::max( std::abs( minVvis ), std::abs( maxVvis ) );
708
709 m_tickValues.clear();
710 m_tickLabels.clear();
711
712 double minErr = 1000000000000.0;
713 double bestStep = 1.0;
714 int m_scrX = w.GetXScreen();
715
716 for( int i = 10; i <= 20; i += 2 )
717 {
718 double curr_step = fabs( maxVvis - minVvis ) / (double) i;
719 double base = pow( 10, floor( log10( curr_step ) ) );
720 double stepInt = floor( curr_step / base ) * base;
721 double err = fabs( curr_step - stepInt );
722
723 if( err < minErr )
724 {
725 minErr = err;
726 bestStep = stepInt;
727 }
728 }
729
730 double numberSteps = floor( ( maxVvis - minVvis ) / bestStep );
731
732 // Half the number of ticks according to window size.
733 // The value 96 is used to have only 4 ticks when m_scrX is 268.
734 // For each 96 device context units, is possible to add a new tick.
735 while( numberSteps - 2.0 >= m_scrX/96.0 )
736 {
737 bestStep *= 2;
738 numberSteps = floor( ( maxVvis - minVvis ) / bestStep );
739 }
740
741 double v = floor( minVvis / bestStep ) * bestStep;
742 double zeroOffset = 100000000.0;
743
744 while( v < maxVvis )
745 {
746 m_tickValues.push_back( v );
747
748 if( fabs( v ) < zeroOffset )
749 zeroOffset = fabs( v );
750
751 v += bestStep;
752 }
753
754 if( zeroOffset <= bestStep )
755 {
756 for( double& t : m_tickValues )
757 t -= zeroOffset;
758 }
759
760 for( double t : m_tickValues )
761 m_tickLabels.emplace_back( t );
762
763 updateTickLabels( dc, w );
764}
765
766
768{
769 m_rangeSet = false;
770 m_axisLocked = false;
771 m_axisMin = 0;
772 m_axisMax = 0;
774
775 // initialize these members mainly to avoid not initialized values
776 m_offset = 0.0;
777 m_scale = 1.0;
778 m_absVisibleMaxV = 0.0;
779 m_flags = 0; // Flag for axis alignment
780 m_ticks = true; // Flag to toggle between ticks or grid
781 m_minV = 0.0;
782 m_maxV = 0.0;
784 m_maxLabelWidth = 1;
785}
786
787
789{
791 m_maxLabelWidth = 0;
792
793 for( const TICK_LABEL& tickLabel : m_tickLabels )
794 {
795 int tx, ty;
796 const wxString s = tickLabel.label;
797
798 dc.GetTextExtent( s, &tx, &ty );
799 m_maxLabelHeight = std::max( ty, m_maxLabelHeight );
800 m_maxLabelWidth = std::max( tx, m_maxLabelWidth );
801 }
802}
803
804
806{
807 formatLabels();
808 computeLabelExtents( dc, w );
809}
810
811
812void mpScaleY::getVisibleDataRange( mpWindow& w, double& minV, double& maxV )
813{
814 wxCoord minYpx = w.GetMarginTop();
815 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
816
817 double pymin = w.p2y( minYpx );
818 double pymax = w.p2y( maxYpx );
819
820 minV = TransformFromPlot( pymax );
821 maxV = TransformFromPlot( pymin );
822}
823
824
826{
827 // No need for slave ticks when there aren't 2 main ticks for them to go between
828 if( m_masterScale->m_tickValues.size() < 2 )
829 return;
830
831 m_tickValues.clear();
832 m_tickLabels.clear();
833
836
837 m_scale = 1.0 / ( m_maxV - m_minV );
838 m_offset = -m_minV;
839
840 double y_slave0 = p0 / m_scale;
841 double y_slave1 = p1 / m_scale;
842
843 double dy_slave = (y_slave1 - y_slave0);
844 double exponent = floor( log10( dy_slave ) );
845 double base = dy_slave / pow( 10.0, exponent );
846
847 double dy_scaled = ceil( 2.0 * base ) / 2.0 * pow( 10.0, exponent );
848
849 double minvv, maxvv;
850
851 getVisibleDataRange( w, minvv, maxvv );
852
853 minvv = floor( minvv / dy_scaled ) * dy_scaled;
854
855 m_scale = 1.0 / ( m_maxV - m_minV );
856 m_scale *= dy_slave / dy_scaled;
857
858 m_offset = p0 / m_scale - minvv;
859
860 m_tickValues.clear();
861
863
864 for( double tickValue : m_masterScale->m_tickValues )
865 {
866 double m = TransformFromPlot( m_masterScale->TransformToPlot( tickValue ) );
867 m_tickValues.push_back( m );
868 m_tickLabels.emplace_back( m );
869 m_absVisibleMaxV = std::max( m_absVisibleMaxV, fabs( m ) );
870 }
871}
872
873
875{
876 double minVvis, maxVvis;
877
878 if( m_axisLocked )
879 {
880 minVvis = m_axisMin;
881 maxVvis = m_axisMax;
883 m_scale = 1.0 / ( m_axisMax - m_axisMin );
884 }
885 else if( m_masterScale )
886 {
888 updateTickLabels( dc, w );
889
890 return;
891 }
892 else
893 {
894 getVisibleDataRange( w, minVvis, maxVvis );
895 }
896
897 m_absVisibleMaxV = std::max( std::abs( minVvis ), std::abs( maxVvis ) );
898 m_tickValues.clear();
899 m_tickLabels.clear();
900
901 double minErr = 1000000000000.0;
902 double bestStep = 1.0;
903 int m_scrY = w.GetYScreen();
904
905 for( int i = 10; i <= 20; i += 2 )
906 {
907 double curr_step = fabs( maxVvis - minVvis ) / (double) i;
908 double base = pow( 10, floor( log10( curr_step ) ) );
909 double stepInt = floor( curr_step / base ) * base;
910 double err = fabs( curr_step - stepInt );
911
912 if( err< minErr )
913 {
914 minErr = err;
915 bestStep = stepInt;
916 }
917 }
918
919 double numberSteps = floor( ( maxVvis - minVvis ) / bestStep );
920
921 // Half the number of ticks according to window size.
922 // For each 32 device context units, is possible to add a new tick.
923 while( numberSteps >= m_scrY/32.0 )
924 {
925 bestStep *= 2;
926 numberSteps = floor( ( maxVvis - minVvis ) / bestStep );
927 }
928
929 double v = floor( minVvis / bestStep ) * bestStep;
930 double zeroOffset = 100000000.0;
931 const int iterLimit = 1000;
932 int i = 0;
933
934 while( v <= maxVvis && i < iterLimit )
935 {
936 m_tickValues.push_back( v );
937
938 if( fabs( v ) < zeroOffset )
939 zeroOffset = fabs( v );
940
941 v += bestStep;
942 i++;
943 }
944
945 // something weird happened...
946 if( i == iterLimit )
947 m_tickValues.clear();
948
949 if( zeroOffset <= bestStep )
950 {
951 for( double& t : m_tickValues )
952 t -= zeroOffset;
953 }
954
955 for( double t : m_tickValues )
956 m_tickLabels.emplace_back( t );
957
958 updateTickLabels( dc, w );
959}
960
961
962void mpScaleXBase::getVisibleDataRange( mpWindow& w, double& minV, double& maxV )
963{
964 wxCoord startPx = w.GetMarginLeft();
965 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
966 double pxmin = w.p2x( startPx );
967 double pxmax = w.p2x( endPx );
968
969 minV = TransformFromPlot( pxmin );
970 maxV = TransformFromPlot( pxmax );
971}
972
973
975{
976 double minV, maxV, minVvis, maxVvis;
977
978 GetDataRange( minV, maxV );
979 getVisibleDataRange( w, minVvis, maxVvis );
980
981 // double decades = log( maxV / minV ) / log(10);
982 double minDecade = pow( 10, floor( log10( minV ) ) );
983 double maxDecade = pow( 10, ceil( log10( maxV ) ) );
984 double visibleDecades = log( maxVvis / minVvis ) / log( 10 );
985 double step = 10.0;
986 int m_scrX = w.GetXScreen();
987
988 double d;
989
990 m_tickValues.clear();
991 m_tickLabels.clear();
992
993 if( minDecade == 0.0 )
994 return;
995
996 // Half the number of ticks according to window size.
997 // The value 96 is used to have only 4 ticks when m_scrX is 268.
998 // For each 96 device context units, is possible to add a new tick.
999 while( visibleDecades - 2 >= m_scrX / 96.0 )
1000 {
1001 step *= 10.0;
1002 visibleDecades = log( maxVvis / minVvis ) / log( step );
1003 }
1004
1005 for( d = minDecade; d<=maxDecade; d *= step )
1006 {
1007 m_tickLabels.emplace_back( d );
1008
1009 for( double dd = d; dd < d * step; dd += d )
1010 {
1011 if( visibleDecades < 2 )
1012 m_tickLabels.emplace_back( dd );
1013
1014 m_tickValues.push_back( dd );
1015 }
1016 }
1017
1018 updateTickLabels( dc, w );
1019}
1020
1021
1022IMPLEMENT_ABSTRACT_CLASS( mpScaleXBase, mpLayer )
1023IMPLEMENT_DYNAMIC_CLASS( mpScaleX, mpScaleXBase )
1024IMPLEMENT_DYNAMIC_CLASS( mpScaleXLog, mpScaleXBase )
1025
1026mpScaleXBase::mpScaleXBase( const wxString& name, int flags, bool ticks, unsigned int type )
1027{
1028 SetName( name );
1029 SetFont( (wxFont&) *wxSMALL_FONT );
1030 SetPen( (wxPen&) *wxGREY_PEN );
1031 m_flags = flags;
1032 m_ticks = ticks;
1033 m_type = mpLAYER_AXIS;
1034}
1035
1036
1037mpScaleX::mpScaleX( const wxString& name, int flags, bool ticks, unsigned int type ) :
1038 mpScaleXBase( name, flags, ticks, type )
1039{
1040}
1041
1042
1043mpScaleXLog::mpScaleXLog( const wxString& name, int flags, bool ticks, unsigned int type ) :
1044 mpScaleXBase( name, flags, ticks, type )
1045{
1046}
1047
1048
1049void mpScaleXBase::Plot( wxDC& dc, mpWindow& w )
1050{
1051 int tx, ty;
1052
1053 m_offset = -m_minV;
1054 m_scale = 1.0 / ( m_maxV - m_minV );
1055
1056 recalculateTicks( dc, w );
1057
1058 if( m_visible )
1059 {
1060 dc.SetPen( m_pen );
1061 dc.SetFont( m_font );
1062 int orgy = 0;
1063
1064 const int extend = w.GetScrX();
1065
1066 if( m_flags == mpALIGN_CENTER )
1067 orgy = w.y2p( 0 );
1068
1069 if( m_flags == mpALIGN_TOP )
1070 orgy = w.GetMarginTop();
1071
1072 if( m_flags == mpALIGN_BOTTOM )
1073 orgy = w.GetScrY() - w.GetMarginBottom();
1074
1076 orgy = w.GetScrY() - 1;
1077
1079 orgy = 1;
1080
1081 wxCoord startPx = w.GetMarginLeft();
1082 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
1083 wxCoord minYpx = w.GetMarginTop();
1084 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
1085
1086 // int tmp=-65535;
1087 int labelH = m_maxLabelHeight; // Control labels height to decide where to put axis name (below labels or on top of axis)
1088
1089 // int maxExtent = tc.MaxLabelWidth();
1090 for( double tp : m_tickValues )
1091 {
1092 double px = TransformToPlot( tp );
1093 const int p = (int) ( ( px - w.GetPosX() ) * w.GetScaleX() );
1094
1095 if( p >= startPx && p <= endPx )
1096 {
1097 if( m_ticks ) // draw axis ticks
1098 {
1100 dc.DrawLine( p, orgy, p, orgy - 4 );
1101 else
1102 dc.DrawLine( p, orgy, p, orgy + 4 );
1103 }
1104 else // draw grid dotted lines
1105 {
1106 m_pen.SetStyle( wxPENSTYLE_DOT );
1107 dc.SetPen( m_pen );
1108
1109 if( m_flags == mpALIGN_BOTTOM )
1110 {
1111 m_pen.SetStyle( wxPENSTYLE_DOT );
1112 dc.SetPen( m_pen );
1113 dc.DrawLine( p, orgy + 4, p, minYpx );
1114 m_pen.SetStyle( wxPENSTYLE_SOLID );
1115 dc.SetPen( m_pen );
1116 dc.DrawLine( p, orgy + 4, p, orgy - 4 );
1117 }
1118 else
1119 {
1120 if( m_flags == mpALIGN_TOP )
1121 dc.DrawLine( p, orgy - 4, p, maxYpx );
1122 else
1123 dc.DrawLine( p, minYpx, p, maxYpx );
1124 }
1125
1126 m_pen.SetStyle( wxPENSTYLE_SOLID );
1127 dc.SetPen( m_pen );
1128 }
1129 }
1130 }
1131
1132 m_pen.SetStyle( wxPENSTYLE_SOLID );
1133 dc.SetPen( m_pen );
1134 dc.DrawLine( startPx, minYpx, endPx, minYpx );
1135 dc.DrawLine( startPx, maxYpx, endPx, maxYpx );
1136
1137 // Actually draw labels, taking care of not overlapping them, and distributing them
1138 // regularly
1139 for( const TICK_LABEL& tickLabel : m_tickLabels )
1140 {
1141 if( !tickLabel.visible )
1142 continue;
1143
1144 double px = TransformToPlot( tickLabel.pos );
1145 const int p = (int) ( ( px - w.GetPosX() ) * w.GetScaleX() );
1146
1147 if( (p >= startPx) && (p <= endPx) )
1148 {
1149 // Write ticks labels in s string
1150 wxString s = tickLabel.label;
1151
1152 dc.GetTextExtent( s, &tx, &ty );
1153
1155 dc.DrawText( s, p - tx / 2, orgy - 4 - ty );
1156 else
1157 dc.DrawText( s, p - tx / 2, orgy + 4 );
1158 }
1159 }
1160
1161 // Draw axis name
1162 dc.GetTextExtent( m_name, &tx, &ty );
1163
1164 switch( m_nameFlags )
1165 {
1167 dc.DrawText( m_name, extend - tx - 4, orgy - 8 - ty - labelH );
1168 break;
1169
1170 case mpALIGN_BOTTOM:
1171 dc.DrawText( m_name, (endPx + startPx) / 2 - tx / 2, orgy + 6 + labelH );
1172 break;
1173
1174 case mpALIGN_CENTER:
1175 dc.DrawText( m_name, extend - tx - 4, orgy - 4 - ty );
1176 break;
1177
1178 case mpALIGN_TOP:
1179 if( w.GetMarginTop() > (ty + labelH + 8) )
1180 dc.DrawText( m_name, (endPx - startPx - tx) >> 1, orgy - 6 - ty - labelH );
1181 else
1182 dc.DrawText( m_name, extend - tx - 4, orgy + 4 );
1183
1184 break;
1185
1186 case mpALIGN_BORDER_TOP:
1187 dc.DrawText( m_name, extend - tx - 4, orgy + 6 + labelH );
1188 break;
1189
1190 default:
1191 break;
1192 }
1193 }
1194}
1195
1196
1197IMPLEMENT_DYNAMIC_CLASS( mpScaleY, mpLayer )
1198
1199mpScaleY::mpScaleY( const wxString& name, int flags, bool ticks )
1200{
1201 SetName( name );
1202 SetFont( (wxFont&) *wxSMALL_FONT );
1203 SetPen( (wxPen&) *wxGREY_PEN );
1204 m_flags = flags;
1205 m_ticks = ticks;
1206 m_type = mpLAYER_AXIS;
1207 m_masterScale = nullptr;
1208 m_nameFlags = mpALIGN_BORDER_LEFT;
1209}
1210
1211
1212void mpScaleY::Plot( wxDC& dc, mpWindow& w )
1213{
1214 m_offset = -m_minV;
1215 m_scale = 1.0 / ( m_maxV - m_minV );
1216
1217 recalculateTicks( dc, w );
1218
1219 if( m_visible )
1220 {
1221 dc.SetPen( m_pen );
1222 dc.SetFont( m_font );
1223
1224 int orgx = 0;
1225
1226 if( m_flags == mpALIGN_CENTER )
1227 orgx = w.x2p( 0 );
1228
1229 if( m_flags == mpALIGN_LEFT )
1230 orgx = w.GetMarginLeft();
1231
1232 if( m_flags == mpALIGN_RIGHT )
1233 orgx = w.GetScrX() - w.GetMarginRight();
1234
1235 if( m_flags == mpALIGN_FAR_RIGHT )
1236 orgx = w.GetScrX() - ( w.GetMarginRight() / 2 );
1237
1239 orgx = w.GetScrX() - 1;
1240
1242 orgx = 1;
1243
1244 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
1245 wxCoord minYpx = w.GetMarginTop();
1246 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
1247 // Draw line
1248 dc.DrawLine( orgx, minYpx, orgx, maxYpx );
1249
1250 wxCoord tx, ty;
1251 wxString s;
1252 wxString fmt;
1253
1254 int labelW = 0;
1255 // Before staring cycle, calculate label height
1256 int labelHeight = 0;
1257 s.Printf( fmt, 0 );
1258 dc.GetTextExtent( s, &tx, &labelHeight );
1259
1260 for( double tp : m_tickValues )
1261 {
1262 double py = TransformToPlot( tp );
1263 const int p = (int) ( ( w.GetPosY() - py ) * w.GetScaleY() );
1264
1265 if( p >= minYpx && p <= maxYpx )
1266 {
1267 if( m_ticks ) // Draw axis ticks
1268 {
1270 dc.DrawLine( orgx, p, orgx + 4, p );
1271 else
1272 dc.DrawLine( orgx - 4, p, orgx, p );
1273 }
1274 else
1275 {
1276 dc.DrawLine( orgx - 4, p, orgx + 4, p );
1277
1278 m_pen.SetStyle( wxPENSTYLE_DOT );
1279 dc.SetPen( m_pen );
1280
1281 dc.DrawLine( orgx - 4, p, endPx, p );
1282
1283 m_pen.SetStyle( wxPENSTYLE_SOLID );
1284 dc.SetPen( m_pen );
1285 }
1286
1287 // Print ticks labels
1288 }
1289 }
1290
1291 for( const TICK_LABEL& tickLabel : m_tickLabels )
1292 {
1293 double py = TransformToPlot( tickLabel.pos );
1294 const int p = (int) ( ( w.GetPosY() - py ) * w.GetScaleY() );
1295
1296 if( !tickLabel.visible )
1297 continue;
1298
1299 if( p >= minYpx && p <= maxYpx )
1300 {
1301 s = tickLabel.label;
1302 dc.GetTextExtent( s, &tx, &ty );
1303
1305 dc.DrawText( s, orgx + 4, p - ty / 2 );
1306 else
1307 dc.DrawText( s, orgx - 4 - tx, p - ty / 2 ); // ( s, orgx+4, p-ty/2);
1308 }
1309 }
1310
1311 // Draw axis name
1312
1313 dc.GetTextExtent( m_name, &tx, &ty );
1314
1315 switch( m_nameFlags )
1316 {
1318 dc.DrawText( m_name, labelW + 8, 4 );
1319 break;
1320
1321 case mpALIGN_LEFT:
1322 dc.DrawText( m_name, orgx - ( tx / 2 ), minYpx - ty - 4 );
1323 break;
1324
1325 case mpALIGN_CENTER:
1326 dc.DrawText( m_name, orgx + 4, 4 );
1327 break;
1328
1329 case mpALIGN_RIGHT:
1330 case mpALIGN_FAR_RIGHT:
1331 dc.DrawText( m_name, orgx - ( tx / 2 ), minYpx - ty - 4 );
1332 break;
1333
1335 dc.DrawText( m_name, orgx - 6 - tx - labelW, 4 );
1336 break;
1337
1338 default:
1339 break;
1340 }
1341 }
1342}
1343
1344
1345// -----------------------------------------------------------------------------
1346// mpWindow
1347// -----------------------------------------------------------------------------
1348
1349IMPLEMENT_DYNAMIC_CLASS( mpWindow, wxWindow )
1350
1351BEGIN_EVENT_TABLE( mpWindow, wxWindow )
1352EVT_PAINT( mpWindow::OnPaint )
1353EVT_SIZE( mpWindow::OnSize )
1354
1355EVT_MIDDLE_DOWN( mpWindow::OnMouseMiddleDown ) // JLB
1356EVT_RIGHT_UP( mpWindow::OnShowPopupMenu )
1357EVT_MOUSEWHEEL( mpWindow::onMouseWheel ) // JLB
1358EVT_MAGNIFY( mpWindow::onMagnify )
1359EVT_MOTION( mpWindow::onMouseMove ) // JLB
1360EVT_LEFT_DOWN( mpWindow::onMouseLeftDown )
1361EVT_LEFT_UP( mpWindow::onMouseLeftRelease )
1362
1364EVT_MENU( mpID_FIT, mpWindow::OnFit )
1369END_EVENT_TABLE()
1370
1372{
1373 initializeGraphicsContext();
1374}
1375
1376mpWindow::mpWindow( wxWindow* parent, wxWindowID id ) :
1377 mpWindow( DelegatingContructorTag(), parent, id, wxDefaultPosition, wxDefaultSize, 0,
1378 wxT( "mathplot" ) )
1379{
1380 m_popmenu.Append( mpID_ZOOM_UNDO, _( "Undo Last Zoom" ), _( "Return zoom to level prior to last zoom action" ) );
1381 m_popmenu.Append( mpID_ZOOM_REDO, _( "Redo Last Zoom" ), _( "Return zoom to level prior to last zoom undo" ) );
1382 m_popmenu.AppendSeparator();
1383 m_popmenu.Append( mpID_ZOOM_IN, _( "Zoom In" ), _( "Zoom in plot view." ) );
1384 m_popmenu.Append( mpID_ZOOM_OUT, _( "Zoom Out" ), _( "Zoom out plot view." ) );
1385 m_popmenu.Append( mpID_CENTER, _( "Center on Cursor" ), _( "Center plot view to this position" ) );
1386 m_popmenu.Append( mpID_FIT, _( "Fit on Screen" ), _( "Set plot view to show all items" ) );
1387
1388 m_layers.clear();
1389 SetBackgroundColour( *wxWHITE );
1390 m_bgColour = *wxWHITE;
1391 m_fgColour = *wxBLACK;
1392
1393 SetSizeHints( 128, 128 );
1394
1395 // J.L.Blanco: Eliminates the "flick" with the double buffer.
1396 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1397
1399 UpdateAll();
1400}
1401
1402
1404{
1405 // Free all the layers:
1406 DelAllLayers( true, false );
1407
1408 delete m_buff_bmp;
1409 m_buff_bmp = nullptr;
1410}
1411
1412
1413// Mouse handler, for detecting when the user drag with the right button or just "clicks" for the menu
1414// JLB
1415void mpWindow::OnMouseMiddleDown( wxMouseEvent& event )
1416{
1417 m_mouseMClick.x = event.GetX();
1418 m_mouseMClick.y = event.GetY();
1419}
1420
1421
1422void mpWindow::onMagnify( wxMouseEvent& event )
1423{
1425 {
1426 event.Skip();
1427 return;
1428 }
1429
1430 float zoom = event.GetMagnification() + 1.0f;
1431 wxPoint pos( event.GetX(), event.GetY() );
1432
1433 if( zoom > 1.0f )
1434 ZoomIn( pos, zoom );
1435 else if( zoom < 1.0f )
1436 ZoomOut( pos, 1.0f / zoom );
1437}
1438
1439
1440// Process mouse wheel events
1441// JLB
1442void mpWindow::onMouseWheel( wxMouseEvent& event )
1443{
1445 {
1446 event.Skip();
1447 return;
1448 }
1449
1450 const wxMouseWheelAxis axis = event.GetWheelAxis();
1451 const int modifiers = event.GetModifiers();
1453
1454 if( axis == wxMOUSE_WHEEL_HORIZONTAL )
1455 {
1457 }
1458 else if( modifiers == wxMOD_NONE )
1459 {
1461 }
1462 else if( modifiers == wxMOD_CONTROL )
1463 {
1465 }
1466 else if( modifiers == wxMOD_SHIFT )
1467 {
1469 }
1470 else if( modifiers == wxMOD_ALT )
1471 {
1473 }
1474 else
1475 {
1476 event.Skip();
1477 return;
1478 }
1479
1480 PerformMouseWheelAction( event, action );
1481}
1482
1483
1484// If the user "drags" with the right button pressed, do "pan"
1485// JLB
1486void mpWindow::onMouseMove( wxMouseEvent& event )
1487{
1489 {
1490 event.Skip();
1491 return;
1492 }
1493
1494 wxCursor cursor = wxCURSOR_MAGNIFIER;
1495
1496 if( event.m_middleDown )
1497 {
1498 cursor = wxCURSOR_ARROW;
1499
1500 // The change:
1501 int Ax = m_mouseMClick.x - event.GetX();
1502 int Ay = m_mouseMClick.y - event.GetY();
1503
1504 // For the next event, use relative to this coordinates.
1505 m_mouseMClick.x = event.GetX();
1506 m_mouseMClick.y = event.GetY();
1507
1508 if( Ax )
1509 {
1510 double Ax_units = Ax / m_scaleX;
1511 SetXView( m_posX + Ax_units, m_desiredXmax + Ax_units, m_desiredXmin + Ax_units );
1512 }
1513
1514 if( Ay )
1515 {
1516 double Ay_units = -Ay / m_scaleY;
1517 SetYView( m_posY + Ay_units, m_desiredYmax + Ay_units, m_desiredYmin + Ay_units );
1518 }
1519
1520 if( Ax || Ay )
1521 UpdateAll();
1522 }
1523 else if( event.m_leftDown )
1524 {
1525 if( m_movingInfoLayer )
1526 {
1527 if( dynamic_cast<mpInfoLegend*>( m_movingInfoLayer ) )
1528 cursor = wxCURSOR_SIZING;
1529 else
1530 cursor = wxCURSOR_SIZEWE;
1531
1532 wxPoint moveVector( event.GetX() - m_mouseLClick.x, event.GetY() - m_mouseLClick.y );
1533 m_movingInfoLayer->Move( moveVector );
1534 m_zooming = false;
1535 }
1536 else
1537 {
1538 cursor = wxCURSOR_MAGNIFIER;
1539
1540 wxClientDC dc( this );
1541 wxPen pen( m_fgColour, 1, wxPENSTYLE_DOT );
1542 dc.SetPen( pen );
1543 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1544 dc.DrawRectangle( m_mouseLClick.x, m_mouseLClick.y,
1545 event.GetX() - m_mouseLClick.x, event.GetY() - m_mouseLClick.y );
1546 m_zooming = true;
1549 m_zoomRect.width = event.GetX() - m_mouseLClick.x;
1550 m_zoomRect.height = event.GetY() - m_mouseLClick.y;
1551 }
1552
1553 UpdateAll();
1554 }
1555 else
1556 {
1557 for( mpLayer* layer : m_layers)
1558 {
1559 if( layer->IsInfo() && layer->IsVisible() )
1560 {
1561 mpInfoLayer* infoLayer = (mpInfoLayer*) layer;
1562
1563 if( infoLayer->Inside( event.GetPosition() ) )
1564 {
1565 if( dynamic_cast<mpInfoLegend*>( infoLayer ) )
1566 cursor = wxCURSOR_SIZING;
1567 else
1568 cursor = wxCURSOR_SIZEWE;
1569 }
1570 }
1571 }
1572 }
1573
1574 SetCursor( cursor );
1575
1576 event.Skip();
1577}
1578
1579
1580void mpWindow::onMouseLeftDown( wxMouseEvent& event )
1581{
1582 m_mouseLClick.x = event.GetX();
1583 m_mouseLClick.y = event.GetY();
1584 m_zooming = true;
1585 wxPoint pointClicked = event.GetPosition();
1586 m_movingInfoLayer = IsInsideInfoLayer( pointClicked );
1587
1588 event.Skip();
1589}
1590
1591
1592void mpWindow::onMouseLeftRelease( wxMouseEvent& event )
1593{
1594 wxPoint release( event.GetX(), event.GetY() );
1595 wxPoint press( m_mouseLClick.x, m_mouseLClick.y );
1596
1597 m_zooming = false;
1598
1599 if( m_movingInfoLayer != nullptr )
1600 {
1602 m_movingInfoLayer = nullptr;
1603 }
1604 else
1605 {
1606 if( release != press )
1607 ZoomRect( press, release );
1608 }
1609
1610 event.Skip();
1611}
1612
1613
1615{
1616 if( UpdateBBox() )
1618}
1619
1620
1621// JL
1622void mpWindow::Fit( double xMin, double xMax, double yMin, double yMax, const wxCoord* printSizeX,
1623 const wxCoord* printSizeY, wxOrientation directions )
1624{
1625 const bool isPrinting = printSizeX != nullptr && printSizeY != nullptr;
1626
1627 // Save desired borders:
1628 double newDesiredXmin = xMin;
1629 double newDesiredXmax = xMax;
1630 double newDesiredYmin = yMin;
1631 double newDesiredYmax = yMax;
1632
1633 // Provide a gap between the extrema of the curve and the top/bottom edges of the
1634 // plot area. Not to be confused with the left/right/top/bottom margins outside the plot area.
1635 const double xGap = fabs( xMax - xMin ) * m_leftRightPlotGapFactor;
1636 const double yGap = fabs( yMax - yMin ) * m_topBottomPlotGapFactor;
1637 xMin -= xGap;
1638 xMax += xGap;
1639 yMin -= yGap;
1640 yMax += yGap;
1641
1642 int newScrX = m_scrX;
1643 int newScrY = m_scrY;
1644
1645 if( isPrinting )
1646 {
1647 // Printer:
1648 newScrX = *printSizeX;
1649 newScrY = *printSizeY;
1650 }
1651 else
1652 {
1653 // Normal case (screen):
1654 GetClientSize( &newScrX, &newScrY );
1655 }
1656
1657 // Compute the width/height in pixels for the plot area.
1658 const int plotScreenWidth = newScrX - m_marginLeft - m_marginRight;
1659 const int plotScreenHeight = newScrY - m_marginTop - m_marginBottom;
1660
1661 // Adjust scale so that desired X/Y span plus extra gap fits in the plot area
1662 double desiredSpanX = xMax - xMin;
1663 double desiredSpanY = yMax - yMin;
1664 double newScaleX = ( desiredSpanX != 0 ) ? double( plotScreenWidth ) / desiredSpanX : 1;
1665 double newScaleY = ( desiredSpanY != 0 ) ? double( plotScreenHeight ) / desiredSpanY : 1;
1666
1667 // Adjust corner coordinates:
1668 // Upstream's aspect lock code has been removed, so no need to account for centering.
1669 double newPosX = xMin - ( m_marginLeft / newScaleX );
1670 double newPosY = yMax + ( m_marginTop / newScaleY );
1671
1672 // Commit above changes to member variables only if enabled for their respective dimension.
1673 if( ( ( directions & wxHORIZONTAL ) != 0 ) || isPrinting )
1674 {
1675 // Don't commit the passed desired bounds when printing
1676 if( !isPrinting )
1677 {
1678 m_desiredXmin = newDesiredXmin;
1679 m_desiredXmax = newDesiredXmax;
1680 }
1681
1682 m_scrX = newScrX;
1683 m_scaleX = newScaleX;
1684 m_posX = newPosX;
1685 }
1686
1687 if( ( ( directions & wxVERTICAL ) != 0 ) || isPrinting )
1688 {
1689 // Don't commit the passed desired bounds when printing
1690 if( !isPrinting )
1691 {
1692 m_desiredYmin = newDesiredYmin;
1693 m_desiredYmax = newDesiredYmax;
1694 }
1695
1696 m_scrY = newScrY;
1697 m_scaleY = newScaleY;
1698 m_posY = newPosY;
1699 }
1700
1701 // It is VERY IMPORTANT to NOT call Refresh if we are drawing to the printer!!
1702 // Otherwise, the DC dimensions will be those of the window instead of the printer device
1703 // The caller wanting to print should perform another Fit() afterwards to restore this
1704 // object's state.
1705 if( !isPrinting )
1706 UpdateAll();
1707}
1708
1709
1710void mpWindow::AdjustLimitedView( wxOrientation directions )
1711{
1712 if( !m_enableLimitedView )
1713 return;
1714
1715 // The m_desired* members are expressed in plot coordinates.
1716 // They should be clamped against their respective m_minX, m_maxX, m_minY, m_maxY limits.
1717
1718 if( ( directions & wxHORIZONTAL ) != 0 )
1719 {
1720 if( m_desiredXmin < m_minX )
1721 {
1722 double diff = m_minX - m_desiredXmin;
1723 m_posX += diff;
1724 m_desiredXmax += diff;
1726 }
1727
1728 if( m_desiredXmax > m_maxX )
1729 {
1730 double diff = m_desiredXmax - m_maxX;
1731 m_posX -= diff;
1732 m_desiredXmin -= diff;
1734 }
1735 }
1736
1737 if( ( directions & wxVERTICAL ) != 0 )
1738 {
1739 if( m_desiredYmin < m_minY )
1740 {
1741 double diff = m_minY - m_desiredYmin;
1742 m_posY += diff;
1743 m_desiredYmax += diff;
1745 }
1746
1747 if( m_desiredYmax > m_maxY )
1748 {
1749 double diff = m_desiredYmax - m_maxY;
1750 m_posY -= diff;
1751 m_desiredYmin -= diff;
1753 }
1754 }
1755}
1756
1757
1758bool mpWindow::SetXView( double pos, double desiredMax, double desiredMin )
1759{
1760 // TODO (ecorm): Investigate X scale flickering when panning at minimum zoom level
1761 // Possible cause: When AdjustLimitedView subtracts the out-of-bound delta, it does not
1762 // revert back to the exact same original coordinates due to floating point rounding errors.
1763 m_posX = pos;
1764 m_desiredXmax = desiredMax;
1765 m_desiredXmin = desiredMin;
1766 AdjustLimitedView( wxHORIZONTAL );
1767
1768 return true;
1769}
1770
1771
1772bool mpWindow::SetYView( double pos, double desiredMax, double desiredMin )
1773{
1774 m_posY = pos;
1775 m_desiredYmax = desiredMax;
1776 m_desiredYmin = desiredMin;
1777 AdjustLimitedView( wxVERTICAL );
1778
1779 return true;
1780}
1781
1782
1783void mpWindow::ZoomIn( const wxPoint& centerPoint )
1784{
1785 ZoomIn( centerPoint, zoomIncrementalFactor, wxBOTH );
1786}
1787
1788
1789void mpWindow::ZoomIn( const wxPoint& centerPoint, double zoomFactor, wxOrientation directions )
1790{
1791 DoZoom( centerPoint, zoomFactor, directions );
1792}
1793
1794
1795void mpWindow::ZoomOut( const wxPoint& centerPoint )
1796{
1797 ZoomOut( centerPoint, zoomIncrementalFactor, wxBOTH );
1798}
1799
1800
1801void mpWindow::ZoomOut( const wxPoint& centerPoint, double zoomFactor, wxOrientation directions )
1802{
1803 if( zoomFactor == 0 )
1804 zoomFactor = 1.0;
1805 DoZoom( centerPoint, 1.0 / zoomFactor, directions );
1806}
1807
1808
1809void mpWindow::ZoomRect( wxPoint p0, wxPoint p1 )
1810{
1812
1813 // Constrain given rectangle to plot area
1814 const int pMinX = m_marginLeft;
1815 const int pMaxX = m_scrX - m_marginRight;
1816 const int pMinY = m_marginTop;
1817 const int pMaxY = m_scrY - m_marginBottom;
1818 p0.x = std::max( p0.x, pMinX );
1819 p0.x = std::min( p0.x, pMaxX );
1820 p0.y = std::max( p0.y, pMinY );
1821 p0.y = std::min( p0.y, pMaxY );
1822 p1.x = std::max( p1.x, pMinX );
1823 p1.x = std::min( p1.x, pMaxX );
1824 p1.y = std::max( p1.y, pMinY );
1825 p1.y = std::min( p1.y, pMaxY );
1826
1827 // Compute the 2 corners in graph coordinates:
1828 double p0x = p2x( p0.x );
1829 double p0y = p2y( p0.y );
1830 double p1x = p2x( p1.x );
1831 double p1y = p2y( p1.y );
1832
1833 // Order them:
1834 double zoom_x_min = p0x<p1x ? p0x : p1x;
1835 double zoom_x_max = p0x>p1x ? p0x : p1x;
1836 double zoom_y_min = p0y<p1y ? p0y : p1y;
1837 double zoom_y_max = p0y>p1y ? p0y : p1y;
1838
1839 if( m_yLocked )
1840 {
1841 zoom_y_min = m_desiredYmin;
1842 zoom_y_max = m_desiredYmax;
1843 }
1844
1845 Fit( zoom_x_min, zoom_x_max, zoom_y_min, zoom_y_max );
1846
1847 // Even with the input rectangle contrained to the plot area, it's still possible for the
1848 // resulting view to exceed limits when a portion of the gap is grabbed.
1850
1851 // These additional checks are needed because AdjustLimitedView only adjusts the position
1852 // and not the scale.
1853 wxOrientation directionsNeedingRefitting = ViewNeedsRefitting( wxBOTH );
1854 if( directionsNeedingRefitting != 0 )
1855 Fit( m_minX, m_maxX, m_minY, m_maxY, nullptr, nullptr, directionsNeedingRefitting );
1856}
1857
1858
1859void mpWindow::pushZoomUndo( const std::array<double, 4>& aZoom )
1860{
1861 m_undoZoomStack.push( aZoom );
1862
1863 while( !m_redoZoomStack.empty() )
1864 m_redoZoomStack.pop();
1865}
1866
1867
1869{
1870 if( m_undoZoomStack.size() )
1871 {
1873
1874 std::array<double, 4> zoom = m_undoZoomStack.top();
1875 m_undoZoomStack.pop();
1876
1877 Fit( zoom[0], zoom[1], zoom[2], zoom[3] );
1879 }
1880}
1881
1882
1884{
1885 if( m_redoZoomStack.size() )
1886 {
1888
1889 std::array<double, 4> zoom = m_redoZoomStack.top();
1890 m_redoZoomStack.pop();
1891
1892 Fit( zoom[0], zoom[1], zoom[2], zoom[3] );
1894 }
1895}
1896
1897
1898void mpWindow::OnShowPopupMenu( wxMouseEvent& event )
1899{
1900 m_clickedX = event.GetX();
1901 m_clickedY = event.GetY();
1902
1903 m_popmenu.Enable( mpID_ZOOM_UNDO, !m_undoZoomStack.empty() );
1904 m_popmenu.Enable( mpID_ZOOM_REDO, !m_redoZoomStack.empty() );
1905
1906 PopupMenu( &m_popmenu, event.GetX(), event.GetY() );
1907}
1908
1909
1910void mpWindow::OnFit( wxCommandEvent& WXUNUSED( event ) )
1911{
1913
1914 Fit();
1915}
1916
1917
1918void mpWindow::OnCenter( wxCommandEvent& WXUNUSED( event ) )
1919{
1920 GetClientSize( &m_scrX, &m_scrY );
1921 int centerX = (m_scrX - m_marginLeft - m_marginRight) / 2;
1922 int centerY = (m_scrY - m_marginTop - m_marginBottom) / 2;
1923 SetPos( p2x( m_clickedX - centerX ), p2y( m_clickedY - centerY ) );
1924}
1925
1926
1928{
1929 MouseWheelActionSet actions;
1935 return actions;
1936}
1937
1938
1939void mpWindow::onZoomIn( wxCommandEvent& WXUNUSED( event ) )
1940{
1941 ZoomIn( wxPoint( m_mouseMClick.x, m_mouseMClick.y ) );
1942}
1943
1944
1945void mpWindow::onZoomOut( wxCommandEvent& WXUNUSED( event ) )
1946{
1947 ZoomOut();
1948}
1949
1950
1951void mpWindow::onZoomUndo( wxCommandEvent& WXUNUSED( event ) )
1952{
1953 ZoomUndo();
1954}
1955
1956
1957void mpWindow::onZoomRedo( wxCommandEvent& WXUNUSED( event ) )
1958{
1959 ZoomRedo();
1960}
1961
1962
1963void mpWindow::OnSize( wxSizeEvent& WXUNUSED( event ) )
1964{
1965 // Try to fit again with the new window size:
1967}
1968
1969
1970bool mpWindow::AddLayer( mpLayer* layer, bool refreshDisplay )
1971{
1972 if( layer )
1973 {
1974 m_layers.push_back( layer );
1975
1976 if( refreshDisplay )
1977 UpdateAll();
1978
1979 return true;
1980 }
1981
1982 return false;
1983}
1984
1985
1986bool mpWindow::DelLayer( mpLayer* layer, bool alsoDeleteObject, bool refreshDisplay )
1987{
1988 wxLayerList::iterator layIt;
1989
1990 for( layIt = m_layers.begin(); layIt != m_layers.end(); layIt++ )
1991 {
1992 if( *layIt == layer )
1993 {
1994 // Also delete the object?
1995 if( alsoDeleteObject )
1996 delete *layIt;
1997
1998 m_layers.erase( layIt ); // this deleted the reference only
1999
2000 if( refreshDisplay )
2001 UpdateAll();
2002
2003 return true;
2004 }
2005 }
2006
2007 return false;
2008}
2009
2010
2011void mpWindow::DelAllLayers( bool alsoDeleteObject, bool refreshDisplay )
2012{
2013 while( m_layers.size()>0 )
2014 {
2015 // Also delete the object?
2016 if( alsoDeleteObject )
2017 delete m_layers[0];
2018
2019 m_layers.erase( m_layers.begin() ); // this deleted the reference only
2020 }
2021
2022 if( refreshDisplay )
2023 UpdateAll();
2024}
2025
2026
2027void mpWindow::OnPaint( wxPaintEvent& WXUNUSED( event ) )
2028{
2029 wxPaintDC paintDC( this );
2030
2031 paintDC.GetSize( &m_scrX, &m_scrY ); // This is the size of the visible area only!
2032
2033 // Selects direct or buffered draw:
2034 wxDC* targetDC = &paintDC;
2035
2036 // J.L.Blanco @ Aug 2007: Added double buffer support
2038 {
2039 if( m_last_lx != m_scrX || m_last_ly != m_scrY )
2040 {
2041 delete m_buff_bmp;
2042 m_buff_bmp = new wxBitmap( m_scrX, m_scrY );
2043 m_buff_dc.SelectObject( *m_buff_bmp );
2044 m_last_lx = m_scrX;
2045 m_last_ly = m_scrY;
2046 }
2047
2048 targetDC = &m_buff_dc;
2049 }
2050
2051 if( wxGraphicsContext* ctx = targetDC->GetGraphicsContext() )
2052 {
2053 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_BEST ) )
2054 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_GOOD ) )
2055 ctx->SetInterpolationQuality( wxINTERPOLATION_FAST );
2056
2057 ctx->SetAntialiasMode( wxANTIALIAS_DEFAULT );
2058 }
2059
2060 // Draw background:
2061 targetDC->SetPen( *wxTRANSPARENT_PEN );
2062 wxBrush brush( GetBackgroundColour() );
2063 targetDC->SetBrush( brush );
2064 targetDC->SetTextForeground( m_fgColour );
2065 targetDC->DrawRectangle( 0, 0, m_scrX, m_scrY );
2066
2067 // Draw all the layers:
2068 for( mpLayer* layer : m_layers )
2069 layer->Plot( *targetDC, *this );
2070
2071 if( m_zooming )
2072 {
2073 wxPen pen( m_fgColour, 1, wxPENSTYLE_DOT );
2074 targetDC->SetPen( pen );
2075 targetDC->SetBrush( *wxTRANSPARENT_BRUSH );
2076 targetDC->DrawRectangle( m_zoomRect );
2077 }
2078
2079 // If doublebuffer, draw now to the window:
2081 paintDC.Blit( 0, 0, m_scrX, m_scrY, targetDC, 0, 0 );
2082}
2083
2084void mpWindow::DoZoom( const wxPoint& centerPoint, double zoomFactor, wxOrientation directions )
2085{
2086 if( m_yLocked )
2087 {
2088 if( directions == wxVERTICAL )
2089 return;
2090 directions = wxHORIZONTAL;
2091 }
2092
2093 const bool horizontally = ( directions & wxHORIZONTAL ) != 0;
2094 const bool vertically = ( directions & wxVERTICAL ) != 0;
2095
2097
2098 // Preserve the position of the clicked point:
2099 wxPoint c( centerPoint );
2100 if( c == wxDefaultPosition )
2101 {
2102 GetClientSize( &m_scrX, &m_scrY );
2103 c.x = ( m_scrX - m_marginLeft - m_marginRight ) / 2 + m_marginLeft;
2104 c.y = ( m_scrY - m_marginTop - m_marginBottom ) / 2 + m_marginTop;
2105 }
2106 else
2107 {
2108 c.x = std::max( c.x, m_marginLeft );
2109 c.x = std::min( c.x, m_scrX - m_marginRight );
2110 c.y = std::max( c.y, m_marginTop );
2111 c.y = std::min( c.y, m_scrY - m_marginBottom );
2112 }
2113
2114 // Zoom in/out:
2115 const double MAX_SCALE = 1e6;
2116 const double newScaleX = horizontally ? ( m_scaleX * zoomFactor ) : m_scaleX;
2117 const double newScaleY = vertically ? ( m_scaleY * zoomFactor ) : m_scaleY;
2118
2119 // Baaaaad things happen when you zoom in too much..
2120 if( newScaleX > MAX_SCALE || newScaleY > MAX_SCALE )
2121 return;
2122
2123 if( horizontally )
2124 {
2125 // Transform the clicked X point to layer coordinates:
2126 const double prior_layer_x = p2x( c.x );
2127
2128 // Adjust the new X scale and plot X origin:
2129 m_scaleX = newScaleX;
2130 m_posX = prior_layer_x - c.x / newScaleX;
2131
2132 // Recompute the desired X view extents:
2134 }
2135
2136 if( vertically )
2137 {
2138 // Transform the clicked Y point to layer coordinates:
2139 const double prior_layer_y = p2y( c.y );
2140
2141 // Adjust the new Y scale and plot Y origin:
2142 m_scaleY = newScaleY;
2143 m_posY = prior_layer_y + c.y / newScaleY;
2144
2145 // Recompute the desired Y view extents:
2147 }
2148
2149 AdjustLimitedView( directions );
2150
2151 if( zoomFactor < 1.0 )
2152 {
2153 // These additional checks are needed because AdjustLimitedView only adjusts the position
2154 // and not the scale.
2155 wxOrientation directionsNeedingRefitting = ViewNeedsRefitting( directions );
2156
2157 // If the view is still out-of-limits after AdjustLimitedView is called, perform a Fit
2158 // along the offending dimension(s).
2159 if( directionsNeedingRefitting != 0 )
2160 Fit( m_minX, m_maxX, m_minY, m_maxY, nullptr, nullptr, directionsNeedingRefitting );
2161 }
2162
2163 UpdateAll();
2164}
2165
2166
2167void mpWindow::RecomputeDesiredX( double& min, double& max )
2168{
2169 const int plotScreenWidth = m_scrX - m_marginLeft - m_marginRight;
2170 const double plotSpanX = plotScreenWidth / m_scaleX;
2171 const double desiredSpanX = plotSpanX / ( 2 * m_leftRightPlotGapFactor + 1 );
2172 const double xGap = desiredSpanX * m_leftRightPlotGapFactor;
2173 min = m_posX + ( m_marginLeft / m_scaleX ) + xGap;
2174 max = m_desiredXmin + desiredSpanX;
2175}
2176
2177
2178void mpWindow::RecomputeDesiredY( double& min, double& max )
2179{
2180 const int plotScreenHeight = m_scrY - m_marginTop - m_marginBottom;
2181 const double plotSpanY = plotScreenHeight / m_scaleY;
2182 const double desiredSpanY = plotSpanY / ( 2 * m_topBottomPlotGapFactor + 1 );
2183 const double yGap = desiredSpanY * m_topBottomPlotGapFactor;
2184 max = m_posY - ( m_marginTop / m_scaleY ) - yGap;
2185 min = m_desiredYmax - desiredSpanY;
2186}
2187
2188
2189wxOrientation mpWindow::ViewNeedsRefitting( wxOrientation directions ) const
2190{
2191 if( !m_enableLimitedView )
2192 return static_cast<wxOrientation>( 0 );
2193
2194 // Allow a gap between the extrema of the curve and the edges of the plot area. Not to be
2195 // confused with the left/right/top/bottom margins outside the plot area.
2196 const double xGap = fabs( m_maxX - m_minX ) * m_leftRightPlotGapFactor;
2197 const double yGap = fabs( m_maxY - m_minY ) * m_topBottomPlotGapFactor;
2198
2199 wxOrientation result = {};
2200
2201 if( ( directions & wxHORIZONTAL ) != 0 )
2202 {
2203 if( ( m_desiredXmax > m_maxX + xGap ) || ( m_desiredXmin < m_minX - xGap ) )
2204 result = static_cast<wxOrientation>( result | wxHORIZONTAL );
2205 }
2206
2207 if( ( directions & wxVERTICAL ) != 0 )
2208 {
2209 if( ( m_desiredYmax > m_maxY + yGap ) || ( m_desiredYmin < m_minY - yGap ) )
2210 result = static_cast<wxOrientation>( result | wxVERTICAL );
2211 }
2212
2213 return result;
2214}
2215
2216
2217void mpWindow::PerformMouseWheelAction( wxMouseEvent& event, MouseWheelAction action )
2218{
2219 const int change = event.GetWheelRotation();
2220 const double changeUnitsX = change / m_scaleX;
2221 const double changeUnitsY = change / m_scaleY;
2222 const wxPoint clickPt( event.GetX(), event.GetY() );
2223
2224 switch( action )
2225 {
2226 case MouseWheelAction::NONE: break;
2227
2229 SetXView( m_posX + changeUnitsX, m_desiredXmax + changeUnitsX,
2230 m_desiredXmin + changeUnitsX );
2231 UpdateAll();
2232 break;
2233
2235 SetXView( m_posX - changeUnitsX, m_desiredXmax - changeUnitsX,
2236 m_desiredXmin - changeUnitsX );
2237 UpdateAll();
2238 break;
2239
2241 if( !m_yLocked )
2242 {
2243 SetYView( m_posY + changeUnitsY, m_desiredYmax + changeUnitsY,
2244 m_desiredYmin + changeUnitsY );
2245 UpdateAll();
2246 }
2247 break;
2248
2250 if( event.GetWheelRotation() > 0 )
2251 ZoomIn( clickPt );
2252 else
2253 ZoomOut( clickPt );
2254 break;
2255
2257 if( event.GetWheelRotation() > 0 )
2258 ZoomIn( clickPt, zoomIncrementalFactor, wxHORIZONTAL );
2259 else
2260 ZoomOut( clickPt, zoomIncrementalFactor, wxHORIZONTAL );
2261 break;
2262
2264 if( event.GetWheelRotation() > 0 )
2265 ZoomIn( clickPt, zoomIncrementalFactor, wxVERTICAL );
2266 else
2267 ZoomOut( clickPt, zoomIncrementalFactor, wxVERTICAL );
2268 break;
2269
2270 default: break;
2271 }
2272}
2273
2274
2276{
2277 m_minX = 0.0;
2278 m_maxX = 1.0;
2279 m_minY = 0.0;
2280 m_maxY = 1.0;
2281
2282 return true;
2283}
2284
2285
2287{
2288 UpdateBBox();
2289 Refresh( false );
2290}
2291
2292
2293void mpWindow::SetScaleX( double scaleX )
2294{
2295 if( scaleX != 0 )
2296 m_scaleX = scaleX;
2297
2298 UpdateAll();
2299}
2300
2301
2302// New methods implemented by Davide Rondini
2303
2304mpLayer* mpWindow::GetLayer( int position ) const
2305{
2306 if( ( position >= (int) m_layers.size() ) || position < 0 )
2307 return nullptr;
2308
2309 return m_layers[position];
2310}
2311
2312
2313const mpLayer* mpWindow::GetLayerByName( const wxString& name ) const
2314{
2315 for( const mpLayer* layer : m_layers )
2316 {
2317 if( !layer->GetName().Cmp( name ) )
2318 return layer;
2319 }
2320
2321 return nullptr; // Not found
2322}
2323
2324
2325void mpWindow::GetBoundingBox( double* bbox ) const
2326{
2327 bbox[0] = m_minX;
2328 bbox[1] = m_maxX;
2329 bbox[2] = m_minY;
2330 bbox[3] = m_maxY;
2331}
2332
2333
2334bool mpWindow::SaveScreenshot( wxImage& aImage, wxSize aImageSize, bool aFit )
2335{
2336 int sizeX, sizeY;
2337
2338 if( aImageSize == wxDefaultSize )
2339 {
2340 sizeX = m_scrX;
2341 sizeY = m_scrY;
2342 }
2343 else
2344 {
2345 sizeX = aImageSize.x;
2346 sizeY = aImageSize.y;
2347 SetScr( sizeX, sizeY );
2348 }
2349
2350 wxBitmap screenBuffer( sizeX, sizeY );
2351 wxMemoryDC screenDC;
2352 screenDC.SelectObject( screenBuffer );
2353 screenDC.SetPen( *wxWHITE_PEN );
2354 screenDC.SetTextForeground( m_fgColour );
2355 wxBrush brush( GetBackgroundColour() );
2356 screenDC.SetBrush( brush );
2357 screenDC.DrawRectangle( 0, 0, sizeX, sizeY );
2358
2359 if( aFit )
2360 Fit( m_minX, m_maxX, m_minY, m_maxY, &sizeX, &sizeY );
2361 else
2363
2364 // Draw all the layers:
2365 for( mpLayer* layer : m_layers )
2366 layer->Plot( screenDC, *this );
2367
2368 if( aImageSize != wxDefaultSize )
2369 {
2370 // Restore dimensions
2371 int bk_scrX = m_scrX;
2372 int bk_scrY = m_scrY;
2373 SetScr( bk_scrX, bk_scrY );
2374 Fit( m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax, &bk_scrX, &bk_scrY );
2375 UpdateAll();
2376 }
2377
2378 // Once drawing is complete, actually save screen shot
2379 aImage = screenBuffer.ConvertToImage();
2380
2381 return true;
2382}
2383
2384
2385void mpWindow::SetMargins( int top, int right, int bottom, int left )
2386{
2387 m_marginTop = top;
2389 m_marginBottom = bottom;
2391}
2392
2393
2395{
2396 for( mpLayer* layer : m_layers )
2397 {
2398 if( layer->IsInfo() )
2399 {
2400 mpInfoLayer* tmpLyr = static_cast<mpInfoLayer*>( layer );
2401
2402 if( tmpLyr->Inside( point ) )
2403 return tmpLyr;
2404 }
2405 }
2406
2407 return nullptr;
2408}
2409
2410
2411void mpWindow::SetLayerVisible( const wxString& name, bool viewable )
2412{
2413 if( mpLayer* lx = GetLayerByName( name ) )
2414 {
2415 lx->SetVisible( viewable );
2416 UpdateAll();
2417 }
2418}
2419
2420
2421bool mpWindow::IsLayerVisible( const wxString& name ) const
2422{
2423 if( const mpLayer* lx = GetLayerByName( name ) )
2424 return lx->IsVisible();
2425
2426 return false;
2427}
2428
2429
2430void mpWindow::SetLayerVisible( const unsigned int position, bool viewable )
2431{
2432 if( mpLayer* lx = GetLayer( position ) )
2433 {
2434 lx->SetVisible( viewable );
2435 UpdateAll();
2436 }
2437}
2438
2439
2440bool mpWindow::IsLayerVisible( unsigned int position ) const
2441{
2442 if( const mpLayer* lx = GetLayer( position ) )
2443 return lx->IsVisible();
2444
2445 return false;
2446}
2447
2448
2449void mpWindow::SetColourTheme( const wxColour& bgColour, const wxColour& drawColour,
2450 const wxColour& axesColour )
2451{
2452 SetBackgroundColour( bgColour );
2453 SetForegroundColour( drawColour );
2454 m_bgColour = bgColour;
2455 m_fgColour = drawColour;
2456 m_axColour = axesColour;
2457
2458 // Cycle between layers to set colours and properties to them
2459 for( mpLayer* layer : m_layers )
2460 {
2461 if( layer->GetLayerType() == mpLAYER_AXIS )
2462 {
2463 wxPen axisPen = layer->GetPen(); // Get the old pen to modify only colour, not style or width
2464 axisPen.SetColour( axesColour );
2465 layer->SetPen( axisPen );
2466 }
2467
2468 if( layer->GetLayerType() == mpLAYER_INFO )
2469 {
2470 wxPen infoPen = layer->GetPen(); // Get the old pen to modify only colour, not style or width
2471 infoPen.SetColour( drawColour );
2472 layer->SetPen( infoPen );
2473 }
2474 }
2475}
2476
2477
2478template <typename... Ts>
2480 wxWindow( std::forward<Ts>( windowArgs )... ),
2481 m_minX( 0.0 ),
2482 m_maxX( 0.0 ),
2483 m_minY( 0.0 ),
2484 m_maxY( 0.0 ),
2485 m_scaleX( 1.0 ),
2486 m_scaleY( 1.0 ),
2487 m_posX( 0.0 ),
2488 m_posY( 0.0 ),
2489 m_scrX( 64 ),
2490 m_scrY( 64 ),
2491 m_clickedX( 0 ),
2492 m_clickedY( 0 ),
2493 m_yLocked( false ),
2494 m_desiredXmin( 0.0 ),
2495 m_desiredXmax( 1.0 ),
2496 m_desiredYmin( 0.0 ),
2497 m_desiredYmax( 1.0 ),
2498 m_topBottomPlotGapFactor( 0.03 ),
2499 m_leftRightPlotGapFactor( 0.0 ),
2500 m_marginTop( 0 ),
2501 m_marginRight( 0 ),
2502 m_marginBottom( 0 ),
2503 m_marginLeft( 0 ),
2504 m_last_lx( 0 ),
2505 m_last_ly( 0 ),
2506 m_buff_bmp( nullptr ),
2507 m_enableDoubleBuffer( false ),
2508 m_enableMouseNavigation( true ),
2509 m_enableLimitedView( false ),
2510 m_mouseWheelActions( defaultMouseWheelActions() ),
2511 m_movingInfoLayer( nullptr ),
2512 m_zooming( false )
2513{}
2514
2515
2517{
2518 if( wxGraphicsContext* ctx = m_buff_dc.GetGraphicsContext() )
2519 {
2520 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_BEST )
2521 || !ctx->SetInterpolationQuality( wxINTERPOLATION_GOOD ) )
2522 {
2523 ctx->SetInterpolationQuality( wxINTERPOLATION_FAST );
2524 }
2525
2526 ctx->SetAntialiasMode( wxANTIALIAS_DEFAULT );
2527 }
2528}
2529
2530
2531// -----------------------------------------------------------------------------
2532// mpFXYVector implementation - by Jose Luis Blanco (AGO-2007)
2533// -----------------------------------------------------------------------------
2534
2535IMPLEMENT_DYNAMIC_CLASS( mpFXYVector, mpFXY )
2536
2537// Constructor
2538mpFXYVector::mpFXYVector( const wxString& name, int flags ) :
2539 mpFXY( name, flags )
2540{
2541 m_index = 0;
2542 m_sweepWindow = 0;
2543 m_minX = -1;
2544 m_maxX = 1;
2545 m_minY = -1;
2546 m_maxY = 1;
2547 m_type = mpLAYER_PLOT;
2548}
2549
2550
2551double mpScaleX::TransformToPlot( double x ) const
2552{
2553 return (x + m_offset) * m_scale;
2554}
2555
2556
2557double mpScaleX::TransformFromPlot( double xplot ) const
2558{
2559 return xplot / m_scale - m_offset;
2560}
2561
2562
2563double mpScaleY::TransformToPlot( double x ) const
2564{
2565 return (x + m_offset) * m_scale;
2566}
2567
2568
2569double mpScaleY::TransformFromPlot( double xplot ) const
2570{
2571 return xplot / m_scale - m_offset;
2572}
2573
2574
2575double mpScaleXLog::TransformToPlot( double x ) const
2576{
2577 double xlogmin = log10( m_minV );
2578 double xlogmax = log10( m_maxV );
2579
2580 return ( log10( x ) - xlogmin) / (xlogmax - xlogmin);
2581}
2582
2583
2584double mpScaleXLog::TransformFromPlot( double xplot ) const
2585{
2586 double xlogmin = log10( m_minV );
2587 double xlogmax = log10( m_maxV );
2588
2589 return pow( 10.0, xplot * (xlogmax - xlogmin) + xlogmin );
2590}
2591
2592
2594{
2595 m_index = 0;
2596 m_sweepWindow = std::numeric_limits<size_t>::max();
2597}
2598
2599
2600void mpFXYVector::SetSweepWindow( int aSweepIdx )
2601{
2602 m_index = aSweepIdx * m_sweepSize;
2603 m_sweepWindow = ( aSweepIdx + 1 ) * m_sweepSize;
2604}
2605
2606
2607bool mpFXYVector::GetNextXY( double& x, double& y )
2608{
2609 if( m_index >= m_xs.size() || m_index >= m_sweepWindow )
2610 {
2611 return false;
2612 }
2613 else
2614 {
2615 x = m_xs[m_index];
2616 y = m_ys[m_index++];
2617 return m_index <= m_xs.size() && m_index <= m_sweepWindow;
2618 }
2619}
2620
2621
2623{
2624 m_xs.clear();
2625 m_ys.clear();
2626}
2627
2628
2629void mpFXYVector::SetData( const std::vector<double>& xs, const std::vector<double>& ys )
2630{
2631 // Check if the data vectors are of the same size
2632 if( xs.size() != ys.size() )
2633 return;
2634
2635 // Copy the data:
2636 m_xs = xs;
2637 m_ys = ys;
2638
2639 // Update internal variables for the bounding box.
2640 if( xs.size() > 0 )
2641 {
2642 m_minX = xs[0];
2643 m_maxX = xs[0];
2644 m_minY = ys[0];
2645 m_maxY = ys[0];
2646
2647 for( const double x : xs )
2648 {
2649 if( x < m_minX )
2650 m_minX = x;
2651
2652 if( x > m_maxX )
2653 m_maxX = x;
2654 }
2655
2656 for( const double y : ys )
2657 {
2658 if( y < m_minY )
2659 m_minY = y;
2660
2661 if( y > m_maxY )
2662 m_maxY = y;
2663 }
2664 }
2665 else
2666 {
2667 m_minX = 0;
2668 m_maxX = 0;
2669 m_minY = 0;
2670 m_maxY = 0;
2671 }
2672}
2673
2674
2676{
2677 m_scaleX = scaleX;
2678 m_scaleY = scaleY;
2679
2680 UpdateScales();
2681}
2682
2683
2685{
2686 if( m_scaleX )
2688
2689 if( m_scaleY )
2691}
2692
2693
2694double mpFXY::s2x( double plotCoordX ) const
2695{
2696 return m_scaleX ? m_scaleX->TransformFromPlot( plotCoordX ) : plotCoordX;
2697}
2698
2699
2700double mpFXY::s2y( double plotCoordY ) const
2701{
2702 return m_scaleY ? m_scaleY->TransformFromPlot( plotCoordY ) : plotCoordY;
2703}
2704
2705
2706double mpFXY::x2s( double x ) const
2707{
2708 return m_scaleX ? m_scaleX->TransformToPlot( x ) : x;
2709}
2710
2711
2712double mpFXY::y2s( double y ) const
2713{
2714 return m_scaleY ? m_scaleY->TransformToPlot( y ) : y;
2715}
const char * name
Definition: DXF_plotter.cpp:57
A class providing graphs functionality for a 2D plot (either continuous or a set of points),...
Definition: mathplot.h:1409
void SetSweepWindow(int aSweepIdx) override
Definition: mathplot.cpp:2600
bool GetNextXY(double &x, double &y) override
Get locus value for next N.
Definition: mathplot.cpp:2607
double m_maxY
Definition: mathplot.h:1442
std::vector< double > m_ys
Definition: mathplot.h:1435
double m_minX
Loaded at SetData.
Definition: mathplot.h:1442
std::vector< double > m_xs
The internal copy of the set of data to draw.
Definition: mathplot.h:1435
size_t m_sweepWindow
Definition: mathplot.h:1438
virtual void SetData(const std::vector< double > &xs, const std::vector< double > &ys)
Changes the internal data: the set of points to draw.
Definition: mathplot.cpp:2629
double m_maxX
Definition: mathplot.h:1442
void Clear()
Clears all the data, leaving the layer empty.
Definition: mathplot.cpp:2622
size_t m_sweepSize
Definition: mathplot.h:1444
void Rewind() override
Rewind value enumeration with mpFXY::GetNextXY.
Definition: mathplot.cpp:2593
size_t m_index
Definition: mathplot.h:1437
double m_minY
Definition: mathplot.h:1442
Abstract base class providing plot and labeling functionality for a locus plot F:N->X,...
Definition: mathplot.h:542
mpScaleBase * m_scaleX
Definition: mathplot.h:587
wxCoord maxDrawY
Definition: mathplot.h:585
double s2y(double plotCoordY) const
Definition: mathplot.cpp:2700
virtual void SetScale(mpScaleBase *scaleX, mpScaleBase *scaleY)
Definition: mathplot.cpp:2675
mpScaleBase * m_scaleY
Definition: mathplot.h:587
wxCoord maxDrawX
Definition: mathplot.h:585
virtual void Rewind()=0
Rewind value enumeration with mpFXY::GetNextXY.
void UpdateScales()
Definition: mathplot.cpp:2684
virtual void SetSweepWindow(int aSweepIdx)
Definition: mathplot.h:553
virtual size_t GetCount() const =0
int m_flags
Definition: mathplot.h:582
virtual void Plot(wxDC &dc, mpWindow &w) override
Layer plot handler.
Definition: mathplot.cpp:439
double y2s(double y) const
Definition: mathplot.cpp:2712
void UpdateViewBoundary(wxCoord xnew, wxCoord ynew)
Update label positioning data.
Definition: mathplot.cpp:428
double x2s(double x) const
Definition: mathplot.cpp:2706
virtual int GetSweepCount() const
Definition: mathplot.h:563
wxCoord minDrawX
Definition: mathplot.h:585
double s2x(double plotCoordX) const
Definition: mathplot.cpp:2694
wxCoord minDrawY
Definition: mathplot.h:585
virtual bool GetNextXY(double &x, double &y)=0
Get locus value for next N.
Abstract base class providing plot and labeling functionality for functions F:X->Y.
Definition: mathplot.h:476
virtual double GetY(double x) const =0
Get function value for argument.
virtual void Plot(wxDC &dc, mpWindow &w) override
Layer plot handler.
Definition: mathplot.cpp:293
int m_flags
Definition: mathplot.h:497
Abstract base class providing plot and labeling functionality for functions F:Y->X.
Definition: mathplot.h:508
int m_flags
Definition: mathplot.h:529
virtual double GetX(double y) const =0
Get function value for argument.
virtual void Plot(wxDC &dc, mpWindow &w) override
Layer plot handler.
Definition: mathplot.cpp:359
Base class to create small rectangular info boxes mpInfoLayer is the base class to create a small rec...
Definition: mathplot.h:330
virtual ~mpInfoLayer()
Destructor.
Definition: mathplot.cpp:101
wxPoint m_reference
Definition: mathplot.h:381
wxRect m_dim
Definition: mathplot.h:379
mpInfoLayer()
Default constructor.
Definition: mathplot.cpp:78
wxPoint GetPosition() const
Returns the position of the upper left corner of the box (in pixels)
Definition: mathplot.cpp:164
int m_winY
Definition: mathplot.h:384
virtual void Plot(wxDC &dc, mpWindow &w) override
Plot method.
Definition: mathplot.cpp:126
virtual void UpdateReference()
Updates the rectangle reference point.
Definition: mathplot.cpp:119
virtual bool Inside(const wxPoint &point) const
Checks whether a point is inside the info box rectangle.
Definition: mathplot.cpp:106
wxBrush m_brush
Definition: mathplot.h:382
virtual void Move(wxPoint delta)
Moves the layer rectangle of given pixel deltas.
Definition: mathplot.cpp:112
int m_winX
Definition: mathplot.h:383
wxSize GetSize() const
Returns the size of the box (in pixels)
Definition: mathplot.cpp:170
Implements the legend to be added to the plot This layer allows you to add a legend to describe the p...
Definition: mathplot.h:393
mpInfoLegend()
Default constructor.
Definition: mathplot.cpp:176
virtual void Plot(wxDC &dc, mpWindow &w) override
Plot method.
Definition: mathplot.cpp:193
~mpInfoLegend()
Default destructor.
Definition: mathplot.cpp:188
const wxString & GetDisplayName() const
Definition: mathplot.h:241
mpLayerType GetLayerType() const
Get layer type: a Layer can be of different types: plot lines, axis, info boxes, etc,...
Definition: mathplot.h:287
wxFont m_font
Definition: mathplot.h:307
bool IsVisible() const
Checks whether the layer is visible or not.
Definition: mathplot.h:291
virtual double GetMinY() const
Get inclusive bottom border of bounding box.
Definition: mathplot.h:186
bool m_continuous
Definition: mathplot.h:312
bool m_showName
Definition: mathplot.h:313
bool m_visible
Definition: mathplot.h:315
const wxPen & GetPen() const
Get pen set for this layer.
Definition: mathplot.h:254
mpLayerType m_type
Definition: mathplot.h:314
wxString m_name
Definition: mathplot.h:310
wxPen m_pen
Definition: mathplot.h:308
virtual double GetMaxX() const
Get inclusive right border of bounding box.
Definition: mathplot.h:181
virtual double GetMinX() const
Get inclusive left border of bounding box.
Definition: mathplot.h:176
virtual double GetMaxY() const
Get inclusive top border of bounding box.
Definition: mathplot.h:191
Plot layer implementing a x-scale ruler.
Definition: mathplot.h:614
bool m_axisLocked
Definition: mathplot.h:744
double m_scale
Definition: mathplot.h:737
virtual void recalculateTicks(wxDC &dc, mpWindow &w)
Definition: mathplot.h:729
double m_offset
Definition: mathplot.h:737
void GetDataRange(double &minV, double &maxV) const
Definition: mathplot.h:641
std::vector< double > m_tickValues
Definition: mathplot.h:734
bool m_rangeSet
Definition: mathplot.h:743
virtual double TransformToPlot(double x) const
Definition: mathplot.h:706
virtual void formatLabels()
Definition: mathplot.h:731
int m_maxLabelHeight
Definition: mathplot.h:747
double m_axisMin
Definition: mathplot.h:745
double m_maxV
Definition: mathplot.h:742
void computeLabelExtents(wxDC &dc, mpWindow &w)
Definition: mathplot.cpp:788
int m_maxLabelWidth
Definition: mathplot.h:748
virtual void ExtendDataRange(double minV, double maxV)
Definition: mathplot.h:647
std::vector< TICK_LABEL > m_tickLabels
Definition: mathplot.h:735
double m_absVisibleMaxV
Definition: mathplot.h:738
bool m_ticks
Definition: mathplot.h:741
double m_minV
Definition: mathplot.h:742
void updateTickLabels(wxDC &dc, mpWindow &w)
Definition: mathplot.cpp:805
int m_flags
Definition: mathplot.h:739
int m_nameFlags
Definition: mathplot.h:740
virtual double TransformFromPlot(double xplot) const
Definition: mathplot.h:707
double m_axisMax
Definition: mathplot.h:746
virtual void getVisibleDataRange(mpWindow &w, double &minV, double &maxV) override
Definition: mathplot.cpp:962
virtual void Plot(wxDC &dc, mpWindow &w) override
Plot given view of layer to the given device context.
Definition: mathplot.cpp:1049
mpScaleXLog(const wxString &name=wxT("log(X)"), int flags=mpALIGN_CENTER, bool ticks=true, unsigned int type=mpX_NORMAL)
Full constructor.
Definition: mathplot.cpp:1043
virtual double TransformFromPlot(double xplot) const override
Definition: mathplot.cpp:2584
virtual double TransformToPlot(double x) const override
Definition: mathplot.cpp:2575
void recalculateTicks(wxDC &dc, mpWindow &w) override
Definition: mathplot.cpp:974
virtual double TransformToPlot(double x) const override
Definition: mathplot.cpp:2551
virtual double TransformFromPlot(double xplot) const override
Definition: mathplot.cpp:2557
virtual void recalculateTicks(wxDC &dc, mpWindow &w) override
Definition: mathplot.cpp:700
mpScaleX(const wxString &name=wxT("X"), int flags=mpALIGN_CENTER, bool ticks=true, unsigned int type=mpX_NORMAL)
Full constructor.
Definition: mathplot.cpp:1037
Plot layer implementing a y-scale ruler.
Definition: mathplot.h:828
int m_flags
Definition: mathplot.h:862
mpScaleY * m_masterScale
Definition: mathplot.h:861
virtual void getVisibleDataRange(mpWindow &w, double &minV, double &maxV) override
Definition: mathplot.cpp:812
virtual double TransformFromPlot(double xplot) const override
Definition: mathplot.cpp:2569
void computeSlaveTicks(mpWindow &w)
Definition: mathplot.cpp:825
virtual void Plot(wxDC &dc, mpWindow &w) override
Layer plot handler.
Definition: mathplot.cpp:1212
bool m_ticks
Definition: mathplot.h:863
virtual void recalculateTicks(wxDC &dc, mpWindow &w) override
Definition: mathplot.cpp:874
virtual double TransformToPlot(double x) const override
Definition: mathplot.cpp:2563
Canvas for plotting mpLayer implementations.
Definition: mathplot.h:908
double m_desiredYmin
Definition: mathplot.h:1349
bool SaveScreenshot(wxImage &aImage, wxSize aImageSize=wxDefaultSize, bool aFit=false)
Draw the window on a wxBitmap, then save it to a file.
Definition: mathplot.cpp:2334
mpInfoLayer * m_movingInfoLayer
Definition: mathplot.h:1367
void DelAllLayers(bool alsoDeleteObject, bool refreshDisplay=true)
Remove all layers from the plot.
Definition: mathplot.cpp:2011
bool m_zooming
Definition: mathplot.h:1368
void SetColourTheme(const wxColour &bgColour, const wxColour &drawColour, const wxColour &axesColour)
Set Color theme.
Definition: mathplot.cpp:2449
virtual bool SetYView(double pos, double desiredMax, double desiredMin)
Applies new Y view coordinates depending on the settings.
Definition: mathplot.cpp:1772
void ZoomRect(wxPoint p0, wxPoint p1)
Zoom view fitting given coordinates to the window (p0 and p1 do not need to be in any specific order)
Definition: mathplot.cpp:1809
double m_maxY
Definition: mathplot.h:1331
void onMouseLeftRelease(wxMouseEvent &event)
Definition: mathplot.cpp:1592
double m_posY
Definition: mathplot.h:1335
int GetMarginLeft() const
Definition: mathplot.h:1219
bool m_enableMouseNavigation
Definition: mathplot.h:1362
int GetYScreen() const
Definition: mathplot.h:1039
void RecomputeDesiredY(double &min, double &max)
Definition: mathplot.cpp:2178
wxOrientation ViewNeedsRefitting(wxOrientation directions) const
Definition: mathplot.cpp:2189
void OnPaint(wxPaintEvent &event)
Definition: mathplot.cpp:2027
void OnShowPopupMenu(wxMouseEvent &event)
Definition: mathplot.cpp:1898
double m_desiredXmax
Definition: mathplot.h:1349
int m_last_lx
Definition: mathplot.h:1358
void onMouseLeftDown(wxMouseEvent &event)
Definition: mathplot.cpp:1580
void onMouseWheel(wxMouseEvent &event)
Definition: mathplot.cpp:1442
static MouseWheelActionSet defaultMouseWheelActions()
Definition: mathplot.cpp:1927
void SetMargins(int top, int right, int bottom, int left)
Set window margins, creating a blank area where some kinds of layers cannot draw.
Definition: mathplot.cpp:2385
MouseWheelActionSet m_mouseWheelActions
Definition: mathplot.h:1364
int m_marginLeft
Definition: mathplot.h:1356
int m_marginTop
Definition: mathplot.h:1356
double m_minY
Definition: mathplot.h:1330
int GetScrX() const
Get current view's X dimension in device context units.
Definition: mathplot.h:1029
int GetScrY() const
Get current view's Y dimension in device context units.
Definition: mathplot.h:1038
double p2x(wxCoord pixelCoordX)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
Definition: mathplot.h:1082
double p2y(wxCoord pixelCoordY)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
Definition: mathplot.h:1086
wxMemoryDC m_buff_dc
Definition: mathplot.h:1359
double m_maxX
Definition: mathplot.h:1329
void initializeGraphicsContext()
Definition: mathplot.cpp:2516
int m_marginBottom
Definition: mathplot.h:1356
void RecomputeDesiredX(double &min, double &max)
Definition: mathplot.cpp:2167
int m_clickedY
Definition: mathplot.h:1339
wxCoord x2p(double x)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition: mathplot.h:1090
wxColour m_bgColour
Definition: mathplot.h:1324
MouseWheelAction
Enumerates the possible mouse wheel actions that can be performed on the plot.
Definition: mathplot.h:914
double m_leftRightPlotGapFactor
Definition: mathplot.h:1354
double m_posX
Definition: mathplot.h:1334
wxPoint m_mouseLClick
Definition: mathplot.h:1366
mpInfoLayer * IsInsideInfoLayer(wxPoint &point)
Check if a given point is inside the area of a mpInfoLayer and eventually returns its pointer.
Definition: mathplot.cpp:2394
void OnMouseMiddleDown(wxMouseEvent &event)
Definition: mathplot.cpp:1415
std::stack< std::array< double, 4 > > m_redoZoomStack
Definition: mathplot.h:1371
void SetLayerVisible(const wxString &name, bool viewable)
Sets the visibility of a layer by its name.
Definition: mathplot.cpp:2411
int GetXScreen() const
Definition: mathplot.h:1030
int GetMarginTop() const
Definition: mathplot.h:1213
double m_scaleY
Definition: mathplot.h:1333
void DoZoom(const wxPoint &centerPoint, double zoomFactor, wxOrientation directions)
Definition: mathplot.cpp:2084
int m_marginRight
Definition: mathplot.h:1356
mpLayer * GetLayer(int position) const
Definition: mathplot.cpp:2304
void onZoomRedo(wxCommandEvent &event)
Definition: mathplot.cpp:1957
void ZoomIn(const wxPoint &centerPoint=wxDefaultPosition)
Zoom into current view and refresh display.
Definition: mathplot.cpp:1783
wxColour m_fgColour
Definition: mathplot.h:1325
void ZoomOut(const wxPoint &centerPoint=wxDefaultPosition)
Zoom out current view and refresh display.
Definition: mathplot.cpp:1795
virtual bool UpdateBBox()
Recalculate global layer bounding box, and save it in m_minX,...
Definition: mathplot.cpp:2275
double m_minX
Definition: mathplot.h:1328
wxRect m_zoomRect
Definition: mathplot.h:1369
bool DelLayer(mpLayer *layer, bool alsoDeleteObject=false, bool refreshDisplay=true)
Remove a plot layer from the canvas.
Definition: mathplot.cpp:1986
void UpdateAll()
Refresh display.
Definition: mathplot.cpp:2286
wxLayerList m_layers
Definition: mathplot.h:1322
void AdjustLimitedView(wxOrientation directions=wxBOTH)
Limits the zoomed or panned view to the area used by the plots.
Definition: mathplot.cpp:1710
wxCoord y2p(double y)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition: mathplot.h:1094
wxMenu m_popmenu
Definition: mathplot.h:1323
void OnCenter(wxCommandEvent &event)
Definition: mathplot.cpp:1918
virtual bool SetXView(double pos, double desiredMax, double desiredMin)
Applies new X view coordinates depending on the settings.
Definition: mathplot.cpp:1758
bool m_enableLimitedView
Definition: mathplot.h:1363
int m_clickedX
Definition: mathplot.h:1338
void SetScaleX(double scaleX)
Set current view's X scale and refresh display.
Definition: mathplot.cpp:2293
void onMagnify(wxMouseEvent &event)
Definition: mathplot.cpp:1422
wxBitmap * m_buff_bmp
Definition: mathplot.h:1360
double m_topBottomPlotGapFactor
Definition: mathplot.h:1353
void onZoomOut(wxCommandEvent &event)
Definition: mathplot.cpp:1945
wxPoint m_mouseMClick
Definition: mathplot.h:1365
void pushZoomUndo(const std::array< double, 4 > &aZoom)
Definition: mathplot.cpp:1859
double m_desiredYmax
Definition: mathplot.h:1349
std::stack< std::array< double, 4 > > m_undoZoomStack
Definition: mathplot.h:1370
int GetMarginRight() const
Definition: mathplot.h:1215
void GetBoundingBox(double *bbox) const
Returns the bounding box coordinates.
Definition: mathplot.cpp:2325
void SetScr(int scrX, int scrY)
Set current view's dimensions in device context units.
Definition: mathplot.h:1078
int GetMarginBottom() const
Definition: mathplot.h:1217
void PerformMouseWheelAction(wxMouseEvent &event, MouseWheelAction action)
Definition: mathplot.cpp:2217
wxColour m_axColour
Definition: mathplot.h:1326
static double zoomIncrementalFactor
This value sets the zoom steps whenever the user clicks "Zoom in/out" or performs zoom with the mouse...
Definition: mathplot.h:1193
bool m_yLocked
Definition: mathplot.h:1341
double m_desiredXmin
These are updated in Fit, ZoomIn, ZoomOut, ZoomRect, SetXView, SetYView and may be different from the...
Definition: mathplot.h:1349
double m_scaleX
Definition: mathplot.h:1332
void ZoomRedo()
Definition: mathplot.cpp:1883
bool m_enableDoubleBuffer
Definition: mathplot.h:1361
void ZoomUndo()
Definition: mathplot.cpp:1868
void OnFit(wxCommandEvent &event)
Definition: mathplot.cpp:1910
void OnSize(wxSizeEvent &event)
Definition: mathplot.cpp:1963
const mpLayer * GetLayerByName(const wxString &name) const
Definition: mathplot.cpp:2313
unsigned int CountAllLayers() const
Counts the number of plot layers, whether or not they have a bounding box.
Definition: mathplot.h:1150
void SetPos(double posX, double posY)
Set current view's X and Y position and refresh display.
Definition: mathplot.h:1071
void onZoomIn(wxCommandEvent &event)
Definition: mathplot.cpp:1939
double GetScaleY() const
Get current view's Y scale.
Definition: mathplot.h:1009
int m_scrY
Definition: mathplot.h:1337
double GetScaleX() const
Get current view's X scale.
Definition: mathplot.h:1003
bool AddLayer(mpLayer *layer, bool refreshDisplay=true)
Add a plot layer to the canvas.
Definition: mathplot.cpp:1970
void Fit() override
Set view to fit global bounding box of all plot layers and refresh display.
Definition: mathplot.cpp:1614
void onZoomUndo(wxCommandEvent &event)
Definition: mathplot.cpp:1951
double GetPosY() const
Get current view's Y position.
Definition: mathplot.h:1021
void onMouseMove(wxMouseEvent &event)
Definition: mathplot.cpp:1486
double GetPosX() const
Get current view's X position.
Definition: mathplot.h:1015
int m_scrX
Definition: mathplot.h:1336
bool IsLayerVisible(const wxString &name) const
Check whether a layer with given name is visible.
Definition: mathplot.cpp:2421
int m_last_ly
Definition: mathplot.h:1358
#define _(s)
#define mpLEGEND_MARGIN
Definition: mathplot.cpp:50
#define mpLEGEND_LINEWIDTH
Definition: mathplot.cpp:51
#define mpALIGN_BORDER_RIGHT
Aligns Y axis to right border.
Definition: mathplot.h:455
@ mpLAYER_INFO
Definition: mathplot.h:132
@ mpLAYER_UNDEF
Definition: mathplot.h:129
@ mpLAYER_AXIS
Definition: mathplot.h:130
@ mpLAYER_PLOT
Definition: mathplot.h:131
@ mpID_ZOOM_REDO
Definition: mathplot.h:117
@ mpID_FIT
Definition: mathplot.h:115
@ mpID_ZOOM_IN
Definition: mathplot.h:118
@ mpID_CENTER
Definition: mathplot.h:120
@ mpID_ZOOM_UNDO
Definition: mathplot.h:116
@ mpID_ZOOM_OUT
Definition: mathplot.h:119
#define mpALIGN_RIGHT
Aligns label to the right.
Definition: mathplot.h:427
#define mpALIGN_NW
Aligns label to north-west.
Definition: mathplot.h:459
#define mpALIGN_FAR_RIGHT
Aligns label to the right of mpALIGN_RIGHT.
Definition: mathplot.h:441
#define mpALIGN_BORDER_TOP
Aligns X axis to top border.
Definition: mathplot.h:439
#define mpALIGN_CENTER
Aligns label to the center.
Definition: mathplot.h:429
#define mpALIGN_LEFT
Aligns label to the left.
Definition: mathplot.h:431
#define mpALIGN_TOP
Aligns label to the top.
Definition: mathplot.h:433
#define mpALIGN_BORDER_LEFT
Aligns Y axis to left border.
Definition: mathplot.h:453
#define mpALIGNMASK
Definition: mathplot.h:425
#define mpALIGN_SE
Aligns label to south-east.
Definition: mathplot.h:463
#define mpALIGN_NE
Aligns label to north-east.
Definition: mathplot.h:457
#define mpALIGN_BOTTOM
Aligns label to the bottom.
Definition: mathplot.h:435
#define mpALIGN_BORDER_BOTTOM
Aligns X axis to bottom border.
Definition: mathplot.h:437
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
#define MAX_SCALE
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
Contains the set of modified mouse wheel actions that can be performed on the plot.
Definition: mathplot.h:929
MouseWheelAction horizontal
Definition: mathplot.h:939
MouseWheelAction verticalWithShift
Definition: mathplot.h:937
MouseWheelAction verticalWithCtrl
Definition: mathplot.h:936
MouseWheelAction verticalWithAlt
Definition: mathplot.h:938
MouseWheelAction verticalUnmodified
Definition: mathplot.h:935
constexpr int delta
static thread_pool * tp
Definition: thread_pool.cpp:30