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