MRPT  1.9.9
mathplot.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 /////////////////////////////////////////////////////////////////////////////
10 // Name: mathplot.cpp
11 // Purpose: Framework for plotting in wxWindows
12 // Original Author: David Schalig
13 // Maintainer: Davide Rondini
14 // Contributors: Jose Luis Blanco, Val Greene
15 // Created: 21/07/2003
16 // Last edit: 09/09/2007
17 // Copyright: (c) David Schalig, Davide Rondini
18 // Licence: wxWindows licence
19 /////////////////////////////////////////////////////////////////////////////
20 
21 #if defined(__GNUG__) && !defined(__clang__)
22 #pragma implementation "mathplot.h"
23 #endif
24 
25 // For compilers that support precompilation, includes "wx.h".
26 #include <wx/window.h>
27 
28 // Comment out for release operation:
29 // (Added by J.L.Blanco, Aug 2007)
30 // #define MATHPLOT_DO_LOGGING
31 
32 const int INVALID_CLICK_COORDS = -99999;
33 
34 #ifdef __BORLANDC__
35 #pragma hdrstop
36 #endif
37 
38 #ifndef WX_PRECOMP
39 #include "wx/colour.h"
40 #include "wx/cursor.h"
41 #include "wx/dcclient.h"
42 #include "wx/font.h"
43 #include "wx/intl.h"
44 #include "wx/log.h"
45 #include "wx/object.h"
46 #include "wx/settings.h"
47 #include "wx/sizer.h"
48 #endif
49 
50 #include <mrpt/3rdparty/mathplot/mathplot.h>
51 
52 #include <wx/bmpbuttn.h>
53 #include <wx/image.h>
54 #include <wx/module.h>
55 #include <wx/msgdlg.h>
56 #include <wx/tipwin.h>
57 
58 #ifndef _USE_MATH_DEFINES
59 #define _USE_MATH_DEFINES // (For VS to define M_PI, etc. in cmath)
60 #endif
61 #include <algorithm> // For std::min()/max()
62 #include <cmath>
63 #include <cstdio> // used only for debug
64 #include <ctime> // used for representation of x axes involving date
65 
66 // #include "pixel.xpm"
67 
68 // Memory leak debugging
69 //#ifdef _DEBUG
70 //#define new DEBUG_NEW
71 //#endif
72 
73 // Legend margins
74 #define mpLEGEND_MARGIN 5
75 #define mpLEGEND_LINEWIDTH 10
76 
77 // Minimum axis label separation
78 #define mpMIN_X_AXIS_LABEL_SEPARATION 64
79 #define mpMIN_Y_AXIS_LABEL_SEPARATION 32
80 
81 // Number of pixels to scroll when scrolling by a line
82 #define mpSCROLL_NUM_PIXELS_PER_LINE 10
83 
84 // See doxygen comments.
85 double mpWindow::zoomIncrementalFactor = 1.5;
86 
87 //-----------------------------------------------------------------------------
88 // mpLayer
89 //-----------------------------------------------------------------------------
90 
91 IMPLEMENT_ABSTRACT_CLASS(mpLayer, wxObject)
92 
93 mpLayer::mpLayer()
94 {
95  SetPen((wxPen&)*wxBLACK_PEN);
96  SetFont((wxFont&)*wxNORMAL_FONT);
97  m_continuous = FALSE; // Default
98  m_showName = TRUE; // Default
99  m_drawOutsideMargins = TRUE;
100  m_visible = true;
101 }
102 
103 wxBitmap mpLayer::GetColourSquare(int side)
104 {
105  wxBitmap square(side, side, -1);
106  wxColour filler = m_pen.GetColour();
107  wxBrush brush(filler, wxBRUSHSTYLE_SOLID);
108  wxMemoryDC dc;
109  dc.SelectObject(square);
110  dc.SetBackground(brush);
111  dc.Clear();
112  dc.SelectObject(wxNullBitmap);
113  return square;
114 }
115 
116 //-----------------------------------------------------------------------------
117 // mpInfoLayer
118 //-----------------------------------------------------------------------------
119 IMPLEMENT_DYNAMIC_CLASS(mpInfoLayer, mpLayer)
120 
121 mpInfoLayer::mpInfoLayer()
122 {
123  m_dim = wxRect(0, 0, 1, 1);
124  m_brush = *wxTRANSPARENT_BRUSH;
125  m_reference.x = 0;
126  m_reference.y = 0;
127  m_winX = 1; // parent->GetScrX();
128  m_winY = 1; // parent->GetScrY();
129  m_type = mpLAYER_INFO;
130 }
131 
132 mpInfoLayer::mpInfoLayer(wxRect rect, const wxBrush* brush) : m_dim(rect)
133 {
134  m_brush = *brush;
135  m_reference.x = rect.x;
136  m_reference.y = rect.y;
137  m_winX = 1; // parent->GetScrX();
138  m_winY = 1; // parent->GetScrY();
139  m_type = mpLAYER_INFO;
140 }
141 
142 mpInfoLayer::~mpInfoLayer() = default;
143 void mpInfoLayer::UpdateInfo(mpWindow& w, wxEvent& event) {}
144 bool mpInfoLayer::Inside(wxPoint& point) { return m_dim.Contains(point); }
145 void mpInfoLayer::Move(wxPoint delta)
146 {
147  m_dim.SetX(m_reference.x + delta.x);
148  m_dim.SetY(m_reference.y + delta.y);
149 }
150 
151 void mpInfoLayer::UpdateReference()
152 {
153  m_reference.x = m_dim.x;
154  m_reference.y = m_dim.y;
155 }
156 
157 void mpInfoLayer::Plot(wxDC& dc, mpWindow& w)
158 {
159  if (m_visible)
160  {
161  // Adjust relative position inside the window
162  int scrx = w.GetScrX();
163  int scry = w.GetScrY();
164  // Avoid dividing by 0
165  if (scrx == 0) scrx = 1;
166  if (scry == 0) scry = 1;
167 
168  if ((m_winX != scrx) || (m_winY != scry))
169  {
170 #ifdef MATHPLOT_DO_LOGGING
171 // wxLogMessage(_("mpInfoLayer::Plot() screen size has changed from %d x %d to
172 // %d x %d"), m_winX, m_winY, scrx, scry);
173 #endif
174  if (m_winX != 1)
175  m_dim.x = (int)floor((double)(m_dim.x * scrx) / m_winX);
176  if (m_winY != 1)
177  {
178  m_dim.y = (int)floor((double)(m_dim.y * scry) / m_winY);
179  UpdateReference();
180  }
181  // Finally update window size
182  m_winX = scrx;
183  m_winY = scry;
184  }
185  dc.SetPen(m_pen);
186  // wxImage image0(wxT("pixel.png"), wxBITMAP_TYPE_PNG);
187  // wxBitmap image1(image0);
188  // wxBrush semiWhite(image1);
189  dc.SetBrush(m_brush);
190  dc.DrawRectangle(m_dim.x, m_dim.y, m_dim.width, m_dim.height);
191  }
192 }
193 
194 wxPoint mpInfoLayer::GetPosition() const { return m_dim.GetPosition(); }
195 wxSize mpInfoLayer::GetSize() { return m_dim.GetSize(); }
196 mpInfoCoords::mpInfoCoords() : mpInfoLayer() {}
197 mpInfoCoords::mpInfoCoords(wxRect rect, const wxBrush* brush)
198  : mpInfoLayer(rect, brush)
199 {
200 }
201 
202 mpInfoCoords::~mpInfoCoords() = default;
203 void mpInfoCoords::UpdateInfo(mpWindow& w, wxEvent& event)
204 {
205  if (event.GetEventType() == wxEVT_MOTION)
206  {
207  int mouseX = ((wxMouseEvent&)event).GetX();
208  int mouseY = ((wxMouseEvent&)event).GetY();
209 /* It seems that Windows port of wxWidgets don't support multi-line test to be
210  drawn in a wxDC.
211  wxGTK instead works perfectly with it.
212  Info on wxForum:
213  http://wxforum.shadonet.com/viewtopic.php?t=3451&highlight=drawtext+eol */
214 #ifdef _WINDOWS
215  m_content.Printf(wxT("x = %f y = %f"), w.p2x(mouseX), w.p2y(mouseY));
216 #else
217  m_content.Printf(wxT("x = %f\ny = %f"), w.p2x(mouseX), w.p2y(mouseY));
218 #endif
219  }
220 }
221 
222 void mpInfoCoords::Plot(wxDC& dc, mpWindow& w)
223 {
224  if (m_visible)
225  {
226  // Adjust relative position inside the window
227  int scrx = w.GetScrX();
228  int scry = w.GetScrY();
229  if ((m_winX != scrx) || (m_winY != scry))
230  {
231 #ifdef MATHPLOT_DO_LOGGING
232 // wxLogMessage(_("mpInfoLayer::Plot() screen size has changed from %d x %d to
233 // %d x %d"), m_winX, m_winY, scrx, scry);
234 #endif
235  if (m_winX != 1)
236  m_dim.x = (int)floor((double)(m_dim.x * scrx) / m_winX);
237  if (m_winY != 1)
238  {
239  m_dim.y = (int)floor((double)(m_dim.y * scry) / m_winY);
240  UpdateReference();
241  }
242  // Finally update window size
243  m_winX = scrx;
244  m_winY = scry;
245  }
246  dc.SetPen(m_pen);
247  // wxImage image0(wxT("pixel.png"), wxBITMAP_TYPE_PNG);
248  // wxBitmap image1(image0);
249  // wxBrush semiWhite(image1);
250  dc.SetBrush(m_brush);
251  dc.SetFont(m_font);
252  int textX, textY;
253  dc.GetTextExtent(m_content, &textX, &textY);
254  if (m_dim.width < textX + 10) m_dim.width = textX + 10;
255  if (m_dim.height < textY + 10) m_dim.height = textY + 10;
256  dc.DrawRectangle(m_dim.x, m_dim.y, m_dim.width, m_dim.height);
257  dc.DrawText(m_content, m_dim.x + 5, m_dim.y + 5);
258  }
259 }
260 
261 mpInfoLegend::mpInfoLegend() : mpInfoLayer() {}
262 mpInfoLegend::mpInfoLegend(wxRect rect, const wxBrush* brush)
263  : mpInfoLayer(rect, brush)
264 {
265 }
266 
267 mpInfoLegend::~mpInfoLegend() = default;
268 void mpInfoLegend::UpdateInfo(mpWindow& w, wxEvent& event) {}
269 void mpInfoLegend::Plot(wxDC& dc, mpWindow& w)
270 {
271  if (m_visible)
272  {
273  // Adjust relative position inside the window
274  int scrx = w.GetScrX();
275  int scry = w.GetScrY();
276  if ((m_winX != scrx) || (m_winY != scry))
277  {
278 #ifdef MATHPLOT_DO_LOGGING
279 // wxLogMessage(_("mpInfoLayer::Plot() screen size has changed from %d x %d to
280 // %d x %d"), m_winX, m_winY, scrx, scry);
281 #endif
282  if (m_winX != 1)
283  m_dim.x = (int)floor((double)(m_dim.x * scrx) / m_winX);
284  if (m_winY != 1)
285  {
286  m_dim.y = (int)floor((double)(m_dim.y * scry) / m_winY);
287  UpdateReference();
288  }
289  // Finally update window size
290  m_winX = scrx;
291  m_winY = scry;
292  }
293  // wxImage image0(wxT("pixel.png"), wxBITMAP_TYPE_PNG);
294  // wxBitmap image1(image0);
295  // wxBrush semiWhite(image1);
296  dc.SetBrush(m_brush);
297  dc.SetFont(m_font);
298  const int baseWidth = (mpLEGEND_MARGIN * 2 + mpLEGEND_LINEWIDTH);
299  int textX = baseWidth, textY = mpLEGEND_MARGIN;
300  int plotCount = 0;
301  int posY = 0;
302  int tmpX = 0, tmpY = 0;
303  mpLayer* ly = nullptr;
304  wxPen lpen;
305  wxString label;
306  for (size_t p = 0; p < w.CountAllLayers(); p++)
307  {
308  ly = w.GetLayer(p);
309  if ((ly->GetLayerType() == mpLAYER_PLOT) && (ly->IsVisible()))
310  {
311  label = ly->GetName();
312  dc.GetTextExtent(label, &tmpX, &tmpY);
313  textX = (textX > (tmpX + baseWidth))
314  ? textX
315  : (tmpX + baseWidth + mpLEGEND_MARGIN);
316  textY += (tmpY);
317 #ifdef MATHPLOT_DO_LOGGING
318 // wxLogMessage(_("mpInfoLegend::Plot() Adding layer %d: %s"), p,
319 // label.c_str());
320 #endif
321  }
322  }
323  dc.SetPen(m_pen);
324  dc.SetBrush(m_brush);
325  m_dim.width = textX;
326  if (textY != mpLEGEND_MARGIN)
327  { // Don't draw any thing if there are no visible layers
328  textY += mpLEGEND_MARGIN;
329  m_dim.height = textY;
330  dc.DrawRectangle(m_dim.x, m_dim.y, m_dim.width, m_dim.height);
331  for (size_t p2 = 0; p2 < w.CountAllLayers(); p2++)
332  {
333  ly = w.GetLayer(p2);
334  if ((ly->GetLayerType() == mpLAYER_PLOT) && (ly->IsVisible()))
335  {
336  label = ly->GetName();
337  lpen = ly->GetPen();
338  dc.GetTextExtent(label, &tmpX, &tmpY);
339  dc.SetPen(lpen);
340  // textX = (textX > (tmpX + baseWidth)) ? textX : (tmpX +
341  // baseWidth);
342  // textY += (tmpY + mpLEGEND_MARGIN);
343  posY = m_dim.y + mpLEGEND_MARGIN + plotCount * tmpY +
344  (tmpY >> 1);
345  dc.DrawLine(
346  m_dim.x + mpLEGEND_MARGIN, // X start coord
347  posY, // Y start coord
348  m_dim.x + mpLEGEND_LINEWIDTH +
349  mpLEGEND_MARGIN, // X end coord
350  posY);
351  // dc.DrawRectangle(m_dim.x + 5, m_dim.y + 5 +
352  // plotCount*tmpY, 5, 5);
353  dc.DrawText(
354  label, m_dim.x + baseWidth,
355  m_dim.y + mpLEGEND_MARGIN + plotCount * tmpY);
356  plotCount++;
357  }
358  }
359  }
360  }
361 }
362 
363 //-----------------------------------------------------------------------------
364 // mpLayer implementations - functions
365 //-----------------------------------------------------------------------------
366 
367 IMPLEMENT_ABSTRACT_CLASS(mpFX, mpLayer)
368 
369 mpFX::mpFX(wxString name, int flags)
370 {
371  SetName(name);
372  m_flags = flags;
373  m_type = mpLAYER_PLOT;
374 }
375 
376 void mpFX::Plot(wxDC& dc, mpWindow& w)
377 {
378  if (m_visible)
379  {
380  dc.SetPen(m_pen);
381 
382  wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft();
383  wxCoord endPx = m_drawOutsideMargins ? w.GetScrX()
384  : w.GetScrX() - w.GetMarginRight();
385  wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop();
386  wxCoord maxYpx = m_drawOutsideMargins
387  ? w.GetScrY()
388  : w.GetScrY() - w.GetMarginBottom();
389 
390  wxCoord iy = 0;
391  if (m_pen.GetWidth() <= 1)
392  {
393  for (wxCoord i = startPx; i < endPx; ++i)
394  {
395  iy = w.y2p(GetY(w.p2x(i)));
396  // Draw the point only if you can draw outside margins or if the
397  // point is inside margins
398  if (m_drawOutsideMargins || ((iy >= minYpx) && (iy <= maxYpx)))
399  dc.DrawPoint(i, iy); // (wxCoord) ((w.GetPosY() - GetY(
400  // (double)i / w.GetScaleX() +
401  // w.GetPosX()) ) * w.GetScaleY()));
402  }
403  }
404  else
405  {
406  for (wxCoord i = startPx; i < endPx; ++i)
407  {
408  iy = w.y2p(GetY(w.p2x(i)));
409  // Draw the point only if you can draw outside margins or if the
410  // point is inside margins
411  if (m_drawOutsideMargins || ((iy >= minYpx) && (iy <= maxYpx)))
412  dc.DrawLine(i, iy, i, iy);
413  // wxCoord c = w.y2p( GetY(w.p2x(i)) ); //(wxCoord)
414  // ((w.GetPosY() - GetY( (double)i / w.GetScaleX() +
415  // w.GetPosX()) ) * w.GetScaleY());
416  }
417  }
418 
419  if (!m_name.IsEmpty() && m_showName)
420  {
421  dc.SetFont(m_font);
422 
423  wxCoord tx, ty;
424  dc.GetTextExtent(m_name, &tx, &ty);
425 
426  /*if ((m_flags & mpALIGNMASK) == mpALIGN_RIGHT)
427  tx = (w.GetScrX()>>1) - tx - 8;
428  else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER)
429  tx = -tx/2;
430  else
431  tx = -(w.GetScrX()>>1) + 8;
432  */
433  if ((m_flags & mpALIGNMASK) == mpALIGN_RIGHT)
434  tx = (w.GetScrX() - tx) - w.GetMarginRight() - 8;
435  else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER)
436  tx = ((w.GetScrX() - w.GetMarginRight() - w.GetMarginLeft() -
437  tx) /
438  2) +
439  w.GetMarginLeft();
440  else
441  tx = w.GetMarginLeft() + 8;
442  dc.DrawText(
443  m_name, tx, w.y2p(GetY(w.p2x(tx)))); // (wxCoord) ((w.GetPosY()
444  // - GetY( (double)tx /
445  // w.GetScaleX() +
446  // w.GetPosX())) *
447  // w.GetScaleY()) );
448  }
449  }
450 }
451 
452 IMPLEMENT_ABSTRACT_CLASS(mpFY, mpLayer)
453 
454 mpFY::mpFY(wxString name, int flags)
455 {
456  SetName(name);
457  m_flags = flags;
458  m_type = mpLAYER_PLOT;
459 }
460 
461 void mpFY::Plot(wxDC& dc, mpWindow& w)
462 {
463  if (m_visible)
464  {
465  dc.SetPen(m_pen);
466 
467  wxCoord i, ix;
468 
469  wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft();
470  wxCoord endPx = m_drawOutsideMargins ? w.GetScrX()
471  : w.GetScrX() - w.GetMarginRight();
472  wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop();
473  wxCoord maxYpx = m_drawOutsideMargins
474  ? w.GetScrY()
475  : w.GetScrY() - w.GetMarginBottom();
476 
477  if (m_pen.GetWidth() <= 1)
478  {
479  for (i = minYpx; i < maxYpx; ++i)
480  {
481  ix = w.x2p(GetX(w.p2y(i)));
482  if (m_drawOutsideMargins || ((ix >= startPx) && (ix <= endPx)))
483  dc.DrawPoint(ix, i);
484  }
485  }
486  else
487  {
488  for (i = 0; i < w.GetScrY(); ++i)
489  {
490  ix = w.x2p(GetX(w.p2y(i)));
491  if (m_drawOutsideMargins || ((ix >= startPx) && (ix <= endPx)))
492  dc.DrawLine(ix, i, ix, i);
493  // wxCoord c = w.x2p(GetX(w.p2y(i))); //(wxCoord)
494  // ((GetX( (double)i / w.GetScaleY() + w.GetPosY())
495  // - w.GetPosX()) * w.GetScaleX());
496  // dc.DrawLine(c, i, c, i);
497  }
498  }
499 
500  if (!m_name.IsEmpty() && m_showName)
501  {
502  dc.SetFont(m_font);
503 
504  wxCoord tx, ty;
505  dc.GetTextExtent(m_name, &tx, &ty);
506 
507  if ((m_flags & mpALIGNMASK) == mpALIGN_TOP)
508  ty = w.GetMarginTop() + 8;
509  else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER)
510  ty = ((w.GetScrY() - w.GetMarginTop() - w.GetMarginBottom() -
511  ty) /
512  2) +
513  w.GetMarginTop();
514  else
515  ty = w.GetScrY() - 8 - ty - w.GetMarginBottom();
516 
517  dc.DrawText(
518  m_name, w.x2p(GetX(w.p2y(ty))),
519  ty); // (wxCoord) ((GetX( (double)i / w.GetScaleY() +
520  // w.GetPosY()) - w.GetPosX()) * w.GetScaleX()), -ty);
521  }
522  }
523 }
524 
525 IMPLEMENT_ABSTRACT_CLASS(mpFXY, mpLayer)
526 
527 mpFXY::mpFXY(wxString name, int flags)
528 {
529  SetName(name);
530  m_flags = flags;
531  m_type = mpLAYER_PLOT;
532 }
533 
534 void mpFXY::UpdateViewBoundary(wxCoord xnew, wxCoord ynew)
535 {
536  // Keep track of how many points have been drawn and the bouding box
537  maxDrawX = (xnew > maxDrawX) ? xnew : maxDrawX;
538  minDrawX = (xnew < minDrawX) ? xnew : minDrawX;
539  maxDrawY = (maxDrawY > ynew) ? maxDrawY : ynew;
540  minDrawY = (minDrawY < ynew) ? minDrawY : ynew;
541  // drawnPoints++;
542 }
543 
544 void mpFXY::Plot(wxDC& dc, mpWindow& w)
545 {
546  if (m_visible)
547  {
548  dc.SetPen(m_pen);
549 
550  double x, y;
551  // Do this to reset the counters to evaluate bounding box for label
552  // positioning
553  Rewind();
554  GetNextXY(x, y);
555  maxDrawX = static_cast<int>(x);
556  minDrawX = static_cast<int>(x);
557  maxDrawY = static_cast<int>(y);
558  minDrawY = static_cast<int>(y);
559  // drawnPoints = 0;
560  Rewind();
561 
562  wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft();
563  wxCoord endPx = m_drawOutsideMargins ? w.GetScrX()
564  : w.GetScrX() - w.GetMarginRight();
565  wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop();
566  wxCoord maxYpx = m_drawOutsideMargins
567  ? w.GetScrY()
568  : w.GetScrY() - w.GetMarginBottom();
569 
570  wxCoord ix = 0, iy = 0;
571 
572  if (!m_continuous)
573  {
574  // for some reason DrawPoint does not use the current pen,
575  // so we use DrawLine for fat pens
576  if (m_pen.GetWidth() <= 1)
577  {
578  while (GetNextXY(x, y))
579  {
580  ix = w.x2p(x);
581  iy = w.y2p(y);
582  if (m_drawOutsideMargins ||
583  ((ix >= startPx) && (ix <= endPx) && (iy >= minYpx) &&
584  (iy <= maxYpx)))
585  {
586  dc.DrawPoint(ix, iy);
587  UpdateViewBoundary(ix, iy);
588  };
589  }
590  }
591  else
592  {
593  while (GetNextXY(x, y))
594  {
595  ix = w.x2p(x);
596  iy = w.y2p(y);
597  if (m_drawOutsideMargins ||
598  ((ix >= startPx) && (ix <= endPx) && (iy >= minYpx) &&
599  (iy <= maxYpx)))
600  {
601  dc.DrawLine(ix, iy, ix, iy);
602  UpdateViewBoundary(ix, iy);
603  }
604  // dc.DrawLine(cx, cy, cx, cy);
605  }
606  }
607  }
608  else
609  {
610  // Old code
611  wxCoord x0 = 0, c0 = 0;
612  bool first = TRUE;
613  while (GetNextXY(x, y))
614  {
615  wxCoord x1 =
616  w.x2p(x); // (wxCoord) ((x - w.GetPosX()) * w.GetScaleX());
617  wxCoord c1 =
618  w.y2p(y); // (wxCoord) ((w.GetPosY() - y) * w.GetScaleY());
619  if (first)
620  {
621  first = FALSE;
622  x0 = x1;
623  c0 = c1;
624  }
625  bool outUp, outDown;
626  if ((x1 >= startPx) && (x0 <= endPx))
627  {
628  outDown = (c0 > maxYpx) && (c1 > maxYpx);
629  outUp = (c0 < minYpx) && (c1 < minYpx);
630  if (!outUp && !outDown)
631  {
632  if (c1 != c0)
633  {
634  if (c0 < minYpx)
635  {
636  x0 =
637  (int)(((float)(minYpx - c0)) / ((float)(c1 - c0)) * (x1 - x0)) +
638  x0;
639  c0 = minYpx;
640  }
641  if (c0 > maxYpx)
642  {
643  x0 =
644  (int)(((float)(maxYpx - c0)) / ((float)(c1 - c0)) * (x1 - x0)) +
645  x0;
646  // wxLogDebug(wxT("old x0 = %d, new x0 = %d"),
647  // x0, newX0);
648  // x0 = newX0;
649  c0 = maxYpx;
650  }
651  if (c1 < minYpx)
652  {
653  x1 =
654  (int)(((float)(minYpx - c0)) / ((float)(c1 - c0)) * (x1 - x0)) +
655  x0;
656  c1 = minYpx;
657  }
658  if (c1 > maxYpx)
659  {
660  x1 =
661  (int)(((float)(maxYpx - c0)) / ((float)(c1 - c0)) * (x1 - x0)) +
662  x0;
663  // wxLogDebug(wxT("old x0 = %d, old x1 = %d, new
664  // x1 = %d, c0 = %d, c1 = %d, maxYpx = %d"), x0,
665  // x1, newX1, c0, c1, maxYpx);
666  // x1 = newX1;
667  c1 = maxYpx;
668  }
669  }
670  if (x1 != x0)
671  {
672  if (x0 < startPx)
673  {
674  c0 =
675  (int)(((float)(startPx - x0)) / ((float)(x1 - x0)) * (c1 - c0)) +
676  c0;
677  x0 = startPx;
678  }
679  if (x1 > endPx)
680  {
681  c1 =
682  (int)(((float)(endPx - x0)) / ((float)(x1 - x0)) * (c1 - c0)) +
683  c0;
684  x1 = endPx;
685  }
686  }
687  dc.DrawLine(x0, c0, x1, c1);
688  UpdateViewBoundary(x1, c1);
689  }
690  }
691  x0 = x1;
692  c0 = c1;
693  }
694  }
695 
696  if (!m_name.IsEmpty() && m_showName)
697  {
698  dc.SetFont(m_font);
699 
700  wxCoord tx, ty;
701  dc.GetTextExtent(m_name, &tx, &ty);
702 
703  // xxx implement else ... if (!HasBBox())
704  {
705  // const int sx = w.GetScrX();
706  // const int sy = w.GetScrY();
707 
708  if ((m_flags & mpALIGNMASK) == mpALIGN_NW)
709  {
710  tx = minDrawX + 8;
711  ty = maxDrawY + 8;
712  }
713  else if ((m_flags & mpALIGNMASK) == mpALIGN_NE)
714  {
715  tx = maxDrawX - tx - 8;
716  ty = maxDrawY + 8;
717  }
718  else if ((m_flags & mpALIGNMASK) == mpALIGN_SE)
719  {
720  tx = maxDrawX - tx - 8;
721  ty = minDrawY - ty - 8;
722  }
723  else
724  { // mpALIGN_SW
725  tx = minDrawX + 8;
726  ty = minDrawY - ty - 8;
727  }
728  }
729 
730  dc.DrawText(m_name, tx, ty);
731  }
732  }
733 }
734 
735 //-----------------------------------------------------------------------------
736 // mpProfile implementation
737 //-----------------------------------------------------------------------------
738 
739 IMPLEMENT_ABSTRACT_CLASS(mpProfile, mpLayer)
740 
741 mpProfile::mpProfile(wxString name, int flags)
742 {
743  SetName(name);
744  m_flags = flags;
745  m_type = mpLAYER_PLOT;
746 }
747 
748 void mpProfile::Plot(wxDC& dc, mpWindow& w)
749 {
750  if (m_visible)
751  {
752  dc.SetPen(m_pen);
753 
754  wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft();
755  wxCoord endPx = m_drawOutsideMargins ? w.GetScrX()
756  : w.GetScrX() - w.GetMarginRight();
757  wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop();
758  wxCoord maxYpx = m_drawOutsideMargins
759  ? w.GetScrY()
760  : w.GetScrY() - w.GetMarginBottom();
761 
762  // Plot profile linking subsequent point of the profile, instead of
763  // mpFY, which plots simple points.
764  for (wxCoord i = startPx; i < endPx; ++i)
765  {
766  wxCoord c0 = w.y2p(GetY(w.p2x(i))); // (wxCoord) ((w.GetYpos() -
767  // GetY( (double)i /
768  // w.GetXscl() + w.GetXpos()) )
769  // * w.GetYscl());
770  wxCoord c1 =
771  w.y2p(GetY(w.p2x(i + 1))); //(wxCoord) ((w.GetYpos() - GetY(
772  //(double)(i+1) / w.GetXscl() +
773  //(w.GetXpos() ) ) ) * w.GetYscl());
774  // c0 = (c0 <= maxYpx) ? ((c0 >= minYpx) ? c0 : minYpx) : maxYpx;
775  // c1 = (c1 <= maxYpx) ? ((c1 >= minYpx) ? c1 : minYpx) : maxYpx;
776  if (!m_drawOutsideMargins)
777  {
778  c0 = (c0 <= maxYpx) ? ((c0 >= minYpx) ? c0 : minYpx) : maxYpx;
779  c1 = (c1 <= maxYpx) ? ((c1 >= minYpx) ? c1 : minYpx) : maxYpx;
780  }
781  dc.DrawLine(i, c0, i + 1, c1);
782  };
783  if (!m_name.IsEmpty())
784  {
785  dc.SetFont(m_font);
786 
787  wxCoord tx, ty;
788  dc.GetTextExtent(m_name, &tx, &ty);
789 
790  if ((m_flags & mpALIGNMASK) == mpALIGN_RIGHT)
791  tx = (w.GetScrX() - tx) - w.GetMarginRight() - 8;
792  else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER)
793  tx = ((w.GetScrX() - w.GetMarginRight() - w.GetMarginLeft() -
794  tx) /
795  2) +
796  w.GetMarginLeft();
797  else
798  tx = w.GetMarginLeft() + 8;
799 
800  dc.DrawText(m_name, tx, w.y2p(GetY(w.p2x(tx)))); //(wxCoord)
801  //((w.GetPosY() -
802  // GetY( (double)tx
803  /// w.GetScaleX()
804  //+ w.GetPosX()))
805  //* w.GetScaleY())
806  //);
807  }
808  }
809 }
810 
811 //-----------------------------------------------------------------------------
812 // mpLayer implementations - furniture (scales, ...)
813 //-----------------------------------------------------------------------------
814 
815 #define mpLN10 2.3025850929940456840179914546844
816 
817 IMPLEMENT_DYNAMIC_CLASS(mpScaleX, mpLayer)
818 
819 mpScaleX::mpScaleX(wxString name, int flags, bool ticks, unsigned int type)
820 {
821  SetName(name);
822  SetFont((wxFont&)*wxSMALL_FONT);
823  SetPen((wxPen&)*wxGREY_PEN);
824  m_flags = flags;
825  m_ticks = ticks;
826  m_labelType = type;
827  m_type = mpLAYER_AXIS;
828  m_labelFormat = wxT("");
829 }
830 
831 void mpScaleX::Plot(wxDC& dc, mpWindow& w)
832 {
833  if (m_visible)
834  {
835  dc.SetPen(m_pen);
836  dc.SetFont(m_font);
837  int orgy = 0;
838 
839  const int extend = w.GetScrX(); // /2;
840  if (m_flags == mpALIGN_CENTER)
841  orgy = w.y2p(0); //(int)(w.GetPosY() * w.GetScaleY());
842  if (m_flags == mpALIGN_TOP)
843  {
844  if (m_drawOutsideMargins)
845  orgy = X_BORDER_SEPARATION;
846  else
847  orgy = w.GetMarginTop();
848  }
849  if (m_flags == mpALIGN_BOTTOM)
850  {
851  if (m_drawOutsideMargins)
852  orgy = X_BORDER_SEPARATION;
853  else
854  orgy = w.GetScrY() - w.GetMarginBottom();
855  }
856  if (m_flags == mpALIGN_BORDER_BOTTOM)
857  orgy = w.GetScrY() - 1; // dc.LogicalToDeviceY(0) - 1;
858  if (m_flags == mpALIGN_BORDER_TOP) orgy = 1; //-dc.LogicalToDeviceY(0);
859 
860  dc.DrawLine(0, orgy, w.GetScrX(), orgy);
861 
862  // To cut the axis line when draw outside margin is false, use this code
863  /*if (m_drawOutsideMargins == true)
864  dc.DrawLine( 0, orgy, w.GetScrX(), orgy);
865  else
866  dc.DrawLine( w.GetMarginLeft(), orgy, w.GetScrX() -
867  w.GetMarginRight(), orgy); */
868 
869  const double dig = floor(log(128.0 / w.GetScaleX()) / mpLN10);
870  const double step = exp(mpLN10 * dig);
871  const double end = w.GetPosX() + (double)extend / w.GetScaleX();
872 
873  wxCoord tx, ty;
874  wxString s;
875  wxString fmt;
876  int tmp = (int)dig;
877  if (m_labelType == mpX_NORMAL)
878  {
879  if (!m_labelFormat.IsEmpty())
880  {
881  fmt = m_labelFormat;
882  }
883  else
884  {
885  if (tmp >= 1)
886  {
887  fmt = wxT("%.f");
888  }
889  else
890  {
891  tmp = 8 - tmp;
892  fmt.Printf(wxT("%%.%df"), tmp >= -1 ? 2 : -tmp);
893  }
894  }
895  }
896  else
897  {
898  // Date and/or time axis representation
899  if (m_labelType == mpX_DATETIME)
900  {
901  fmt = (wxT("%04.0f-%02.0f-%02.0fT%02.0f:%02.0f:%02.0f"));
902  }
903  else if (m_labelType == mpX_DATE)
904  {
905  fmt = (wxT("%04.0f-%02.0f-%02.0f"));
906  }
907  else if ((m_labelType == mpX_TIME) && (end / 60 < 2))
908  {
909  fmt = (wxT("%02.0f:%02.3f"));
910  }
911  else
912  {
913  fmt = (wxT("%02.0f:%02.0f:%02.0f"));
914  }
915  }
916 
917  // double n = floor( (w.GetPosX() - (double)extend / w.GetScaleX()) /
918  // step ) * step ;
919  double n0 =
920  floor(
921  (w.GetPosX() /* - (double)(extend - w.GetMarginLeft() - w.GetMarginRight())/ w.GetScaleX() */) /
922  step) *
923  step;
924  double n = 0;
925 #ifdef MATHPLOT_DO_LOGGING
926  wxLogMessage(
927  wxT("mpScaleX::Plot: dig: %f , step: %f, end: %f, n: %f"), dig,
928  step, end, n0);
929 #endif
930  wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft();
931  wxCoord endPx = m_drawOutsideMargins ? w.GetScrX()
932  : w.GetScrX() - w.GetMarginRight();
933  wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop();
934  wxCoord maxYpx = m_drawOutsideMargins
935  ? w.GetScrY()
936  : w.GetScrY() - w.GetMarginBottom();
937 
938  tmp = -65535;
939  int labelH = 0; // Control labels heigth to decide where to put axis
940  // name (below labels or on top of axis)
941  int maxExtent = 0;
942  for (n = n0; n < end; n += step)
943  {
944  const int p = (int)((n - w.GetPosX()) * w.GetScaleX());
945 #ifdef MATHPLOT_DO_LOGGING
946  wxLogMessage(wxT("mpScaleX::Plot: n: %f -> p = %d"), n, p);
947 #endif
948  if ((p >= startPx) && (p <= endPx))
949  {
950  if (m_ticks)
951  { // draw axis ticks
952  if (m_flags == mpALIGN_BORDER_BOTTOM)
953  dc.DrawLine(p, orgy, p, orgy - 4);
954  else
955  dc.DrawLine(p, orgy, p, orgy + 4);
956  }
957  else
958  { // draw grid dotted lines
959 #if wxCHECK_VERSION(3, 0, 0)
960  m_pen.SetStyle(wxPENSTYLE_DOT);
961 #else
962  m_pen.SetStyle(wxDOT);
963 #endif
964  dc.SetPen(m_pen);
965  if ((m_flags == mpALIGN_BOTTOM) && !m_drawOutsideMargins)
966  {
967  dc.DrawLine(p, orgy + 4, p, minYpx);
968  }
969  else
970  {
971  if ((m_flags == mpALIGN_TOP) && !m_drawOutsideMargins)
972  {
973  dc.DrawLine(p, orgy - 4, p, maxYpx);
974  }
975  else
976  {
977  dc.DrawLine(p, 0 /*-w.GetScrY()*/, p, w.GetScrY());
978  }
979  }
980 #if wxCHECK_VERSION(3, 0, 0)
981  m_pen.SetStyle(wxPENSTYLE_SOLID);
982 #else
983  m_pen.SetStyle(wxSOLID);
984 #endif
985  dc.SetPen(m_pen);
986  }
987  // Write ticks labels in s string
988  if (m_labelType == mpX_NORMAL)
989  s.Printf(fmt, n);
990  else if (m_labelType == mpX_DATETIME)
991  {
992  auto when = (time_t)n;
993  struct tm tm = *localtime(&when);
994  s.Printf(
995  fmt, (double)tm.tm_year + 1900, (double)tm.tm_mon + 1,
996  (double)tm.tm_mday, (double)tm.tm_hour,
997  (double)tm.tm_min, (double)tm.tm_sec);
998  }
999  else if (m_labelType == mpX_DATE)
1000  {
1001  auto when = (time_t)n;
1002  struct tm tm = *localtime(&when);
1003  s.Printf(
1004  fmt, (double)tm.tm_year + 1900, (double)tm.tm_mon + 1,
1005  (double)tm.tm_mday);
1006  }
1007  else if (
1008  (m_labelType == mpX_TIME) || (m_labelType == mpX_HOURS))
1009  {
1010  double modulus = fabs(n);
1011  double sign = n / modulus;
1012  double hh = floor(modulus / 3600);
1013  double mm = floor((modulus - hh * 3600) / 60);
1014  double ss = modulus - hh * 3600 - mm * 60;
1015 #ifdef MATHPLOT_DO_LOGGING
1016  wxLogMessage(
1017  wxT("%02.0f Hours, %02.0f minutes, %02.0f seconds"),
1018  sign * hh, mm, ss);
1019 #endif // MATHPLOT_DO_LOGGING
1020  if (fmt.Len() == 20) // Format with hours has 11 chars
1021  s.Printf(fmt, sign * hh, mm, floor(ss));
1022  else
1023  s.Printf(fmt, sign * mm, ss);
1024  }
1025  dc.GetTextExtent(s, &tx, &ty);
1026  labelH = (labelH <= ty) ? ty : labelH;
1027  /* if ((p-tx/2-tmp) > 64) { // Problem about
1028  non-regular
1029  axis labels
1030  if ((m_flags == mpALIGN_BORDER_BOTTOM) ||
1031  (m_flags == mpALIGN_TOP)) {
1032  dc.DrawText( s, p-tx/2, orgy-4-ty);
1033  } else {
1034  dc.DrawText( s, p-tx/2, orgy+4);
1035  }
1036  tmp=p+tx/2;
1037  }
1038  */
1039  maxExtent = (tx > maxExtent)
1040  ? tx
1041  : maxExtent; // Keep in mind max label width
1042  }
1043  }
1044  // Actually draw labels, taking care of not overlapping them, and
1045  // distributing them regularly
1046  double labelStep = ceil(
1047  (maxExtent + mpMIN_X_AXIS_LABEL_SEPARATION) /
1048  (w.GetScaleX() * step)) *
1049  step;
1050  for (n = n0; n < end; n += labelStep)
1051  {
1052  const int p = (int)((n - w.GetPosX()) * w.GetScaleX());
1053 #ifdef MATHPLOT_DO_LOGGING
1054  wxLogMessage(
1055  wxT("mpScaleX::Plot: n_label = %f -> p_label = %d"), n, p);
1056 #endif
1057  if ((p >= startPx) && (p <= endPx))
1058  {
1059  // Write ticks labels in s string
1060  if (m_labelType == mpX_NORMAL)
1061  s.Printf(fmt, n);
1062  else if (m_labelType == mpX_DATETIME)
1063  {
1064  auto when = (time_t)n;
1065  struct tm tm = *localtime(&when);
1066  s.Printf(
1067  fmt, (double)tm.tm_year + 1900, (double)tm.tm_mon + 1,
1068  (double)tm.tm_mday, (double)tm.tm_hour,
1069  (double)tm.tm_min, (double)tm.tm_sec);
1070  }
1071  else if (m_labelType == mpX_DATE)
1072  {
1073  auto when = (time_t)n;
1074  struct tm tm = *localtime(&when);
1075  s.Printf(
1076  fmt, (double)tm.tm_year + 1900, (double)tm.tm_mon + 1,
1077  (double)tm.tm_mday);
1078  }
1079  else if (
1080  (m_labelType == mpX_TIME) || (m_labelType == mpX_HOURS))
1081  {
1082  double modulus = fabs(n);
1083  double sign = n / modulus;
1084  double hh = floor(modulus / 3600);
1085  double mm = floor((modulus - hh * 3600) / 60);
1086  double ss = modulus - hh * 3600 - mm * 60;
1087 #ifdef MATHPLOT_DO_LOGGING
1088  wxLogMessage(
1089  wxT("%02.0f Hours, %02.0f minutes, %02.0f seconds"),
1090  sign * hh, mm, ss);
1091 #endif // MATHPLOT_DO_LOGGING
1092  if (fmt.Len() == 20) // Format with hours has 11 chars
1093  s.Printf(fmt, sign * hh, mm, floor(ss));
1094  else
1095  s.Printf(fmt, sign * mm, ss);
1096  }
1097  dc.GetTextExtent(s, &tx, &ty);
1098  if ((m_flags == mpALIGN_BORDER_BOTTOM) ||
1099  (m_flags == mpALIGN_TOP))
1100  {
1101  dc.DrawText(s, p - tx / 2, orgy - 4 - ty);
1102  }
1103  else
1104  {
1105  dc.DrawText(s, p - tx / 2, orgy + 4);
1106  }
1107  }
1108  }
1109 
1110  // Draw axis name
1111  dc.GetTextExtent(m_name, &tx, &ty);
1112  switch (m_flags)
1113  {
1114  case mpALIGN_BORDER_BOTTOM:
1115  dc.DrawText(m_name, extend - tx - 4, orgy - 8 - ty - labelH);
1116  break;
1117  case mpALIGN_BOTTOM:
1118  {
1119  if ((!m_drawOutsideMargins) &&
1120  (w.GetMarginBottom() > (ty + labelH + 8)))
1121  {
1122  dc.DrawText(
1123  m_name, (endPx - startPx - tx) >> 1, orgy + 6 + labelH);
1124  }
1125  else
1126  {
1127  dc.DrawText(m_name, extend - tx - 4, orgy - 4 - ty);
1128  }
1129  }
1130  break;
1131  case mpALIGN_CENTER:
1132  dc.DrawText(m_name, extend - tx - 4, orgy - 4 - ty);
1133  break;
1134  case mpALIGN_TOP:
1135  {
1136  if ((!m_drawOutsideMargins) &&
1137  (w.GetMarginTop() > (ty + labelH + 8)))
1138  {
1139  dc.DrawText(
1140  m_name, (endPx - startPx - tx) >> 1,
1141  orgy - 6 - ty - labelH);
1142  }
1143  else
1144  {
1145  dc.DrawText(m_name, extend - tx - 4, orgy + 4);
1146  }
1147  }
1148  break;
1149  case mpALIGN_BORDER_TOP:
1150  dc.DrawText(m_name, extend - tx - 4, orgy + 6 + labelH);
1151  break;
1152  default:
1153  break;
1154  }
1155  }
1156  /* if (m_flags != mpALIGN_TOP) {
1157 
1158  if ((m_flags == mpALIGN_BORDER_BOTTOM) || (m_flags == mpALIGN_TOP))
1159  {
1160  dc.DrawText( m_name, extend - tx - 4, orgy - 4 - (ty*2));
1161  } else {
1162  dc.DrawText( m_name, extend - tx - 4, orgy - 4 - ty); //orgy + 4
1163  + ty);
1164  }
1165  }; */
1166 }
1167 
1168 IMPLEMENT_DYNAMIC_CLASS(mpScaleY, mpLayer)
1169 
1170 mpScaleY::mpScaleY(wxString name, int flags, bool ticks)
1171 {
1172  SetName(name);
1173  SetFont((wxFont&)*wxSMALL_FONT);
1174  SetPen((wxPen&)*wxGREY_PEN);
1175  m_flags = flags;
1176  m_ticks = ticks;
1177  m_type = mpLAYER_AXIS;
1178  m_labelFormat = wxT("");
1179 }
1180 
1181 void mpScaleY::Plot(wxDC& dc, mpWindow& w)
1182 {
1183  if (m_visible)
1184  {
1185  dc.SetPen(m_pen);
1186  dc.SetFont(m_font);
1187 
1188  int orgx = 0;
1189  const int extend = w.GetScrY(); // /2;
1190  if (m_flags == mpALIGN_CENTER)
1191  orgx = w.x2p(0); //(int)(w.GetPosX() * w.GetScaleX());
1192  if (m_flags == mpALIGN_LEFT)
1193  {
1194  if (m_drawOutsideMargins)
1195  orgx = Y_BORDER_SEPARATION;
1196  else
1197  orgx = w.GetMarginLeft();
1198  }
1199  if (m_flags == mpALIGN_RIGHT)
1200  {
1201  if (m_drawOutsideMargins)
1202  orgx = w.GetScrX() - Y_BORDER_SEPARATION;
1203  else
1204  orgx = w.GetScrX() - w.GetMarginRight();
1205  }
1206  if (m_flags == mpALIGN_BORDER_RIGHT)
1207  orgx = w.GetScrX() - 1; // dc.LogicalToDeviceX(0) - 1;
1208  if (m_flags == mpALIGN_BORDER_LEFT)
1209  orgx = 1; //-dc.LogicalToDeviceX(0);
1210 
1211  // Draw line
1212  dc.DrawLine(orgx, 0, orgx, extend);
1213 
1214  // To cut the axis line when draw outside margin is false, use this code
1215  /* if (m_drawOutsideMargins == true)
1216  dc.DrawLine( orgx, 0, orgx, extend);
1217  else
1218  dc.DrawLine( orgx, w.GetMarginTop(), orgx, w.GetScrY() -
1219  w.GetMarginBottom()); */
1220 
1221  const double dig = floor(log(128.0 / w.GetScaleY()) / mpLN10);
1222  const double step = exp(mpLN10 * dig);
1223  const double end = w.GetPosY() + (double)extend / w.GetScaleY();
1224 
1225  wxCoord tx, ty;
1226  wxString s;
1227  wxString fmt;
1228  int tmp = (int)dig;
1229  double maxScaleAbs = fabs(w.GetDesiredYmax());
1230  double minScaleAbs = fabs(w.GetDesiredYmin());
1231  double endscale =
1232  (maxScaleAbs > minScaleAbs) ? maxScaleAbs : minScaleAbs;
1233  if (m_labelFormat.IsEmpty())
1234  {
1235  if ((endscale < 1e4) && (endscale > 1e-3))
1236  fmt = wxT("%.2f");
1237  else
1238  fmt = wxT("%.1e");
1239  }
1240  else
1241  {
1242  fmt = m_labelFormat;
1243  }
1244  /* if (tmp>=1)
1245  {*/
1246  // fmt = wxT("%7.5g");
1247  // }
1248  // else
1249  // {
1250  // tmp=8-tmp;
1251  // fmt.Printf(wxT("%%.%dg"), (tmp >= -1) ? 2 : -tmp);
1252  // }
1253 
1254  double n =
1255  floor(
1256  (w.GetPosY() -
1257  (double)(extend - w.GetMarginTop() - w.GetMarginBottom()) /
1258  w.GetScaleY()) /
1259  step) *
1260  step;
1261 
1262  /* wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft(); */
1263  wxCoord endPx = m_drawOutsideMargins ? w.GetScrX()
1264  : w.GetScrX() - w.GetMarginRight();
1265  wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop();
1266  wxCoord maxYpx = m_drawOutsideMargins
1267  ? w.GetScrY()
1268  : w.GetScrY() - w.GetMarginBottom();
1269 
1270  tmp = 65536;
1271  int labelW = 0;
1272  // Before staring cycle, calculate label height
1273  int labelHeigth = 0;
1274  s.Printf(fmt, n);
1275  dc.GetTextExtent(s, &tx, &labelHeigth);
1276  for (; n < end; n += step)
1277  {
1278  const int p = (int)((w.GetPosY() - n) * w.GetScaleY());
1279  if ((p >= minYpx) && (p <= maxYpx))
1280  {
1281  if (m_ticks)
1282  { // Draw axis ticks
1283  if (m_flags == mpALIGN_BORDER_LEFT)
1284  {
1285  dc.DrawLine(orgx, p, orgx + 4, p);
1286  }
1287  else
1288  {
1289  dc.DrawLine(
1290  orgx - 4, p, orgx, p); //( orgx, p, orgx+4, p);
1291  }
1292  }
1293  else
1294  {
1295 #if wxCHECK_VERSION(3, 0, 0)
1296  m_pen.SetStyle(wxPENSTYLE_DOT);
1297 #else
1298  m_pen.SetStyle(wxDOT);
1299 #endif
1300  dc.SetPen(m_pen);
1301  if ((m_flags == mpALIGN_LEFT) && !m_drawOutsideMargins)
1302  {
1303  dc.DrawLine(orgx - 4, p, endPx, p);
1304  }
1305  else
1306  {
1307  if ((m_flags == mpALIGN_RIGHT) && !m_drawOutsideMargins)
1308  {
1309  dc.DrawLine(minYpx, p, orgx + 4, p);
1310  }
1311  else
1312  {
1313  dc.DrawLine(0 /*-w.GetScrX()*/, p, w.GetScrX(), p);
1314  }
1315  }
1316 #if wxCHECK_VERSION(3, 0, 0)
1317  m_pen.SetStyle(wxPENSTYLE_SOLID);
1318 #else
1319  m_pen.SetStyle(wxSOLID);
1320 #endif
1321  dc.SetPen(m_pen);
1322  }
1323  // Print ticks labels
1324  s.Printf(fmt, n);
1325  dc.GetTextExtent(s, &tx, &ty);
1326 #ifdef MATHPLOT_DO_LOGGING
1327  if (ty != labelHeigth)
1328  wxLogMessage(
1329  wxT("mpScaleY::Plot: ty(%f) and labelHeigth(%f) "
1330  "differ!"),
1331  ty, labelHeigth);
1332 #endif
1333  labelW = (labelW <= tx) ? tx : labelW;
1334  if ((tmp - p + labelHeigth / 2) > mpMIN_Y_AXIS_LABEL_SEPARATION)
1335  {
1336  if ((m_flags == mpALIGN_BORDER_LEFT) ||
1337  (m_flags == mpALIGN_RIGHT))
1338  dc.DrawText(s, orgx + 4, p - ty / 2);
1339  else
1340  dc.DrawText(
1341  s, orgx - 4 - tx,
1342  p - ty / 2); //( s, orgx+4, p-ty/2);
1343  tmp = p - labelHeigth / 2;
1344  }
1345  }
1346  }
1347  // Draw axis name
1348 
1349  dc.GetTextExtent(m_name, &tx, &ty);
1350  switch (m_flags)
1351  {
1352  case mpALIGN_BORDER_LEFT:
1353  dc.DrawText(m_name, labelW + 8, 4);
1354  break;
1355  case mpALIGN_LEFT:
1356  {
1357  if ((!m_drawOutsideMargins) &&
1358  (w.GetMarginLeft() > (ty + labelW + 8)))
1359  {
1360  dc.DrawRotatedText(
1361  m_name, orgx - 6 - labelW - ty,
1362  (maxYpx - minYpx + tx) >> 1, 90);
1363  }
1364  else
1365  {
1366  dc.DrawText(m_name, orgx + 4, 4);
1367  }
1368  }
1369  break;
1370  case mpALIGN_CENTER:
1371  dc.DrawText(m_name, orgx + 4, 4);
1372  break;
1373  case mpALIGN_RIGHT:
1374  {
1375  if ((!m_drawOutsideMargins) &&
1376  (w.GetMarginRight() > (ty + labelW + 8)))
1377  {
1378  dc.DrawRotatedText(
1379  m_name, orgx + 6 + labelW, (maxYpx - minYpx + tx) >> 1,
1380  90);
1381  }
1382  else
1383  {
1384  dc.DrawText(m_name, orgx - tx - 4, 4);
1385  }
1386  }
1387  break;
1388  case mpALIGN_BORDER_RIGHT:
1389  dc.DrawText(m_name, orgx - 6 - tx - labelW, 4);
1390  break;
1391  default:
1392  break;
1393  }
1394  }
1395 
1396  /* if (m_flags != mpALIGN_RIGHT) {
1397  dc.GetTextExtent(m_name, &tx, &ty);
1398  if (m_flags == mpALIGN_BORDER_LEFT) {
1399  dc.DrawText( m_name, orgx-tx-4, -extend + ty + 4);
1400  } else {
1401  if (m_flags == mpALIGN_BORDER_RIGHT )
1402  dc.DrawText( m_name, orgx-(tx*2)-4, -extend + ty + 4);
1403  else
1404  dc.DrawText( m_name, orgx + 4, -extend + 4);
1405  }
1406  }; */
1407 }
1408 
1409 //-----------------------------------------------------------------------------
1410 // mpWindow
1411 //-----------------------------------------------------------------------------
1412 
1413 IMPLEMENT_DYNAMIC_CLASS(mpWindow, wxWindow)
1414 
1415 BEGIN_EVENT_TABLE(mpWindow, wxWindow)
1416 EVT_PAINT(mpWindow::OnPaint)
1417 EVT_SIZE(mpWindow::OnSize)
1418 EVT_SCROLLWIN_THUMBTRACK(mpWindow::OnScrollThumbTrack)
1419 EVT_SCROLLWIN_PAGEUP(mpWindow::OnScrollPageUp)
1420 EVT_SCROLLWIN_PAGEDOWN(mpWindow::OnScrollPageDown)
1421 EVT_SCROLLWIN_LINEUP(mpWindow::OnScrollLineUp)
1422 EVT_SCROLLWIN_LINEDOWN(mpWindow::OnScrollLineDown)
1423 EVT_SCROLLWIN_TOP(mpWindow::OnScrollTop)
1424 EVT_SCROLLWIN_BOTTOM(mpWindow::OnScrollBottom)
1425 
1426 EVT_MIDDLE_UP(mpWindow::OnShowPopupMenu)
1427 EVT_RIGHT_DOWN(mpWindow::OnMouseRightDown) // JLB
1428 EVT_RIGHT_UP(mpWindow::OnShowPopupMenu)
1429 EVT_MOUSEWHEEL(mpWindow::OnMouseWheel) // JLB
1430 EVT_MOTION(mpWindow::OnMouseMove) // JLB
1431 EVT_LEFT_DOWN(mpWindow::OnMouseLeftDown)
1432 EVT_LEFT_UP(mpWindow::OnMouseLeftRelease)
1433 
1434 EVT_MENU(mpID_CENTER, mpWindow::OnCenter)
1435 EVT_MENU(mpID_FIT, mpWindow::OnFit)
1436 EVT_MENU(mpID_ZOOM_IN, mpWindow::OnZoomIn)
1437 EVT_MENU(mpID_ZOOM_OUT, mpWindow::OnZoomOut)
1438 EVT_MENU(mpID_LOCKASPECT, mpWindow::OnLockAspect)
1439 EVT_MENU(mpID_HELP_MOUSE, mpWindow::OnMouseHelp)
1440 EVT_MENU(mpID_PRINT, mpWindow::OnPrintMenu)
1441 END_EVENT_TABLE()
1442 
1443 mpWindow::mpWindow(
1444  wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
1445  long flag)
1446  : wxWindow(parent, id, pos, size, flag, wxT("mathplot"))
1447 {
1448  m_scaleX = m_scaleY = 1.0;
1449  m_posX = m_posY = 0;
1450  m_desiredXmin = m_desiredYmin = 0;
1451  m_desiredXmax = m_desiredYmax = 1;
1452  m_scrX = m_scrY = 64; // Fixed from m_scrX = m_scrX = 64;
1453  m_minX = m_minY = 0;
1454  m_maxX = m_maxY = 0;
1455  m_last_lx = m_last_ly = 0;
1456  m_buff_bmp = nullptr;
1457  m_enableDoubleBuffer = FALSE;
1458  m_enableMouseNavigation = TRUE;
1459  m_mouseMovedAfterRightClick = FALSE;
1460  m_movingInfoLayer = nullptr;
1461  // Set margins to 0
1462  m_marginTop = 0;
1463  m_marginRight = 0;
1464  m_marginBottom = 0;
1465  m_marginLeft = 0;
1466 
1467  m_lockaspect = FALSE;
1468 
1469  m_popmenu.Append(
1470  mpID_CENTER, _("Center"), _("Center plot view to this position"));
1471  m_popmenu.Append(mpID_FIT, _("Fit"), _("Set plot view to show all items"));
1472  m_popmenu.Append(mpID_ZOOM_IN, _("Zoom in"), _("Zoom in plot view."));
1473  m_popmenu.Append(mpID_ZOOM_OUT, _("Zoom out"), _("Zoom out plot view."));
1474  m_popmenu.AppendCheckItem(
1475  mpID_LOCKASPECT, _("Lock aspect"),
1476  _("Lock horizontal and vertical zoom aspect."));
1477  m_popmenu.Append(
1478  mpID_PRINT, _("Print..."), _("Allows printing the graph."));
1479  m_popmenu.Append(
1480  mpID_HELP_MOUSE, _("Show mouse commands..."),
1481  _("Show help about the mouse commands."));
1482 
1483  m_layers.clear();
1484  SetBackgroundColour(*wxWHITE);
1485  m_bgColour = *wxWHITE;
1486  m_fgColour = *wxBLACK;
1487 
1488  m_enableScrollBars = false;
1489  SetSizeHints(128, 128);
1490 
1491  m_mouseLClick_X = INVALID_CLICK_COORDS;
1492  m_mouseLClick_Y = INVALID_CLICK_COORDS;
1493 
1494  // J.L.Blanco: Eliminates the "flick" with the double buffer.
1495  SetBackgroundStyle(wxBG_STYLE_CUSTOM);
1496 
1497  UpdateAll();
1498 }
1499 
1500 mpWindow::~mpWindow()
1501 {
1502  // Free all the layers:
1503  DelAllLayers(true, false);
1504 
1505  if (m_buff_bmp)
1506  {
1507  delete m_buff_bmp;
1508  m_buff_bmp = nullptr;
1509  }
1510 }
1511 
1512 // Mouse handler, for detecting when the user drag with the right button or just
1513 // "clicks" for the menu
1514 // JLB
1515 void mpWindow::OnMouseRightDown(wxMouseEvent& event)
1516 {
1517  m_mouseMovedAfterRightClick = FALSE;
1518  m_mouseRClick_X = event.GetX();
1519  m_mouseRClick_Y = event.GetY();
1520  if (m_enableMouseNavigation)
1521  {
1522  SetCursor(*wxCROSS_CURSOR);
1523  }
1524 }
1525 
1526 // Process mouse wheel events
1527 // JLB
1528 void mpWindow::OnMouseWheel(wxMouseEvent& event)
1529 {
1530  if (!m_enableMouseNavigation)
1531  {
1532  event.Skip();
1533  return;
1534  }
1535 
1536  // GetClientSize( &m_scrX,&m_scrY);
1537 
1538  if (event.m_controlDown)
1539  {
1540  wxPoint clickPt(event.GetX(), event.GetY());
1541  // CTRL key hold: Zoom in/out:
1542  if (event.GetWheelRotation() > 0)
1543  ZoomIn(clickPt);
1544  else
1545  ZoomOut(clickPt);
1546  }
1547  else
1548  {
1549  // Scroll vertically or horizontally (this is SHIFT is hold down).
1550  int change =
1551  -event.GetWheelRotation(); // Opposite direction (More intuitive)!
1552  float changeUnitsX = change / static_cast<float>(m_scaleX);
1553  float changeUnitsY = change / static_cast<float>(m_scaleY);
1554 
1555  if (event.m_shiftDown)
1556  {
1557  m_posX += changeUnitsX;
1558  m_desiredXmax += changeUnitsX;
1559  m_desiredXmin += changeUnitsX;
1560  }
1561  else
1562  {
1563  m_posY -= changeUnitsY;
1564  m_desiredYmax -= changeUnitsY;
1565  m_desiredYmax -= changeUnitsY;
1566  }
1567 
1568  UpdateAll();
1569  }
1570 }
1571 
1572 // If the user "drags" with the right buttom pressed, do "pan"
1573 // JLB
1574 void mpWindow::OnMouseMove(wxMouseEvent& event)
1575 {
1576  if (!m_enableMouseNavigation)
1577  {
1578  event.Skip();
1579  return;
1580  }
1581 
1582  if (event.m_rightDown)
1583  {
1584  m_mouseMovedAfterRightClick =
1585  TRUE; // Hides the popup menu after releasing the button!
1586 
1587  // The change:
1588  int Ax = m_mouseRClick_X - event.GetX();
1589  int Ay = m_mouseRClick_Y - event.GetY();
1590 
1591  // For the next event, use relative to this coordinates.
1592  m_mouseRClick_X = event.GetX();
1593  m_mouseRClick_Y = event.GetY();
1594 
1595  double Ax_units = Ax / m_scaleX;
1596  double Ay_units = -Ay / m_scaleY;
1597 
1598  m_posX += Ax_units;
1599  m_posY += Ay_units;
1600  m_desiredXmax += Ax_units;
1601  m_desiredXmin += Ax_units;
1602  m_desiredYmax += Ay_units;
1603  m_desiredYmin += Ay_units;
1604 
1605  UpdateAll();
1606 
1607 #ifdef MATHPLOT_DO_LOGGING
1608  wxLogMessage(
1609  _("[mpWindow::OnMouseMove] Ax:%i Ay:%i m_posX:%f m_posY:%f"), Ax,
1610  Ay, m_posX, m_posY);
1611 #endif
1612  }
1613  else
1614  {
1615  if (event.m_leftDown)
1616  {
1617  if (m_movingInfoLayer == nullptr)
1618  {
1619  wxClientDC dc(this);
1620  wxPen pen(*wxBLACK, 1, wxPENSTYLE_DOT);
1621  dc.SetPen(pen);
1622  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1623  dc.DrawRectangle(
1624  m_mouseLClick_X, m_mouseLClick_Y,
1625  event.GetX() - m_mouseLClick_X,
1626  event.GetY() - m_mouseLClick_Y);
1627  }
1628  else
1629  {
1630  wxPoint moveVector(
1631  event.GetX() - m_mouseLClick_X,
1632  event.GetY() - m_mouseLClick_Y);
1633  m_movingInfoLayer->Move(moveVector);
1634  }
1635  UpdateAll();
1636  }
1637  else
1638  {
1639  wxLayerList::iterator li;
1640  for (li = m_layers.begin(); li != m_layers.end(); ++li)
1641  {
1642  if ((*li)->IsInfo() && (*li)->IsVisible())
1643  {
1644  auto* tmpLyr = (mpInfoLayer*)(*li);
1645  tmpLyr->UpdateInfo(*this, event);
1646  // UpdateAll();
1647  RefreshRect(tmpLyr->GetRectangle());
1648  }
1649  }
1650  /* if (m_coordTooltip) {
1651  wxString toolTipContent;
1652  toolTipContent.Printf(_("X = %f\nY = %f"), p2x(event.GetX()),
1653  p2y(event.GetY()));
1654  wxTipWindow** ptr = nullptr;
1655  wxRect rectBounds(event.GetX(), event.GetY(), 5, 5);
1656  wxTipWindow* tip = new wxTipWindow(this, toolTipContent, 100,
1657  ptr, &rectBounds);
1658 
1659  } */
1660  }
1661  }
1662  event.Skip();
1663 }
1664 
1665 void mpWindow::OnMouseLeftDown(wxMouseEvent& event)
1666 {
1667  m_mouseLClick_X = event.GetX();
1668  m_mouseLClick_Y = event.GetY();
1669 #ifdef MATHPLOT_DO_LOGGING
1670  wxLogMessage(
1671  _("mpWindow::OnMouseLeftDown() X = %d , Y = %d"), event.GetX(),
1672  event.GetY()); /*m_mouseLClick_X, m_mouseLClick_Y);*/
1673 #endif
1674  wxPoint pointClicked = event.GetPosition();
1675  m_movingInfoLayer = IsInsideInfoLayer(pointClicked);
1676  if (m_movingInfoLayer != nullptr)
1677  {
1678 #ifdef MATHPLOT_DO_LOGGING
1679  wxLogMessage(
1680  _("mpWindow::OnMouseLeftDown() started moving layer %lx"),
1681  (long int)m_movingInfoLayer); /*m_mouseLClick_X, m_mouseLClick_Y);*/
1682 #endif
1683  }
1684  event.Skip();
1685 }
1686 
1687 void mpWindow::OnMouseLeftRelease(wxMouseEvent& event)
1688 {
1689  if (m_mouseLClick_X != INVALID_CLICK_COORDS) // Don't get events that don't
1690  // started in this window:
1691  {
1692  wxPoint release(event.GetX(), event.GetY());
1693  wxPoint press(m_mouseLClick_X, m_mouseLClick_Y);
1694  if (m_movingInfoLayer != nullptr)
1695  {
1696  m_movingInfoLayer->UpdateReference();
1697  m_movingInfoLayer = nullptr;
1698  }
1699  else
1700  {
1701  if (release != press)
1702  {
1703  ZoomRect(press, release);
1704  } /*else {
1705  if (m_coordTooltip) {
1706  wxString toolTipContent;
1707  toolTipContent.Printf(_("X = %f\nY = %f"),
1708  p2x(event.GetX()), p2y(event.GetY()));
1709  SetToolTip(toolTipContent);
1710  }
1711  } */
1712  }
1713  m_mouseLClick_X = INVALID_CLICK_COORDS;
1714  }
1715  event.Skip();
1716 }
1717 
1718 void mpWindow::Fit()
1719 {
1720  if (UpdateBBox()) Fit(m_minX, m_maxX, m_minY, m_maxY);
1721 }
1722 
1723 // JL
1724 void mpWindow::Fit(
1725  double xMin, double xMax, double yMin, double yMax, wxCoord* printSizeX,
1726  wxCoord* printSizeY)
1727 {
1728  // Save desired borders:
1729  m_desiredXmin = xMin;
1730  m_desiredXmax = xMax;
1731  m_desiredYmin = yMin;
1732  m_desiredYmax = yMax;
1733 
1734  if (printSizeX != nullptr && printSizeY != nullptr)
1735  {
1736  // Printer:
1737  m_scrX = *printSizeX;
1738  m_scrY = *printSizeY;
1739  }
1740  else
1741  {
1742  // Normal case (screen):
1743  GetClientSize(&m_scrX, &m_scrY);
1744  }
1745 
1746  double Ax, Ay;
1747 
1748  Ax = xMax - xMin;
1749  Ay = yMax - yMin;
1750 
1751  m_scaleX = (Ax != 0) ? (m_scrX - m_marginLeft - m_marginRight) / Ax
1752  : 1; // m_scaleX = (Ax!=0) ? m_scrX/Ax : 1;
1753  m_scaleY = (Ay != 0) ? (m_scrY - m_marginTop - m_marginBottom) / Ay
1754  : 1; // m_scaleY = (Ay!=0) ? m_scrY/Ay : 1;
1755 
1756  if (m_lockaspect)
1757  {
1758 #ifdef MATHPLOT_DO_LOGGING
1759  wxLogMessage(
1760  _("mpWindow::Fit()(lock) m_scaleX=%f,m_scaleY=%f"), m_scaleX,
1761  m_scaleY);
1762 #endif
1763  // Keep the lowest "scale" to fit the whole range required by that axis
1764  // (to actually "fit"!):
1765  double s = m_scaleX < m_scaleY ? m_scaleX : m_scaleY;
1766  m_scaleX = s;
1767  m_scaleY = s;
1768  }
1769 
1770  // Adjusts corner coordinates: This should be simply:
1771  // m_posX = m_minX;
1772  // m_posY = m_maxY;
1773  // But account for centering if we have lock aspect:
1774  m_posX = (xMin + xMax) / 2 -
1775  ((m_scrX - m_marginLeft - m_marginRight) / 2. + m_marginLeft) /
1776  m_scaleX; // m_posX = (xMin+xMax)/2 - (m_scrX/2)/m_scaleX;
1777  // m_posY = (yMin+yMax)/2 + ((m_scrY - m_marginTop - m_marginBottom)/2. -
1778  // m_marginTop)/m_scaleY; // m_posY = (yMin+yMax)/2 + (m_scrY/2)/m_scaleY;
1779  m_posY = (yMin + yMax) / 2 +
1780  ((m_scrY - m_marginTop - m_marginBottom) / 2. + m_marginTop) /
1781  m_scaleY; // m_posY = (yMin+yMax)/2 + (m_scrY/2)/m_scaleY;
1782 
1783 #ifdef MATHPLOT_DO_LOGGING
1784  wxLogMessage(
1785  _("mpWindow::Fit() m_desiredXmin=%f m_desiredXmax=%f m_desiredYmin=%f "
1786  "m_desiredYmax=%f"),
1787  xMin, xMax, yMin, yMax);
1788  wxLogMessage(
1789  _("mpWindow::Fit() m_scaleX = %f , m_scrX = %d,m_scrY=%d, Ax=%f, "
1790  "Ay=%f, m_posX=%f, m_posY=%f"),
1791  m_scaleX, m_scrX, m_scrY, Ax, Ay, m_posX, m_posY);
1792 #endif
1793 
1794  // It is VERY IMPORTANT to DO NOT call Refresh if we are drawing to the
1795  // printer!!
1796  // Otherwise, the DC dimensions will be those of the window instead of the
1797  // printer device
1798  if (printSizeX == nullptr || printSizeY == nullptr) UpdateAll();
1799 }
1800 
1801 // Patch ngpaton
1802 void mpWindow::DoZoomInXCalc(const int staticXpixel)
1803 {
1804  // Preserve the position of the clicked point:
1805  double staticX = p2x(staticXpixel);
1806  // Zoom in:
1807  m_scaleX = m_scaleX * zoomIncrementalFactor;
1808  // Adjust the new m_posx
1809  m_posX = staticX - (staticXpixel / m_scaleX);
1810  // Adjust desired
1811  m_desiredXmin = m_posX;
1812  m_desiredXmax =
1813  m_posX + (m_scrX - (m_marginLeft + m_marginRight)) / m_scaleX;
1814 #ifdef MATHPLOT_DO_LOGGING
1815  wxLogMessage(
1816  _("mpWindow::DoZoomInXCalc() prior X coord: (%f), new X coord: (%f) "
1817  "SHOULD BE EQUAL!!"),
1818  staticX, p2x(staticXpixel));
1819 #endif
1820 }
1821 
1822 void mpWindow::DoZoomInYCalc(const int staticYpixel)
1823 {
1824  // Preserve the position of the clicked point:
1825  double staticY = p2y(staticYpixel);
1826  // Zoom in:
1827  m_scaleY = m_scaleY * zoomIncrementalFactor;
1828  // Adjust the new m_posy:
1829  m_posY = staticY + (staticYpixel / m_scaleY);
1830  // Adjust desired
1831  m_desiredYmax = m_posY;
1832  m_desiredYmin =
1833  m_posY - (m_scrY - (m_marginTop + m_marginBottom)) / m_scaleY;
1834 #ifdef MATHPLOT_DO_LOGGING
1835  wxLogMessage(
1836  _("mpWindow::DoZoomInYCalc() prior Y coord: (%f), new Y coord: (%f) "
1837  "SHOULD BE EQUAL!!"),
1838  staticY, p2y(staticYpixel));
1839 #endif
1840 }
1841 
1842 void mpWindow::DoZoomOutXCalc(const int staticXpixel)
1843 {
1844  // Preserve the position of the clicked point:
1845  double staticX = p2x(staticXpixel);
1846  // Zoom out:
1847  m_scaleX = m_scaleX / zoomIncrementalFactor;
1848  // Adjust the new m_posx/y:
1849  m_posX = staticX - (staticXpixel / m_scaleX);
1850  // Adjust desired
1851  m_desiredXmin = m_posX;
1852  m_desiredXmax =
1853  m_posX + (m_scrX - (m_marginLeft + m_marginRight)) / m_scaleX;
1854 #ifdef MATHPLOT_DO_LOGGING
1855  wxLogMessage(
1856  _("mpWindow::DoZoomOutXCalc() prior X coord: (%f), new X coord: (%f) "
1857  "SHOULD BE EQUAL!!"),
1858  staticX, p2x(staticXpixel));
1859 #endif
1860 }
1861 
1862 void mpWindow::DoZoomOutYCalc(const int staticYpixel)
1863 {
1864  // Preserve the position of the clicked point:
1865  double staticY = p2y(staticYpixel);
1866  // Zoom out:
1867  m_scaleY = m_scaleY / zoomIncrementalFactor;
1868  // Adjust the new m_posx/y:
1869  m_posY = staticY + (staticYpixel / m_scaleY);
1870  // Adjust desired
1871  m_desiredYmax = m_posY;
1872  m_desiredYmin =
1873  m_posY - (m_scrY - (m_marginTop + m_marginBottom)) / m_scaleY;
1874 #ifdef MATHPLOT_DO_LOGGING
1875  wxLogMessage(
1876  _("mpWindow::DoZoomOutYCalc() prior Y coord: (%f), new Y coord: (%f) "
1877  "SHOULD BE EQUAL!!"),
1878  staticY, p2y(staticYpixel));
1879 #endif
1880 }
1881 
1882 void mpWindow::ZoomIn(const wxPoint& centerPoint)
1883 {
1884  wxPoint c(centerPoint);
1885  if (c == wxDefaultPosition)
1886  {
1887  GetClientSize(&m_scrX, &m_scrY);
1888  c.x = (m_scrX - m_marginLeft - m_marginRight) / 2 +
1889  m_marginLeft; // c.x = m_scrX/2;
1890  c.y = (m_scrY - m_marginTop - m_marginBottom) / 2 -
1891  m_marginTop; // c.y = m_scrY/2;
1892  }
1893 
1894  // Preserve the position of the clicked point:
1895  double prior_layer_x = p2x(c.x);
1896  double prior_layer_y = p2y(c.y);
1897 
1898  // Zoom in:
1899  m_scaleX = m_scaleX * zoomIncrementalFactor;
1900  m_scaleY = m_scaleY * zoomIncrementalFactor;
1901 
1902  // Adjust the new m_posx/y:
1903  m_posX = prior_layer_x - c.x / m_scaleX;
1904  m_posY = prior_layer_y + c.y / m_scaleY;
1905 
1906  m_desiredXmin = m_posX;
1907  m_desiredXmax =
1908  m_posX + (m_scrX - m_marginLeft - m_marginRight) /
1909  m_scaleX; // m_desiredXmax = m_posX + m_scrX / m_scaleX;
1910  m_desiredYmax = m_posY;
1911  m_desiredYmin =
1912  m_posY - (m_scrY - m_marginTop - m_marginBottom) /
1913  m_scaleY; // m_desiredYmin = m_posY - m_scrY / m_scaleY;
1914 
1915 #ifdef MATHPLOT_DO_LOGGING
1916  wxLogMessage(
1917  _("mpWindow::ZoomIn() prior coords: (%f,%f), new coords: (%f,%f) "
1918  "SHOULD BE EQUAL!!"),
1919  prior_layer_x, prior_layer_y, p2x(c.x), p2y(c.y));
1920 #endif
1921 
1922  UpdateAll();
1923 }
1924 
1925 void mpWindow::ZoomOut(const wxPoint& centerPoint)
1926 {
1927  wxPoint c(centerPoint);
1928  if (c == wxDefaultPosition)
1929  {
1930  GetClientSize(&m_scrX, &m_scrY);
1931  c.x = (m_scrX - m_marginLeft - m_marginRight) / 2 +
1932  m_marginLeft; // c.x = m_scrX/2;
1933  c.y = (m_scrY - m_marginTop - m_marginBottom) / 2 -
1934  m_marginTop; // c.y = m_scrY/2;
1935  }
1936 
1937  // Preserve the position of the clicked point:
1938  double prior_layer_x = p2x(c.x);
1939  double prior_layer_y = p2y(c.y);
1940 
1941  // Zoom out:
1942  m_scaleX = m_scaleX / zoomIncrementalFactor;
1943  m_scaleY = m_scaleY / zoomIncrementalFactor;
1944 
1945  // Adjust the new m_posx/y:
1946  m_posX = prior_layer_x - c.x / m_scaleX;
1947  m_posY = prior_layer_y + c.y / m_scaleY;
1948 
1949  m_desiredXmin = m_posX;
1950  m_desiredXmax =
1951  m_posX + (m_scrX - m_marginLeft - m_marginRight) /
1952  m_scaleX; // m_desiredXmax = m_posX + m_scrX / m_scaleX;
1953  m_desiredYmax = m_posY;
1954  m_desiredYmin =
1955  m_posY - (m_scrY - m_marginTop - m_marginBottom) /
1956  m_scaleY; // m_desiredYmin = m_posY - m_scrY / m_scaleY;
1957 
1958 #ifdef MATHPLOT_DO_LOGGING
1959  wxLogMessage(
1960  _("mpWindow::ZoomOut() prior coords: (%f,%f), new coords: (%f,%f) "
1961  "SHOULD BE EQUAL!!"),
1962  prior_layer_x, prior_layer_y, p2x(c.x), p2y(c.y));
1963 #endif
1964  UpdateAll();
1965 }
1966 
1967 void mpWindow::ZoomInX()
1968 {
1969  m_scaleX = m_scaleX * zoomIncrementalFactor;
1970  UpdateAll();
1971 }
1972 
1973 void mpWindow::ZoomOutX()
1974 {
1975  m_scaleX = m_scaleX / zoomIncrementalFactor;
1976  UpdateAll();
1977 }
1978 
1979 void mpWindow::ZoomInY()
1980 {
1981  m_scaleY = m_scaleY * zoomIncrementalFactor;
1982  UpdateAll();
1983 }
1984 
1985 void mpWindow::ZoomOutY()
1986 {
1987  m_scaleY = m_scaleY / zoomIncrementalFactor;
1988  UpdateAll();
1989 }
1990 
1991 void mpWindow::ZoomRect(wxPoint p0, wxPoint p1)
1992 {
1993  // Compute the 2 corners in graph coordinates:
1994  double p0x = p2x(p0.x);
1995  double p0y = p2y(p0.y);
1996  double p1x = p2x(p1.x);
1997  double p1y = p2y(p1.y);
1998 
1999  // Order them:
2000  double zoom_x_min = p0x < p1x ? p0x : p1x;
2001  double zoom_x_max = p0x > p1x ? p0x : p1x;
2002  double zoom_y_min = p0y < p1y ? p0y : p1y;
2003  double zoom_y_max = p0y > p1y ? p0y : p1y;
2004 
2005 #ifdef MATHPLOT_DO_LOGGING
2006  wxLogMessage(
2007  _("Zoom: (%f,%f)-(%f,%f)"), zoom_x_min, zoom_y_min, zoom_x_max,
2008  zoom_y_max);
2009 #endif
2010 
2011  Fit(zoom_x_min, zoom_x_max, zoom_y_min, zoom_y_max);
2012 }
2013 
2014 void mpWindow::LockAspect(bool enable)
2015 {
2016  m_lockaspect = enable;
2017  m_popmenu.Check(mpID_LOCKASPECT, enable);
2018 
2019  // Try to fit again with the new config:
2020  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax);
2021 }
2022 
2023 void mpWindow::OnShowPopupMenu(wxMouseEvent& event)
2024 {
2025  // Only display menu if the user has not "dragged" the figure
2026  if (m_enableMouseNavigation)
2027  {
2028  SetCursor(*wxSTANDARD_CURSOR);
2029  }
2030 
2031  if (!m_mouseMovedAfterRightClick) // JLB
2032  {
2033  m_clickedX = event.GetX();
2034  m_clickedY = event.GetY();
2035  PopupMenu(&m_popmenu, event.GetX(), event.GetY());
2036  }
2037 }
2038 
2039 void mpWindow::OnLockAspect(wxCommandEvent& WXUNUSED(event))
2040 {
2041  LockAspect(!m_lockaspect);
2042 }
2043 
2044 void mpWindow::OnMouseHelp(wxCommandEvent& WXUNUSED(event))
2045 {
2046  wxMessageBox(
2047  _("Supported Mouse commands:\n \
2048  - Left button down + Mark area: Rectangular zoom\n \
2049  - Right button down + Move: Pan (Move)\n \
2050  - Wheel: Vertical scroll\n \
2051  - Wheel + SHIFT: Horizontal scroll\n \
2052  - Wheel + CTRL: Zoom in/out"),
2053  _("wxMathPlot help"), wxOK, this);
2054 }
2055 
2056 void mpWindow::OnPrintMenu(wxCommandEvent& WXUNUSED(event))
2057 {
2058  // Pass two printout objects: for preview, and possible printing.
2059  auto* plotPrint = new mpPrintout(this);
2060  auto* plotPrintPreview = new mpPrintout(this);
2061  auto* preview = new wxPrintPreview(plotPrintPreview, plotPrint);
2062  wxPreviewFrame* frame = new wxPreviewFrame(
2063  preview, nullptr, wxT("Print Plot"), wxPoint(100, 100),
2064  wxSize(600, 650));
2065  frame->Centre(wxBOTH);
2066  frame->Initialize();
2067  frame->Show();
2068 }
2069 
2070 void mpWindow::OnFit(wxCommandEvent& WXUNUSED(event)) { Fit(); }
2071 void mpWindow::OnCenter(wxCommandEvent& WXUNUSED(event))
2072 {
2073  GetClientSize(&m_scrX, &m_scrY);
2074  int centerX = (m_scrX - m_marginLeft - m_marginRight) /
2075  2; // + m_marginLeft; // c.x = m_scrX/2;
2076  int centerY = (m_scrY - m_marginTop - m_marginBottom) /
2077  2; // - m_marginTop; // c.y = m_scrY/2;
2078  SetPos(p2x(m_clickedX - centerX), p2y(m_clickedY - centerY));
2079  // SetPos( p2x(m_clickedX-m_scrX/2), p2y(m_clickedY-m_scrY/2) ); //SetPos(
2080  // (double)(m_clickedX-m_scrX/2) / m_scaleX + m_posX,
2081  // (double)(m_scrY/2-m_clickedY) / m_scaleY + m_posY);
2082 }
2083 
2084 void mpWindow::OnZoomIn(wxCommandEvent& WXUNUSED(event))
2085 {
2086  ZoomIn(wxPoint(m_mouseRClick_X, m_mouseRClick_Y));
2087 }
2088 
2089 void mpWindow::OnZoomOut(wxCommandEvent& WXUNUSED(event)) { ZoomOut(); }
2090 void mpWindow::OnSize(wxSizeEvent& WXUNUSED(event))
2091 {
2092  // Try to fit again with the new window size:
2093  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax);
2094 #ifdef MATHPLOT_DO_LOGGING
2095  wxLogMessage(
2096  _("mpWindow::OnSize() m_scrX = %d, m_scrY = %d"), m_scrX, m_scrY);
2097 #endif // MATHPLOT_DO_LOGGING
2098 }
2099 
2100 bool mpWindow::AddLayer(mpLayer* layer, bool refreshDisplay)
2101 {
2102  if (layer != nullptr)
2103  {
2104  m_layers.push_back(layer);
2105  if (refreshDisplay) UpdateAll();
2106  return true;
2107  };
2108  return false;
2109 }
2110 
2111 bool mpWindow::DelLayer(
2112  mpLayer* layer, bool alsoDeleteObject, bool refreshDisplay)
2113 {
2114  wxLayerList::iterator layIt;
2115  for (layIt = m_layers.begin(); layIt != m_layers.end(); ++layIt)
2116  {
2117  if (*layIt == layer)
2118  {
2119  // Also delete the object?
2120  if (alsoDeleteObject) delete *layIt;
2121  m_layers.erase(layIt); // this deleted the reference only
2122  if (refreshDisplay) UpdateAll();
2123  return true;
2124  }
2125  }
2126  return false;
2127 }
2128 
2129 void mpWindow::DelAllLayers(bool alsoDeleteObject, bool refreshDisplay)
2130 {
2131  while (m_layers.size() > 0)
2132  {
2133  // Also delete the object?
2134  if (alsoDeleteObject) delete m_layers[0];
2135  m_layers.erase(m_layers.begin()); // this deleted the reference only
2136  }
2137  if (refreshDisplay) UpdateAll();
2138 }
2139 
2140 // void mpWindow::DoPrepareDC(wxDC& dc)
2141 // {
2142 // dc.SetDeviceOrigin(x2p(m_minX), y2p(m_maxY));
2143 // }
2144 
2145 void mpWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
2146 {
2147  wxPaintDC dc(this);
2148  dc.GetSize(&m_scrX, &m_scrY); // This is the size of the visible area only!
2149  // DoPrepareDC(dc);
2150 
2151 #ifdef MATHPLOT_DO_LOGGING
2152  {
2153  int px, py;
2154  GetViewStart(&px, &py);
2155  wxLogMessage(
2156  _("[mpWindow::OnPaint] vis.area:%ix%i px=%i py=%i"), m_scrX, m_scrY,
2157  px, py);
2158  }
2159 #endif
2160 
2161  // Selects direct or buffered draw:
2162  wxDC* trgDc;
2163 
2164  // J.L.Blanco @ Aug 2007: Added double buffer support
2165  if (m_enableDoubleBuffer)
2166  {
2167  if (m_last_lx != m_scrX || m_last_ly != m_scrY)
2168  {
2169  if (m_buff_bmp) delete m_buff_bmp;
2170  m_buff_bmp = new wxBitmap(m_scrX, m_scrY);
2171  m_buff_dc.SelectObject(*m_buff_bmp);
2172  m_last_lx = m_scrX;
2173  m_last_ly = m_scrY;
2174  }
2175  trgDc = &m_buff_dc;
2176  }
2177  else
2178  {
2179  trgDc = &dc;
2180  }
2181 
2182  // Draw background:
2183  // trgDc->SetDeviceOrigin(0,0);
2184  trgDc->SetPen(*wxTRANSPARENT_PEN);
2185  wxBrush brush(GetBackgroundColour());
2186  trgDc->SetBrush(brush);
2187  trgDc->SetTextForeground(m_fgColour);
2188  trgDc->DrawRectangle(0, 0, m_scrX, m_scrY);
2189 
2190  // Draw all the layers:
2191  // trgDc->SetDeviceOrigin( m_scrX>>1, m_scrY>>1); // Origin at the center
2192  wxLayerList::iterator li;
2193  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2194  {
2195  (*li)->Plot(*trgDc, *this);
2196  };
2197 
2198  // If doublebuffer, draw now to the window:
2199  if (m_enableDoubleBuffer)
2200  {
2201  // trgDc->SetDeviceOrigin(0,0);
2202  // dc.SetDeviceOrigin(0,0); // Origin at the center
2203  dc.Blit(0, 0, m_scrX, m_scrY, trgDc, 0, 0);
2204  }
2205 
2206  /* if (m_coordTooltip) {
2207  wxString toolTipContent;
2208  wxPoint mousePoint = wxGetMousePosition();
2209  toolTipContent.Printf(_("X = %f\nY = %f"), p2x(mousePoint.x),
2210  p2y(mousePoint.y));
2211  SetToolTip(toolTipContent);
2212  }*/
2213  // If scrollbars are enabled, refresh them
2214  if (m_enableScrollBars)
2215  {
2216  /* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2217  m_scrollY = (int) floor((m_maxY - m_posY )*m_scaleY);
2218  Scroll(m_scrollX, m_scrollY);*/
2219  // Scroll(x2p(m_posX), y2p(m_posY));
2220  // SetVirtualSize((int) ((m_maxX - m_minX)*m_scaleX), (int)
2221  // ((m_maxY - m_minY)*m_scaleY));
2222  // int centerX = (m_scrX - m_marginLeft - m_marginRight)/2; // +
2223  // m_marginLeft; // c.x = m_scrX/2;
2224  // int centerY = (m_scrY - m_marginTop - m_marginBottom)/2; // -
2225  // m_marginTop; // c.y = m_scrY/2;
2226  /*SetScrollbars(1, 1, (int) ((m_maxX - m_minX)*m_scaleX), (int) ((m_maxY
2227  * - m_minY)*m_scaleY));*/ //, x2p(m_posX + centerX/m_scaleX),
2228  // y2p(m_posY - centerY/m_scaleY), true);
2229  }
2230 }
2231 
2232 // void mpWindow::OnScroll2(wxScrollWinEvent &event)
2233 // {
2234 // #ifdef MATHPLOT_DO_LOGGING
2235 // wxLogMessage(_("[mpWindow::OnScroll2] Init: m_posX=%f m_posY=%f, sc_pos =
2236 // %d"),m_posX,m_posY, event.GetPosition());
2237 // #endif
2238 // // If scrollbars are not enabled, Skip operation
2239 // if (!m_enableScrollBars) {
2240 // event.Skip();
2241 // return;
2242 // }
2243 // // m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2244 // // m_scrollY = (int) floor((m_maxY - m_posY /*- m_minY*/)*m_scaleY);
2245 // // Scroll(m_scrollX, m_scrollY);
2246 //
2247 // // GetClientSize( &m_scrX, &m_scrY);
2248 // //Scroll(x2p(m_desiredXmin), y2p(m_desiredYmin));
2249 // int pixelStep = 1;
2250 // if (event.GetOrientation() == wxHORIZONTAL) {
2251 // //m_desiredXmin -= (m_scrollX - event.GetPosition())/m_scaleX;
2252 // //m_desiredXmax -= (m_scrollX - event.GetPosition())/m_scaleX;
2253 // m_posX -= (m_scrollX - event.GetPosition())/m_scaleX;
2254 // m_scrollX = event.GetPosition();
2255 // }
2256 // Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax);
2257 // // /* int pixelStep = 1;
2258 // // if (event.GetOrientation() == wxHORIZONTAL) {
2259 // // m_posX -= (px -
2260 // event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX);
2261 // // m_desiredXmax -= (px -
2262 // event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX);
2263 // // m_desiredXmin -= (px -
2264 // event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX);
2265 // // //SetPosX( (double)px / GetScaleX() + m_minX +
2266 // (double)(width>>1)/GetScaleX());
2267 // // // m_posX = p2x(px); //m_minX + (double)(px /*+
2268 // (m_scrX)*/)/GetScaleX();
2269 // // } else {
2270 // // m_posY += (py -
2271 // event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY);
2272 // // m_desiredYmax += (py -
2273 // event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY);
2274 // // m_desiredYmax += (py -
2275 // event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY);
2276 // // //SetPosY( m_maxY - (double)py / GetScaleY() -
2277 // (double)(height>>1)/GetScaleY());
2278 // // //m_posY = m_maxY - (double)py / GetScaleY() -
2279 // (double)(height>>1)/GetScaleY();
2280 // // // m_posY = p2y(py);//m_maxY - (double)(py /*+
2281 // (m_scrY)*/)/GetScaleY();
2282 // // }*/
2283 // #ifdef MATHPLOT_DO_LOGGING
2284 // int px, py;
2285 // GetViewStart( &px, &py);
2286 // wxLogMessage(_("[mpWindow::OnScroll2] End: m_posX = %f, m_posY = %f, px
2287 // = %f, py = %f"),m_posX, m_posY, px, py);
2288 // #endif
2289 //
2290 // UpdateAll();
2291 // // event.Skip();
2292 // }
2293 
2294 void mpWindow::SetMPScrollbars(bool status)
2295 {
2296  // Temporary behaviour: always disable scrollbars
2297  m_enableScrollBars = status; // false;
2298  if (status == false)
2299  {
2300  SetScrollbar(wxHORIZONTAL, 0, 0, 0);
2301  SetScrollbar(wxVERTICAL, 0, 0, 0);
2302  }
2303  // else the scroll bars will be updated in UpdateAll();
2304  UpdateAll();
2305 
2306  // EnableScrolling(false, false);
2307  // m_enableScrollBars = status;
2308  // EnableScrolling(status, status);
2309  /* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2310  m_scrollY = (int) floor((m_posY - m_minY)*m_scaleY);*/
2311  // int scrollWidth = (int) floor((m_maxX - m_minX)*m_scaleX) - m_scrX;
2312  // int scrollHeight = (int) floor((m_minY - m_maxY)*m_scaleY) - m_scrY;
2313 
2314  // /* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2315  // m_scrollY = (int) floor((m_maxY - m_posY /*- m_minY*/)*m_scaleY);
2316  // int scrollWidth = (int) floor(((m_maxX - m_minX) - (m_desiredXmax -
2317  // m_desiredXmin))*m_scaleX);
2318  // int scrollHeight = (int) floor(((m_maxY - m_minY) - (m_desiredYmax -
2319  // m_desiredYmin))*m_scaleY);
2320  // #ifdef MATHPLOT_DO_LOGGING
2321  // wxLogMessage(_("mpWindow::SetMPScrollbars() scrollWidth = %d,
2322  // scrollHeight = %d"), scrollWidth, scrollHeight);
2323  // #endif
2324  // if(status) {
2325  // SetScrollbars(1,
2326  // 1,
2327  // scrollWidth,
2328  // scrollHeight,
2329  // m_scrollX,
2330  // m_scrollY);
2331  // // SetVirtualSize((int) (m_maxX - m_minX), (int) (m_maxY -
2332  // m_minY));
2333  // }
2334  // Refresh(false);*/
2335 }
2336 
2337 bool mpWindow::UpdateBBox()
2338 {
2339  bool first = TRUE;
2340 
2341  for (auto f : m_layers)
2342  {
2343  if (f->HasBBox())
2344  {
2345  if (first)
2346  {
2347  first = FALSE;
2348  m_minX = f->GetMinX();
2349  m_maxX = f->GetMaxX();
2350  m_minY = f->GetMinY();
2351  m_maxY = f->GetMaxY();
2352  }
2353  else
2354  {
2355  if (f->GetMinX() < m_minX) m_minX = f->GetMinX();
2356  if (f->GetMaxX() > m_maxX) m_maxX = f->GetMaxX();
2357  if (f->GetMinY() < m_minY) m_minY = f->GetMinY();
2358  if (f->GetMaxY() > m_maxY) m_maxY = f->GetMaxY();
2359  }
2360  }
2361  // node = node->GetNext();
2362  }
2363 #ifdef MATHPLOT_DO_LOGGING
2364  wxLogDebug(
2365  wxT("[mpWindow::UpdateBBox] Bounding box: Xmin = %f, Xmax = %f, Ymin = "
2366  "%f, YMax = %f"),
2367  m_minX, m_maxX, m_minY, m_maxY);
2368 #endif // MATHPLOT_DO_LOGGING
2369  return first == FALSE;
2370 }
2371 
2372 // void mpWindow::UpdateAll()
2373 // {
2374 // GetClientSize( &m_scrX,&m_scrY);
2375 /* if (m_enableScrollBars) {
2376  // The "virtual size" of the scrolled window:
2377  const int sx = (int)((m_maxX - m_minX) * GetScaleX());
2378  const int sy = (int)((m_maxY - m_minY) * GetScaleY());
2379  SetVirtualSize(sx, sy);
2380  SetScrollRate(1, 1);*/
2381 // const int px = (int)((GetPosX() - m_minX) * GetScaleX());// -
2382 // m_scrX); //(cx>>1));
2383 
2384 // J.L.Blanco, Aug 2007: Formula fixed:
2385 // const int py = (int)((m_maxY - GetPosY()) * GetScaleY());// -
2386 // m_scrY); //(cy>>1));
2387 // int px, py;
2388 // GetViewStart(&px0, &py0);
2389 // px = (int)((m_posX - m_minX)*m_scaleX);
2390 // py = (int)((m_maxY - m_posY)*m_scaleY);
2391 
2392 // SetScrollbars( 1, 1, sx - m_scrX, sy - m_scrY, px, py, TRUE);
2393 // }
2394 
2395 // Working code
2396 // UpdateBBox();
2397 // Refresh( FALSE );
2398 // end working code
2399 
2400 // Old version
2401 /* bool box = UpdateBBox();
2402  if (box)
2403 {
2404  int cx, cy;
2405  GetClientSize( &cx, &cy);
2406 
2407  // The "virtual size" of the scrolled window:
2408  const int sx = (int)((m_maxX - m_minX) * GetScaleX());
2409  const int sy = (int)((m_maxY - m_minY) * GetScaleY());
2410 
2411  const int px = (int)((GetPosX() - m_minX) * GetScaleX() - (cx>>1));
2412 
2413  // J.L.Blanco, Aug 2007: Formula fixed:
2414  const int py = (int)((m_maxY - GetPosY()) * GetScaleY() - (cy>>1));
2415 
2416  SetScrollbars( 1, 1, sx, sy, px, py, TRUE);
2417 
2418 #ifdef MATHPLOT_DO_LOGGING
2419  wxLogMessage(_("[mpWindow::UpdateAll] Size:%ix%i
2420 ScrollBars:%i,%i"),sx,sy,px,py);
2421 #endif
2422 }
2423 
2424  FitInside();
2425  Refresh( FALSE );
2426 */
2427 // }
2428 
2429 void mpWindow::UpdateAll()
2430 {
2431  if (UpdateBBox())
2432  {
2433  if (m_enableScrollBars)
2434  {
2435  int cx, cy;
2436  GetClientSize(&cx, &cy);
2437  // Do x scroll bar
2438  {
2439  // Convert margin sizes from pixels to coordinates
2440  double leftMargin = m_marginLeft / m_scaleX;
2441  // Calculate the range in coords that we want to scroll over
2442  double maxX = (m_desiredXmax > m_maxX) ? m_desiredXmax : m_maxX;
2443  double minX = (m_desiredXmin < m_minX) ? m_desiredXmin : m_minX;
2444  if ((m_posX + leftMargin) < minX) minX = m_posX + leftMargin;
2445  // Calculate scroll bar size and thumb position
2446  int sizeX = (int)((maxX - minX) * m_scaleX);
2447  int thumbX = (int)(((m_posX + leftMargin) - minX) * m_scaleX);
2448  SetScrollbar(
2449  wxHORIZONTAL, thumbX, cx - (m_marginRight + m_marginLeft),
2450  sizeX);
2451  }
2452  // Do y scroll bar
2453  {
2454  // Convert margin sizes from pixels to coordinates
2455  double topMargin = m_marginTop / m_scaleY;
2456  // Calculate the range in coords that we want to scroll over
2457  double maxY = (m_desiredYmax > m_maxY) ? m_desiredYmax : m_maxY;
2458  if ((m_posY - topMargin) > maxY) maxY = m_posY - topMargin;
2459  double minY = (m_desiredYmin < m_minY) ? m_desiredYmin : m_minY;
2460  // Calculate scroll bar size and thumb position
2461  int sizeY = (int)((maxY - minY) * m_scaleY);
2462  int thumbY = (int)((maxY - (m_posY - topMargin)) * m_scaleY);
2463  SetScrollbar(
2464  wxVERTICAL, thumbY, cy - (m_marginTop + m_marginBottom),
2465  sizeY);
2466  }
2467  }
2468  }
2469 
2470  Refresh(FALSE);
2471 }
2472 
2473 void mpWindow::DoScrollCalc(const int position, const int orientation)
2474 {
2475  if (orientation == wxVERTICAL)
2476  {
2477  // Y axis
2478  // Get top margin in coord units
2479  double topMargin = m_marginTop / m_scaleY;
2480  // Calculate maximum Y coord to be shown in the graph
2481  double maxY = m_desiredYmax > m_maxY ? m_desiredYmax : m_maxY;
2482  // Set new position
2483  SetPosY((maxY - (position / m_scaleY)) + topMargin);
2484  }
2485  else
2486  {
2487  // X Axis
2488  // Get left margin in coord units
2489  double leftMargin = m_marginLeft / m_scaleX;
2490  // Calculate minimum X coord to be shown in the graph
2491  double minX = (m_desiredXmin < m_minX) ? m_desiredXmin : m_minX;
2492  // Set new position
2493  SetPosX((minX + (position / m_scaleX)) - leftMargin);
2494  }
2495 }
2496 
2497 void mpWindow::OnScrollThumbTrack(wxScrollWinEvent& event)
2498 {
2499  DoScrollCalc(event.GetPosition(), event.GetOrientation());
2500 }
2501 
2502 void mpWindow::OnScrollPageUp(wxScrollWinEvent& event)
2503 {
2504  int scrollOrientation = event.GetOrientation();
2505  // Get position before page up
2506  int position = GetScrollPos(scrollOrientation);
2507  // Get thumb size
2508  int thumbSize = GetScrollThumb(scrollOrientation);
2509  // Need to adjust position by a page
2510  position -= thumbSize;
2511  if (position < 0) position = 0;
2512 
2513  DoScrollCalc(position, scrollOrientation);
2514 }
2515 void mpWindow::OnScrollPageDown(wxScrollWinEvent& event)
2516 {
2517  int scrollOrientation = event.GetOrientation();
2518  // Get position before page up
2519  int position = GetScrollPos(scrollOrientation);
2520  // Get thumb size
2521  int thumbSize = GetScrollThumb(scrollOrientation);
2522  // Get scroll range
2523  int scrollRange = GetScrollRange(scrollOrientation);
2524  // Need to adjust position by a page
2525  position += thumbSize;
2526  if (position > (scrollRange - thumbSize))
2527  position = scrollRange - thumbSize;
2528 
2529  DoScrollCalc(position, scrollOrientation);
2530 }
2531 
2532 void mpWindow::OnScrollLineUp(wxScrollWinEvent& event)
2533 {
2534  int scrollOrientation = event.GetOrientation();
2535  // Get position before page up
2536  int position = GetScrollPos(scrollOrientation);
2537  // Need to adjust position by a line
2538  position -= mpSCROLL_NUM_PIXELS_PER_LINE;
2539  if (position < 0) position = 0;
2540 
2541  DoScrollCalc(position, scrollOrientation);
2542 }
2543 
2544 void mpWindow::OnScrollLineDown(wxScrollWinEvent& event)
2545 {
2546  int scrollOrientation = event.GetOrientation();
2547  // Get position before page up
2548  int position = GetScrollPos(scrollOrientation);
2549  // Get thumb size
2550  int thumbSize = GetScrollThumb(scrollOrientation);
2551  // Get scroll range
2552  int scrollRange = GetScrollRange(scrollOrientation);
2553  // Need to adjust position by a page
2554  position += mpSCROLL_NUM_PIXELS_PER_LINE;
2555  if (position > (scrollRange - thumbSize))
2556  position = scrollRange - thumbSize;
2557 
2558  DoScrollCalc(position, scrollOrientation);
2559 }
2560 
2561 void mpWindow::OnScrollTop(wxScrollWinEvent& event)
2562 {
2563  DoScrollCalc(0, event.GetOrientation());
2564 }
2565 
2566 void mpWindow::OnScrollBottom(wxScrollWinEvent& event)
2567 {
2568  int scrollOrientation = event.GetOrientation();
2569  // Get thumb size
2570  int thumbSize = GetScrollThumb(scrollOrientation);
2571  // Get scroll range
2572  int scrollRange = GetScrollRange(scrollOrientation);
2573 
2574  DoScrollCalc(scrollRange - thumbSize, scrollOrientation);
2575 }
2576 // End patch ngpaton
2577 
2578 void mpWindow::SetScaleX(double scaleX)
2579 {
2580  if (scaleX != 0) m_scaleX = scaleX;
2581  UpdateAll();
2582 }
2583 
2584 // New methods implemented by Davide Rondini
2585 
2586 unsigned int mpWindow::CountLayers()
2587 {
2588  // wxNode *node = m_layers.GetFirst();
2589  unsigned int layerNo = 0;
2590  for (auto& m_layer : m_layers) // while(node)
2591  {
2592  if (m_layer->HasBBox()) layerNo++;
2593  // node = node->GetNext();
2594  };
2595  return layerNo;
2596 }
2597 
2598 mpLayer* mpWindow::GetLayer(int position)
2599 {
2600  if ((position >= (int)m_layers.size()) || position < 0) return nullptr;
2601  return m_layers[position];
2602 }
2603 
2604 mpLayer* mpWindow::GetLayerByName(const wxString& name)
2605 {
2606  for (auto& m_layer : m_layers)
2607  if (!m_layer->GetName().Cmp(name)) return m_layer;
2608  return nullptr; // Not found
2609 }
2610 
2611 void mpWindow::GetBoundingBox(double* bbox)
2612 {
2613  bbox[0] = m_minX;
2614  bbox[1] = m_maxX;
2615  bbox[2] = m_minY;
2616  bbox[3] = m_maxY;
2617 }
2618 
2619 bool mpWindow::SaveScreenshot(
2620  const wxString& filename, int type, wxSize imageSize, bool fit)
2621 {
2622  int sizeX, sizeY;
2623  int bk_scrX, bk_scrY;
2624  if (imageSize == wxDefaultSize)
2625  {
2626  sizeX = m_scrX;
2627  sizeY = m_scrY;
2628  }
2629  else
2630  {
2631  sizeX = imageSize.x;
2632  sizeY = imageSize.y;
2633  bk_scrX = m_scrX;
2634  bk_scrY = m_scrY;
2635  SetScr(sizeX, sizeY);
2636  }
2637 
2638  wxBitmap screenBuffer(sizeX, sizeY);
2639  wxMemoryDC screenDC;
2640  screenDC.SelectObject(screenBuffer);
2641  screenDC.SetPen(*wxTRANSPARENT_PEN);
2642  wxBrush brush(GetBackgroundColour());
2643  screenDC.SetBrush(brush);
2644  screenDC.DrawRectangle(0, 0, sizeX, sizeY);
2645 
2646  if (fit)
2647  {
2648  Fit(m_minX, m_maxX, m_minY, m_maxY, &sizeX, &sizeY);
2649  }
2650  else
2651  {
2652  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax, &sizeX,
2653  &sizeY);
2654  }
2655  // Draw all the layers:
2656  wxLayerList::iterator li;
2657  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2658  (*li)->Plot(screenDC, *this);
2659 
2660  if (imageSize != wxDefaultSize)
2661  {
2662  // Restore dimensions
2663  SetScr(bk_scrX, bk_scrY);
2664  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax,
2665  &bk_scrX, &bk_scrY);
2666  UpdateAll();
2667  }
2668  // Once drawing is complete, actually save screen shot
2669  wxImage screenImage = screenBuffer.ConvertToImage();
2670  return screenImage.SaveFile(filename, (wxBitmapType)type);
2671 }
2672 
2673 void mpWindow::SetMargins(int top, int right, int bottom, int left)
2674 {
2675  m_marginTop = top;
2676  m_marginRight = right;
2677  m_marginBottom = bottom;
2678  m_marginLeft = left;
2679 }
2680 
2681 mpInfoLayer* mpWindow::IsInsideInfoLayer(wxPoint& point)
2682 {
2683  wxLayerList::iterator li;
2684  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2685  {
2686 #ifdef MATHPLOT_DO_LOGGING
2687  wxLogMessage(
2688  _("mpWindow::IsInsideInfoLayer() examinining layer = %p"), (*li));
2689 #endif // MATHPLOT_DO_LOGGING
2690  if ((*li)->IsInfo())
2691  {
2692  auto* tmpLyr = (mpInfoLayer*)(*li);
2693 #ifdef MATHPLOT_DO_LOGGING
2694  wxLogMessage(_("mpWindow::IsInsideInfoLayer() layer = %p"), (*li));
2695 #endif // MATHPLOT_DO_LOGGING
2696  if (tmpLyr->Inside(point))
2697  {
2698  return tmpLyr;
2699  }
2700  }
2701  }
2702  return nullptr;
2703 }
2704 
2705 void mpWindow::SetLayerVisible(const wxString& name, bool viewable)
2706 {
2707  mpLayer* lx = GetLayerByName(name);
2708  if (lx)
2709  {
2710  lx->SetVisible(viewable);
2711  UpdateAll();
2712  }
2713 }
2714 
2715 bool mpWindow::IsLayerVisible(const wxString& name)
2716 {
2717  mpLayer* lx = GetLayerByName(name);
2718  return (lx) ? lx->IsVisible() : false;
2719 }
2720 
2721 void mpWindow::SetLayerVisible(const unsigned int position, bool viewable)
2722 {
2723  mpLayer* lx = GetLayer(position);
2724  if (lx)
2725  {
2726  lx->SetVisible(viewable);
2727  UpdateAll();
2728  }
2729 }
2730 
2731 bool mpWindow::IsLayerVisible(const unsigned int position)
2732 {
2733  mpLayer* lx = GetLayer(position);
2734  return (lx) ? lx->IsVisible() : false;
2735 }
2736 
2737 void mpWindow::SetColourTheme(
2738  const wxColour& bgColour, const wxColour& drawColour,
2739  const wxColour& axesColour)
2740 {
2741  SetBackgroundColour(bgColour);
2742  SetForegroundColour(drawColour);
2743  m_bgColour = bgColour;
2744  m_fgColour = drawColour;
2745  m_axColour = axesColour;
2746  // cycle between layers to set colours and properties to them
2747  wxLayerList::iterator li;
2748  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2749  {
2750  if ((*li)->GetLayerType() == mpLAYER_AXIS)
2751  {
2752  wxPen axisPen = (*li)->GetPen(); // Get the old pen to modify only
2753  // colour, not style or width
2754  axisPen.SetColour(axesColour);
2755  (*li)->SetPen(axisPen);
2756  }
2757  if ((*li)->GetLayerType() == mpLAYER_INFO)
2758  {
2759  wxPen infoPen = (*li)->GetPen(); // Get the old pen to modify only
2760  // colour, not style or width
2761  infoPen.SetColour(drawColour);
2762  (*li)->SetPen(infoPen);
2763  }
2764  }
2765 }
2766 
2767 // void mpWindow::EnableCoordTooltip(bool value)
2768 // {
2769 // m_coordTooltip = value;
2770 // // if (value) GetToolTip()->SetDelay(100);
2771 // }
2772 
2773 /*
2774 double mpWindow::p2x(wxCoord pixelCoordX, bool drawOutside )
2775 {
2776  if (drawOutside) {
2777  return m_posX + pixelCoordX/m_scaleX;
2778  }
2779  // Draw inside margins
2780  double marginScaleX = ((double)(m_scrX - m_marginLeft -
2781 m_marginRight))/m_scrX;
2782  return m_marginLeft + (m_posX + pixelCoordX/m_scaleX)/marginScaleX;
2783 }
2784 
2785 double mpWindow::p2y(wxCoord pixelCoordY, bool drawOutside )
2786 {
2787  if (drawOutside) {
2788  return m_posY - pixelCoordY/m_scaleY;
2789  }
2790  // Draw inside margins
2791  double marginScaleY = ((double)(m_scrY - m_marginTop -
2792 m_marginBottom))/m_scrY;
2793  return m_marginTop + (m_posY - pixelCoordY/m_scaleY)/marginScaleY;
2794 }
2795 
2796 wxCoord mpWindow::x2p(double x, bool drawOutside)
2797 {
2798  if (drawOutside) {
2799  return (wxCoord) ((x-m_posX) * m_scaleX);
2800  }
2801  // Draw inside margins
2802  double marginScaleX = ((double)(m_scrX - m_marginLeft -
2803 m_marginRight))/m_scrX;
2804 #ifdef MATHPLOT_DO_LOGGING
2805  wxLogMessage(wxT("x2p ScrX = %d, marginRight = %d, marginLeft = %d,
2806 marginScaleX = %f"), m_scrX, m_marginRight, m_marginLeft, marginScaleX);
2807 #endif // MATHPLOT_DO_LOGGING
2808  return (wxCoord) (int)(((x-m_posX) * m_scaleX)*marginScaleX) - m_marginLeft;
2809 }
2810 
2811 wxCoord mpWindow::y2p(double y, bool drawOutside)
2812 {
2813  if (drawOutside) {
2814  return (wxCoord) ( (m_posY-y) * m_scaleY);
2815  }
2816  // Draw inside margins
2817  double marginScaleY = ((double)(m_scrY - m_marginTop -
2818 m_marginBottom))/m_scrY;
2819 #ifdef MATHPLOT_DO_LOGGING
2820  wxLogMessage(wxT("y2p ScrY = %d, marginTop = %d, marginBottom = %d,
2821 marginScaleY = %f"), m_scrY, m_marginTop, m_marginBottom, marginScaleY);
2822 #endif // MATHPLOT_DO_LOGGING
2823  return (wxCoord) ((int)((m_posY-y) * m_scaleY)*marginScaleY) - m_marginTop;
2824 }
2825 */
2826 
2827 //-----------------------------------------------------------------------------
2828 // mpFXYVector implementation - by Jose Luis Blanco (AGO-2007)
2829 //-----------------------------------------------------------------------------
2830 
2831 IMPLEMENT_DYNAMIC_CLASS(mpFXYVector, mpFXY)
2832 
2833 // Constructor
2834 mpFXYVector::mpFXYVector(wxString name, int flags) : mpFXY(name, flags)
2835 {
2836  m_index = 0;
2837  m_minX = -1;
2838  m_maxX = 1;
2839  m_minY = -1;
2840  m_maxY = 1;
2841  m_type = mpLAYER_PLOT;
2842 }
2843 
2844 void mpFXYVector::Rewind() { m_index = 0; }
2845 bool mpFXYVector::GetNextXY(double& x, double& y)
2846 {
2847  if (m_index >= m_xs.size())
2848  return FALSE;
2849  else
2850  {
2851  x = m_xs[m_index];
2852  y = m_ys[m_index++];
2853  return m_index <= m_xs.size();
2854  }
2855 }
2856 
2857 void mpFXYVector::Clear()
2858 {
2859  m_xs.clear();
2860  m_ys.clear();
2861 }
2862 
2863 void mpFXYVector::SetData(
2864  const std::vector<float>& xs, const std::vector<float>& ys)
2865 {
2866  // Check if the data vectora are of the same size
2867  if (xs.size() != ys.size())
2868  {
2869  wxLogError(
2870  _("wxMathPlot error: X and Y vector are not of the same length!"));
2871  return;
2872  }
2873  const size_t N = xs.size();
2874  std::vector<double> Xd(N), Yd(N);
2875  for (size_t i = 0; i < xs.size(); i++)
2876  {
2877  Xd[i] = xs[i];
2878  Yd[i] = ys[i];
2879  }
2880  SetData(Xd, Yd);
2881 }
2882 
2883 void mpFXYVector::SetData(
2884  const std::vector<double>& xs, const std::vector<double>& ys)
2885 {
2886  // Check if the data vectora are of the same size
2887  if (xs.size() != ys.size())
2888  {
2889  wxLogError(
2890  _("wxMathPlot error: X and Y vector are not of the same length!"));
2891  return;
2892  }
2893  // Copy the data:
2894  m_xs = xs;
2895  m_ys = ys;
2896 
2897  // Update internal variables for the bounding box.
2898  if (xs.size() > 0)
2899  {
2900  m_minX = xs[0];
2901  m_maxX = xs[0];
2902  m_minY = ys[0];
2903  m_maxY = ys[0];
2904 
2905  std::vector<double>::const_iterator it;
2906 
2907  for (it = xs.begin(); it != xs.end(); ++it)
2908  {
2909  if (*it < m_minX) m_minX = *it;
2910  if (*it > m_maxX) m_maxX = *it;
2911  }
2912  for (it = ys.begin(); it != ys.end(); ++it)
2913  {
2914  if (*it < m_minY) m_minY = *it;
2915  if (*it > m_maxY) m_maxY = *it;
2916  }
2917  m_minX -= 0.5f;
2918  m_minY -= 0.5f;
2919  m_maxX += 0.5f;
2920  m_maxY += 0.5f;
2921  }
2922  else
2923  {
2924  m_minX = -1;
2925  m_maxX = 1;
2926  m_minY = -1;
2927  m_maxY = 1;
2928  }
2929 }
2930 
2931 void mpFXYVector::AppendDataPoint(float x, float y)
2932 {
2933  // Append the data:
2934  m_xs.push_back(x);
2935  m_ys.push_back(y);
2936 
2937  if (m_xs.size() == 1)
2938  {
2939  m_minX = x;
2940  m_maxX = x;
2941  m_minY = y;
2942  m_maxY = y;
2943 
2944  const double margX = std::max(fabs(m_minX), fabs(m_maxX)) * 0.05;
2945  const double margY = std::max(fabs(m_minY), fabs(m_maxY)) * 0.05;
2946 
2947  m_minX -= margX;
2948  m_maxX += margX;
2949  m_minY -= margY;
2950  m_maxY += margY;
2951  }
2952  else
2953  {
2954  m_minX = std::min(x - fabs(x) * 0.05, m_minX);
2955  m_maxX = std::max(x + fabs(x) * 0.05, m_maxX);
2956  m_minY = std::min(y - fabs(y) * 0.05, m_minY);
2957  m_maxY = std::max(y + fabs(y) * 0.05, m_maxY);
2958  }
2959 }
2960 
2961 //-----------------------------------------------------------------------------
2962 // mpText - provided by Val Greene
2963 //-----------------------------------------------------------------------------
2964 
2965 IMPLEMENT_DYNAMIC_CLASS(mpText, mpLayer)
2966 
2967 /** @param name text to be displayed
2968 @param offsetx x position in percentage (0-100)
2969 @param offsetx y position in percentage (0-100)
2970 */
2971 mpText::mpText(wxString name, int offsetx, int offsety)
2972 {
2973  SetName(name);
2974 
2975  if (offsetx >= 0 && offsetx <= 100)
2976  m_offsetx = offsetx;
2977  else
2978  m_offsetx = 5;
2979 
2980  if (offsety >= 0 && offsety <= 100)
2981  m_offsety = offsety;
2982  else
2983  m_offsetx = 50;
2984  m_type = mpLAYER_INFO;
2985 }
2986 
2987 /** mpText Layer plot handler.
2988 This implementation will plot the text adjusted to the visible area.
2989 */
2990 
2991 void mpText::Plot(wxDC& dc, mpWindow& w)
2992 {
2993  if (m_visible)
2994  {
2995  dc.SetPen(m_pen);
2996  dc.SetFont(m_font);
2997 
2998  wxCoord tw = 0, th = 0;
2999  dc.GetTextExtent(GetName(), &tw, &th);
3000 
3001  // int left = -dc.LogicalToDeviceX(0);
3002  // int width = dc.LogicalToDeviceX(0) - left;
3003  // int bottom = dc.LogicalToDeviceY(0);
3004  // int height = bottom - -dc.LogicalToDeviceY(0);
3005 
3006  /* dc.DrawText( GetName(),
3007  (int)((((float)width/100.0) * m_offsety) + left - (tw/2)),
3008  (int)((((float)height/100.0) * m_offsetx) - bottom) );*/
3009  int px = m_offsetx *
3010  (w.GetScrX() - w.GetMarginLeft() - w.GetMarginRight()) / 100;
3011  int py = m_offsety *
3012  (w.GetScrY() - w.GetMarginTop() - w.GetMarginBottom()) / 100;
3013  dc.DrawText(GetName(), px, py);
3014  }
3015 }
3016 
3017 //-----------------------------------------------------------------------------
3018 // mpPrintout - provided by Davide Rondini
3019 //-----------------------------------------------------------------------------
3020 
3021 mpPrintout::mpPrintout(mpWindow* drawWindow, const wxChar* title)
3022  : wxPrintout(title)
3023 {
3024  drawn = false;
3025  plotWindow = drawWindow;
3026 }
3027 
3028 bool mpPrintout::OnPrintPage(int page)
3029 {
3030  wxDC* trgDc = GetDC();
3031  if ((trgDc) && (page == 1))
3032  {
3033  wxCoord m_prnX, m_prnY;
3034  int marginX = 50;
3035  int marginY = 50;
3036  trgDc->GetSize(&m_prnX, &m_prnY);
3037 
3038  m_prnX -= (2 * marginX);
3039  m_prnY -= (2 * marginY);
3040  trgDc->SetDeviceOrigin(marginX, marginY);
3041 
3042 #ifdef MATHPLOT_DO_LOGGING
3043  wxLogMessage(wxT("Print Size: %d x %d\n"), m_prnX, m_prnY);
3044  wxLogMessage(
3045  wxT("Screen Size: %d x %d\n"), plotWindow->GetScrX(),
3046  plotWindow->GetScrY());
3047 #endif
3048 
3049  // Set the scale according to the page:
3050  plotWindow->Fit(
3051  plotWindow->GetDesiredXmin(), plotWindow->GetDesiredXmax(),
3052  plotWindow->GetDesiredYmin(), plotWindow->GetDesiredYmax(), &m_prnX,
3053  &m_prnY);
3054 
3055  // Get the colours of the plotWindow to restore them ath the end
3056  wxColour oldBgColour = plotWindow->GetBackgroundColour();
3057  wxColour oldFgColour = plotWindow->GetForegroundColour();
3058  wxColour oldAxColour = plotWindow->GetAxesColour();
3059 
3060  // Draw background, ensuring to use white background for printing.
3061  trgDc->SetPen(*wxTRANSPARENT_PEN);
3062  // wxBrush brush( plotWindow->GetBackgroundColour() );
3063  wxBrush brush = *wxWHITE_BRUSH;
3064  trgDc->SetBrush(brush);
3065  trgDc->DrawRectangle(0, 0, m_prnX, m_prnY);
3066 
3067  // Draw all the layers:
3068  // trgDc->SetDeviceOrigin( m_prnX>>1, m_prnY>>1); // Origin at the
3069  // center
3070  mpLayer* layer;
3071  for (size_t li = 0; li < plotWindow->CountAllLayers(); ++li)
3072  {
3073  layer = plotWindow->GetLayer(li);
3074  layer->Plot(*trgDc, *plotWindow);
3075  };
3076  // Restore device origin
3077  // trgDc->SetDeviceOrigin(0, 0);
3078  // Restore colours
3079  plotWindow->SetColourTheme(oldBgColour, oldFgColour, oldAxColour);
3080  // Restore drawing
3081  plotWindow->Fit(
3082  plotWindow->GetDesiredXmin(), plotWindow->GetDesiredXmax(),
3083  plotWindow->GetDesiredYmin(), plotWindow->GetDesiredYmax(), nullptr,
3084  nullptr);
3085  plotWindow->UpdateAll();
3086  }
3087  return true;
3088 }
3089 
3090 bool mpPrintout::HasPage(int page) { return (page == 1); }
3091 //-----------------------------------------------------------------------------
3092 // mpMovableObject - provided by Jose Luis Blanco
3093 //-----------------------------------------------------------------------------
3094 void mpMovableObject::TranslatePoint(
3095  double x, double y, double& out_x, double& out_y)
3096 {
3097  double ccos = cos(m_reference_phi); // Avoid computing cos/sin twice.
3098  double csin = sin(m_reference_phi);
3099 
3100  out_x = m_reference_x + ccos * x - csin * y;
3101  out_y = m_reference_y + csin * x + ccos * y;
3102 }
3103 
3104 // This method updates the buffers m_trans_shape_xs/ys, and the precomputed
3105 // bounding box.
3106 void mpMovableObject::ShapeUpdated()
3107 {
3108  // Just in case...
3109  if (m_shape_xs.size() != m_shape_ys.size())
3110  {
3111  wxLogError(
3112  wxT("[mpMovableObject::ShapeUpdated] Error, m_shape_xs and "
3113  "m_shape_ys have different lengths!"));
3114  }
3115  else
3116  {
3117  double ccos = cos(m_reference_phi); // Avoid computing cos/sin twice.
3118  double csin = sin(m_reference_phi);
3119 
3120  m_trans_shape_xs.resize(m_shape_xs.size());
3121  m_trans_shape_ys.resize(m_shape_xs.size());
3122 
3123  std::vector<double>::iterator itXi, itXo;
3124  std::vector<double>::iterator itYi, itYo;
3125 
3126  m_bbox_min_x = 1e300;
3127  m_bbox_max_x = -1e300;
3128  m_bbox_min_y = 1e300;
3129  m_bbox_max_y = -1e300;
3130 
3131  for (itXo = m_trans_shape_xs.begin(), itYo = m_trans_shape_ys.begin(),
3132  itXi = m_shape_xs.begin(), itYi = m_shape_ys.begin();
3133  itXo != m_trans_shape_xs.end(); ++itXo, ++itYo, ++itXi, ++itYi)
3134  {
3135  *itXo = m_reference_x + ccos * (*itXi) - csin * (*itYi);
3136  *itYo = m_reference_y + csin * (*itXi) + ccos * (*itYi);
3137 
3138  // Keep BBox:
3139  if (*itXo < m_bbox_min_x) m_bbox_min_x = *itXo;
3140  if (*itXo > m_bbox_max_x) m_bbox_max_x = *itXo;
3141  if (*itYo < m_bbox_min_y) m_bbox_min_y = *itYo;
3142  if (*itYo > m_bbox_max_y) m_bbox_max_y = *itYo;
3143  }
3144  }
3145 }
3146 
3147 void mpMovableObject::Plot(wxDC& dc, mpWindow& w)
3148 {
3149  if (m_visible)
3150  {
3151  dc.SetPen(m_pen);
3152 
3153  auto itX = m_trans_shape_xs.begin();
3154  auto itY = m_trans_shape_ys.begin();
3155 
3156  if (!m_continuous)
3157  {
3158  // for some reason DrawPoint does not use the current pen,
3159  // so we use DrawLine for fat pens
3160  if (m_pen.GetWidth() <= 1)
3161  {
3162  while (itX != m_trans_shape_xs.end())
3163  {
3164  dc.DrawPoint(w.x2p(*(itX++)), w.y2p(*(itY++)));
3165  }
3166  }
3167  else
3168  {
3169  while (itX != m_trans_shape_xs.end())
3170  {
3171  wxCoord cx = w.x2p(*(itX++));
3172  wxCoord cy = w.y2p(*(itY++));
3173  dc.DrawLine(cx, cy, cx, cy);
3174  }
3175  }
3176  }
3177  else
3178  {
3179  wxCoord cx0 = 0, cy0 = 0;
3180  bool first = TRUE;
3181  while (itX != m_trans_shape_xs.end())
3182  {
3183  wxCoord cx = w.x2p(*(itX++));
3184  wxCoord cy = w.y2p(*(itY++));
3185  if (first)
3186  {
3187  first = FALSE;
3188  cx0 = cx;
3189  cy0 = cy;
3190  }
3191  dc.DrawLine(cx0, cy0, cx, cy);
3192  cx0 = cx;
3193  cy0 = cy;
3194  }
3195  }
3196 
3197  if (!m_name.IsEmpty() && m_showName)
3198  {
3199  dc.SetFont(m_font);
3200 
3201  wxCoord tx, ty;
3202  dc.GetTextExtent(m_name, &tx, &ty);
3203 
3204  if (HasBBox())
3205  {
3206  auto sx =
3207  (wxCoord)((m_bbox_max_x - w.GetPosX()) * w.GetScaleX());
3208  auto sy =
3209  (wxCoord)((w.GetPosY() - m_bbox_max_y) * w.GetScaleY());
3210 
3211  tx = sx - tx - 8;
3212  ty = sy - 8 - ty;
3213  }
3214  else
3215  {
3216  const int sx = w.GetScrX() >> 1;
3217  const int sy = w.GetScrY() >> 1;
3218 
3219  if ((m_flags & mpALIGNMASK) == mpALIGN_NE)
3220  {
3221  tx = sx - tx - 8;
3222  ty = -sy + 8;
3223  }
3224  else if ((m_flags & mpALIGNMASK) == mpALIGN_NW)
3225  {
3226  tx = -sx + 8;
3227  ty = -sy + 8;
3228  }
3229  else if ((m_flags & mpALIGNMASK) == mpALIGN_SW)
3230  {
3231  tx = -sx + 8;
3232  ty = sy - 8 - ty;
3233  }
3234  else
3235  {
3236  tx = sx - tx - 8;
3237  ty = sy - 8 - ty;
3238  }
3239  }
3240 
3241  dc.DrawText(m_name, tx, ty);
3242  }
3243  }
3244 }
3245 
3246 //-----------------------------------------------------------------------------
3247 // mpCovarianceEllipse - provided by Jose Luis Blanco
3248 //-----------------------------------------------------------------------------
3249 
3250 // Called to update the m_shape_xs, m_shape_ys vectors, whenever a parameter
3251 // changes.
3252 void mpCovarianceEllipse::RecalculateShape()
3253 {
3254  m_shape_xs.clear();
3255  m_shape_ys.clear();
3256 
3257  // Preliminary checks:
3258  if (m_quantiles < 0)
3259  {
3260  wxLogError(
3261  wxT("[mpCovarianceEllipse] Error: quantiles must be non-negative"));
3262  return;
3263  }
3264  if (m_cov_00 < 0)
3265  {
3266  wxLogError(
3267  wxT("[mpCovarianceEllipse] Error: cov(0,0) must be non-negative"));
3268  return;
3269  }
3270  if (m_cov_11 < 0)
3271  {
3272  wxLogError(
3273  wxT("[mpCovarianceEllipse] Error: cov(1,1) must be non-negative"));
3274  return;
3275  }
3276 
3277  m_shape_xs.resize(m_segments, 0);
3278  m_shape_ys.resize(m_segments, 0);
3279 
3280  // Compute the two eigenvalues of the covariance:
3281  // -------------------------------------------------
3282  double b = -m_cov_00 - m_cov_11;
3283  double c = m_cov_00 * m_cov_11 - m_cov_01 * m_cov_01;
3284 
3285  double D = b * b - 4 * c;
3286 
3287  if (D < 0)
3288  {
3289  wxLogError(
3290  wxT("[mpCovarianceEllipse] Error: cov is not positive definite"));
3291  return;
3292  }
3293 
3294  double eigenVal0 = 0.5 * (-b + sqrt(D));
3295  double eigenVal1 = 0.5 * (-b - sqrt(D));
3296 
3297  // Compute the two corresponding eigenvectors:
3298  // -------------------------------------------------
3299  double eigenVec0_x, eigenVec0_y;
3300  double eigenVec1_x, eigenVec1_y;
3301 
3302  if (D == 0) // Square ellipse
3303  {
3304  eigenVec0_y = 0;
3305  eigenVec0_x = 1;
3306  }
3307  else if (fabs(eigenVal0 - m_cov_00) > 1e-6)
3308  {
3309  double k1x = m_cov_01 / (eigenVal0 - m_cov_00);
3310  eigenVec0_y = 1;
3311  eigenVec0_x = eigenVec0_y * k1x;
3312  }
3313  else
3314  {
3315  double k1y = m_cov_01 / (eigenVal0 - m_cov_11);
3316  eigenVec0_x = 1;
3317  eigenVec0_y = eigenVec0_x * k1y;
3318  }
3319 
3320  if (D == 0) // Square ellipse
3321  {
3322  eigenVec1_y = 1;
3323  eigenVec1_x = 0;
3324  }
3325  else if (fabs(eigenVal1 - m_cov_00) > 1e-6)
3326  {
3327  double k2x = m_cov_01 / (eigenVal1 - m_cov_00);
3328  eigenVec1_y = 1;
3329  eigenVec1_x = eigenVec1_y * k2x;
3330  }
3331  else
3332  {
3333  double k2y = m_cov_01 / (eigenVal1 - m_cov_11);
3334  eigenVec1_x = 1;
3335  eigenVec1_y = eigenVec1_x * k2y;
3336  }
3337 
3338  // Normalize the eigenvectors:
3339  double len = sqrt(eigenVec0_x * eigenVec0_x + eigenVec0_y * eigenVec0_y);
3340  eigenVec0_x /= len; // It *CANNOT* be zero
3341  eigenVec0_y /= len;
3342 
3343  len = sqrt(eigenVec1_x * eigenVec1_x + eigenVec1_y * eigenVec1_y);
3344  eigenVec1_x /= len; // It *CANNOT* be zero
3345  eigenVec1_y /= len;
3346 
3347  // Take the sqrt of the eigenvalues (required for the ellipse scale):
3348  eigenVal0 = sqrt(eigenVal0);
3349  eigenVal1 = sqrt(eigenVal1);
3350 
3351  // Compute the 2x2 matrix M = diag(eigVal) * (~eigVec) (each eigen vector
3352  // is a row):
3353  double M_00 = eigenVec0_x * eigenVal0;
3354  double M_01 = eigenVec0_y * eigenVal0;
3355 
3356  double M_10 = eigenVec1_x * eigenVal1;
3357  double M_11 = eigenVec1_y * eigenVal1;
3358 
3359  // The points of the 2D ellipse:
3360  double ang;
3361  double Aang = 6.283185308 / (m_segments - 1);
3362  int i;
3363  for (i = 0, ang = 0; i < m_segments; i++, ang += Aang)
3364  {
3365  double ccos = cos(ang);
3366  double csin = sin(ang);
3367 
3368  m_shape_xs[i] = m_quantiles * (ccos * M_00 + csin * M_10);
3369  m_shape_ys[i] = m_quantiles * (ccos * M_01 + csin * M_11);
3370  } // end for points on ellipse
3371 
3372  ShapeUpdated();
3373 }
3374 
3375 //-----------------------------------------------------------------------------
3376 // mpPolygon - provided by Jose Luis Blanco
3377 //-----------------------------------------------------------------------------
3378 void mpPolygon::setPoints(
3379  const std::vector<double>& points_xs, const std::vector<double>& points_ys,
3380  bool closedShape)
3381 {
3382  if (points_xs.size() != points_ys.size())
3383  {
3384  wxLogError(
3385  wxT("[mpPolygon] Error: points_xs and points_ys must have the same "
3386  "number of elements"));
3387  }
3388  else
3389  {
3390  m_shape_xs = points_xs;
3391  m_shape_ys = points_ys;
3392 
3393  if (closedShape && !points_xs.empty())
3394  {
3395  m_shape_xs.push_back(points_xs[0]);
3396  m_shape_ys.push_back(points_ys[0]);
3397  }
3398 
3399  ShapeUpdated();
3400  }
3401 }
3402 void mpPolygon::setPoints(
3403  const std::vector<float>& points_xs, const std::vector<float>& points_ys,
3404  bool closedShape)
3405 {
3406  if (points_xs.size() != points_ys.size())
3407  {
3408  wxLogError(
3409  wxT("[mpPolygon] Error: points_xs and points_ys must have the same "
3410  "number of elements"));
3411  }
3412  else
3413  {
3414  m_shape_xs.resize(points_xs.size());
3415  m_shape_ys.resize(points_xs.size());
3416 
3417  if (!points_xs.empty())
3418  {
3419  std::vector<float>::const_iterator itX, itY;
3420  std::vector<double>::iterator itXo, itYo;
3421  for (itX = points_xs.begin(), itY = points_ys.begin(),
3422  itXo = m_shape_xs.begin(), itYo = m_shape_ys.begin();
3423  itX != points_xs.end(); ++itX, ++itY, ++itXo, ++itYo)
3424  {
3425  *itXo = (double)*itX;
3426  *itYo = (double)*itY;
3427  }
3428 
3429  if (closedShape)
3430  {
3431  m_shape_xs.push_back((double)points_xs[0]);
3432  m_shape_ys.push_back((double)points_ys[0]);
3433  }
3434  }
3435 
3436  ShapeUpdated();
3437  }
3438 }
3439 
3440 //-----------------------------------------------------------------------------
3441 // mpBitmapLayer - provided by Jose Luis Blanco
3442 //-----------------------------------------------------------------------------
3443 void mpBitmapLayer::GetBitmapCopy(wxImage& outBmp) const
3444 {
3445  if (m_validImg) outBmp = m_bitmap;
3446 }
3447 
3448 void mpBitmapLayer::SetBitmap(
3449  const wxImage& inBmp, double x, double y, double lx, double ly)
3450 {
3451  if (!inBmp.Ok())
3452  {
3453  wxLogError(wxT("[mpBitmapLayer] Assigned bitmap is not Ok()!"));
3454  }
3455  else
3456  {
3457  if (lx < 0)
3458  {
3459  wxLogError(
3460  wxT("[mpBitmapLayer::SetBitmap] Assigned lx is negative!!"));
3461  }
3462  if (ly < 0)
3463  {
3464  wxLogError(
3465  wxT("[mpBitmapLayer::SetBitmap] Assigned ly is negative!!"));
3466  }
3467 
3468  m_bitmap = inBmp; //.GetSubBitmap( wxRect(0, 0, inBmp.GetWidth(),
3469  // inBmp.GetHeight()));
3470  m_min_x = x;
3471  m_min_y = y;
3472  m_max_x = x + lx;
3473  m_max_y = y + ly;
3474  m_validImg = true;
3475  }
3476 }
3477 
3478 void mpBitmapLayer::Plot(wxDC& dc, mpWindow& w)
3479 {
3480  if (m_visible && m_validImg)
3481  {
3482  /* 1st: We compute (x0,y0)-(x1,y1), the pixel coordinates of the real
3483  outer limits
3484  of the image rectangle within the (screen) mpWindow. Note that
3485  these coordinates
3486  might fall well far away from the real view limits when the
3487  user zoom in.
3488 
3489  2nd: We compute (dx0,dy0)-(dx1,dy1), the pixel coordinates the
3490  rectangle that will
3491  be actually drawn into the mpWindow, i.e. the clipped real
3492  rectangle that
3493  avoids the non-visible parts. (offset_x,offset_y) are the pixel
3494  coordinates
3495  that correspond to the window point (dx0,dy0) within the image
3496  "m_bitmap", and
3497  (b_width,b_height) is the size of the bitmap patch that will be
3498  drawn.
3499 
3500  (x0,y0) ................. (x1,y0)
3501  . .
3502  . .
3503  (x0,y1) ................ (x1,y1)
3504  (In pixels!!)
3505  */
3506 
3507  // 1st step -------------------------------
3508  wxCoord x0 = w.x2p(m_min_x);
3509  wxCoord y0 = w.y2p(m_max_y);
3510  wxCoord x1 = w.x2p(m_max_x);
3511  wxCoord y1 = w.y2p(m_min_y);
3512 
3513  // 2nd step -------------------------------
3514  // Precompute the size of the actual bitmap pixel on the screen (e.g.
3515  // will be >1 if zoomed in)
3516  double screenPixelX = (x1 - x0) / (double)m_bitmap.GetWidth();
3517  double screenPixelY = (y1 - y0) / (double)m_bitmap.GetHeight();
3518 
3519  // The minimum number of pixels that the streched image will overpass
3520  // the actual mpWindow borders:
3521  auto borderMarginX = (wxCoord)(screenPixelX + 1); // ceil
3522  auto borderMarginY = (wxCoord)(screenPixelY + 1); // ceil
3523 
3524  // The actual drawn rectangle (dx0,dy0)-(dx1,dy1) is (x0,y0)-(x1,y1)
3525  // clipped:
3526  wxCoord dx0 = x0, dx1 = x1, dy0 = y0, dy1 = y1;
3527  if (dx0 < 0) dx0 = -borderMarginX;
3528  if (dy0 < 0) dy0 = -borderMarginY;
3529  if (dx1 > w.GetScrX()) dx1 = w.GetScrX() + borderMarginX;
3530  if (dy1 > w.GetScrY()) dy1 = w.GetScrY() + borderMarginY;
3531 
3532  // For convenience, compute the width/height of the rectangle to be
3533  // actually drawn:
3534  wxCoord d_width = dx1 - dx0 + 1;
3535  wxCoord d_height = dy1 - dy0 + 1;
3536 
3537  // Compute the pixel offsets in the internally stored bitmap:
3538  auto offset_x = (wxCoord)((dx0 - x0) / screenPixelX);
3539  auto offset_y = (wxCoord)((dy0 - y0) / screenPixelY);
3540 
3541  // and the size in pixel of the area to be actually drawn from the
3542  // internally stored bitmap:
3543  auto b_width = (wxCoord)((dx1 - dx0 + 1) / screenPixelX);
3544  auto b_height = (wxCoord)((dy1 - dy0 + 1) / screenPixelY);
3545 
3546 #ifdef MATHPLOT_DO_LOGGING
3547  wxLogMessage(
3548  _("[mpBitmapLayer::Plot] screenPixel: x=%f y=%f d_width=%ix%i"),
3549  screenPixelX, screenPixelY, d_width, d_height);
3550  wxLogMessage(
3551  _("[mpBitmapLayer::Plot] offset: x=%i y=%i bmpWidth=%ix%i"),
3552  offset_x, offset_y, b_width, b_height);
3553 #endif
3554 
3555  // Is there any visible region?
3556  if (d_width > 0 && d_height > 0)
3557  {
3558  // Build the scaled bitmap from the image, only if it has changed:
3559  if (m_scaledBitmap.GetWidth() != d_width ||
3560  m_scaledBitmap.GetHeight() != d_height ||
3561  m_scaledBitmap_offset_x != offset_x ||
3562  m_scaledBitmap_offset_y != offset_y)
3563  {
3564  wxRect r(wxRect(offset_x, offset_y, b_width, b_height));
3565  // Just for the case....
3566  if (r.x < 0) r.x = 0;
3567  if (r.y < 0) r.y = 0;
3568  if (r.width > m_bitmap.GetWidth())
3569  r.width = m_bitmap.GetWidth();
3570  if (r.height > m_bitmap.GetHeight())
3571  r.height = m_bitmap.GetHeight();
3572 
3573  m_scaledBitmap = wxBitmap(
3574  wxBitmap(m_bitmap).GetSubBitmap(r).ConvertToImage().Scale(
3575  d_width, d_height));
3576  m_scaledBitmap_offset_x = offset_x;
3577  m_scaledBitmap_offset_y = offset_y;
3578  }
3579 
3580  // Draw it:
3581  dc.DrawBitmap(m_scaledBitmap, dx0, dy0, true);
3582  }
3583  }
3584 
3585  // Draw the name label
3586  if (!m_name.IsEmpty() && m_showName)
3587  {
3588  dc.SetFont(m_font);
3589 
3590  wxCoord tx, ty;
3591  dc.GetTextExtent(m_name, &tx, &ty);
3592 
3593  if (HasBBox())
3594  {
3595  auto sx = (wxCoord)((m_max_x - w.GetPosX()) * w.GetScaleX());
3596  auto sy = (wxCoord)((w.GetPosY() - m_max_y) * w.GetScaleY());
3597 
3598  tx = sx - tx - 8;
3599  ty = sy - 8 - ty;
3600  }
3601  else
3602  {
3603  const int sx = w.GetScrX() >> 1;
3604  const int sy = w.GetScrY() >> 1;
3605 
3606  if ((m_flags & mpALIGNMASK) == mpALIGN_NE)
3607  {
3608  tx = sx - tx - 8;
3609  ty = -sy + 8;
3610  }
3611  else if ((m_flags & mpALIGNMASK) == mpALIGN_NW)
3612  {
3613  tx = -sx + 8;
3614  ty = -sy + 8;
3615  }
3616  else if ((m_flags & mpALIGNMASK) == mpALIGN_SW)
3617  {
3618  tx = -sx + 8;
3619  ty = sy - 8 - ty;
3620  }
3621  else
3622  {
3623  tx = sx - tx - 8;
3624  ty = sy - 8 - ty;
3625  }
3626  }
3627 
3628  dc.DrawText(m_name, tx, ty);
3629  }
3630 }
size_t size(const MATRIXLIKE &m, const int dim)
const int INVALID_CLICK_COORDS
Definition: mathplot.cpp:32
#define mpMIN_Y_AXIS_LABEL_SEPARATION
Definition: mathplot.cpp:79
int sign(T x)
Returns the sign of X as "1" or "-1".
return_t square(const num_t x)
Inline function for the square of a number.
const_iterator end() const
Definition: ts_hash_map.h:246
#define mpSCROLL_NUM_PIXELS_PER_LINE
Definition: mathplot.cpp:82
#define mpMIN_X_AXIS_LABEL_SEPARATION
Definition: mathplot.cpp:78
EVT_RIGHT_DOWN(mpWindow::OnMouseRightDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1427
#define mpLEGEND_MARGIN
Definition: mathplot.cpp:74
#define mpLN10
Definition: mathplot.cpp:815
#define mpLEGEND_LINEWIDTH
Definition: mathplot.cpp:75



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: c7a3bec24 Sun Mar 29 18:33:13 2020 +0200 at dom mar 29 18:50:38 CEST 2020