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