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