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: 2023
9// Copyright: (c) David Schalig, Davide Rondini
10// Copyright (c) 2021-2023 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 wxWindow(),
1371 m_minX( 0.0 ),
1372 m_maxX( 0.0 ),
1373 m_minY( 0.0 ),
1374 m_maxY( 0.0 ),
1375 m_scaleX( 1.0 ),
1376 m_scaleY( 1.0 ),
1377 m_posX( 0.0 ),
1378 m_posY( 0.0 ),
1379 m_scrX( 64 ),
1380 m_scrY( 64 ),
1381 m_clickedX( 0 ),
1382 m_clickedY( 0 ),
1383 m_yLocked( false ),
1384 m_desiredXmin( 0.0 ),
1385 m_desiredXmax( 1.0 ),
1386 m_desiredYmin( 0.0 ),
1387 m_desiredYmax( 1.0 ),
1388 m_marginTop( 0 ),
1389 m_marginRight( 0 ),
1390 m_marginBottom( 0 ),
1391 m_marginLeft( 0 ),
1392 m_last_lx( 0 ),
1393 m_last_ly( 0 ),
1394 m_buff_bmp( nullptr ),
1395 m_enableDoubleBuffer( false ),
1396 m_enableMouseNavigation( true ),
1397 m_enableMouseWheelPan( false ),
1398 m_enableLimitedView( false ),
1399 m_movingInfoLayer( nullptr ),
1400 m_zooming( false )
1401{
1402 if( wxGraphicsContext *ctx = m_buff_dc.GetGraphicsContext() )
1403 {
1404 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_BEST )
1405 || !ctx->SetInterpolationQuality( wxINTERPOLATION_GOOD ) )
1406 {
1407 ctx->SetInterpolationQuality( wxINTERPOLATION_FAST );
1408 }
1409
1410 ctx->SetAntialiasMode( wxANTIALIAS_DEFAULT );
1411 }
1412}
1413
1414mpWindow::mpWindow( wxWindow* parent, wxWindowID id ) :
1415 wxWindow( parent, id, wxDefaultPosition, wxDefaultSize, 0, wxT( "mathplot" ) ),
1416 m_minX( 0.0 ),
1417 m_maxX( 0.0 ),
1418 m_minY( 0.0 ),
1419 m_maxY( 0.0 ),
1420 m_scaleX( 1.0 ),
1421 m_scaleY( 1.0 ),
1422 m_posX( 0.0 ),
1423 m_posY( 0.0 ),
1424 m_scrX( 64 ),
1425 m_scrY( 64 ),
1426 m_clickedX( 0 ),
1427 m_clickedY( 0 ),
1428 m_yLocked( false ),
1429 m_desiredXmin( 0.0 ),
1430 m_desiredXmax( 1.0 ),
1431 m_desiredYmin( 0.0 ),
1432 m_desiredYmax( 1.0 ),
1433 m_marginTop( 0 ),
1434 m_marginRight( 0 ),
1435 m_marginBottom( 0 ),
1436 m_marginLeft( 0 ),
1437 m_last_lx( 0 ),
1438 m_last_ly( 0 ),
1439 m_buff_bmp( nullptr ),
1440 m_enableDoubleBuffer( false ),
1441 m_enableMouseNavigation( true ),
1442 m_enableMouseWheelPan( false ),
1443 m_enableLimitedView( false ),
1444 m_movingInfoLayer( nullptr ),
1445 m_zooming( false )
1446{
1447 m_popmenu.Append( mpID_ZOOM_UNDO, _( "Undo Last Zoom" ), _( "Return zoom to level prior to last zoom action" ) );
1448 m_popmenu.Append( mpID_ZOOM_REDO, _( "Redo Last Zoom" ), _( "Return zoom to level prior to last zoom undo" ) );
1449 m_popmenu.AppendSeparator();
1450 m_popmenu.Append( mpID_ZOOM_IN, _( "Zoom In" ), _( "Zoom in plot view." ) );
1451 m_popmenu.Append( mpID_ZOOM_OUT, _( "Zoom Out" ), _( "Zoom out plot view." ) );
1452 m_popmenu.Append( mpID_CENTER, _( "Center on Cursor" ), _( "Center plot view to this position" ) );
1453 m_popmenu.Append( mpID_FIT, _( "Fit on Screen" ), _( "Set plot view to show all items" ) );
1454
1455 m_layers.clear();
1456 SetBackgroundColour( *wxWHITE );
1457 m_bgColour = *wxWHITE;
1458 m_fgColour = *wxBLACK;
1459
1460 SetSizeHints( 128, 128 );
1461
1462 // J.L.Blanco: Eliminates the "flick" with the double buffer.
1463 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1464
1465 if( wxGraphicsContext* ctx = m_buff_dc.GetGraphicsContext() )
1466 {
1467 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_BEST )
1468 || !ctx->SetInterpolationQuality( wxINTERPOLATION_GOOD ) )
1469 {
1470 ctx->SetInterpolationQuality( wxINTERPOLATION_FAST );
1471 }
1472
1473 ctx->SetAntialiasMode( wxANTIALIAS_DEFAULT );
1474 }
1475
1476 UpdateAll();
1477}
1478
1479
1481{
1482 // Free all the layers:
1483 DelAllLayers( true, false );
1484
1485 delete m_buff_bmp;
1486 m_buff_bmp = nullptr;
1487}
1488
1489
1490// Mouse handler, for detecting when the user drag with the right button or just "clicks" for the menu
1491// JLB
1492void mpWindow::OnMouseMiddleDown( wxMouseEvent& event )
1493{
1494 m_mouseMClick.x = event.GetX();
1495 m_mouseMClick.y = event.GetY();
1496}
1497
1498
1499void mpWindow::onMagnify( wxMouseEvent& event )
1500{
1502 {
1503 event.Skip();
1504 return;
1505 }
1506
1507 float zoom = event.GetMagnification() + 1.0f;
1508 wxPoint pos( event.GetX(), event.GetY() );
1509
1510 if( zoom > 1.0f )
1511 ZoomIn( pos, zoom );
1512 else if( zoom < 1.0f )
1513 ZoomOut( pos, 1.0f / zoom );
1514}
1515
1516
1517// Process mouse wheel events
1518// JLB
1519void mpWindow::onMouseWheel( wxMouseEvent& event )
1520{
1522 {
1523 event.Skip();
1524 return;
1525 }
1526
1527 int change = event.GetWheelRotation();
1528 const int axis = event.GetWheelAxis();
1529 double changeUnitsX = change / m_scaleX;
1530 double changeUnitsY = change / m_scaleY;
1531
1532 if( ( !m_enableMouseWheelPan && ( event.ControlDown() || event.ShiftDown() ) )
1533 || ( m_enableMouseWheelPan && !event.ControlDown() ) )
1534 {
1535 // Scrolling
1537 {
1538 if( axis == wxMOUSE_WHEEL_HORIZONTAL || event.ShiftDown() )
1539 {
1540 SetXView( m_posX + changeUnitsX, m_desiredXmax + changeUnitsX,
1541 m_desiredXmin + changeUnitsX );
1542 }
1543 else if( !m_yLocked )
1544 {
1545 SetYView( m_posY + changeUnitsY, m_desiredYmax + changeUnitsY,
1546 m_desiredYmin + changeUnitsY );
1547 }
1548 }
1549 else
1550 {
1551 if( event.ControlDown() )
1552 {
1553 SetXView( m_posX + changeUnitsX, m_desiredXmax + changeUnitsX,
1554 m_desiredXmin + changeUnitsX );
1555 }
1556 else if( !m_yLocked )
1557 {
1558 SetYView( m_posY + changeUnitsY, m_desiredYmax + changeUnitsY,
1559 m_desiredYmin + changeUnitsY );
1560 }
1561 }
1562
1563 UpdateAll();
1564 }
1565 else
1566 {
1567 // zoom in/out
1568 wxPoint clickPt( event.GetX(), event.GetY() );
1569
1570 if( event.GetWheelRotation() > 0 )
1571 ZoomIn( clickPt );
1572 else
1573 ZoomOut( clickPt );
1574
1575 return;
1576 }
1577}
1578
1579
1580// If the user "drags" with the right button pressed, do "pan"
1581// JLB
1582void mpWindow::onMouseMove( wxMouseEvent& event )
1583{
1585 {
1586 event.Skip();
1587 return;
1588 }
1589
1590 wxCursor cursor = wxCURSOR_MAGNIFIER;
1591
1592 if( event.m_middleDown )
1593 {
1594 cursor = wxCURSOR_ARROW;
1595
1596 // The change:
1597 int Ax = m_mouseMClick.x - event.GetX();
1598 int Ay = m_mouseMClick.y - event.GetY();
1599
1600 // For the next event, use relative to this coordinates.
1601 m_mouseMClick.x = event.GetX();
1602 m_mouseMClick.y = event.GetY();
1603
1604 if( Ax )
1605 {
1606 double Ax_units = Ax / m_scaleX;
1607 SetXView( m_posX + Ax_units, m_desiredXmax + Ax_units, m_desiredXmin + Ax_units );
1608 }
1609
1610 if( Ay )
1611 {
1612 double Ay_units = -Ay / m_scaleY;
1613 SetYView( m_posY + Ay_units, m_desiredYmax + Ay_units, m_desiredYmin + Ay_units );
1614 }
1615
1616 if( Ax || Ay )
1617 UpdateAll();
1618 }
1619 else if( event.m_leftDown )
1620 {
1621 if( m_movingInfoLayer )
1622 {
1623 if( dynamic_cast<mpInfoLegend*>( m_movingInfoLayer ) )
1624 cursor = wxCURSOR_SIZING;
1625 else
1626 cursor = wxCURSOR_SIZEWE;
1627
1628 wxPoint moveVector( event.GetX() - m_mouseLClick.x, event.GetY() - m_mouseLClick.y );
1629 m_movingInfoLayer->Move( moveVector );
1630 m_zooming = false;
1631 }
1632 else
1633 {
1634 cursor = wxCURSOR_MAGNIFIER;
1635
1636 wxClientDC dc( this );
1637 wxPen pen( m_fgColour, 1, wxPENSTYLE_DOT );
1638 dc.SetPen( pen );
1639 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1640 dc.DrawRectangle( m_mouseLClick.x, m_mouseLClick.y,
1641 event.GetX() - m_mouseLClick.x, event.GetY() - m_mouseLClick.y );
1642 m_zooming = true;
1645 m_zoomRect.width = event.GetX() - m_mouseLClick.x;
1646 m_zoomRect.height = event.GetY() - m_mouseLClick.y;
1647 }
1648
1649 UpdateAll();
1650 }
1651 else
1652 {
1653 for( mpLayer* layer : m_layers)
1654 {
1655 if( layer->IsInfo() && layer->IsVisible() )
1656 {
1657 mpInfoLayer* infoLayer = (mpInfoLayer*) layer;
1658
1659 if( infoLayer->Inside( event.GetPosition() ) )
1660 {
1661 if( dynamic_cast<mpInfoLegend*>( infoLayer ) )
1662 cursor = wxCURSOR_SIZING;
1663 else
1664 cursor = wxCURSOR_SIZEWE;
1665 }
1666 }
1667 }
1668 }
1669
1670 SetCursor( cursor );
1671
1672 event.Skip();
1673}
1674
1675
1676void mpWindow::onMouseLeftDown( wxMouseEvent& event )
1677{
1678 m_mouseLClick.x = event.GetX();
1679 m_mouseLClick.y = event.GetY();
1680 m_zooming = true;
1681 wxPoint pointClicked = event.GetPosition();
1682 m_movingInfoLayer = IsInsideInfoLayer( pointClicked );
1683
1684 event.Skip();
1685}
1686
1687
1688void mpWindow::onMouseLeftRelease( wxMouseEvent& event )
1689{
1690 wxPoint release( event.GetX(), event.GetY() );
1691 wxPoint press( m_mouseLClick.x, m_mouseLClick.y );
1692
1693 m_zooming = false;
1694
1695 if( m_movingInfoLayer != nullptr )
1696 {
1698 m_movingInfoLayer = nullptr;
1699 }
1700 else
1701 {
1702 if( release != press )
1703 ZoomRect( press, release );
1704 }
1705
1706 event.Skip();
1707}
1708
1709
1711{
1712 if( UpdateBBox() )
1714}
1715
1716
1717// JL
1718void mpWindow::Fit( double xMin, double xMax, double yMin, double yMax,
1719 const wxCoord* printSizeX, const wxCoord* printSizeY )
1720{
1721 // Save desired borders:
1722 m_desiredXmin = xMin; m_desiredXmax = xMax;
1723 m_desiredYmin = yMin; m_desiredYmax = yMax;
1724
1725 // Give a small margin to plot area
1726 double xExtra = fabs( xMax - xMin ) * 0.00;
1727 double yExtra = fabs( yMax - yMin ) * 0.03;
1728
1729 xMin -= xExtra;
1730 xMax += xExtra;
1731 yMin -= yExtra;
1732 yMax += yExtra;
1733
1734 if( printSizeX != nullptr && printSizeY != nullptr )
1735 {
1736 // Printer:
1737 m_scrX = *printSizeX;
1738 m_scrY = *printSizeY;
1739 }
1740 else
1741 {
1742 // Normal case (screen):
1743 GetClientSize( &m_scrX, &m_scrY );
1744 }
1745
1746 double Ax = xMax - xMin;
1747 double Ay = yMax - yMin;
1748
1749 m_scaleX = (Ax != 0) ? (m_scrX - m_marginLeft - m_marginRight) / Ax : 1;
1750 m_scaleY = (Ay != 0) ? (m_scrY - m_marginTop - m_marginBottom) / Ay : 1;
1751
1752 // Adjusts corner coordinates: This should be simply:
1753 // m_posX = m_minX;
1754 // m_posY = m_maxY;
1755 // But account for centering if we have lock aspect:
1756 m_posX = (xMin + xMax) / 2 - ( (m_scrX - m_marginLeft - m_marginRight) / 2 + m_marginLeft ) /
1757 m_scaleX;
1758 m_posY = (yMin + yMax) / 2 + ( (m_scrY - m_marginTop - m_marginBottom) / 2 + m_marginTop ) /
1759 m_scaleY;
1760
1761 // It is VERY IMPORTANT to DO NOT call Refresh if we are drawing to the printer!!
1762 // Otherwise, the DC dimensions will be those of the window instead of the printer device
1763 if( printSizeX == nullptr || printSizeY == nullptr )
1764 UpdateAll();
1765}
1766
1767
1769{
1770 if( !m_enableLimitedView )
1771 return;
1772
1773 // m_min and m_max are plot limits for curves
1774 // xMin, xMax, yMin, yMax are the full limits (plot limit + margin)
1775 const double xMin = m_minX - m_marginLeft / m_scaleX;
1776 const double xMax = m_maxX + m_marginRight / m_scaleX;
1777 const double yMin = m_minY - m_marginBottom / m_scaleY;
1778 const double yMax = m_maxY + m_marginTop / m_scaleY;
1779
1780 if( m_desiredXmin < xMin )
1781 {
1782 double diff = xMin - m_desiredXmin;
1783 m_posX += diff;
1784 m_desiredXmax += diff;
1785 m_desiredXmin = xMin;
1786 }
1787
1788 if( m_desiredXmax > xMax )
1789 {
1790 double diff = m_desiredXmax - xMax;
1791 m_posX -= diff;
1792 m_desiredXmin -= diff;
1793 m_desiredXmax = xMax;
1794 }
1795
1796 if( m_desiredYmin < yMin )
1797 {
1798 double diff = yMin - m_desiredYmin;
1799 m_posY += diff;
1800 m_desiredYmax += diff;
1801 m_desiredYmin = yMin;
1802 }
1803
1804 if( m_desiredYmax > yMax )
1805 {
1806 double diff = m_desiredYmax - yMax;
1807 m_posY -= diff;
1808 m_desiredYmin -= diff;
1809 m_desiredYmax = yMax;
1810 }
1811}
1812
1813
1814bool mpWindow::SetXView( double pos, double desiredMax, double desiredMin )
1815{
1816 m_posX = pos;
1817 m_desiredXmax = desiredMax;
1818 m_desiredXmin = desiredMin;
1820
1821 return true;
1822}
1823
1824
1825bool mpWindow::SetYView( double pos, double desiredMax, double desiredMin )
1826{
1827 m_posY = pos;
1828 m_desiredYmax = desiredMax;
1829 m_desiredYmin = desiredMin;
1831
1832 return true;
1833}
1834
1835
1836void mpWindow::ZoomIn( const wxPoint& centerPoint )
1837{
1838 ZoomIn( centerPoint, zoomIncrementalFactor );
1839}
1840
1841
1842void mpWindow::ZoomIn( const wxPoint& centerPoint, double zoomFactor )
1843{
1845
1846 wxPoint c( centerPoint );
1847
1848 if( c == wxDefaultPosition )
1849 {
1850 GetClientSize( &m_scrX, &m_scrY );
1851 c.x = (m_scrX - m_marginLeft - m_marginRight) / 2 + m_marginLeft; // c.x = m_scrX/2;
1852 c.y = (m_scrY - m_marginTop - m_marginBottom) / 2 - m_marginTop; // c.y = m_scrY/2;
1853 }
1854 else
1855 {
1856 c.x = std::max( c.x, m_marginLeft );
1857 c.x = std::min( c.x, m_scrX - m_marginRight );
1858 c.y = std::max( c.y, m_marginTop );
1859 c.y = std::min( c.y, m_scrY - m_marginBottom );
1860 }
1861
1862 // Preserve the position of the clicked point:
1863 double prior_layer_x = p2x( c.x );
1864 double prior_layer_y = p2y( c.y );
1865
1866 // Zoom in:
1867 const double MAX_SCALE = 1e6;
1868 double newScaleX = m_scaleX * zoomFactor;
1869 double newScaleY = m_scaleY * zoomFactor;
1870
1871 // Baaaaad things happen when you zoom in too much..
1872 if( newScaleX <= MAX_SCALE && newScaleY <= MAX_SCALE )
1873 {
1874 m_scaleX = newScaleX;
1875
1876 if( !m_yLocked )
1877 m_scaleY = newScaleY;
1878 }
1879 else
1880 {
1881 return;
1882 }
1883
1884 // Adjust the new m_posx/y:
1885 m_posX = prior_layer_x - c.x / m_scaleX;
1886
1887 if( !m_yLocked )
1888 m_posY = prior_layer_y + c.y / m_scaleY;
1889
1895 UpdateAll();
1896}
1897
1898
1899void mpWindow::ZoomOut( const wxPoint& centerPoint )
1900{
1901 ZoomOut( centerPoint, zoomIncrementalFactor );
1902}
1903
1904
1905void mpWindow::ZoomOut( const wxPoint& centerPoint, double zoomFactor )
1906{
1908
1909 wxPoint c( centerPoint );
1910
1911 if( c == wxDefaultPosition )
1912 {
1913 GetClientSize( &m_scrX, &m_scrY );
1915 c.y = (m_scrY - m_marginTop - m_marginBottom) / 2 - m_marginTop;
1916 }
1917
1918 // Preserve the position of the clicked point:
1919 double prior_layer_x = p2x( c.x );
1920 double prior_layer_y = p2y( c.y );
1921
1922 // Zoom out:
1923 m_scaleX = m_scaleX / zoomFactor;
1924
1925 if( !m_yLocked )
1926 m_scaleY = m_scaleY / zoomFactor;
1927
1928 // Adjust the new m_posx/y:
1929 m_posX = prior_layer_x - c.x / m_scaleX;
1930
1931 if( !m_yLocked )
1932 m_posY = prior_layer_y + c.y / m_scaleY;
1933
1938
1940
1943 {
1944 Fit();
1945 }
1946
1947 UpdateAll();
1948}
1949
1950
1951void mpWindow::ZoomRect( wxPoint p0, wxPoint p1 )
1952{
1954
1955 // Compute the 2 corners in graph coordinates:
1956 double p0x = p2x( p0.x );
1957 double p0y = p2y( p0.y );
1958 double p1x = p2x( p1.x );
1959 double p1y = p2y( p1.y );
1960
1961 // Order them:
1962 double zoom_x_min = p0x<p1x ? p0x : p1x;
1963 double zoom_x_max = p0x>p1x ? p0x : p1x;
1964 double zoom_y_min = p0y<p1y ? p0y : p1y;
1965 double zoom_y_max = p0y>p1y ? p0y : p1y;
1966
1967 if( m_yLocked )
1968 {
1969 zoom_y_min = m_desiredYmin;
1970 zoom_y_max = m_desiredYmax;
1971 }
1972
1973 Fit( zoom_x_min, zoom_x_max, zoom_y_min, zoom_y_max );
1975}
1976
1977
1978void mpWindow::pushZoomUndo( const std::array<double, 4>& aZoom )
1979{
1980 m_undoZoomStack.push( aZoom );
1981
1982 while( !m_redoZoomStack.empty() )
1983 m_redoZoomStack.pop();
1984}
1985
1986
1988{
1989 if( m_undoZoomStack.size() )
1990 {
1992
1993 std::array<double, 4> zoom = m_undoZoomStack.top();
1994 m_undoZoomStack.pop();
1995
1996 Fit( zoom[0], zoom[1], zoom[2], zoom[3] );
1998 }
1999}
2000
2001
2003{
2004 if( m_redoZoomStack.size() )
2005 {
2007
2008 std::array<double, 4> zoom = m_redoZoomStack.top();
2009 m_redoZoomStack.pop();
2010
2011 Fit( zoom[0], zoom[1], zoom[2], zoom[3] );
2013 }
2014}
2015
2016
2017void mpWindow::OnShowPopupMenu( wxMouseEvent& event )
2018{
2019 m_clickedX = event.GetX();
2020 m_clickedY = event.GetY();
2021
2022 m_popmenu.Enable( mpID_ZOOM_UNDO, !m_undoZoomStack.empty() );
2023 m_popmenu.Enable( mpID_ZOOM_REDO, !m_redoZoomStack.empty() );
2024
2025 PopupMenu( &m_popmenu, event.GetX(), event.GetY() );
2026}
2027
2028
2029void mpWindow::OnFit( wxCommandEvent& WXUNUSED( event ) )
2030{
2032
2033 Fit();
2034}
2035
2036
2037void mpWindow::OnCenter( wxCommandEvent& WXUNUSED( event ) )
2038{
2039 GetClientSize( &m_scrX, &m_scrY );
2040 int centerX = (m_scrX - m_marginLeft - m_marginRight) / 2;
2041 int centerY = (m_scrY - m_marginTop - m_marginBottom) / 2;
2042 SetPos( p2x( m_clickedX - centerX ), p2y( m_clickedY - centerY ) );
2043}
2044
2045
2046void mpWindow::onZoomIn( wxCommandEvent& WXUNUSED( event ) )
2047{
2048 ZoomIn( wxPoint( m_mouseMClick.x, m_mouseMClick.y ) );
2049}
2050
2051
2052void mpWindow::onZoomOut( wxCommandEvent& WXUNUSED( event ) )
2053{
2054 ZoomOut();
2055}
2056
2057
2058void mpWindow::onZoomUndo( wxCommandEvent& WXUNUSED( event ) )
2059{
2060 ZoomUndo();
2061}
2062
2063
2064void mpWindow::onZoomRedo( wxCommandEvent& WXUNUSED( event ) )
2065{
2066 ZoomRedo();
2067}
2068
2069
2070void mpWindow::OnSize( wxSizeEvent& WXUNUSED( event ) )
2071{
2072 // Try to fit again with the new window size:
2074}
2075
2076
2077bool mpWindow::AddLayer( mpLayer* layer, bool refreshDisplay )
2078{
2079 if( layer )
2080 {
2081 m_layers.push_back( layer );
2082
2083 if( refreshDisplay )
2084 UpdateAll();
2085
2086 return true;
2087 }
2088
2089 return false;
2090}
2091
2092
2093bool mpWindow::DelLayer( mpLayer* layer, bool alsoDeleteObject, bool refreshDisplay )
2094{
2095 wxLayerList::iterator layIt;
2096
2097 for( layIt = m_layers.begin(); layIt != m_layers.end(); layIt++ )
2098 {
2099 if( *layIt == layer )
2100 {
2101 // Also delete the object?
2102 if( alsoDeleteObject )
2103 delete *layIt;
2104
2105 m_layers.erase( layIt ); // this deleted the reference only
2106
2107 if( refreshDisplay )
2108 UpdateAll();
2109
2110 return true;
2111 }
2112 }
2113
2114 return false;
2115}
2116
2117
2118void mpWindow::DelAllLayers( bool alsoDeleteObject, bool refreshDisplay )
2119{
2120 while( m_layers.size()>0 )
2121 {
2122 // Also delete the object?
2123 if( alsoDeleteObject )
2124 delete m_layers[0];
2125
2126 m_layers.erase( m_layers.begin() ); // this deleted the reference only
2127 }
2128
2129 if( refreshDisplay )
2130 UpdateAll();
2131}
2132
2133
2134void mpWindow::OnPaint( wxPaintEvent& WXUNUSED( event ) )
2135{
2136 wxPaintDC paintDC( this );
2137
2138 paintDC.GetSize( &m_scrX, &m_scrY ); // This is the size of the visible area only!
2139
2140 // Selects direct or buffered draw:
2141 wxDC* targetDC = &paintDC;
2142
2143 // J.L.Blanco @ Aug 2007: Added double buffer support
2145 {
2146 if( m_last_lx != m_scrX || m_last_ly != m_scrY )
2147 {
2148 delete m_buff_bmp;
2149 m_buff_bmp = new wxBitmap( m_scrX, m_scrY );
2150 m_buff_dc.SelectObject( *m_buff_bmp );
2151 m_last_lx = m_scrX;
2152 m_last_ly = m_scrY;
2153 }
2154
2155 targetDC = &m_buff_dc;
2156 }
2157
2158 if( wxGraphicsContext* ctx = targetDC->GetGraphicsContext() )
2159 {
2160 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_BEST ) )
2161 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_GOOD ) )
2162 ctx->SetInterpolationQuality( wxINTERPOLATION_FAST );
2163
2164 ctx->SetAntialiasMode( wxANTIALIAS_DEFAULT );
2165 }
2166
2167 // Draw background:
2168 targetDC->SetPen( *wxTRANSPARENT_PEN );
2169 wxBrush brush( GetBackgroundColour() );
2170 targetDC->SetBrush( brush );
2171 targetDC->SetTextForeground( m_fgColour );
2172 targetDC->DrawRectangle( 0, 0, m_scrX, m_scrY );
2173
2174 // Draw all the layers:
2175 for( mpLayer* layer : m_layers )
2176 layer->Plot( *targetDC, *this );
2177
2178 if( m_zooming )
2179 {
2180 wxPen pen( m_fgColour, 1, wxPENSTYLE_DOT );
2181 targetDC->SetPen( pen );
2182 targetDC->SetBrush( *wxTRANSPARENT_BRUSH );
2183 targetDC->DrawRectangle( m_zoomRect );
2184 }
2185
2186 // If doublebuffer, draw now to the window:
2188 paintDC.Blit( 0, 0, m_scrX, m_scrY, targetDC, 0, 0 );
2189}
2190
2191
2193{
2194 m_minX = 0.0;
2195 m_maxX = 1.0;
2196 m_minY = 0.0;
2197 m_maxY = 1.0;
2198
2199 return true;
2200}
2201
2202
2204{
2205 UpdateBBox();
2206 Refresh( false );
2207}
2208
2209
2210void mpWindow::SetScaleX( double scaleX )
2211{
2212 if( scaleX != 0 )
2213 m_scaleX = scaleX;
2214
2215 UpdateAll();
2216}
2217
2218
2219// New methods implemented by Davide Rondini
2220
2221mpLayer* mpWindow::GetLayer( int position ) const
2222{
2223 if( ( position >= (int) m_layers.size() ) || position < 0 )
2224 return nullptr;
2225
2226 return m_layers[position];
2227}
2228
2229
2230const mpLayer* mpWindow::GetLayerByName( const wxString& name ) const
2231{
2232 for( const mpLayer* layer : m_layers )
2233 {
2234 if( !layer->GetName().Cmp( name ) )
2235 return layer;
2236 }
2237
2238 return nullptr; // Not found
2239}
2240
2241
2242void mpWindow::GetBoundingBox( double* bbox ) const
2243{
2244 bbox[0] = m_minX;
2245 bbox[1] = m_maxX;
2246 bbox[2] = m_minY;
2247 bbox[3] = m_maxY;
2248}
2249
2250
2251bool mpWindow::SaveScreenshot( const wxString& filename, wxBitmapType type, wxSize imageSize,
2252 bool fit )
2253{
2254 int sizeX, sizeY;
2255
2256 if( imageSize == wxDefaultSize )
2257 {
2258 sizeX = m_scrX;
2259 sizeY = m_scrY;
2260 }
2261 else
2262 {
2263 sizeX = imageSize.x;
2264 sizeY = imageSize.y;
2265 SetScr( sizeX, sizeY );
2266 }
2267
2268 wxBitmap screenBuffer( sizeX, sizeY );
2269 wxMemoryDC screenDC;
2270 screenDC.SelectObject( screenBuffer );
2271 screenDC.SetPen( *wxWHITE_PEN );
2272 screenDC.SetTextForeground( m_fgColour );
2273 wxBrush brush( GetBackgroundColour() );
2274 screenDC.SetBrush( brush );
2275 screenDC.DrawRectangle( 0, 0, sizeX, sizeY );
2276
2277 if( fit )
2278 Fit( m_minX, m_maxX, m_minY, m_maxY, &sizeX, &sizeY );
2279 else
2281
2282 // Draw all the layers:
2283 for( mpLayer* layer : m_layers )
2284 layer->Plot( screenDC, *this );
2285
2286 if( imageSize != wxDefaultSize )
2287 {
2288 // Restore dimensions
2289 int bk_scrX = m_scrX;
2290 int bk_scrY = m_scrY;
2291 SetScr( bk_scrX, bk_scrY );
2292 Fit( m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax, &bk_scrX, &bk_scrY );
2293 UpdateAll();
2294 }
2295
2296 // Once drawing is complete, actually save screen shot
2297 wxImage screenImage = screenBuffer.ConvertToImage();
2298 return screenImage.SaveFile( filename, type );
2299}
2300
2301
2302void mpWindow::SetMargins( int top, int right, int bottom, int left )
2303{
2304 m_marginTop = top;
2306 m_marginBottom = bottom;
2308}
2309
2310
2312{
2313 for( mpLayer* layer : m_layers )
2314 {
2315 if( layer->IsInfo() )
2316 {
2317 mpInfoLayer* tmpLyr = static_cast<mpInfoLayer*>( layer );
2318
2319 if( tmpLyr->Inside( point ) )
2320 return tmpLyr;
2321 }
2322 }
2323
2324 return nullptr;
2325}
2326
2327
2328void mpWindow::SetLayerVisible( const wxString& name, bool viewable )
2329{
2330 if( mpLayer* lx = GetLayerByName( name ) )
2331 {
2332 lx->SetVisible( viewable );
2333 UpdateAll();
2334 }
2335}
2336
2337
2338bool mpWindow::IsLayerVisible( const wxString& name ) const
2339{
2340 if( const mpLayer* lx = GetLayerByName( name ) )
2341 return lx->IsVisible();
2342
2343 return false;
2344}
2345
2346
2347void mpWindow::SetLayerVisible( const unsigned int position, bool viewable )
2348{
2349 if( mpLayer* lx = GetLayer( position ) )
2350 {
2351 lx->SetVisible( viewable );
2352 UpdateAll();
2353 }
2354}
2355
2356
2357bool mpWindow::IsLayerVisible( unsigned int position ) const
2358{
2359 if( const mpLayer* lx = GetLayer( position ) )
2360 return lx->IsVisible();
2361
2362 return false;
2363}
2364
2365
2366void mpWindow::SetColourTheme( const wxColour& bgColour, const wxColour& drawColour,
2367 const wxColour& axesColour )
2368{
2369 SetBackgroundColour( bgColour );
2370 SetForegroundColour( drawColour );
2371 m_bgColour = bgColour;
2372 m_fgColour = drawColour;
2373 m_axColour = axesColour;
2374
2375 // Cycle between layers to set colours and properties to them
2376 for( mpLayer* layer : m_layers )
2377 {
2378 if( layer->GetLayerType() == mpLAYER_AXIS )
2379 {
2380 wxPen axisPen = layer->GetPen(); // Get the old pen to modify only colour, not style or width
2381 axisPen.SetColour( axesColour );
2382 layer->SetPen( axisPen );
2383 }
2384
2385 if( layer->GetLayerType() == mpLAYER_INFO )
2386 {
2387 wxPen infoPen = layer->GetPen(); // Get the old pen to modify only colour, not style or width
2388 infoPen.SetColour( drawColour );
2389 layer->SetPen( infoPen );
2390 }
2391 }
2392}
2393
2394
2395// -----------------------------------------------------------------------------
2396// mpFXYVector implementation - by Jose Luis Blanco (AGO-2007)
2397// -----------------------------------------------------------------------------
2398
2399IMPLEMENT_DYNAMIC_CLASS( mpFXYVector, mpFXY )
2400
2401// Constructor
2402mpFXYVector::mpFXYVector( const wxString& name, int flags ) :
2403 mpFXY( name, flags )
2404{
2405 m_index = 0;
2406 m_minX = -1;
2407 m_maxX = 1;
2408 m_minY = -1;
2409 m_maxY = 1;
2410 m_type = mpLAYER_PLOT;
2411}
2412
2413
2414double mpScaleX::TransformToPlot( double x ) const
2415{
2416 return (x + m_offset) * m_scale;
2417}
2418
2419
2420double mpScaleX::TransformFromPlot( double xplot ) const
2421{
2422 return xplot / m_scale - m_offset;
2423}
2424
2425
2426double mpScaleY::TransformToPlot( double x ) const
2427{
2428 return (x + m_offset) * m_scale;
2429}
2430
2431
2432double mpScaleY::TransformFromPlot( double xplot ) const
2433{
2434 return xplot / m_scale - m_offset;
2435}
2436
2437
2438double mpScaleXLog::TransformToPlot( double x ) const
2439{
2440 double xlogmin = log10( m_minV );
2441 double xlogmax = log10( m_maxV );
2442
2443 return ( log10( x ) - xlogmin) / (xlogmax - xlogmin);
2444}
2445
2446
2447double mpScaleXLog::TransformFromPlot( double xplot ) const
2448{
2449 double xlogmin = log10( m_minV );
2450 double xlogmax = log10( m_maxV );
2451
2452 return pow( 10.0, xplot * (xlogmax - xlogmin) + xlogmin );
2453}
2454
2455
2457{
2458 m_index = 0;
2459}
2460
2461
2463{
2464 return m_xs.size();
2465}
2466
2467
2468bool mpFXYVector::GetNextXY( double& x, double& y )
2469{
2470 if( m_index >= m_xs.size() )
2471 {
2472 return false;
2473 }
2474 else
2475 {
2476 x = m_xs[m_index];
2477 y = m_ys[m_index++];
2478 return m_index <= m_xs.size();
2479 }
2480}
2481
2482
2484{
2485 m_xs.clear();
2486 m_ys.clear();
2487}
2488
2489
2490void mpFXYVector::SetData( const std::vector<double>& xs, const std::vector<double>& ys )
2491{
2492 // Check if the data vectors are of the same size
2493 if( xs.size() != ys.size() )
2494 return;
2495
2496 // Copy the data:
2497 m_xs = xs;
2498 m_ys = ys;
2499
2500 // Update internal variables for the bounding box.
2501 if( xs.size() > 0 )
2502 {
2503 m_minX = xs[0];
2504 m_maxX = xs[0];
2505 m_minY = ys[0];
2506 m_maxY = ys[0];
2507
2508 for( const double x : xs )
2509 {
2510 if( x < m_minX )
2511 m_minX = x;
2512
2513 if( x > m_maxX )
2514 m_maxX = x;
2515 }
2516
2517 for( const double y : ys )
2518 {
2519 if( y < m_minY )
2520 m_minY = y;
2521
2522 if( y > m_maxY )
2523 m_maxY = y;
2524 }
2525 }
2526 else
2527 {
2528 m_minX = 0;
2529 m_maxX = 0;
2530 m_minY = 0;
2531 m_maxY = 0;
2532 }
2533}
2534
2535
2537{
2538 m_scaleX = scaleX;
2539 m_scaleY = scaleY;
2540
2541 UpdateScales();
2542}
2543
2544
2546{
2547 if( m_scaleX )
2549
2550 if( m_scaleY )
2552}
2553
2554
2555double mpFXY::s2x( double plotCoordX ) const
2556{
2557 return m_scaleX ? m_scaleX->TransformFromPlot( plotCoordX ) : plotCoordX;
2558}
2559
2560
2561double mpFXY::s2y( double plotCoordY ) const
2562{
2563 return m_scaleY ? m_scaleY->TransformFromPlot( plotCoordY ) : plotCoordY;
2564}
2565
2566
2567double mpFXY::x2s( double x ) const
2568{
2569 return m_scaleX ? m_scaleX->TransformToPlot( x ) : x;
2570}
2571
2572
2573double mpFXY::y2s( double y ) const
2574{
2575 return m_scaleY ? m_scaleY->TransformToPlot( y ) : y;
2576}
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:1362
bool GetNextXY(double &x, double &y) override
Get locus value for next N.
Definition: mathplot.cpp:2468
double m_maxY
Definition: mathplot.h:1393
std::vector< double > m_ys
Definition: mathplot.h:1385
double m_minX
Loaded at SetData.
Definition: mathplot.h:1393
std::vector< double > m_xs
The internal copy of the set of data to draw.
Definition: mathplot.h:1385
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:2490
double m_maxX
Definition: mathplot.h:1393
void Clear()
Clears all the data, leaving the layer empty.
Definition: mathplot.cpp:2483
void Rewind() override
Rewind value enumeration with mpFXY::GetNextXY.
Definition: mathplot.cpp:2456
size_t GetCount() const override
Definition: mathplot.cpp:2462
size_t m_index
The internal counter for the "GetNextXY" interface.
Definition: mathplot.h:1389
double m_minY
Definition: mathplot.h:1393
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:2561
virtual void SetScale(mpScaleBase *scaleX, mpScaleBase *scaleY)
Definition: mathplot.cpp:2536
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:2545
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:2573
void UpdateViewBoundary(wxCoord xnew, wxCoord ynew)
Update label positioning data.
Definition: mathplot.cpp:428
double x2s(double x) const
Definition: mathplot.cpp:2567
wxCoord minDrawX
Definition: mathplot.h:583
double s2x(double plotCoordX) const
Definition: mathplot.cpp:2555
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:2447
virtual double TransformToPlot(double x) const override
Definition: mathplot.cpp:2438
void recalculateTicks(wxDC &dc, mpWindow &w) override
Definition: mathplot.cpp:972
virtual double TransformToPlot(double x) const override
Definition: mathplot.cpp:2414
virtual double TransformFromPlot(double xplot) const override
Definition: mathplot.cpp:2420
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:2432
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:2426
Canvas for plotting mpLayer implementations.
Definition: mathplot.h:906
double m_desiredYmin
Definition: mathplot.h:1315
mpInfoLayer * m_movingInfoLayer
Definition: mathplot.h:1328
void DelAllLayers(bool alsoDeleteObject, bool refreshDisplay=true)
Remove all layers from the plot.
Definition: mathplot.cpp:2118
bool m_zooming
Definition: mathplot.h:1329
void SetColourTheme(const wxColour &bgColour, const wxColour &drawColour, const wxColour &axesColour)
Set Color theme.
Definition: mathplot.cpp:2366
virtual bool SetYView(double pos, double desiredMax, double desiredMin)
Applies new Y view coordinates depending on the settings.
Definition: mathplot.cpp:1825
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:1951
double m_maxY
Definition: mathplot.h:1300
void onMouseLeftRelease(wxMouseEvent &event)
Definition: mathplot.cpp:1688
double m_posY
Definition: mathplot.h:1304
int GetMarginLeft() const
Definition: mathplot.h:1184
bool m_enableMouseNavigation
Definition: mathplot.h:1323
int GetYScreen() const
Definition: mathplot.h:1005
void OnPaint(wxPaintEvent &event)
Definition: mathplot.cpp:2134
void OnShowPopupMenu(wxMouseEvent &event)
Definition: mathplot.cpp:2017
double m_desiredXmax
Definition: mathplot.h:1315
int m_last_lx
Definition: mathplot.h:1319
void onMouseLeftDown(wxMouseEvent &event)
Definition: mathplot.cpp:1676
void onMouseWheel(wxMouseEvent &event)
Definition: mathplot.cpp:1519
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:2302
int m_marginLeft
Definition: mathplot.h:1317
bool m_enableMouseWheelPan
Definition: mathplot.h:1324
int m_marginTop
Definition: mathplot.h:1317
double m_minY
Definition: mathplot.h:1299
int GetScrX() const
Get current view's X dimension in device context units.
Definition: mathplot.h:995
void AdjustLimitedView()
Definition: mathplot.cpp:1768
int GetScrY() const
Get current view's Y dimension in device context units.
Definition: mathplot.h:1004
double p2x(wxCoord pixelCoordX)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
Definition: mathplot.h:1048
double p2y(wxCoord pixelCoordY)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
Definition: mathplot.h:1052
wxMemoryDC m_buff_dc
Definition: mathplot.h:1320
double m_maxX
Definition: mathplot.h:1298
int m_marginBottom
Definition: mathplot.h:1317
int m_clickedY
Definition: mathplot.h:1308
wxCoord x2p(double x)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition: mathplot.h:1056
wxColour m_bgColour
Definition: mathplot.h:1293
double m_posX
Definition: mathplot.h:1303
wxPoint m_mouseLClick
Definition: mathplot.h:1327
mpInfoLayer * IsInsideInfoLayer(wxPoint &point)
Check if a given point is inside the area of a mpInfoLayer and eventually returns its pointer.
Definition: mathplot.cpp:2311
void OnMouseMiddleDown(wxMouseEvent &event)
Definition: mathplot.cpp:1492
std::stack< std::array< double, 4 > > m_redoZoomStack
Definition: mathplot.h:1332
void SetLayerVisible(const wxString &name, bool viewable)
Sets the visibility of a layer by its name.
Definition: mathplot.cpp:2328
int GetXScreen() const
Definition: mathplot.h:996
int GetMarginTop() const
Definition: mathplot.h:1178
double m_scaleY
Definition: mathplot.h:1302
int m_marginRight
Definition: mathplot.h:1317
mpLayer * GetLayer(int position) const
Definition: mathplot.cpp:2221
void onZoomRedo(wxCommandEvent &event)
Definition: mathplot.cpp:2064
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:2251
void ZoomIn(const wxPoint &centerPoint=wxDefaultPosition)
Zoom into current view and refresh display.
Definition: mathplot.cpp:1836
wxColour m_fgColour
Definition: mathplot.h:1294
void ZoomOut(const wxPoint &centerPoint=wxDefaultPosition)
Zoom out current view and refresh display.
Definition: mathplot.cpp:1899
virtual bool UpdateBBox()
Recalculate global layer bounding box, and save it in m_minX,...
Definition: mathplot.cpp:2192
double m_minX
Definition: mathplot.h:1297
bool CheckYLimits(double &desiredMax, double &desiredMin) const
Definition: mathplot.h:1268
wxRect m_zoomRect
Definition: mathplot.h:1330
bool DelLayer(mpLayer *layer, bool alsoDeleteObject=false, bool refreshDisplay=true)
Remove a plot layer from the canvas.
Definition: mathplot.cpp:2093
void UpdateAll()
Refresh display.
Definition: mathplot.cpp:2203
wxLayerList m_layers
Definition: mathplot.h:1291
wxCoord y2p(double y)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition: mathplot.h:1060
wxMenu m_popmenu
Definition: mathplot.h:1292
void OnCenter(wxCommandEvent &event)
Definition: mathplot.cpp:2037
virtual bool SetXView(double pos, double desiredMax, double desiredMin)
Applies new X view coordinates depending on the settings.
Definition: mathplot.cpp:1814
bool m_enableLimitedView
Definition: mathplot.h:1325
int m_clickedX
Definition: mathplot.h:1307
void SetScaleX(double scaleX)
Set current view's X scale and refresh display.
Definition: mathplot.cpp:2210
bool CheckXLimits(double &desiredMax, double &desiredMin) const
Definition: mathplot.h:1261
void onMagnify(wxMouseEvent &event)
Definition: mathplot.cpp:1499
wxBitmap * m_buff_bmp
Definition: mathplot.h:1321
void onZoomOut(wxCommandEvent &event)
Definition: mathplot.cpp:2052
wxPoint m_mouseMClick
Definition: mathplot.h:1326
void pushZoomUndo(const std::array< double, 4 > &aZoom)
Definition: mathplot.cpp:1978
double m_desiredYmax
Definition: mathplot.h:1315
std::stack< std::array< double, 4 > > m_undoZoomStack
Definition: mathplot.h:1331
int GetMarginRight() const
Definition: mathplot.h:1180
void GetBoundingBox(double *bbox) const
Returns the bounding box coordinates.
Definition: mathplot.cpp:2242
void SetScr(int scrX, int scrY)
Set current view's dimensions in device context units.
Definition: mathplot.h:1044
int GetMarginBottom() const
Definition: mathplot.h:1182
wxColour m_axColour
Definition: mathplot.h:1295
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:1158
bool m_yLocked
Definition: mathplot.h:1310
double m_desiredXmin
These are updated in Fit() only, and may be different from the real borders (layer coordinates) only ...
Definition: mathplot.h:1315
double m_scaleX
Definition: mathplot.h:1301
void ZoomRedo()
Definition: mathplot.cpp:2002
bool m_enableDoubleBuffer
Definition: mathplot.h:1322
void ZoomUndo()
Definition: mathplot.cpp:1987
void OnFit(wxCommandEvent &event)
Definition: mathplot.cpp:2029
void OnSize(wxSizeEvent &event)
Definition: mathplot.cpp:2070
const mpLayer * GetLayerByName(const wxString &name) const
Definition: mathplot.cpp:2230
unsigned int CountAllLayers() const
Counts the number of plot layers, whether or not they have a bounding box.
Definition: mathplot.h:1115
void SetPos(double posX, double posY)
Set current view's X and Y position and refresh display.
Definition: mathplot.h:1037
void onZoomIn(wxCommandEvent &event)
Definition: mathplot.cpp:2046
double GetScaleY() const
Get current view's Y scale.
Definition: mathplot.h:975
int m_scrY
Definition: mathplot.h:1306
double GetScaleX() const
Get current view's X scale.
Definition: mathplot.h:969
bool AddLayer(mpLayer *layer, bool refreshDisplay=true)
Add a plot layer to the canvas.
Definition: mathplot.cpp:2077
void Fit() override
Set view to fit global bounding box of all plot layers and refresh display.
Definition: mathplot.cpp:1710
void onZoomUndo(wxCommandEvent &event)
Definition: mathplot.cpp:2058
double GetPosY() const
Get current view's Y position.
Definition: mathplot.h:987
void onMouseMove(wxMouseEvent &event)
Definition: mathplot.cpp:1582
double GetPosX() const
Get current view's X position.
Definition: mathplot.h:981
int m_scrX
Definition: mathplot.h:1305
bool IsLayerVisible(const wxString &name) const
Check whether a layer with given name is visible.
Definition: mathplot.cpp:2338
int m_last_ly
Definition: mathplot.h:1319
#define _(s)
#define mpLEGEND_MARGIN
Definition: mathplot.cpp:50
#define mpLEGEND_LINEWIDTH
Definition: mathplot.cpp:51
#define mpALIGN_BORDER_RIGHT
Aligns Y axis to right border.
Definition: mathplot.h:455
@ mpLAYER_INFO
Definition: mathplot.h:132
@ mpLAYER_UNDEF
Definition: mathplot.h:129
@ mpLAYER_AXIS
Definition: mathplot.h:130
@ mpLAYER_PLOT
Definition: mathplot.h:131
#define mpALIGN_RIGHT
Aligns label to the right.
Definition: mathplot.h:427
#define mpALIGN_NW
Aligns label to north-west.
Definition: mathplot.h:459
#define mpALIGN_FAR_RIGHT
Aligns label to the right of mpALIGN_RIGHT.
Definition: mathplot.h:441
#define mpALIGN_BORDER_TOP
Aligns X axis to top border.
Definition: mathplot.h:439
#define mpALIGN_CENTER
Aligns label to the center.
Definition: mathplot.h:429
#define mpALIGN_LEFT
Aligns label to the left.
Definition: mathplot.h:431
#define mpALIGN_TOP
Aligns label to the top.
Definition: mathplot.h:433
#define mpALIGN_BORDER_LEFT
Aligns Y axis to left border.
Definition: mathplot.h:453
@ 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 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
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:426
#define MAX_SCALE
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
constexpr int delta
static thread_pool * tp
Definition: thread_pool.cpp:30