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 if( !std::isfinite( visibleDecades ) )
1011 break;
1012 }
1013
1014 for( d = minDecade; d<=maxDecade; d *= step )
1015 {
1016 m_tickLabels.emplace_back( d );
1017
1018 for( double dd = d; dd < d * step; dd += d )
1019 {
1020 if( visibleDecades < 2 )
1021 m_tickLabels.emplace_back( dd );
1022
1023 m_tickValues.push_back( dd );
1024 }
1025 }
1026
1027 updateTickLabels( dc, w );
1028}
1029
1030
1031IMPLEMENT_ABSTRACT_CLASS( mpScaleXBase, mpLayer )
1032IMPLEMENT_DYNAMIC_CLASS( mpScaleX, mpScaleXBase )
1033IMPLEMENT_DYNAMIC_CLASS( mpScaleXLog, mpScaleXBase )
1034
1035
1036mpScaleXBase::mpScaleXBase( const wxString& name, int flags, bool ticks, unsigned int type )
1037{
1038 SetName( name );
1039 SetFont( (wxFont&) *wxSMALL_FONT );
1040 SetPen( (wxPen&) *wxGREY_PEN );
1041 m_flags = flags;
1042 m_ticks = ticks;
1043 m_type = mpLAYER_AXIS;
1044}
1045
1046
1047mpScaleX::mpScaleX( const wxString& name, int flags, bool ticks, unsigned int type ) :
1048 mpScaleXBase( name, flags, ticks, type )
1049{
1050}
1051
1052
1053mpScaleXLog::mpScaleXLog( const wxString& name, int flags, bool ticks, unsigned int type ) :
1054 mpScaleXBase( name, flags, ticks, type )
1055{
1056}
1057
1058
1059void mpScaleXBase::Plot( wxDC& dc, mpWindow& w )
1060{
1061 int tx, ty;
1062
1063 m_offset = -m_minV;
1064 m_scale = 1.0 / ( m_maxV - m_minV );
1065
1066 recalculateTicks( dc, w );
1067
1068 if( m_visible )
1069 {
1070 dc.SetPen( m_pen );
1071 dc.SetFont( m_font );
1072 int orgy = 0;
1073
1074 const int extend = w.GetScrX();
1075
1076 if( m_flags == mpALIGN_CENTER )
1077 orgy = w.y2p( 0 );
1078
1079 if( m_flags == mpALIGN_TOP )
1080 orgy = w.GetMarginTop();
1081
1082 if( m_flags == mpALIGN_BOTTOM )
1083 orgy = w.GetScrY() - w.GetMarginBottom();
1084
1086 orgy = w.GetScrY() - 1;
1087
1089 orgy = 1;
1090
1091 wxCoord startPx = w.GetMarginLeft();
1092 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
1093 wxCoord minYpx = w.GetMarginTop();
1094 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
1095
1096 // int tmp=-65535;
1097
1098 // Control labels height to decide where to put axis name (below labels or on top of axis).
1099 int labelH = m_maxLabelHeight;
1100
1101 // int maxExtent = tc.MaxLabelWidth();
1102 for( double tp : m_tickValues )
1103 {
1104 double px = TransformToPlot( tp );
1105 const int p = (int) ( ( px - w.GetPosX() ) * w.GetScaleX() );
1106
1107 if( p >= startPx && p <= endPx )
1108 {
1109 if( m_ticks ) // draw axis ticks
1110 {
1112 dc.DrawLine( p, orgy, p, orgy - 4 );
1113 else
1114 dc.DrawLine( p, orgy, p, orgy + 4 );
1115 }
1116 else // draw grid dotted lines
1117 {
1118 m_pen.SetStyle( wxPENSTYLE_DOT );
1119 dc.SetPen( m_pen );
1120
1121 if( m_flags == mpALIGN_BOTTOM )
1122 {
1123 m_pen.SetStyle( wxPENSTYLE_DOT );
1124 dc.SetPen( m_pen );
1125 dc.DrawLine( p, orgy + 4, p, minYpx );
1126 m_pen.SetStyle( wxPENSTYLE_SOLID );
1127 dc.SetPen( m_pen );
1128 dc.DrawLine( p, orgy + 4, p, orgy - 4 );
1129 }
1130 else
1131 {
1132 if( m_flags == mpALIGN_TOP )
1133 dc.DrawLine( p, orgy - 4, p, maxYpx );
1134 else
1135 dc.DrawLine( p, minYpx, p, maxYpx );
1136 }
1137
1138 m_pen.SetStyle( wxPENSTYLE_SOLID );
1139 dc.SetPen( m_pen );
1140 }
1141 }
1142 }
1143
1144 m_pen.SetStyle( wxPENSTYLE_SOLID );
1145 dc.SetPen( m_pen );
1146 dc.DrawLine( startPx, minYpx, endPx, minYpx );
1147 dc.DrawLine( startPx, maxYpx, endPx, maxYpx );
1148
1149 // Actually draw labels, taking care of not overlapping them, and distributing them
1150 // regularly
1151 for( const TICK_LABEL& tickLabel : m_tickLabels )
1152 {
1153 if( !tickLabel.visible )
1154 continue;
1155
1156 double px = TransformToPlot( tickLabel.pos );
1157 const int p = (int) ( ( px - w.GetPosX() ) * w.GetScaleX() );
1158
1159 if( ( p >= startPx ) && ( p <= endPx ) )
1160 {
1161 // Write ticks labels in s string
1162 wxString s = tickLabel.label;
1163
1164 dc.GetTextExtent( s, &tx, &ty );
1165
1166 if( ( m_flags == mpALIGN_BORDER_BOTTOM ) || ( m_flags == mpALIGN_TOP ) )
1167 dc.DrawText( s, p - tx / 2, orgy - 4 - ty );
1168 else
1169 dc.DrawText( s, p - tx / 2, orgy + 4 );
1170 }
1171 }
1172
1173 // Draw axis name
1174 dc.GetTextExtent( m_name, &tx, &ty );
1175
1176 switch( m_nameFlags )
1177 {
1179 dc.DrawText( m_name, extend - tx - 4, orgy - 8 - ty - labelH );
1180 break;
1181
1182 case mpALIGN_BOTTOM:
1183 dc.DrawText( m_name, ( endPx + startPx ) / 2 - tx / 2, orgy + 6 + labelH );
1184 break;
1185
1186 case mpALIGN_CENTER:
1187 dc.DrawText( m_name, extend - tx - 4, orgy - 4 - ty );
1188 break;
1189
1190 case mpALIGN_TOP:
1191 if( w.GetMarginTop() > (ty + labelH + 8) )
1192 dc.DrawText( m_name, ( endPx - startPx - tx ) >> 1, orgy - 6 - ty - labelH );
1193 else
1194 dc.DrawText( m_name, extend - tx - 4, orgy + 4 );
1195
1196 break;
1197
1198 case mpALIGN_BORDER_TOP:
1199 dc.DrawText( m_name, extend - tx - 4, orgy + 6 + labelH );
1200 break;
1201
1202 default:
1203 break;
1204 }
1205 }
1206}
1207
1208
1209IMPLEMENT_DYNAMIC_CLASS( mpScaleY, mpLayer )
1210
1211
1212mpScaleY::mpScaleY( const wxString& name, int flags, bool ticks )
1213{
1214 SetName( name );
1215 SetFont( (wxFont&) *wxSMALL_FONT );
1216 SetPen( (wxPen&) *wxGREY_PEN );
1217 m_flags = flags;
1218 m_ticks = ticks;
1219 m_type = mpLAYER_AXIS;
1220 m_masterScale = nullptr;
1221 m_nameFlags = mpALIGN_BORDER_LEFT;
1222}
1223
1224
1225void mpScaleY::Plot( wxDC& dc, mpWindow& w )
1226{
1227 m_offset = -m_minV;
1228 m_scale = 1.0 / ( m_maxV - m_minV );
1229
1230 recalculateTicks( dc, w );
1231
1232 if( m_visible )
1233 {
1234 dc.SetPen( m_pen );
1235 dc.SetFont( m_font );
1236
1237 int orgx = 0;
1238
1239 if( m_flags == mpALIGN_CENTER )
1240 orgx = w.x2p( 0 );
1241
1242 if( m_flags == mpALIGN_LEFT )
1243 orgx = w.GetMarginLeft();
1244
1245 if( m_flags == mpALIGN_RIGHT )
1246 orgx = w.GetScrX() - w.GetMarginRight();
1247
1248 if( m_flags == mpALIGN_FAR_RIGHT )
1249 orgx = w.GetScrX() - ( w.GetMarginRight() / 2 );
1250
1252 orgx = w.GetScrX() - 1;
1253
1255 orgx = 1;
1256
1257 wxCoord endPx = w.GetScrX() - w.GetMarginRight();
1258 wxCoord minYpx = w.GetMarginTop();
1259 wxCoord maxYpx = w.GetScrY() - w.GetMarginBottom();
1260
1261 // Draw line
1262 dc.DrawLine( orgx, minYpx, orgx, maxYpx );
1263
1264 wxCoord tx, ty;
1265 wxString s;
1266 wxString fmt;
1267
1268 int labelW = 0;
1269
1270 // Before staring cycle, calculate label height
1271 int labelHeight = 0;
1272 s.Printf( fmt, 0 );
1273 dc.GetTextExtent( s, &tx, &labelHeight );
1274
1275 for( double tp : m_tickValues )
1276 {
1277 double py = TransformToPlot( tp );
1278 const int p = (int) ( ( w.GetPosY() - py ) * w.GetScaleY() );
1279
1280 if( p >= minYpx && p <= maxYpx )
1281 {
1282 if( m_ticks ) // Draw axis ticks
1283 {
1285 dc.DrawLine( orgx, p, orgx + 4, p );
1286 else
1287 dc.DrawLine( orgx - 4, p, orgx, p );
1288 }
1289 else
1290 {
1291 dc.DrawLine( orgx - 4, p, orgx + 4, p );
1292
1293 m_pen.SetStyle( wxPENSTYLE_DOT );
1294 dc.SetPen( m_pen );
1295
1296 dc.DrawLine( orgx - 4, p, endPx, p );
1297
1298 m_pen.SetStyle( wxPENSTYLE_SOLID );
1299 dc.SetPen( m_pen );
1300 }
1301
1302 // Print ticks labels
1303 }
1304 }
1305
1306 for( const TICK_LABEL& tickLabel : m_tickLabels )
1307 {
1308 double py = TransformToPlot( tickLabel.pos );
1309 const int p = (int) ( ( w.GetPosY() - py ) * w.GetScaleY() );
1310
1311 if( !tickLabel.visible )
1312 continue;
1313
1314 if( p >= minYpx && p <= maxYpx )
1315 {
1316 s = tickLabel.label;
1317 dc.GetTextExtent( s, &tx, &ty );
1318
1321 dc.DrawText( s, orgx + 4, p - ty / 2 );
1322 else
1323 dc.DrawText( s, orgx - 4 - tx, p - ty / 2 ); // ( s, orgx+4, p-ty/2);
1324 }
1325 }
1326
1327 // Draw axis name
1328 dc.GetTextExtent( m_name, &tx, &ty );
1329
1330 switch( m_nameFlags )
1331 {
1333 dc.DrawText( m_name, labelW + 8, 4 );
1334 break;
1335
1336 case mpALIGN_LEFT:
1337 dc.DrawText( m_name, orgx - ( tx / 2 ), minYpx - ty - 4 );
1338 break;
1339
1340 case mpALIGN_CENTER:
1341 dc.DrawText( m_name, orgx + 4, 4 );
1342 break;
1343
1344 case mpALIGN_RIGHT:
1345 case mpALIGN_FAR_RIGHT:
1346 dc.DrawText( m_name, orgx - ( tx / 2 ), minYpx - ty - 4 );
1347 break;
1348
1350 dc.DrawText( m_name, orgx - 6 - tx - labelW, 4 );
1351 break;
1352
1353 default:
1354 break;
1355 }
1356 }
1357}
1358
1359
1360// -----------------------------------------------------------------------------
1361// mpWindow
1362// -----------------------------------------------------------------------------
1363
1364IMPLEMENT_DYNAMIC_CLASS( mpWindow, wxWindow )
1365
1366BEGIN_EVENT_TABLE( mpWindow, wxWindow )
1367EVT_PAINT( mpWindow::OnPaint )
1368EVT_SIZE( mpWindow::OnSize )
1369
1370EVT_MIDDLE_DOWN( mpWindow::OnMouseMiddleDown ) // JLB
1371EVT_RIGHT_UP( mpWindow::OnShowPopupMenu )
1372EVT_MOUSEWHEEL( mpWindow::onMouseWheel ) // JLB
1373EVT_MAGNIFY( mpWindow::onMagnify )
1374EVT_MOTION( mpWindow::onMouseMove ) // JLB
1375EVT_LEFT_DOWN( mpWindow::onMouseLeftDown )
1376EVT_LEFT_UP( mpWindow::onMouseLeftRelease )
1377
1379EVT_MENU( mpID_FIT, mpWindow::OnFit )
1384END_EVENT_TABLE()
1385
1386
1388{
1389 initializeGraphicsContext();
1390}
1391
1392
1393mpWindow::mpWindow( wxWindow* parent, wxWindowID id ) :
1394 mpWindow( DelegatingContructorTag(), parent, id, wxDefaultPosition, wxDefaultSize, 0,
1395 wxT( "mathplot" ) )
1396{
1397 m_popmenu.Append( mpID_ZOOM_UNDO, _( "Undo Last Zoom" ),
1398 _( "Return zoom to level prior to last zoom action" ) );
1399 m_popmenu.Append( mpID_ZOOM_REDO, _( "Redo Last Zoom" ),
1400 _( "Return zoom to level prior to last zoom undo" ) );
1401 m_popmenu.AppendSeparator();
1402 m_popmenu.Append( mpID_ZOOM_IN, _( "Zoom In" ), _( "Zoom in plot view." ) );
1403 m_popmenu.Append( mpID_ZOOM_OUT, _( "Zoom Out" ), _( "Zoom out plot view." ) );
1404 m_popmenu.Append( mpID_CENTER, _( "Center on Cursor" ),
1405 _( "Center plot view to this position" ) );
1406 m_popmenu.Append( mpID_FIT, _( "Fit on Screen" ), _( "Set plot view to show all items" ) );
1407
1408 m_layers.clear();
1409 SetBackgroundColour( *wxWHITE );
1410 m_bgColour = *wxWHITE;
1411 m_fgColour = *wxBLACK;
1412
1413 SetSizeHints( 128, 128 );
1414
1415 // J.L.Blanco: Eliminates the "flick" with the double buffer.
1416 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1417
1419 UpdateAll();
1420}
1421
1422
1424{
1425 // Free all the layers:
1426 DelAllLayers( true, false );
1427
1428 delete m_buff_bmp;
1429 m_buff_bmp = nullptr;
1430}
1431
1432
1433// Mouse handler, for detecting when the user drag with the right button or just "clicks" for
1434// the menu.
1435// JLB
1436void mpWindow::OnMouseMiddleDown( wxMouseEvent& event )
1437{
1438 m_mouseMClick.x = event.GetX();
1439 m_mouseMClick.y = event.GetY();
1440}
1441
1442
1443void mpWindow::onMagnify( wxMouseEvent& event )
1444{
1446 {
1447 event.Skip();
1448 return;
1449 }
1450
1451 float zoom = event.GetMagnification() + 1.0f;
1452 wxPoint pos( event.GetX(), event.GetY() );
1453
1454 if( zoom > 1.0f )
1455 ZoomIn( pos, zoom );
1456 else if( zoom < 1.0f )
1457 ZoomOut( pos, 1.0f / zoom );
1458}
1459
1460
1461// Process mouse wheel events
1462// JLB
1463void mpWindow::onMouseWheel( wxMouseEvent& event )
1464{
1466 {
1467 event.Skip();
1468 return;
1469 }
1470
1471 const wxMouseWheelAxis axis = event.GetWheelAxis();
1472 const int modifiers = event.GetModifiers();
1474
1475 if( axis == wxMOUSE_WHEEL_HORIZONTAL )
1476 {
1478 }
1479 else if( modifiers == wxMOD_NONE )
1480 {
1482 }
1483 else if( modifiers == wxMOD_CONTROL )
1484 {
1486 }
1487 else if( modifiers == wxMOD_SHIFT )
1488 {
1490 }
1491 else if( modifiers == wxMOD_ALT )
1492 {
1494 }
1495 else
1496 {
1497 event.Skip();
1498 return;
1499 }
1500
1501 PerformMouseWheelAction( event, action );
1502}
1503
1504
1505// If the user "drags" with the right button pressed, do "pan"
1506// JLB
1507void mpWindow::onMouseMove( wxMouseEvent& event )
1508{
1510 {
1511 event.Skip();
1512 return;
1513 }
1514
1515 wxCursor cursor = wxCURSOR_MAGNIFIER;
1516
1517 if( event.m_middleDown )
1518 {
1519 cursor = wxCURSOR_ARROW;
1520
1521 // The change:
1522 int Ax = m_mouseMClick.x - event.GetX();
1523 int Ay = m_mouseMClick.y - event.GetY();
1524
1525 // For the next event, use relative to this coordinates.
1526 m_mouseMClick.x = event.GetX();
1527 m_mouseMClick.y = event.GetY();
1528
1529 if( Ax )
1530 {
1531 double Ax_units = Ax / m_scaleX;
1532 SetXView( m_posX + Ax_units, m_desiredXmax + Ax_units, m_desiredXmin + Ax_units );
1533 }
1534
1535 if( Ay )
1536 {
1537 double Ay_units = -Ay / m_scaleY;
1538 SetYView( m_posY + Ay_units, m_desiredYmax + Ay_units, m_desiredYmin + Ay_units );
1539 }
1540
1541 if( Ax || Ay )
1542 UpdateAll();
1543 }
1544 else if( event.m_leftDown )
1545 {
1546 if( m_movingInfoLayer )
1547 {
1548 if( dynamic_cast<mpInfoLegend*>( m_movingInfoLayer ) )
1549 cursor = wxCURSOR_SIZING;
1550 else
1551 cursor = wxCURSOR_SIZEWE;
1552
1553 wxPoint moveVector( event.GetX() - m_mouseLClick.x, event.GetY() - m_mouseLClick.y );
1554 m_movingInfoLayer->Move( moveVector );
1555 m_zooming = false;
1556 }
1557 else
1558 {
1559 cursor = wxCURSOR_MAGNIFIER;
1560
1561 wxClientDC dc( this );
1562 wxPen pen( m_fgColour, 1, wxPENSTYLE_DOT );
1563 dc.SetPen( pen );
1564 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1565 dc.DrawRectangle( m_mouseLClick.x, m_mouseLClick.y,
1566 event.GetX() - m_mouseLClick.x, event.GetY() - m_mouseLClick.y );
1567 m_zooming = true;
1570 m_zoomRect.width = event.GetX() - m_mouseLClick.x;
1571 m_zoomRect.height = event.GetY() - m_mouseLClick.y;
1572 }
1573
1574 UpdateAll();
1575 }
1576 else
1577 {
1578 for( mpLayer* layer : m_layers)
1579 {
1580 if( layer->IsInfo() && layer->IsVisible() )
1581 {
1582 mpInfoLayer* infoLayer = (mpInfoLayer*) layer;
1583
1584 if( infoLayer->Inside( event.GetPosition() ) )
1585 {
1586 if( dynamic_cast<mpInfoLegend*>( infoLayer ) )
1587 cursor = wxCURSOR_SIZING;
1588 else
1589 cursor = wxCURSOR_SIZEWE;
1590 }
1591 }
1592 }
1593 }
1594
1595 SetCursor( cursor );
1596
1597 event.Skip();
1598}
1599
1600
1601void mpWindow::onMouseLeftDown( wxMouseEvent& event )
1602{
1603 m_mouseLClick.x = event.GetX();
1604 m_mouseLClick.y = event.GetY();
1605 m_zooming = true;
1606 wxPoint pointClicked = event.GetPosition();
1607 m_movingInfoLayer = IsInsideInfoLayer( pointClicked );
1608
1609 event.Skip();
1610}
1611
1612
1613void mpWindow::onMouseLeftRelease( wxMouseEvent& event )
1614{
1615 wxPoint release( event.GetX(), event.GetY() );
1616 wxPoint press( m_mouseLClick.x, m_mouseLClick.y );
1617
1618 m_zooming = false;
1619
1620 if( m_movingInfoLayer != nullptr )
1621 {
1623 m_movingInfoLayer = nullptr;
1624 }
1625 else
1626 {
1627 if( release != press )
1628 ZoomRect( press, release );
1629 }
1630
1631 event.Skip();
1632}
1633
1634
1636{
1637 if( UpdateBBox() )
1639}
1640
1641
1642// JL
1643void mpWindow::Fit( double xMin, double xMax, double yMin, double yMax, const wxCoord* printSizeX,
1644 const wxCoord* printSizeY, wxOrientation directions )
1645{
1646 const bool isPrinting = printSizeX != nullptr && printSizeY != nullptr;
1647
1648 // Save desired borders:
1649 double newDesiredXmin = xMin;
1650 double newDesiredXmax = xMax;
1651 double newDesiredYmin = yMin;
1652 double newDesiredYmax = yMax;
1653
1654 // Provide a gap between the extrema of the curve and the top/bottom edges of the
1655 // plot area. Not to be confused with the left/right/top/bottom margins outside the plot area.
1656 const double xGap = fabs( xMax - xMin ) * m_leftRightPlotGapFactor;
1657 const double yGap = fabs( yMax - yMin ) * m_topBottomPlotGapFactor;
1658 xMin -= xGap;
1659 xMax += xGap;
1660 yMin -= yGap;
1661 yMax += yGap;
1662
1663 int newScrX = m_scrX;
1664 int newScrY = m_scrY;
1665
1666 if( isPrinting )
1667 {
1668 // Printer:
1669 newScrX = *printSizeX;
1670 newScrY = *printSizeY;
1671 }
1672 else
1673 {
1674 // Normal case (screen):
1675 GetClientSize( &newScrX, &newScrY );
1676 }
1677
1678 // Compute the width/height in pixels for the plot area.
1679 const int plotScreenWidth = newScrX - m_marginLeft - m_marginRight;
1680 const int plotScreenHeight = newScrY - m_marginTop - m_marginBottom;
1681
1682 // Adjust scale so that desired X/Y span plus extra gap fits in the plot area
1683 double desiredSpanX = xMax - xMin;
1684 double desiredSpanY = yMax - yMin;
1685 double newScaleX = ( desiredSpanX != 0 ) ? double( plotScreenWidth ) / desiredSpanX : 1;
1686 double newScaleY = ( desiredSpanY != 0 ) ? double( plotScreenHeight ) / desiredSpanY : 1;
1687
1688 // Adjust corner coordinates:
1689 // Upstream's aspect lock code has been removed, so no need to account for centering.
1690 double newPosX = xMin - ( m_marginLeft / newScaleX );
1691 double newPosY = yMax + ( m_marginTop / newScaleY );
1692
1693 // Commit above changes to member variables only if enabled for their respective dimension.
1694 if( ( ( directions & wxHORIZONTAL ) != 0 ) || isPrinting )
1695 {
1696 // Don't commit the passed desired bounds when printing
1697 if( !isPrinting )
1698 {
1699 m_desiredXmin = newDesiredXmin;
1700 m_desiredXmax = newDesiredXmax;
1701 }
1702
1703 m_scrX = newScrX;
1704 m_scaleX = newScaleX;
1705 m_posX = newPosX;
1706 }
1707
1708 if( ( ( directions & wxVERTICAL ) != 0 ) || isPrinting )
1709 {
1710 // Don't commit the passed desired bounds when printing
1711 if( !isPrinting )
1712 {
1713 m_desiredYmin = newDesiredYmin;
1714 m_desiredYmax = newDesiredYmax;
1715 }
1716
1717 m_scrY = newScrY;
1718 m_scaleY = newScaleY;
1719 m_posY = newPosY;
1720 }
1721
1722 // It is VERY IMPORTANT to NOT call Refresh if we are drawing to the printer!!
1723 // Otherwise, the DC dimensions will be those of the window instead of the printer device
1724 // The caller wanting to print should perform another Fit() afterwards to restore this
1725 // object's state.
1726 if( !isPrinting )
1727 UpdateAll();
1728}
1729
1730
1731void mpWindow::AdjustLimitedView( wxOrientation directions )
1732{
1733 if( !m_enableLimitedView )
1734 return;
1735
1736 // The m_desired* members are expressed in plot coordinates.
1737 // They should be clamped against their respective m_minX, m_maxX, m_minY, m_maxY limits.
1738
1739 if( ( directions & wxHORIZONTAL ) != 0 )
1740 {
1741 if( m_desiredXmin < m_minX )
1742 {
1743 double diff = m_minX - m_desiredXmin;
1744 m_posX += diff;
1745 m_desiredXmax += diff;
1747 }
1748
1749 if( m_desiredXmax > m_maxX )
1750 {
1751 double diff = m_desiredXmax - m_maxX;
1752 m_posX -= diff;
1753 m_desiredXmin -= diff;
1755 }
1756 }
1757
1758 if( ( directions & wxVERTICAL ) != 0 )
1759 {
1760 if( m_desiredYmin < m_minY )
1761 {
1762 double diff = m_minY - m_desiredYmin;
1763 m_posY += diff;
1764 m_desiredYmax += diff;
1766 }
1767
1768 if( m_desiredYmax > m_maxY )
1769 {
1770 double diff = m_desiredYmax - m_maxY;
1771 m_posY -= diff;
1772 m_desiredYmin -= diff;
1774 }
1775 }
1776}
1777
1778
1779bool mpWindow::SetXView( double pos, double desiredMax, double desiredMin )
1780{
1781 // TODO (ecorm): Investigate X scale flickering when panning at minimum zoom level
1782 // Possible cause: When AdjustLimitedView subtracts the out-of-bound delta, it does not
1783 // revert back to the exact same original coordinates due to floating point rounding errors.
1784 m_posX = pos;
1785 m_desiredXmax = desiredMax;
1786 m_desiredXmin = desiredMin;
1787 AdjustLimitedView( wxHORIZONTAL );
1788
1789 return true;
1790}
1791
1792
1793bool mpWindow::SetYView( double pos, double desiredMax, double desiredMin )
1794{
1795 m_posY = pos;
1796 m_desiredYmax = desiredMax;
1797 m_desiredYmin = desiredMin;
1798 AdjustLimitedView( wxVERTICAL );
1799
1800 return true;
1801}
1802
1803
1804void mpWindow::ZoomIn( const wxPoint& centerPoint )
1805{
1806 ZoomIn( centerPoint, zoomIncrementalFactor, wxBOTH );
1807}
1808
1809
1810void mpWindow::ZoomIn( const wxPoint& centerPoint, double zoomFactor, wxOrientation directions )
1811{
1812 DoZoom( centerPoint, zoomFactor, directions );
1813}
1814
1815
1816void mpWindow::ZoomOut( const wxPoint& centerPoint )
1817{
1818 ZoomOut( centerPoint, zoomIncrementalFactor, wxBOTH );
1819}
1820
1821
1822void mpWindow::ZoomOut( const wxPoint& centerPoint, double zoomFactor, wxOrientation directions )
1823{
1824 if( zoomFactor == 0 )
1825 zoomFactor = 1.0;
1826
1827 DoZoom( centerPoint, 1.0 / zoomFactor, directions );
1828}
1829
1830
1831void mpWindow::ZoomRect( wxPoint p0, wxPoint p1 )
1832{
1834
1835 // Constrain given rectangle to plot area
1836 const int pMinX = m_marginLeft;
1837 const int pMaxX = m_scrX - m_marginRight;
1838 const int pMinY = m_marginTop;
1839 const int pMaxY = m_scrY - m_marginBottom;
1840 p0.x = std::max( p0.x, pMinX );
1841 p0.x = std::min( p0.x, pMaxX );
1842 p0.y = std::max( p0.y, pMinY );
1843 p0.y = std::min( p0.y, pMaxY );
1844 p1.x = std::max( p1.x, pMinX );
1845 p1.x = std::min( p1.x, pMaxX );
1846 p1.y = std::max( p1.y, pMinY );
1847 p1.y = std::min( p1.y, pMaxY );
1848
1849 // Compute the 2 corners in graph coordinates:
1850 double p0x = p2x( p0.x );
1851 double p0y = p2y( p0.y );
1852 double p1x = p2x( p1.x );
1853 double p1y = p2y( p1.y );
1854
1855 // Order them:
1856 double zoom_x_min = p0x<p1x ? p0x : p1x;
1857 double zoom_x_max = p0x>p1x ? p0x : p1x;
1858 double zoom_y_min = p0y<p1y ? p0y : p1y;
1859 double zoom_y_max = p0y>p1y ? p0y : p1y;
1860
1861 if( m_yLocked )
1862 {
1863 zoom_y_min = m_desiredYmin;
1864 zoom_y_max = m_desiredYmax;
1865 }
1866
1867 Fit( zoom_x_min, zoom_x_max, zoom_y_min, zoom_y_max );
1868
1869 // Even with the input rectangle constrained to the plot area, it's still possible for the
1870 // resulting view to exceed limits when a portion of the gap is grabbed.
1872
1873 // These additional checks are needed because AdjustLimitedView only adjusts the position
1874 // and not the scale.
1875 wxOrientation directionsNeedingRefitting = ViewNeedsRefitting( wxBOTH );
1876
1877 if( directionsNeedingRefitting != 0 )
1878 Fit( m_minX, m_maxX, m_minY, m_maxY, nullptr, nullptr, directionsNeedingRefitting );
1879}
1880
1881
1882void mpWindow::pushZoomUndo( const std::array<double, 4>& aZoom )
1883{
1884 m_undoZoomStack.push( aZoom );
1885
1886 while( !m_redoZoomStack.empty() )
1887 m_redoZoomStack.pop();
1888}
1889
1890
1892{
1893 if( m_undoZoomStack.size() )
1894 {
1896
1897 std::array<double, 4> zoom = m_undoZoomStack.top();
1898 m_undoZoomStack.pop();
1899
1900 Fit( zoom[0], zoom[1], zoom[2], zoom[3] );
1902 }
1903}
1904
1905
1907{
1908 if( m_redoZoomStack.size() )
1909 {
1911
1912 std::array<double, 4> zoom = m_redoZoomStack.top();
1913 m_redoZoomStack.pop();
1914
1915 Fit( zoom[0], zoom[1], zoom[2], zoom[3] );
1917 }
1918}
1919
1920
1921void mpWindow::OnShowPopupMenu( wxMouseEvent& event )
1922{
1923 m_clickedX = event.GetX();
1924 m_clickedY = event.GetY();
1925
1926 m_popmenu.Enable( mpID_ZOOM_UNDO, !m_undoZoomStack.empty() );
1927 m_popmenu.Enable( mpID_ZOOM_REDO, !m_redoZoomStack.empty() );
1928
1929 PopupMenu( &m_popmenu, event.GetX(), event.GetY() );
1930}
1931
1932
1933void mpWindow::OnFit( wxCommandEvent& WXUNUSED( event ) )
1934{
1936
1937 Fit();
1938}
1939
1940
1941void mpWindow::OnCenter( wxCommandEvent& WXUNUSED( event ) )
1942{
1943 GetClientSize( &m_scrX, &m_scrY );
1944 int centerX = ( m_scrX - m_marginLeft - m_marginRight ) / 2;
1945 int centerY = ( m_scrY - m_marginTop - m_marginBottom ) / 2;
1946 SetPos( p2x( m_clickedX - centerX ), p2y( m_clickedY - centerY ) );
1947}
1948
1949
1951{
1952 MouseWheelActionSet actions;
1958 return actions;
1959}
1960
1961
1962void mpWindow::onZoomIn( wxCommandEvent& WXUNUSED( event ) )
1963{
1964 ZoomIn( wxPoint( m_mouseMClick.x, m_mouseMClick.y ) );
1965}
1966
1967
1968void mpWindow::onZoomOut( wxCommandEvent& WXUNUSED( event ) )
1969{
1970 ZoomOut();
1971}
1972
1973
1974void mpWindow::onZoomUndo( wxCommandEvent& WXUNUSED( event ) )
1975{
1976 ZoomUndo();
1977}
1978
1979
1980void mpWindow::onZoomRedo( wxCommandEvent& WXUNUSED( event ) )
1981{
1982 ZoomRedo();
1983}
1984
1985
1986void mpWindow::OnSize( wxSizeEvent& WXUNUSED( event ) )
1987{
1988 // Try to fit again with the new window size:
1990}
1991
1992
1993bool mpWindow::AddLayer( mpLayer* layer, bool refreshDisplay )
1994{
1995 if( layer )
1996 {
1997 m_layers.push_back( layer );
1998
1999 if( refreshDisplay )
2000 UpdateAll();
2001
2002 return true;
2003 }
2004
2005 return false;
2006}
2007
2008
2009bool mpWindow::DelLayer( mpLayer* layer, bool alsoDeleteObject, bool refreshDisplay )
2010{
2011 wxLayerList::iterator layIt;
2012
2013 for( layIt = m_layers.begin(); layIt != m_layers.end(); layIt++ )
2014 {
2015 if( *layIt == layer )
2016 {
2017 // Also delete the object?
2018 if( alsoDeleteObject )
2019 delete *layIt;
2020
2021 m_layers.erase( layIt ); // this deleted the reference only
2022
2023 if( refreshDisplay )
2024 UpdateAll();
2025
2026 return true;
2027 }
2028 }
2029
2030 return false;
2031}
2032
2033
2034void mpWindow::DelAllLayers( bool alsoDeleteObject, bool refreshDisplay )
2035{
2036 while( m_layers.size()>0 )
2037 {
2038 // Also delete the object?
2039 if( alsoDeleteObject )
2040 delete m_layers[0];
2041
2042 m_layers.erase( m_layers.begin() ); // this deleted the reference only
2043 }
2044
2045 if( refreshDisplay )
2046 UpdateAll();
2047}
2048
2049
2050void mpWindow::OnPaint( wxPaintEvent& WXUNUSED( event ) )
2051{
2052 wxPaintDC paintDC( this );
2053
2054 paintDC.GetSize( &m_scrX, &m_scrY ); // This is the size of the visible area only!
2055
2056 // Selects direct or buffered draw:
2057 wxDC* targetDC = &paintDC;
2058
2059 // J.L.Blanco @ Aug 2007: Added double buffer support
2061 {
2062 if( m_last_lx != m_scrX || m_last_ly != m_scrY )
2063 {
2064 delete m_buff_bmp;
2065 m_buff_bmp = new wxBitmap( m_scrX, m_scrY );
2066 m_buff_dc.SelectObject( *m_buff_bmp );
2067 m_last_lx = m_scrX;
2068 m_last_ly = m_scrY;
2069 }
2070
2071 targetDC = &m_buff_dc;
2072 }
2073
2074 if( wxGraphicsContext* ctx = targetDC->GetGraphicsContext() )
2075 {
2076 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_BEST ) )
2077 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_GOOD ) )
2078 ctx->SetInterpolationQuality( wxINTERPOLATION_FAST );
2079
2080 ctx->SetAntialiasMode( wxANTIALIAS_DEFAULT );
2081 }
2082
2083 // Draw background:
2084 targetDC->SetPen( *wxTRANSPARENT_PEN );
2085 wxBrush brush( GetBackgroundColour() );
2086 targetDC->SetBrush( brush );
2087 targetDC->SetTextForeground( m_fgColour );
2088 targetDC->DrawRectangle( 0, 0, m_scrX, m_scrY );
2089
2090 // Draw all the layers:
2091 for( mpLayer* layer : m_layers )
2092 layer->Plot( *targetDC, *this );
2093
2094 if( m_zooming )
2095 {
2096 wxPen pen( m_fgColour, 1, wxPENSTYLE_DOT );
2097 targetDC->SetPen( pen );
2098 targetDC->SetBrush( *wxTRANSPARENT_BRUSH );
2099 targetDC->DrawRectangle( m_zoomRect );
2100 }
2101
2102 // If doublebuffer, draw now to the window:
2104 paintDC.Blit( 0, 0, m_scrX, m_scrY, targetDC, 0, 0 );
2105}
2106
2107
2108void mpWindow::DoZoom( const wxPoint& centerPoint, double zoomFactor, wxOrientation directions )
2109{
2110 if( m_yLocked )
2111 {
2112 if( directions == wxVERTICAL )
2113 return;
2114
2115 directions = wxHORIZONTAL;
2116 }
2117
2118 const bool horizontally = ( directions & wxHORIZONTAL ) != 0;
2119 const bool vertically = ( directions & wxVERTICAL ) != 0;
2120
2122
2123 // Preserve the position of the clicked point:
2124 wxPoint c( centerPoint );
2125 if( c == wxDefaultPosition )
2126 {
2127 GetClientSize( &m_scrX, &m_scrY );
2128 c.x = ( m_scrX - m_marginLeft - m_marginRight ) / 2 + m_marginLeft;
2129 c.y = ( m_scrY - m_marginTop - m_marginBottom ) / 2 + m_marginTop;
2130 }
2131 else
2132 {
2133 c.x = std::max( c.x, m_marginLeft );
2134 c.x = std::min( c.x, m_scrX - m_marginRight );
2135 c.y = std::max( c.y, m_marginTop );
2136 c.y = std::min( c.y, m_scrY - m_marginBottom );
2137 }
2138
2139 // Zoom in/out:
2140 const double MAX_SCALE = 1e6;
2141 const double newScaleX = horizontally ? ( m_scaleX * zoomFactor ) : m_scaleX;
2142 const double newScaleY = vertically ? ( m_scaleY * zoomFactor ) : m_scaleY;
2143
2144 // Baaaaad things happen when you zoom in too much..
2145 if( newScaleX > MAX_SCALE || newScaleY > MAX_SCALE )
2146 return;
2147
2148 if( horizontally )
2149 {
2150 // Transform the clicked X point to layer coordinates:
2151 const double prior_layer_x = p2x( c.x );
2152
2153 // Adjust the new X scale and plot X origin:
2154 m_scaleX = newScaleX;
2155 m_posX = prior_layer_x - c.x / newScaleX;
2156
2157 // Recompute the desired X view extents:
2159 }
2160
2161 if( vertically )
2162 {
2163 // Transform the clicked Y point to layer coordinates:
2164 const double prior_layer_y = p2y( c.y );
2165
2166 // Adjust the new Y scale and plot Y origin:
2167 m_scaleY = newScaleY;
2168 m_posY = prior_layer_y + c.y / newScaleY;
2169
2170 // Recompute the desired Y view extents:
2172 }
2173
2174 AdjustLimitedView( directions );
2175
2176 if( zoomFactor < 1.0 )
2177 {
2178 // These additional checks are needed because AdjustLimitedView only adjusts the position
2179 // and not the scale.
2180 wxOrientation directionsNeedingRefitting = ViewNeedsRefitting( directions );
2181
2182 // If the view is still out-of-limits after AdjustLimitedView is called, perform a Fit
2183 // along the offending dimension(s).
2184 if( directionsNeedingRefitting != 0 )
2185 Fit( m_minX, m_maxX, m_minY, m_maxY, nullptr, nullptr, directionsNeedingRefitting );
2186 }
2187
2188 UpdateAll();
2189}
2190
2191
2192void mpWindow::RecomputeDesiredX( double& min, double& max )
2193{
2194 const int plotScreenWidth = m_scrX - m_marginLeft - m_marginRight;
2195 const double plotSpanX = plotScreenWidth / m_scaleX;
2196 const double desiredSpanX = plotSpanX / ( 2 * m_leftRightPlotGapFactor + 1 );
2197 const double xGap = desiredSpanX * m_leftRightPlotGapFactor;
2198 min = m_posX + ( m_marginLeft / m_scaleX ) + xGap;
2199 max = m_desiredXmin + desiredSpanX;
2200}
2201
2202
2203void mpWindow::RecomputeDesiredY( double& min, double& max )
2204{
2205 const int plotScreenHeight = m_scrY - m_marginTop - m_marginBottom;
2206 const double plotSpanY = plotScreenHeight / m_scaleY;
2207 const double desiredSpanY = plotSpanY / ( 2 * m_topBottomPlotGapFactor + 1 );
2208 const double yGap = desiredSpanY * m_topBottomPlotGapFactor;
2209 max = m_posY - ( m_marginTop / m_scaleY ) - yGap;
2210 min = m_desiredYmax - desiredSpanY;
2211}
2212
2213
2214wxOrientation mpWindow::ViewNeedsRefitting( wxOrientation directions ) const
2215{
2216 if( !m_enableLimitedView )
2217 return static_cast<wxOrientation>( 0 );
2218
2219 // Allow a gap between the extrema of the curve and the edges of the plot area. Not to be
2220 // confused with the left/right/top/bottom margins outside the plot area.
2221 const double xGap = fabs( m_maxX - m_minX ) * m_leftRightPlotGapFactor;
2222 const double yGap = fabs( m_maxY - m_minY ) * m_topBottomPlotGapFactor;
2223
2224 wxOrientation result = {};
2225
2226 if( ( directions & wxHORIZONTAL ) != 0 )
2227 {
2228 if( ( m_desiredXmax > m_maxX + xGap ) || ( m_desiredXmin < m_minX - xGap ) )
2229 result = static_cast<wxOrientation>( result | wxHORIZONTAL );
2230 }
2231
2232 if( ( directions & wxVERTICAL ) != 0 )
2233 {
2234 if( ( m_desiredYmax > m_maxY + yGap ) || ( m_desiredYmin < m_minY - yGap ) )
2235 result = static_cast<wxOrientation>( result | wxVERTICAL );
2236 }
2237
2238 return result;
2239}
2240
2241
2242void mpWindow::PerformMouseWheelAction( wxMouseEvent& event, MouseWheelAction action )
2243{
2244 const int change = event.GetWheelRotation();
2245 const double changeUnitsX = change / m_scaleX;
2246 const double changeUnitsY = change / m_scaleY;
2247 const wxPoint clickPt( event.GetX(), event.GetY() );
2248
2249 switch( action )
2250 {
2251 case MouseWheelAction::NONE: break;
2252
2254 SetXView( m_posX + changeUnitsX, m_desiredXmax + changeUnitsX,
2255 m_desiredXmin + changeUnitsX );
2256 UpdateAll();
2257 break;
2258
2260 SetXView( m_posX - changeUnitsX, m_desiredXmax - changeUnitsX,
2261 m_desiredXmin - changeUnitsX );
2262 UpdateAll();
2263 break;
2264
2266 if( !m_yLocked )
2267 {
2268 SetYView( m_posY + changeUnitsY, m_desiredYmax + changeUnitsY,
2269 m_desiredYmin + changeUnitsY );
2270 UpdateAll();
2271 }
2272
2273 break;
2274
2276 if( event.GetWheelRotation() > 0 )
2277 ZoomIn( clickPt );
2278 else
2279 ZoomOut( clickPt );
2280
2281 break;
2282
2284 if( event.GetWheelRotation() > 0 )
2285 ZoomIn( clickPt, zoomIncrementalFactor, wxHORIZONTAL );
2286 else
2287 ZoomOut( clickPt, zoomIncrementalFactor, wxHORIZONTAL );
2288
2289 break;
2290
2292 if( event.GetWheelRotation() > 0 )
2293 ZoomIn( clickPt, zoomIncrementalFactor, wxVERTICAL );
2294 else
2295 ZoomOut( clickPt, zoomIncrementalFactor, wxVERTICAL );
2296
2297 break;
2298
2299 default:
2300 break;
2301 }
2302}
2303
2304
2306{
2307 m_minX = 0.0;
2308 m_maxX = 1.0;
2309 m_minY = 0.0;
2310 m_maxY = 1.0;
2311
2312 return true;
2313}
2314
2315
2317{
2318 UpdateBBox();
2319 Refresh( false );
2320}
2321
2322
2323void mpWindow::SetScaleX( double scaleX )
2324{
2325 if( scaleX != 0 )
2326 m_scaleX = scaleX;
2327
2328 UpdateAll();
2329}
2330
2331
2332// New methods implemented by Davide Rondini
2333
2334mpLayer* mpWindow::GetLayer( int position ) const
2335{
2336 if( ( position >= (int) m_layers.size() ) || position < 0 )
2337 return nullptr;
2338
2339 return m_layers[position];
2340}
2341
2342
2343const mpLayer* mpWindow::GetLayerByName( const wxString& name ) const
2344{
2345 for( const mpLayer* layer : m_layers )
2346 {
2347 if( !layer->GetName().Cmp( name ) )
2348 return layer;
2349 }
2350
2351 return nullptr; // Not found
2352}
2353
2354
2355void mpWindow::GetBoundingBox( double* bbox ) const
2356{
2357 bbox[0] = m_minX;
2358 bbox[1] = m_maxX;
2359 bbox[2] = m_minY;
2360 bbox[3] = m_maxY;
2361}
2362
2363
2364bool mpWindow::SaveScreenshot( wxImage& aImage, wxSize aImageSize, bool aFit )
2365{
2366 int sizeX, sizeY;
2367
2368 if( aImageSize == wxDefaultSize )
2369 {
2370 sizeX = m_scrX;
2371 sizeY = m_scrY;
2372 }
2373 else
2374 {
2375 sizeX = aImageSize.x;
2376 sizeY = aImageSize.y;
2377 SetScr( sizeX, sizeY );
2378 }
2379
2380 wxBitmap screenBuffer( sizeX, sizeY );
2381 wxMemoryDC screenDC;
2382 screenDC.SelectObject( screenBuffer );
2383 screenDC.SetPen( *wxWHITE_PEN );
2384 screenDC.SetTextForeground( m_fgColour );
2385 wxBrush brush( GetBackgroundColour() );
2386 screenDC.SetBrush( brush );
2387 screenDC.DrawRectangle( 0, 0, sizeX, sizeY );
2388
2389 if( aFit )
2390 Fit( m_minX, m_maxX, m_minY, m_maxY, &sizeX, &sizeY );
2391 else
2393
2394 // Draw all the layers:
2395 for( mpLayer* layer : m_layers )
2396 layer->Plot( screenDC, *this );
2397
2398 if( aImageSize != wxDefaultSize )
2399 {
2400 // Restore dimensions
2401 int bk_scrX = m_scrX;
2402 int bk_scrY = m_scrY;
2403 SetScr( bk_scrX, bk_scrY );
2404 Fit( m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax, &bk_scrX, &bk_scrY );
2405 UpdateAll();
2406 }
2407
2408 // Once drawing is complete, actually save screen shot
2409 aImage = screenBuffer.ConvertToImage();
2410
2411 return true;
2412}
2413
2414
2415void mpWindow::SetMargins( int top, int right, int bottom, int left )
2416{
2417 m_marginTop = top;
2419 m_marginBottom = bottom;
2421}
2422
2423
2425{
2426 for( mpLayer* layer : m_layers )
2427 {
2428 if( layer->IsInfo() )
2429 {
2430 mpInfoLayer* tmpLyr = static_cast<mpInfoLayer*>( layer );
2431
2432 if( tmpLyr->Inside( point ) )
2433 return tmpLyr;
2434 }
2435 }
2436
2437 return nullptr;
2438}
2439
2440
2441void mpWindow::SetLayerVisible( const wxString& name, bool viewable )
2442{
2443 if( mpLayer* lx = GetLayerByName( name ) )
2444 {
2445 lx->SetVisible( viewable );
2446 UpdateAll();
2447 }
2448}
2449
2450
2451bool mpWindow::IsLayerVisible( const wxString& name ) const
2452{
2453 if( const mpLayer* lx = GetLayerByName( name ) )
2454 return lx->IsVisible();
2455
2456 return false;
2457}
2458
2459
2460void mpWindow::SetLayerVisible( const unsigned int position, bool viewable )
2461{
2462 if( mpLayer* lx = GetLayer( position ) )
2463 {
2464 lx->SetVisible( viewable );
2465 UpdateAll();
2466 }
2467}
2468
2469
2470bool mpWindow::IsLayerVisible( unsigned int position ) const
2471{
2472 if( const mpLayer* lx = GetLayer( position ) )
2473 return lx->IsVisible();
2474
2475 return false;
2476}
2477
2478
2479void mpWindow::SetColourTheme( const wxColour& bgColour, const wxColour& drawColour,
2480 const wxColour& axesColour )
2481{
2482 SetBackgroundColour( bgColour );
2483 SetForegroundColour( drawColour );
2484 m_bgColour = bgColour;
2485 m_fgColour = drawColour;
2486 m_axColour = axesColour;
2487
2488 // Cycle between layers to set colours and properties to them
2489 for( mpLayer* layer : m_layers )
2490 {
2491 if( layer->GetLayerType() == mpLAYER_AXIS )
2492 {
2493 // Get the old pen to modify only colour, not style or width.
2494 wxPen axisPen = layer->GetPen();
2495 axisPen.SetColour( axesColour );
2496 layer->SetPen( axisPen );
2497 }
2498
2499 if( layer->GetLayerType() == mpLAYER_INFO )
2500 {
2501 // Get the old pen to modify only colour, not style or width.
2502 wxPen infoPen = layer->GetPen();
2503 infoPen.SetColour( drawColour );
2504 layer->SetPen( infoPen );
2505 }
2506 }
2507}
2508
2509
2510template <typename... Ts>
2512 wxWindow( std::forward<Ts>( windowArgs )... ),
2513 m_minX( 0.0 ),
2514 m_maxX( 0.0 ),
2515 m_minY( 0.0 ),
2516 m_maxY( 0.0 ),
2517 m_scaleX( 1.0 ),
2518 m_scaleY( 1.0 ),
2519 m_posX( 0.0 ),
2520 m_posY( 0.0 ),
2521 m_scrX( 64 ),
2522 m_scrY( 64 ),
2523 m_clickedX( 0 ),
2524 m_clickedY( 0 ),
2525 m_yLocked( false ),
2526 m_desiredXmin( 0.0 ),
2527 m_desiredXmax( 1.0 ),
2528 m_desiredYmin( 0.0 ),
2529 m_desiredYmax( 1.0 ),
2530 m_topBottomPlotGapFactor( 0.03 ),
2531 m_leftRightPlotGapFactor( 0.0 ),
2532 m_marginTop( 0 ),
2533 m_marginRight( 0 ),
2534 m_marginBottom( 0 ),
2535 m_marginLeft( 0 ),
2536 m_last_lx( 0 ),
2537 m_last_ly( 0 ),
2538 m_buff_bmp( nullptr ),
2539 m_enableDoubleBuffer( false ),
2540 m_enableMouseNavigation( true ),
2541 m_enableLimitedView( false ),
2542 m_mouseWheelActions( defaultMouseWheelActions() ),
2543 m_movingInfoLayer( nullptr ),
2544 m_zooming( false )
2545{}
2546
2547
2549{
2550 if( wxGraphicsContext* ctx = m_buff_dc.GetGraphicsContext() )
2551 {
2552 if( !ctx->SetInterpolationQuality( wxINTERPOLATION_BEST )
2553 || !ctx->SetInterpolationQuality( wxINTERPOLATION_GOOD ) )
2554 {
2555 ctx->SetInterpolationQuality( wxINTERPOLATION_FAST );
2556 }
2557
2558 ctx->SetAntialiasMode( wxANTIALIAS_DEFAULT );
2559 }
2560}
2561
2562
2563// -----------------------------------------------------------------------------
2564// mpFXYVector implementation - by Jose Luis Blanco (AGO-2007)
2565// -----------------------------------------------------------------------------
2566
2567IMPLEMENT_DYNAMIC_CLASS( mpFXYVector, mpFXY )
2568
2569
2570mpFXYVector::mpFXYVector( const wxString& name, int flags ) :
2571 mpFXY( name, flags )
2572{
2573 m_index = 0;
2574 m_sweepWindow = 0;
2575 m_minX = -1;
2576 m_maxX = 1;
2577 m_minY = -1;
2578 m_maxY = 1;
2579 m_type = mpLAYER_PLOT;
2580}
2581
2582
2583double mpScaleX::TransformToPlot( double x ) const
2584{
2585 return ( x + m_offset ) * m_scale;
2586}
2587
2588
2589double mpScaleX::TransformFromPlot( double xplot ) const
2590{
2591 return xplot / m_scale - m_offset;
2592}
2593
2594
2595double mpScaleY::TransformToPlot( double x ) const
2596{
2597 return ( x + m_offset ) * m_scale;
2598}
2599
2600
2601double mpScaleY::TransformFromPlot( double xplot ) const
2602{
2603 return xplot / m_scale - m_offset;
2604}
2605
2606
2607double mpScaleXLog::TransformToPlot( double x ) const
2608{
2609 double xlogmin = log10( m_minV );
2610 double xlogmax = log10( m_maxV );
2611
2612 return ( log10( x ) - xlogmin ) / ( xlogmax - xlogmin );
2613}
2614
2615
2616double mpScaleXLog::TransformFromPlot( double xplot ) const
2617{
2618 double xlogmin = log10( m_minV );
2619 double xlogmax = log10( m_maxV );
2620
2621 return pow( 10.0, xplot * ( xlogmax - xlogmin ) + xlogmin );
2622}
2623
2624
2626{
2627 m_index = 0;
2628 m_sweepWindow = std::numeric_limits<size_t>::max();
2629}
2630
2631
2632void mpFXYVector::SetSweepWindow( int aSweepIdx )
2633{
2634 m_index = aSweepIdx * m_sweepSize;
2635 m_sweepWindow = ( aSweepIdx + 1 ) * m_sweepSize;
2636}
2637
2638
2639bool mpFXYVector::GetNextXY( double& x, double& y )
2640{
2641 if( m_index >= m_xs.size() || m_index >= m_sweepWindow )
2642 {
2643 return false;
2644 }
2645 else
2646 {
2647 x = m_xs[m_index];
2648 y = m_ys[m_index++];
2649 return m_index <= m_xs.size() && m_index <= m_sweepWindow;
2650 }
2651}
2652
2653
2655{
2656 m_xs.clear();
2657 m_ys.clear();
2658}
2659
2660
2661void mpFXYVector::SetData( const std::vector<double>& xs, const std::vector<double>& ys )
2662{
2663 // Check if the data vectors are of the same size
2664 if( xs.size() != ys.size() )
2665 return;
2666
2667 // Copy the data:
2668 m_xs = xs;
2669 m_ys = ys;
2670
2671 // Update internal variables for the bounding box.
2672 if( xs.size() > 0 )
2673 {
2674 m_minX = xs[0];
2675 m_maxX = xs[0];
2676 m_minY = ys[0];
2677 m_maxY = ys[0];
2678
2679 for( const double x : xs )
2680 {
2681 if( x < m_minX )
2682 m_minX = x;
2683
2684 if( x > m_maxX )
2685 m_maxX = x;
2686 }
2687
2688 for( const double y : ys )
2689 {
2690 if( y < m_minY )
2691 m_minY = y;
2692
2693 if( y > m_maxY )
2694 m_maxY = y;
2695 }
2696 }
2697 else
2698 {
2699 m_minX = 0;
2700 m_maxX = 0;
2701 m_minY = 0;
2702 m_maxY = 0;
2703 }
2704}
2705
2706
2708{
2709 m_scaleX = scaleX;
2710 m_scaleY = scaleY;
2711
2712 UpdateScales();
2713}
2714
2715
2717{
2718 if( m_scaleX )
2720
2721 if( m_scaleY )
2723}
2724
2725
2726double mpFXY::s2x( double plotCoordX ) const
2727{
2728 return m_scaleX ? m_scaleX->TransformFromPlot( plotCoordX ) : plotCoordX;
2729}
2730
2731
2732double mpFXY::s2y( double plotCoordY ) const
2733{
2734 return m_scaleY ? m_scaleY->TransformFromPlot( plotCoordY ) : plotCoordY;
2735}
2736
2737
2738double mpFXY::x2s( double x ) const
2739{
2740 return m_scaleX ? m_scaleX->TransformToPlot( x ) : x;
2741}
2742
2743
2744double mpFXY::y2s( double y ) const
2745{
2746 return m_scaleY ? m_scaleY->TransformToPlot( y ) : y;
2747}
const char * name
Definition: DXF_plotter.cpp:62
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:2632
bool GetNextXY(double &x, double &y) override
Get locus value for next N.
Definition: mathplot.cpp:2639
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:2661
double m_maxX
Definition: mathplot.h:1442
void Clear()
Clears all the data, leaving the layer empty.
Definition: mathplot.cpp:2654
size_t m_sweepSize
Definition: mathplot.h:1444
void Rewind() override
Rewind value enumeration with mpFXY::GetNextXY.
Definition: mathplot.cpp:2625
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:2732
virtual void SetScale(mpScaleBase *scaleX, mpScaleBase *scaleY)
Definition: mathplot.cpp:2707
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:2716
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:2744
void UpdateViewBoundary(wxCoord xnew, wxCoord ynew)
Update label positioning data.
Definition: mathplot.cpp:434
double x2s(double x) const
Definition: mathplot.cpp:2738
virtual int GetSweepCount() const
Definition: mathplot.h:563
wxCoord minDrawX
Definition: mathplot.h:585
double s2x(double plotCoordX) const
Definition: mathplot.cpp:2726
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:1059
mpScaleXLog(const wxString &name=wxT("log(X)"), int flags=mpALIGN_CENTER, bool ticks=true, unsigned int type=mpX_NORMAL)
Full constructor.
Definition: mathplot.cpp:1053
virtual double TransformFromPlot(double xplot) const override
Definition: mathplot.cpp:2616
virtual double TransformToPlot(double x) const override
Definition: mathplot.cpp:2607
void recalculateTicks(wxDC &dc, mpWindow &w) override
Definition: mathplot.cpp:980
virtual double TransformToPlot(double x) const override
Definition: mathplot.cpp:2583
virtual double TransformFromPlot(double xplot) const override
Definition: mathplot.cpp:2589
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:1047
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:2601
void computeSlaveTicks(mpWindow &w)
Definition: mathplot.cpp:831
virtual void Plot(wxDC &dc, mpWindow &w) override
Layer plot handler.
Definition: mathplot.cpp:1225
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:2595
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:2364
mpInfoLayer * m_movingInfoLayer
Definition: mathplot.h:1367
void DelAllLayers(bool alsoDeleteObject, bool refreshDisplay=true)
Remove all layers from the plot.
Definition: mathplot.cpp:2034
bool m_zooming
Definition: mathplot.h:1368
void SetColourTheme(const wxColour &bgColour, const wxColour &drawColour, const wxColour &axesColour)
Set Color theme.
Definition: mathplot.cpp:2479
virtual bool SetYView(double pos, double desiredMax, double desiredMin)
Applies new Y view coordinates depending on the settings.
Definition: mathplot.cpp:1793
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:1831
double m_maxY
Definition: mathplot.h:1331
void onMouseLeftRelease(wxMouseEvent &event)
Definition: mathplot.cpp:1613
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:2203
wxOrientation ViewNeedsRefitting(wxOrientation directions) const
Definition: mathplot.cpp:2214
void OnPaint(wxPaintEvent &event)
Definition: mathplot.cpp:2050
void OnShowPopupMenu(wxMouseEvent &event)
Definition: mathplot.cpp:1921
double m_desiredXmax
Definition: mathplot.h:1349
int m_last_lx
Definition: mathplot.h:1358
void onMouseLeftDown(wxMouseEvent &event)
Definition: mathplot.cpp:1601
void onMouseWheel(wxMouseEvent &event)
Definition: mathplot.cpp:1463
static MouseWheelActionSet defaultMouseWheelActions()
Definition: mathplot.cpp:1950
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:2415
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:2548
int m_marginBottom
Definition: mathplot.h:1356
void RecomputeDesiredX(double &min, double &max)
Definition: mathplot.cpp:2192
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:2424
void OnMouseMiddleDown(wxMouseEvent &event)
Definition: mathplot.cpp:1436
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:2441
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:2108
int m_marginRight
Definition: mathplot.h:1356
mpLayer * GetLayer(int position) const
Definition: mathplot.cpp:2334
void onZoomRedo(wxCommandEvent &event)
Definition: mathplot.cpp:1980
void ZoomIn(const wxPoint &centerPoint=wxDefaultPosition)
Zoom into current view and refresh display.
Definition: mathplot.cpp:1804
wxColour m_fgColour
Definition: mathplot.h:1325
void ZoomOut(const wxPoint &centerPoint=wxDefaultPosition)
Zoom out current view and refresh display.
Definition: mathplot.cpp:1816
virtual bool UpdateBBox()
Recalculate global layer bounding box, and save it in m_minX,...
Definition: mathplot.cpp:2305
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:2009
void UpdateAll()
Refresh display.
Definition: mathplot.cpp:2316
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:1731
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:1941
virtual bool SetXView(double pos, double desiredMax, double desiredMin)
Applies new X view coordinates depending on the settings.
Definition: mathplot.cpp:1779
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:2323
void onMagnify(wxMouseEvent &event)
Definition: mathplot.cpp:1443
wxBitmap * m_buff_bmp
Definition: mathplot.h:1360
double m_topBottomPlotGapFactor
Definition: mathplot.h:1353
void onZoomOut(wxCommandEvent &event)
Definition: mathplot.cpp:1968
wxPoint m_mouseMClick
Definition: mathplot.h:1365
void pushZoomUndo(const std::array< double, 4 > &aZoom)
Definition: mathplot.cpp:1882
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:2355
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:2242
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:1906
bool m_enableDoubleBuffer
Definition: mathplot.h:1361
void ZoomUndo()
Definition: mathplot.cpp:1891
void OnFit(wxCommandEvent &event)
Definition: mathplot.cpp:1933
void OnSize(wxSizeEvent &event)
Definition: mathplot.cpp:1986
const mpLayer * GetLayerByName(const wxString &name) const
Definition: mathplot.cpp:2343
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:1962
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:1993
void Fit() override
Set view to fit global bounding box of all plot layers and refresh display.
Definition: mathplot.cpp:1635
void onZoomUndo(wxCommandEvent &event)
Definition: mathplot.cpp:1974
double GetPosY() const
Get current view's Y position.
Definition: mathplot.h:1021
void onMouseMove(wxMouseEvent &event)
Definition: mathplot.cpp:1507
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:2451
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
@ mpID_ZOOM_REDO
Definition: mathplot.h:117
@ mpID_FIT
Definition: mathplot.h:115
@ mpID_ZOOM_IN
Definition: mathplot.h:118
@ mpID_CENTER
Definition: mathplot.h:120
@ mpID_ZOOM_UNDO
Definition: mathplot.h:116
@ mpID_ZOOM_OUT
Definition: mathplot.h:119
#define mpALIGN_RIGHT
Aligns label to the right.
Definition: mathplot.h:427
#define mpALIGN_NW
Aligns label to north-west.
Definition: mathplot.h:459
#define mpALIGN_FAR_RIGHT
Aligns label to the right of mpALIGN_RIGHT.
Definition: mathplot.h:441
#define mpALIGN_BORDER_TOP
Aligns X axis to top border.
Definition: mathplot.h:439
#define mpALIGN_CENTER
Aligns label to the center.
Definition: mathplot.h:429
#define mpALIGN_LEFT
Aligns label to the left.
Definition: mathplot.h:431
#define mpALIGN_TOP
Aligns label to the top.
Definition: mathplot.h:433
#define mpALIGN_BORDER_LEFT
Aligns Y axis to left border.
Definition: mathplot.h:453
#define mpALIGNMASK
Definition: mathplot.h:425
#define mpALIGN_SE
Aligns label to south-east.
Definition: mathplot.h:463
#define mpALIGN_NE
Aligns label to north-east.
Definition: mathplot.h:457
#define mpALIGN_BOTTOM
Aligns label to the bottom.
Definition: mathplot.h:435
#define mpALIGN_BORDER_BOTTOM
Aligns X axis to bottom border.
Definition: mathplot.h:437
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:400
#define MAX_SCALE
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
Contains the set of modified mouse wheel actions that can be performed on the plot.
Definition: mathplot.h:929
MouseWheelAction horizontal
Definition: mathplot.h:939
MouseWheelAction verticalWithShift
Definition: mathplot.h:937
MouseWheelAction verticalWithCtrl
Definition: mathplot.h:936
MouseWheelAction verticalWithAlt
Definition: mathplot.h:938
MouseWheelAction verticalUnmodified
Definition: mathplot.h:935
int delta
static thread_pool * tp
Definition: thread_pool.cpp:28