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