MRPT  1.9.9
mathplot.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://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/object.h"
40 #include "wx/font.h"
41 #include "wx/colour.h"
42 #include "wx/settings.h"
43 #include "wx/sizer.h"
44 #include "wx/log.h"
45 #include "wx/intl.h"
46 #include "wx/dcclient.h"
47 #include "wx/cursor.h"
48 #endif
49 
50 #include <mrpt/otherlibs/mathplot/mathplot.h>
51 
52 #include <wx/bmpbuttn.h>
53 #include <wx/module.h>
54 #include <wx/msgdlg.h>
55 #include <wx/image.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 <cmath>
62 #include <cstdio> // used only for debug
63 #include <ctime> // used for representation of x axes involving date
64 #include <algorithm> // For std::min()/max()
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() : m_type(mpLAYER_UNDEF)
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, wxSOLID);
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() {}
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() {}
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() {}
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 (unsigned int 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 (unsigned int 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 = x;
556  minDrawX = x;
557  maxDrawY = y;
558  minDrawY = 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  time_t 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  time_t 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  time_t 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  time_t 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 / m_scaleX;
1553  float changeUnitsY = change / 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, wxDOT);
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  {
1640  for (li = m_layers.begin(); li != m_layers.end(); ++li)
1641  {
1642  if ((*li)->IsInfo() && (*li)->IsVisible())
1643  {
1644  mpInfoLayer* 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 +
1909  (m_scrX - m_marginLeft - m_marginRight) /
1910  m_scaleX; // m_desiredXmax = m_posX + m_scrX / m_scaleX;
1911  m_desiredYmax = m_posY;
1912  m_desiredYmin =
1913  m_posY -
1914  (m_scrY - m_marginTop - m_marginBottom) /
1915  m_scaleY; // m_desiredYmin = m_posY - m_scrY / m_scaleY;
1916 
1917 #ifdef MATHPLOT_DO_LOGGING
1918  wxLogMessage(
1919  _("mpWindow::ZoomIn() prior coords: (%f,%f), new coords: (%f,%f) "
1920  "SHOULD BE EQUAL!!"),
1921  prior_layer_x, prior_layer_y, p2x(c.x), p2y(c.y));
1922 #endif
1923 
1924  UpdateAll();
1925 }
1926 
1927 void mpWindow::ZoomOut(const wxPoint& centerPoint)
1928 {
1929  wxPoint c(centerPoint);
1930  if (c == wxDefaultPosition)
1931  {
1932  GetClientSize(&m_scrX, &m_scrY);
1933  c.x = (m_scrX - m_marginLeft - m_marginRight) / 2 +
1934  m_marginLeft; // c.x = m_scrX/2;
1935  c.y = (m_scrY - m_marginTop - m_marginBottom) / 2 -
1936  m_marginTop; // c.y = m_scrY/2;
1937  }
1938 
1939  // Preserve the position of the clicked point:
1940  double prior_layer_x = p2x(c.x);
1941  double prior_layer_y = p2y(c.y);
1942 
1943  // Zoom out:
1944  m_scaleX = m_scaleX / zoomIncrementalFactor;
1945  m_scaleY = m_scaleY / zoomIncrementalFactor;
1946 
1947  // Adjust the new m_posx/y:
1948  m_posX = prior_layer_x - c.x / m_scaleX;
1949  m_posY = prior_layer_y + c.y / m_scaleY;
1950 
1951  m_desiredXmin = m_posX;
1952  m_desiredXmax =
1953  m_posX +
1954  (m_scrX - m_marginLeft - m_marginRight) /
1955  m_scaleX; // m_desiredXmax = m_posX + m_scrX / m_scaleX;
1956  m_desiredYmax = m_posY;
1957  m_desiredYmin =
1958  m_posY -
1959  (m_scrY - m_marginTop - m_marginBottom) /
1960  m_scaleY; // m_desiredYmin = m_posY - m_scrY / m_scaleY;
1961 
1962 #ifdef MATHPLOT_DO_LOGGING
1963  wxLogMessage(
1964  _("mpWindow::ZoomOut() prior coords: (%f,%f), new coords: (%f,%f) "
1965  "SHOULD BE EQUAL!!"),
1966  prior_layer_x, prior_layer_y, p2x(c.x), p2y(c.y));
1967 #endif
1968  UpdateAll();
1969 }
1970 
1971 void mpWindow::ZoomInX()
1972 {
1973  m_scaleX = m_scaleX * zoomIncrementalFactor;
1974  UpdateAll();
1975 }
1976 
1977 void mpWindow::ZoomOutX()
1978 {
1979  m_scaleX = m_scaleX / zoomIncrementalFactor;
1980  UpdateAll();
1981 }
1982 
1983 void mpWindow::ZoomInY()
1984 {
1985  m_scaleY = m_scaleY * zoomIncrementalFactor;
1986  UpdateAll();
1987 }
1988 
1989 void mpWindow::ZoomOutY()
1990 {
1991  m_scaleY = m_scaleY / zoomIncrementalFactor;
1992  UpdateAll();
1993 }
1994 
1995 void mpWindow::ZoomRect(wxPoint p0, wxPoint p1)
1996 {
1997  // Compute the 2 corners in graph coordinates:
1998  double p0x = p2x(p0.x);
1999  double p0y = p2y(p0.y);
2000  double p1x = p2x(p1.x);
2001  double p1y = p2y(p1.y);
2002 
2003  // Order them:
2004  double zoom_x_min = p0x < p1x ? p0x : p1x;
2005  double zoom_x_max = p0x > p1x ? p0x : p1x;
2006  double zoom_y_min = p0y < p1y ? p0y : p1y;
2007  double zoom_y_max = p0y > p1y ? p0y : p1y;
2008 
2009 #ifdef MATHPLOT_DO_LOGGING
2010  wxLogMessage(
2011  _("Zoom: (%f,%f)-(%f,%f)"), zoom_x_min, zoom_y_min, zoom_x_max,
2012  zoom_y_max);
2013 #endif
2014 
2015  Fit(zoom_x_min, zoom_x_max, zoom_y_min, zoom_y_max);
2016 }
2017 
2018 void mpWindow::LockAspect(bool enable)
2019 {
2020  m_lockaspect = enable;
2021  m_popmenu.Check(mpID_LOCKASPECT, enable);
2022 
2023  // Try to fit again with the new config:
2024  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax);
2025 }
2026 
2027 void mpWindow::OnShowPopupMenu(wxMouseEvent& event)
2028 {
2029  // Only display menu if the user has not "dragged" the figure
2030  if (m_enableMouseNavigation)
2031  {
2032  SetCursor(*wxSTANDARD_CURSOR);
2033  }
2034 
2035  if (!m_mouseMovedAfterRightClick) // JLB
2036  {
2037  m_clickedX = event.GetX();
2038  m_clickedY = event.GetY();
2039  PopupMenu(&m_popmenu, event.GetX(), event.GetY());
2040  }
2041 }
2042 
2043 void mpWindow::OnLockAspect(wxCommandEvent& WXUNUSED(event))
2044 {
2045  LockAspect(!m_lockaspect);
2046 }
2047 
2048 void mpWindow::OnMouseHelp(wxCommandEvent& WXUNUSED(event))
2049 {
2050  wxMessageBox(
2051  _("Supported Mouse commands:\n \
2052  - Left button down + Mark area: Rectangular zoom\n \
2053  - Right button down + Move: Pan (Move)\n \
2054  - Wheel: Vertical scroll\n \
2055  - Wheel + SHIFT: Horizontal scroll\n \
2056  - Wheel + CTRL: Zoom in/out"),
2057  _("wxMathPlot help"), wxOK, this);
2058 }
2059 
2060 void mpWindow::OnPrintMenu(wxCommandEvent& WXUNUSED(event))
2061 {
2062  // Pass two printout objects: for preview, and possible printing.
2063  mpPrintout* plotPrint = new mpPrintout(this);
2064  mpPrintout* plotPrintPreview = new mpPrintout(this);
2065  wxPrintPreview* preview = new wxPrintPreview(plotPrintPreview, plotPrint);
2066  wxPreviewFrame* frame = new wxPreviewFrame(
2067  preview, nullptr, wxT("Print Plot"), wxPoint(100, 100),
2068  wxSize(600, 650));
2069  frame->Centre(wxBOTH);
2070  frame->Initialize();
2071  frame->Show();
2072 }
2073 
2074 void mpWindow::OnFit(wxCommandEvent& WXUNUSED(event)) { Fit(); }
2075 void mpWindow::OnCenter(wxCommandEvent& WXUNUSED(event))
2076 {
2077  GetClientSize(&m_scrX, &m_scrY);
2078  int centerX = (m_scrX - m_marginLeft - m_marginRight) /
2079  2; // + m_marginLeft; // c.x = m_scrX/2;
2080  int centerY = (m_scrY - m_marginTop - m_marginBottom) /
2081  2; // - m_marginTop; // c.y = m_scrY/2;
2082  SetPos(p2x(m_clickedX - centerX), p2y(m_clickedY - centerY));
2083  // SetPos( p2x(m_clickedX-m_scrX/2), p2y(m_clickedY-m_scrY/2) ); //SetPos(
2084  // (double)(m_clickedX-m_scrX/2) / m_scaleX + m_posX,
2085  // (double)(m_scrY/2-m_clickedY) / m_scaleY + m_posY);
2086 }
2087 
2088 void mpWindow::OnZoomIn(wxCommandEvent& WXUNUSED(event))
2089 {
2090  ZoomIn(wxPoint(m_mouseRClick_X, m_mouseRClick_Y));
2091 }
2092 
2093 void mpWindow::OnZoomOut(wxCommandEvent& WXUNUSED(event)) { ZoomOut(); }
2094 void mpWindow::OnSize(wxSizeEvent& WXUNUSED(event))
2095 {
2096  // Try to fit again with the new window size:
2097  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax);
2098 #ifdef MATHPLOT_DO_LOGGING
2099  wxLogMessage(
2100  _("mpWindow::OnSize() m_scrX = %d, m_scrY = %d"), m_scrX, m_scrY);
2101 #endif // MATHPLOT_DO_LOGGING
2102 }
2103 
2104 bool mpWindow::AddLayer(mpLayer* layer, bool refreshDisplay)
2105 {
2106  if (layer != nullptr)
2107  {
2108  m_layers.push_back(layer);
2109  if (refreshDisplay) UpdateAll();
2110  return true;
2111  };
2112  return false;
2113 }
2114 
2115 bool mpWindow::DelLayer(
2116  mpLayer* layer, bool alsoDeleteObject, bool refreshDisplay)
2117 {
2118  wxLayerList::iterator layIt;
2119  for (layIt = m_layers.begin(); layIt != m_layers.end(); ++layIt)
2120  {
2121  if (*layIt == layer)
2122  {
2123  // Also delete the object?
2124  if (alsoDeleteObject) delete *layIt;
2125  m_layers.erase(layIt); // this deleted the reference only
2126  if (refreshDisplay) UpdateAll();
2127  return true;
2128  }
2129  }
2130  return false;
2131 }
2132 
2133 void mpWindow::DelAllLayers(bool alsoDeleteObject, bool refreshDisplay)
2134 {
2135  while (m_layers.size() > 0)
2136  {
2137  // Also delete the object?
2138  if (alsoDeleteObject) delete m_layers[0];
2139  m_layers.erase(m_layers.begin()); // this deleted the reference only
2140  }
2141  if (refreshDisplay) UpdateAll();
2142 }
2143 
2144 // void mpWindow::DoPrepareDC(wxDC& dc)
2145 // {
2146 // dc.SetDeviceOrigin(x2p(m_minX), y2p(m_maxY));
2147 // }
2148 
2149 void mpWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
2150 {
2151  wxPaintDC dc(this);
2152  dc.GetSize(&m_scrX, &m_scrY); // This is the size of the visible area only!
2153 // DoPrepareDC(dc);
2154 
2155 #ifdef MATHPLOT_DO_LOGGING
2156  {
2157  int px, py;
2158  GetViewStart(&px, &py);
2159  wxLogMessage(
2160  _("[mpWindow::OnPaint] vis.area:%ix%i px=%i py=%i"), m_scrX, m_scrY,
2161  px, py);
2162  }
2163 #endif
2164 
2165  // Selects direct or buffered draw:
2166  wxDC* trgDc;
2167 
2168  // J.L.Blanco @ Aug 2007: Added double buffer support
2169  if (m_enableDoubleBuffer)
2170  {
2171  if (m_last_lx != m_scrX || m_last_ly != m_scrY)
2172  {
2173  if (m_buff_bmp) delete m_buff_bmp;
2174  m_buff_bmp = new wxBitmap(m_scrX, m_scrY);
2175  m_buff_dc.SelectObject(*m_buff_bmp);
2176  m_last_lx = m_scrX;
2177  m_last_ly = m_scrY;
2178  }
2179  trgDc = &m_buff_dc;
2180  }
2181  else
2182  {
2183  trgDc = &dc;
2184  }
2185 
2186  // Draw background:
2187  // trgDc->SetDeviceOrigin(0,0);
2188  trgDc->SetPen(*wxTRANSPARENT_PEN);
2189  wxBrush brush(GetBackgroundColour());
2190  trgDc->SetBrush(brush);
2191  trgDc->SetTextForeground(m_fgColour);
2192  trgDc->DrawRectangle(0, 0, m_scrX, m_scrY);
2193 
2194  // Draw all the layers:
2195  // trgDc->SetDeviceOrigin( m_scrX>>1, m_scrY>>1); // Origin at the center
2197  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2198  {
2199  (*li)->Plot(*trgDc, *this);
2200  };
2201 
2202  // If doublebuffer, draw now to the window:
2203  if (m_enableDoubleBuffer)
2204  {
2205  // trgDc->SetDeviceOrigin(0,0);
2206  // dc.SetDeviceOrigin(0,0); // Origin at the center
2207  dc.Blit(0, 0, m_scrX, m_scrY, trgDc, 0, 0);
2208  }
2209 
2210  /* if (m_coordTooltip) {
2211  wxString toolTipContent;
2212  wxPoint mousePoint = wxGetMousePosition();
2213  toolTipContent.Printf(_("X = %f\nY = %f"), p2x(mousePoint.x),
2214  p2y(mousePoint.y));
2215  SetToolTip(toolTipContent);
2216  }*/
2217  // If scrollbars are enabled, refresh them
2218  if (m_enableScrollBars)
2219  {
2220  /* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2221  m_scrollY = (int) floor((m_maxY - m_posY )*m_scaleY);
2222  Scroll(m_scrollX, m_scrollY);*/
2223  // Scroll(x2p(m_posX), y2p(m_posY));
2224  // SetVirtualSize((int) ((m_maxX - m_minX)*m_scaleX), (int)
2225  // ((m_maxY - m_minY)*m_scaleY));
2226  // int centerX = (m_scrX - m_marginLeft - m_marginRight)/2; // +
2227  // m_marginLeft; // c.x = m_scrX/2;
2228  // int centerY = (m_scrY - m_marginTop - m_marginBottom)/2; // -
2229  // m_marginTop; // c.y = m_scrY/2;
2230  /*SetScrollbars(1, 1, (int) ((m_maxX - m_minX)*m_scaleX), (int) ((m_maxY
2231  * - m_minY)*m_scaleY));*/ //, x2p(m_posX + centerX/m_scaleX), y2p(m_posY - centerY/m_scaleY), true);
2232  }
2233 }
2234 
2235 // void mpWindow::OnScroll2(wxScrollWinEvent &event)
2236 // {
2237 // #ifdef MATHPLOT_DO_LOGGING
2238 // wxLogMessage(_("[mpWindow::OnScroll2] Init: m_posX=%f m_posY=%f, sc_pos =
2239 // %d"),m_posX,m_posY, event.GetPosition());
2240 // #endif
2241 // // If scrollbars are not enabled, Skip operation
2242 // if (!m_enableScrollBars) {
2243 // event.Skip();
2244 // return;
2245 // }
2246 // // m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2247 // // m_scrollY = (int) floor((m_maxY - m_posY /*- m_minY*/)*m_scaleY);
2248 // // Scroll(m_scrollX, m_scrollY);
2249 //
2250 // // GetClientSize( &m_scrX, &m_scrY);
2251 // //Scroll(x2p(m_desiredXmin), y2p(m_desiredYmin));
2252 // int pixelStep = 1;
2253 // if (event.GetOrientation() == wxHORIZONTAL) {
2254 // //m_desiredXmin -= (m_scrollX - event.GetPosition())/m_scaleX;
2255 // //m_desiredXmax -= (m_scrollX - event.GetPosition())/m_scaleX;
2256 // m_posX -= (m_scrollX - event.GetPosition())/m_scaleX;
2257 // m_scrollX = event.GetPosition();
2258 // }
2259 // Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax);
2260 // // /* int pixelStep = 1;
2261 // // if (event.GetOrientation() == wxHORIZONTAL) {
2262 // // m_posX -= (px -
2263 // event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX);
2264 // // m_desiredXmax -= (px -
2265 // event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX);
2266 // // m_desiredXmin -= (px -
2267 // event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX);
2268 // // //SetPosX( (double)px / GetScaleX() + m_minX +
2269 // (double)(width>>1)/GetScaleX());
2270 // // // m_posX = p2x(px); //m_minX + (double)(px /*+
2271 // (m_scrX)*/)/GetScaleX();
2272 // // } else {
2273 // // m_posY += (py -
2274 // event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY);
2275 // // m_desiredYmax += (py -
2276 // event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY);
2277 // // m_desiredYmax += (py -
2278 // event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY);
2279 // // //SetPosY( m_maxY - (double)py / GetScaleY() -
2280 // (double)(height>>1)/GetScaleY());
2281 // // //m_posY = m_maxY - (double)py / GetScaleY() -
2282 // (double)(height>>1)/GetScaleY();
2283 // // // m_posY = p2y(py);//m_maxY - (double)(py /*+
2284 // (m_scrY)*/)/GetScaleY();
2285 // // }*/
2286 // #ifdef MATHPLOT_DO_LOGGING
2287 // int px, py;
2288 // GetViewStart( &px, &py);
2289 // wxLogMessage(_("[mpWindow::OnScroll2] End: m_posX = %f, m_posY = %f, px
2290 // = %f, py = %f"),m_posX, m_posY, px, py);
2291 // #endif
2292 //
2293 // UpdateAll();
2294 // // event.Skip();
2295 // }
2296 
2297 void mpWindow::SetMPScrollbars(bool status)
2298 {
2299  // Temporary behaviour: always disable scrollbars
2300  m_enableScrollBars = status; // false;
2301  if (status == false)
2302  {
2303  SetScrollbar(wxHORIZONTAL, 0, 0, 0);
2304  SetScrollbar(wxVERTICAL, 0, 0, 0);
2305  }
2306  // else the scroll bars will be updated in UpdateAll();
2307  UpdateAll();
2308 
2309  // EnableScrolling(false, false);
2310  // m_enableScrollBars = status;
2311  // EnableScrolling(status, status);
2312  /* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2313  m_scrollY = (int) floor((m_posY - m_minY)*m_scaleY);*/
2314  // int scrollWidth = (int) floor((m_maxX - m_minX)*m_scaleX) - m_scrX;
2315  // int scrollHeight = (int) floor((m_minY - m_maxY)*m_scaleY) - m_scrY;
2316 
2317  // /* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX);
2318  // m_scrollY = (int) floor((m_maxY - m_posY /*- m_minY*/)*m_scaleY);
2319  // int scrollWidth = (int) floor(((m_maxX - m_minX) - (m_desiredXmax -
2320  // m_desiredXmin))*m_scaleX);
2321  // int scrollHeight = (int) floor(((m_maxY - m_minY) - (m_desiredYmax -
2322  // m_desiredYmin))*m_scaleY);
2323  // #ifdef MATHPLOT_DO_LOGGING
2324  // wxLogMessage(_("mpWindow::SetMPScrollbars() scrollWidth = %d,
2325  // scrollHeight = %d"), scrollWidth, scrollHeight);
2326  // #endif
2327  // if(status) {
2328  // SetScrollbars(1,
2329  // 1,
2330  // scrollWidth,
2331  // scrollHeight,
2332  // m_scrollX,
2333  // m_scrollY);
2334  // // SetVirtualSize((int) (m_maxX - m_minX), (int) (m_maxY -
2335  // m_minY));
2336  // }
2337  // Refresh(false);*/
2338 }
2339 
2340 bool mpWindow::UpdateBBox()
2341 {
2342  bool first = TRUE;
2343 
2344  for (wxLayerList::iterator li = m_layers.begin(); li != m_layers.end();
2345  ++li)
2346  {
2347  mpLayer* f = *li;
2348 
2349  if (f->HasBBox())
2350  {
2351  if (first)
2352  {
2353  first = FALSE;
2354  m_minX = f->GetMinX();
2355  m_maxX = f->GetMaxX();
2356  m_minY = f->GetMinY();
2357  m_maxY = f->GetMaxY();
2358  }
2359  else
2360  {
2361  if (f->GetMinX() < m_minX) m_minX = f->GetMinX();
2362  if (f->GetMaxX() > m_maxX) m_maxX = f->GetMaxX();
2363  if (f->GetMinY() < m_minY) m_minY = f->GetMinY();
2364  if (f->GetMaxY() > m_maxY) m_maxY = f->GetMaxY();
2365  }
2366  }
2367  // node = node->GetNext();
2368  }
2369 #ifdef MATHPLOT_DO_LOGGING
2370  wxLogDebug(
2371  wxT("[mpWindow::UpdateBBox] Bounding box: Xmin = %f, Xmax = %f, Ymin = "
2372  "%f, YMax = %f"),
2373  m_minX, m_maxX, m_minY, m_maxY);
2374 #endif // MATHPLOT_DO_LOGGING
2375  return first == FALSE;
2376 }
2377 
2378 // void mpWindow::UpdateAll()
2379 // {
2380 // GetClientSize( &m_scrX,&m_scrY);
2381 /* if (m_enableScrollBars) {
2382  // The "virtual size" of the scrolled window:
2383  const int sx = (int)((m_maxX - m_minX) * GetScaleX());
2384  const int sy = (int)((m_maxY - m_minY) * GetScaleY());
2385  SetVirtualSize(sx, sy);
2386  SetScrollRate(1, 1);*/
2387 // const int px = (int)((GetPosX() - m_minX) * GetScaleX());// -
2388 // m_scrX); //(cx>>1));
2389 
2390 // J.L.Blanco, Aug 2007: Formula fixed:
2391 // const int py = (int)((m_maxY - GetPosY()) * GetScaleY());// -
2392 // m_scrY); //(cy>>1));
2393 // int px, py;
2394 // GetViewStart(&px0, &py0);
2395 // px = (int)((m_posX - m_minX)*m_scaleX);
2396 // py = (int)((m_maxY - m_posY)*m_scaleY);
2397 
2398 // SetScrollbars( 1, 1, sx - m_scrX, sy - m_scrY, px, py, TRUE);
2399 // }
2400 
2401 // Working code
2402 // UpdateBBox();
2403 // Refresh( FALSE );
2404 // end working code
2405 
2406 // Old version
2407 /* bool box = UpdateBBox();
2408  if (box)
2409 {
2410  int cx, cy;
2411  GetClientSize( &cx, &cy);
2412 
2413  // The "virtual size" of the scrolled window:
2414  const int sx = (int)((m_maxX - m_minX) * GetScaleX());
2415  const int sy = (int)((m_maxY - m_minY) * GetScaleY());
2416 
2417  const int px = (int)((GetPosX() - m_minX) * GetScaleX() - (cx>>1));
2418 
2419  // J.L.Blanco, Aug 2007: Formula fixed:
2420  const int py = (int)((m_maxY - GetPosY()) * GetScaleY() - (cy>>1));
2421 
2422  SetScrollbars( 1, 1, sx, sy, px, py, TRUE);
2423 
2424 #ifdef MATHPLOT_DO_LOGGING
2425  wxLogMessage(_("[mpWindow::UpdateAll] Size:%ix%i
2426 ScrollBars:%i,%i"),sx,sy,px,py);
2427 #endif
2428 }
2429 
2430  FitInside();
2431  Refresh( FALSE );
2432 */
2433 // }
2434 
2435 void mpWindow::UpdateAll()
2436 {
2437  if (UpdateBBox())
2438  {
2439  if (m_enableScrollBars)
2440  {
2441  int cx, cy;
2442  GetClientSize(&cx, &cy);
2443  // Do x scroll bar
2444  {
2445  // Convert margin sizes from pixels to coordinates
2446  double leftMargin = m_marginLeft / m_scaleX;
2447  // Calculate the range in coords that we want to scroll over
2448  double maxX = (m_desiredXmax > m_maxX) ? m_desiredXmax : m_maxX;
2449  double minX = (m_desiredXmin < m_minX) ? m_desiredXmin : m_minX;
2450  if ((m_posX + leftMargin) < minX) minX = m_posX + leftMargin;
2451  // Calculate scroll bar size and thumb position
2452  int sizeX = (int)((maxX - minX) * m_scaleX);
2453  int thumbX = (int)(((m_posX + leftMargin) - minX) * m_scaleX);
2454  SetScrollbar(
2455  wxHORIZONTAL, thumbX, cx - (m_marginRight + m_marginLeft),
2456  sizeX);
2457  }
2458  // Do y scroll bar
2459  {
2460  // Convert margin sizes from pixels to coordinates
2461  double topMargin = m_marginTop / m_scaleY;
2462  // Calculate the range in coords that we want to scroll over
2463  double maxY = (m_desiredYmax > m_maxY) ? m_desiredYmax : m_maxY;
2464  if ((m_posY - topMargin) > maxY) maxY = m_posY - topMargin;
2465  double minY = (m_desiredYmin < m_minY) ? m_desiredYmin : m_minY;
2466  // Calculate scroll bar size and thumb position
2467  int sizeY = (int)((maxY - minY) * m_scaleY);
2468  int thumbY = (int)((maxY - (m_posY - topMargin)) * m_scaleY);
2469  SetScrollbar(
2470  wxVERTICAL, thumbY, cy - (m_marginTop + m_marginBottom),
2471  sizeY);
2472  }
2473  }
2474  }
2475 
2476  Refresh(FALSE);
2477 }
2478 
2479 void mpWindow::DoScrollCalc(const int position, const int orientation)
2480 {
2481  if (orientation == wxVERTICAL)
2482  {
2483  // Y axis
2484  // Get top margin in coord units
2485  double topMargin = m_marginTop / m_scaleY;
2486  // Calculate maximum Y coord to be shown in the graph
2487  double maxY = m_desiredYmax > m_maxY ? m_desiredYmax : m_maxY;
2488  // Set new position
2489  SetPosY((maxY - (position / m_scaleY)) + topMargin);
2490  }
2491  else
2492  {
2493  // X Axis
2494  // Get left margin in coord units
2495  double leftMargin = m_marginLeft / m_scaleX;
2496  // Calculate minimum X coord to be shown in the graph
2497  double minX = (m_desiredXmin < m_minX) ? m_desiredXmin : m_minX;
2498  // Set new position
2499  SetPosX((minX + (position / m_scaleX)) - leftMargin);
2500  }
2501 }
2502 
2503 void mpWindow::OnScrollThumbTrack(wxScrollWinEvent& event)
2504 {
2505  DoScrollCalc(event.GetPosition(), event.GetOrientation());
2506 }
2507 
2508 void mpWindow::OnScrollPageUp(wxScrollWinEvent& event)
2509 {
2510  int scrollOrientation = event.GetOrientation();
2511  // Get position before page up
2512  int position = GetScrollPos(scrollOrientation);
2513  // Get thumb size
2514  int thumbSize = GetScrollThumb(scrollOrientation);
2515  // Need to adjust position by a page
2516  position -= thumbSize;
2517  if (position < 0) position = 0;
2518 
2519  DoScrollCalc(position, scrollOrientation);
2520 }
2521 void mpWindow::OnScrollPageDown(wxScrollWinEvent& event)
2522 {
2523  int scrollOrientation = event.GetOrientation();
2524  // Get position before page up
2525  int position = GetScrollPos(scrollOrientation);
2526  // Get thumb size
2527  int thumbSize = GetScrollThumb(scrollOrientation);
2528  // Get scroll range
2529  int scrollRange = GetScrollRange(scrollOrientation);
2530  // Need to adjust position by a page
2531  position += thumbSize;
2532  if (position > (scrollRange - thumbSize))
2533  position = scrollRange - thumbSize;
2534 
2535  DoScrollCalc(position, scrollOrientation);
2536 }
2537 
2538 void mpWindow::OnScrollLineUp(wxScrollWinEvent& event)
2539 {
2540  int scrollOrientation = event.GetOrientation();
2541  // Get position before page up
2542  int position = GetScrollPos(scrollOrientation);
2543  // Need to adjust position by a line
2544  position -= mpSCROLL_NUM_PIXELS_PER_LINE;
2545  if (position < 0) position = 0;
2546 
2547  DoScrollCalc(position, scrollOrientation);
2548 }
2549 
2550 void mpWindow::OnScrollLineDown(wxScrollWinEvent& event)
2551 {
2552  int scrollOrientation = event.GetOrientation();
2553  // Get position before page up
2554  int position = GetScrollPos(scrollOrientation);
2555  // Get thumb size
2556  int thumbSize = GetScrollThumb(scrollOrientation);
2557  // Get scroll range
2558  int scrollRange = GetScrollRange(scrollOrientation);
2559  // Need to adjust position by a page
2560  position += mpSCROLL_NUM_PIXELS_PER_LINE;
2561  if (position > (scrollRange - thumbSize))
2562  position = scrollRange - thumbSize;
2563 
2564  DoScrollCalc(position, scrollOrientation);
2565 }
2566 
2567 void mpWindow::OnScrollTop(wxScrollWinEvent& event)
2568 {
2569  DoScrollCalc(0, event.GetOrientation());
2570 }
2571 
2572 void mpWindow::OnScrollBottom(wxScrollWinEvent& event)
2573 {
2574  int scrollOrientation = event.GetOrientation();
2575  // Get thumb size
2576  int thumbSize = GetScrollThumb(scrollOrientation);
2577  // Get scroll range
2578  int scrollRange = GetScrollRange(scrollOrientation);
2579 
2580  DoScrollCalc(scrollRange - thumbSize, scrollOrientation);
2581 }
2582 // End patch ngpaton
2583 
2584 void mpWindow::SetScaleX(double scaleX)
2585 {
2586  if (scaleX != 0) m_scaleX = scaleX;
2587  UpdateAll();
2588 }
2589 
2590 // New methods implemented by Davide Rondini
2591 
2592 unsigned int mpWindow::CountLayers()
2593 {
2594  // wxNode *node = m_layers.GetFirst();
2595  unsigned int layerNo = 0;
2596  for (wxLayerList::iterator li = m_layers.begin(); li != m_layers.end();
2597  ++li) // while(node)
2598  {
2599  if ((*li)->HasBBox()) layerNo++;
2600  // node = node->GetNext();
2601  };
2602  return layerNo;
2603 }
2604 
2605 mpLayer* mpWindow::GetLayer(int position)
2606 {
2607  if ((position >= (int)m_layers.size()) || position < 0) return nullptr;
2608  return m_layers[position];
2609 }
2610 
2611 mpLayer* mpWindow::GetLayerByName(const wxString& name)
2612 {
2613  for (wxLayerList::iterator it = m_layers.begin(); it != m_layers.end();
2614  ++it)
2615  if (!(*it)->GetName().Cmp(name)) return *it;
2616  return nullptr; // Not found
2617 }
2618 
2619 void mpWindow::GetBoundingBox(double* bbox)
2620 {
2621  bbox[0] = m_minX;
2622  bbox[1] = m_maxX;
2623  bbox[2] = m_minY;
2624  bbox[3] = m_maxY;
2625 }
2626 
2627 bool mpWindow::SaveScreenshot(
2628  const wxString& filename, int type, wxSize imageSize, bool fit)
2629 {
2630  int sizeX, sizeY;
2631  int bk_scrX, bk_scrY;
2632  if (imageSize == wxDefaultSize)
2633  {
2634  sizeX = m_scrX;
2635  sizeY = m_scrY;
2636  }
2637  else
2638  {
2639  sizeX = imageSize.x;
2640  sizeY = imageSize.y;
2641  bk_scrX = m_scrX;
2642  bk_scrY = m_scrY;
2643  SetScr(sizeX, sizeY);
2644  }
2645 
2646  wxBitmap screenBuffer(sizeX, sizeY);
2647  wxMemoryDC screenDC;
2648  screenDC.SelectObject(screenBuffer);
2649  screenDC.SetPen(*wxTRANSPARENT_PEN);
2650  wxBrush brush(GetBackgroundColour());
2651  screenDC.SetBrush(brush);
2652  screenDC.DrawRectangle(0, 0, sizeX, sizeY);
2653 
2654  if (fit)
2655  {
2656  Fit(m_minX, m_maxX, m_minY, m_maxY, &sizeX, &sizeY);
2657  }
2658  else
2659  {
2660  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax, &sizeX,
2661  &sizeY);
2662  }
2663  // Draw all the layers:
2665  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2666  (*li)->Plot(screenDC, *this);
2667 
2668  if (imageSize != wxDefaultSize)
2669  {
2670  // Restore dimensions
2671  SetScr(bk_scrX, bk_scrY);
2672  Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax,
2673  &bk_scrX, &bk_scrY);
2674  UpdateAll();
2675  }
2676  // Once drawing is complete, actually save screen shot
2677  wxImage screenImage = screenBuffer.ConvertToImage();
2678  return screenImage.SaveFile(filename, (wxBitmapType)type);
2679 }
2680 
2681 void mpWindow::SetMargins(int top, int right, int bottom, int left)
2682 {
2683  m_marginTop = top;
2684  m_marginRight = right;
2685  m_marginBottom = bottom;
2686  m_marginLeft = left;
2687 }
2688 
2689 mpInfoLayer* mpWindow::IsInsideInfoLayer(wxPoint& point)
2690 {
2692  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2693  {
2694 #ifdef MATHPLOT_DO_LOGGING
2695  wxLogMessage(
2696  _("mpWindow::IsInsideInfoLayer() examinining layer = %p"), (*li));
2697 #endif // MATHPLOT_DO_LOGGING
2698  if ((*li)->IsInfo())
2699  {
2700  mpInfoLayer* tmpLyr = (mpInfoLayer*)(*li);
2701 #ifdef MATHPLOT_DO_LOGGING
2702  wxLogMessage(_("mpWindow::IsInsideInfoLayer() layer = %p"), (*li));
2703 #endif // MATHPLOT_DO_LOGGING
2704  if (tmpLyr->Inside(point))
2705  {
2706  return tmpLyr;
2707  }
2708  }
2709  }
2710  return nullptr;
2711 }
2712 
2713 void mpWindow::SetLayerVisible(const wxString& name, bool viewable)
2714 {
2715  mpLayer* lx = GetLayerByName(name);
2716  if (lx)
2717  {
2718  lx->SetVisible(viewable);
2719  UpdateAll();
2720  }
2721 }
2722 
2723 bool mpWindow::IsLayerVisible(const wxString& name)
2724 {
2725  mpLayer* lx = GetLayerByName(name);
2726  return (lx) ? lx->IsVisible() : false;
2727 }
2728 
2729 void mpWindow::SetLayerVisible(const unsigned int position, bool viewable)
2730 {
2731  mpLayer* lx = GetLayer(position);
2732  if (lx)
2733  {
2734  lx->SetVisible(viewable);
2735  UpdateAll();
2736  }
2737 }
2738 
2739 bool mpWindow::IsLayerVisible(const unsigned int position)
2740 {
2741  mpLayer* lx = GetLayer(position);
2742  return (lx) ? lx->IsVisible() : false;
2743 }
2744 
2745 void mpWindow::SetColourTheme(
2746  const wxColour& bgColour, const wxColour& drawColour,
2747  const wxColour& axesColour)
2748 {
2749  SetBackgroundColour(bgColour);
2750  SetForegroundColour(drawColour);
2751  m_bgColour = bgColour;
2752  m_fgColour = drawColour;
2753  m_axColour = axesColour;
2754  // cycle between layers to set colours and properties to them
2756  for (li = m_layers.begin(); li != m_layers.end(); ++li)
2757  {
2758  if ((*li)->GetLayerType() == mpLAYER_AXIS)
2759  {
2760  wxPen axisPen = (*li)->GetPen(); // Get the old pen to modify only
2761  // colour, not style or width
2762  axisPen.SetColour(axesColour);
2763  (*li)->SetPen(axisPen);
2764  }
2765  if ((*li)->GetLayerType() == mpLAYER_INFO)
2766  {
2767  wxPen infoPen = (*li)->GetPen(); // Get the old pen to modify only
2768  // colour, not style or width
2769  infoPen.SetColour(drawColour);
2770  (*li)->SetPen(infoPen);
2771  }
2772  }
2773 }
2774 
2775 // void mpWindow::EnableCoordTooltip(bool value)
2776 // {
2777 // m_coordTooltip = value;
2778 // // if (value) GetToolTip()->SetDelay(100);
2779 // }
2780 
2781 /*
2782 double mpWindow::p2x(wxCoord pixelCoordX, bool drawOutside )
2783 {
2784  if (drawOutside) {
2785  return m_posX + pixelCoordX/m_scaleX;
2786  }
2787  // Draw inside margins
2788  double marginScaleX = ((double)(m_scrX - m_marginLeft -
2789 m_marginRight))/m_scrX;
2790  return m_marginLeft + (m_posX + pixelCoordX/m_scaleX)/marginScaleX;
2791 }
2792 
2793 double mpWindow::p2y(wxCoord pixelCoordY, bool drawOutside )
2794 {
2795  if (drawOutside) {
2796  return m_posY - pixelCoordY/m_scaleY;
2797  }
2798  // Draw inside margins
2799  double marginScaleY = ((double)(m_scrY - m_marginTop -
2800 m_marginBottom))/m_scrY;
2801  return m_marginTop + (m_posY - pixelCoordY/m_scaleY)/marginScaleY;
2802 }
2803 
2804 wxCoord mpWindow::x2p(double x, bool drawOutside)
2805 {
2806  if (drawOutside) {
2807  return (wxCoord) ((x-m_posX) * m_scaleX);
2808  }
2809  // Draw inside margins
2810  double marginScaleX = ((double)(m_scrX - m_marginLeft -
2811 m_marginRight))/m_scrX;
2812 #ifdef MATHPLOT_DO_LOGGING
2813  wxLogMessage(wxT("x2p ScrX = %d, marginRight = %d, marginLeft = %d,
2814 marginScaleX = %f"), m_scrX, m_marginRight, m_marginLeft, marginScaleX);
2815 #endif // MATHPLOT_DO_LOGGING
2816  return (wxCoord) (int)(((x-m_posX) * m_scaleX)*marginScaleX) - m_marginLeft;
2817 }
2818 
2819 wxCoord mpWindow::y2p(double y, bool drawOutside)
2820 {
2821  if (drawOutside) {
2822  return (wxCoord) ( (m_posY-y) * m_scaleY);
2823  }
2824  // Draw inside margins
2825  double marginScaleY = ((double)(m_scrY - m_marginTop -
2826 m_marginBottom))/m_scrY;
2827 #ifdef MATHPLOT_DO_LOGGING
2828  wxLogMessage(wxT("y2p ScrY = %d, marginTop = %d, marginBottom = %d,
2829 marginScaleY = %f"), m_scrY, m_marginTop, m_marginBottom, marginScaleY);
2830 #endif // MATHPLOT_DO_LOGGING
2831  return (wxCoord) ((int)((m_posY-y) * m_scaleY)*marginScaleY) - m_marginTop;
2832 }
2833 */
2834 
2835 //-----------------------------------------------------------------------------
2836 // mpFXYVector implementation - by Jose Luis Blanco (AGO-2007)
2837 //-----------------------------------------------------------------------------
2838 
2839 IMPLEMENT_DYNAMIC_CLASS(mpFXYVector, mpFXY)
2840 
2841 // Constructor
2842 mpFXYVector::mpFXYVector(wxString name, int flags) : mpFXY(name, flags)
2843 {
2844  m_index = 0;
2845  m_minX = -1;
2846  m_maxX = 1;
2847  m_minY = -1;
2848  m_maxY = 1;
2849  m_type = mpLAYER_PLOT;
2850 }
2851 
2852 void mpFXYVector::Rewind() { m_index = 0; }
2853 bool mpFXYVector::GetNextXY(double& x, double& y)
2854 {
2855  if (m_index >= m_xs.size())
2856  return FALSE;
2857  else
2858  {
2859  x = m_xs[m_index];
2860  y = m_ys[m_index++];
2861  return m_index <= m_xs.size();
2862  }
2863 }
2864 
2865 void mpFXYVector::Clear()
2866 {
2867  m_xs.clear();
2868  m_ys.clear();
2869 }
2870 
2871 void mpFXYVector::SetData(
2872  const std::vector<float>& xs, const std::vector<float>& ys)
2873 {
2874  // Check if the data vectora are of the same size
2875  if (xs.size() != ys.size())
2876  {
2877  wxLogError(
2878  _("wxMathPlot error: X and Y vector are not of the same length!"));
2879  return;
2880  }
2881  const size_t N = xs.size();
2882  std::vector<double> Xd(N), Yd(N);
2883  for (size_t i = 0; i < xs.size(); i++)
2884  {
2885  Xd[i] = xs[i];
2886  Yd[i] = ys[i];
2887  }
2888  SetData(Xd, Yd);
2889 }
2890 
2891 void mpFXYVector::SetData(
2892  const std::vector<double>& xs, const std::vector<double>& ys)
2893 {
2894  // Check if the data vectora are of the same size
2895  if (xs.size() != ys.size())
2896  {
2897  wxLogError(
2898  _("wxMathPlot error: X and Y vector are not of the same length!"));
2899  return;
2900  }
2901  // Copy the data:
2902  m_xs = xs;
2903  m_ys = ys;
2904 
2905  // Update internal variables for the bounding box.
2906  if (xs.size() > 0)
2907  {
2908  m_minX = xs[0];
2909  m_maxX = xs[0];
2910  m_minY = ys[0];
2911  m_maxY = ys[0];
2912 
2914 
2915  for (it = xs.begin(); it != xs.end(); ++it)
2916  {
2917  if (*it < m_minX) m_minX = *it;
2918  if (*it > m_maxX) m_maxX = *it;
2919  }
2920  for (it = ys.begin(); it != ys.end(); ++it)
2921  {
2922  if (*it < m_minY) m_minY = *it;
2923  if (*it > m_maxY) m_maxY = *it;
2924  }
2925  m_minX -= 0.5f;
2926  m_minY -= 0.5f;
2927  m_maxX += 0.5f;
2928  m_maxY += 0.5f;
2929  }
2930  else
2931  {
2932  m_minX = -1;
2933  m_maxX = 1;
2934  m_minY = -1;
2935  m_maxY = 1;
2936  }
2937 }
2938 
2939 void mpFXYVector::AppendDataPoint(float x, float y)
2940 {
2941  // Append the data:
2942  m_xs.push_back(x);
2943  m_ys.push_back(y);
2944 
2945  if (m_xs.size() == 1)
2946  {
2947  m_minX = x;
2948  m_maxX = x;
2949  m_minY = y;
2950  m_maxY = y;
2951 
2952  const double margX = std::max(fabs(m_minX), fabs(m_maxX)) * 0.05;
2953  const double margY = std::max(fabs(m_minY), fabs(m_maxY)) * 0.05;
2954 
2955  m_minX -= margX;
2956  m_maxX += margX;
2957  m_minY -= margY;
2958  m_maxY += margY;
2959  }
2960  else
2961  {
2962  m_minX = std::min(x - fabs(x) * 0.05, m_minX);
2963  m_maxX = std::max(x + fabs(x) * 0.05, m_maxX);
2964  m_minY = std::min(y - fabs(y) * 0.05, m_minY);
2965  m_maxY = std::max(y + fabs(y) * 0.05, m_maxY);
2966  }
2967 }
2968 
2969 //-----------------------------------------------------------------------------
2970 // mpText - provided by Val Greene
2971 //-----------------------------------------------------------------------------
2972 
2973 IMPLEMENT_DYNAMIC_CLASS(mpText, mpLayer)
2974 
2975 /** @param name text to be displayed
2976 @param offsetx x position in percentage (0-100)
2977 @param offsetx y position in percentage (0-100)
2978 */
2979 mpText::mpText(wxString name, int offsetx, int offsety)
2980 {
2981  SetName(name);
2982 
2983  if (offsetx >= 0 && offsetx <= 100)
2984  m_offsetx = offsetx;
2985  else
2986  m_offsetx = 5;
2987 
2988  if (offsety >= 0 && offsety <= 100)
2989  m_offsety = offsety;
2990  else
2991  m_offsetx = 50;
2992  m_type = mpLAYER_INFO;
2993 }
2994 
2995 /** mpText Layer plot handler.
2996 This implementation will plot the text adjusted to the visible area.
2997 */
2998 
2999 void mpText::Plot(wxDC& dc, mpWindow& w)
3000 {
3001  if (m_visible)
3002  {
3003  dc.SetPen(m_pen);
3004  dc.SetFont(m_font);
3005 
3006  wxCoord tw = 0, th = 0;
3007  dc.GetTextExtent(GetName(), &tw, &th);
3008 
3009  // int left = -dc.LogicalToDeviceX(0);
3010  // int width = dc.LogicalToDeviceX(0) - left;
3011  // int bottom = dc.LogicalToDeviceY(0);
3012  // int height = bottom - -dc.LogicalToDeviceY(0);
3013 
3014  /* dc.DrawText( GetName(),
3015  (int)((((float)width/100.0) * m_offsety) + left - (tw/2)),
3016  (int)((((float)height/100.0) * m_offsetx) - bottom) );*/
3017  int px = m_offsetx *
3018  (w.GetScrX() - w.GetMarginLeft() - w.GetMarginRight()) / 100;
3019  int py = m_offsety *
3020  (w.GetScrY() - w.GetMarginTop() - w.GetMarginBottom()) / 100;
3021  dc.DrawText(GetName(), px, py);
3022  }
3023 }
3024 
3025 //-----------------------------------------------------------------------------
3026 // mpPrintout - provided by Davide Rondini
3027 //-----------------------------------------------------------------------------
3028 
3029 mpPrintout::mpPrintout(mpWindow* drawWindow, const wxChar* title)
3030  : wxPrintout(title)
3031 {
3032  drawn = false;
3033  plotWindow = drawWindow;
3034 }
3035 
3036 bool mpPrintout::OnPrintPage(int page)
3037 {
3038  wxDC* trgDc = GetDC();
3039  if ((trgDc) && (page == 1))
3040  {
3041  wxCoord m_prnX, m_prnY;
3042  int marginX = 50;
3043  int marginY = 50;
3044  trgDc->GetSize(&m_prnX, &m_prnY);
3045 
3046  m_prnX -= (2 * marginX);
3047  m_prnY -= (2 * marginY);
3048  trgDc->SetDeviceOrigin(marginX, marginY);
3049 
3050 #ifdef MATHPLOT_DO_LOGGING
3051  wxLogMessage(wxT("Print Size: %d x %d\n"), m_prnX, m_prnY);
3052  wxLogMessage(
3053  wxT("Screen Size: %d x %d\n"), plotWindow->GetScrX(),
3054  plotWindow->GetScrY());
3055 #endif
3056 
3057  // Set the scale according to the page:
3058  plotWindow->Fit(
3059  plotWindow->GetDesiredXmin(), plotWindow->GetDesiredXmax(),
3060  plotWindow->GetDesiredYmin(), plotWindow->GetDesiredYmax(), &m_prnX,
3061  &m_prnY);
3062 
3063  // Get the colours of the plotWindow to restore them ath the end
3064  wxColour oldBgColour = plotWindow->GetBackgroundColour();
3065  wxColour oldFgColour = plotWindow->GetForegroundColour();
3066  wxColour oldAxColour = plotWindow->GetAxesColour();
3067 
3068  // Draw background, ensuring to use white background for printing.
3069  trgDc->SetPen(*wxTRANSPARENT_PEN);
3070  // wxBrush brush( plotWindow->GetBackgroundColour() );
3071  wxBrush brush = *wxWHITE_BRUSH;
3072  trgDc->SetBrush(brush);
3073  trgDc->DrawRectangle(0, 0, m_prnX, m_prnY);
3074 
3075  // Draw all the layers:
3076  // trgDc->SetDeviceOrigin( m_prnX>>1, m_prnY>>1); // Origin at the
3077  // center
3078  mpLayer* layer;
3079  for (unsigned int li = 0; li < plotWindow->CountAllLayers(); ++li)
3080  {
3081  layer = plotWindow->GetLayer(li);
3082  layer->Plot(*trgDc, *plotWindow);
3083  };
3084  // Restore device origin
3085  // trgDc->SetDeviceOrigin(0, 0);
3086  // Restore colours
3087  plotWindow->SetColourTheme(oldBgColour, oldFgColour, oldAxColour);
3088  // Restore drawing
3089  plotWindow->Fit(
3090  plotWindow->GetDesiredXmin(), plotWindow->GetDesiredXmax(),
3091  plotWindow->GetDesiredYmin(), plotWindow->GetDesiredYmax(), nullptr,
3092  nullptr);
3093  plotWindow->UpdateAll();
3094  }
3095  return true;
3096 }
3097 
3098 bool mpPrintout::HasPage(int page) { return (page == 1); }
3099 //-----------------------------------------------------------------------------
3100 // mpMovableObject - provided by Jose Luis Blanco
3101 //-----------------------------------------------------------------------------
3102 void mpMovableObject::TranslatePoint(
3103  double x, double y, double& out_x, double& out_y)
3104 {
3105  double ccos = cos(m_reference_phi); // Avoid computing cos/sin twice.
3106  double csin = sin(m_reference_phi);
3107 
3108  out_x = m_reference_x + ccos * x - csin * y;
3109  out_y = m_reference_y + csin * x + ccos * y;
3110 }
3111 
3112 // This method updates the buffers m_trans_shape_xs/ys, and the precomputed
3113 // bounding box.
3114 void mpMovableObject::ShapeUpdated()
3115 {
3116  // Just in case...
3117  if (m_shape_xs.size() != m_shape_ys.size())
3118  {
3119  wxLogError(
3120  wxT("[mpMovableObject::ShapeUpdated] Error, m_shape_xs and "
3121  "m_shape_ys have different lengths!"));
3122  }
3123  else
3124  {
3125  double ccos = cos(m_reference_phi); // Avoid computing cos/sin twice.
3126  double csin = sin(m_reference_phi);
3127 
3128  m_trans_shape_xs.resize(m_shape_xs.size());
3129  m_trans_shape_ys.resize(m_shape_xs.size());
3130 
3131  std::vector<double>::iterator itXi, itXo;
3132  std::vector<double>::iterator itYi, itYo;
3133 
3134  m_bbox_min_x = 1e300;
3135  m_bbox_max_x = -1e300;
3136  m_bbox_min_y = 1e300;
3137  m_bbox_max_y = -1e300;
3138 
3139  for (itXo = m_trans_shape_xs.begin(), itYo = m_trans_shape_ys.begin(),
3140  itXi = m_shape_xs.begin(), itYi = m_shape_ys.begin();
3141  itXo != m_trans_shape_xs.end(); ++itXo, ++itYo, ++itXi, ++itYi)
3142  {
3143  *itXo = m_reference_x + ccos * (*itXi) - csin * (*itYi);
3144  *itYo = m_reference_y + csin * (*itXi) + ccos * (*itYi);
3145 
3146  // Keep BBox:
3147  if (*itXo < m_bbox_min_x) m_bbox_min_x = *itXo;
3148  if (*itXo > m_bbox_max_x) m_bbox_max_x = *itXo;
3149  if (*itYo < m_bbox_min_y) m_bbox_min_y = *itYo;
3150  if (*itYo > m_bbox_max_y) m_bbox_max_y = *itYo;
3151  }
3152  }
3153 }
3154 
3155 void mpMovableObject::Plot(wxDC& dc, mpWindow& w)
3156 {
3157  if (m_visible)
3158  {
3159  dc.SetPen(m_pen);
3160 
3161  std::vector<double>::iterator itX = m_trans_shape_xs.begin();
3162  std::vector<double>::iterator itY = m_trans_shape_ys.begin();
3163 
3164  if (!m_continuous)
3165  {
3166  // for some reason DrawPoint does not use the current pen,
3167  // so we use DrawLine for fat pens
3168  if (m_pen.GetWidth() <= 1)
3169  {
3170  while (itX != m_trans_shape_xs.end())
3171  {
3172  dc.DrawPoint(w.x2p(*(itX++)), w.y2p(*(itY++)));
3173  }
3174  }
3175  else
3176  {
3177  while (itX != m_trans_shape_xs.end())
3178  {
3179  wxCoord cx = w.x2p(*(itX++));
3180  wxCoord cy = w.y2p(*(itY++));
3181  dc.DrawLine(cx, cy, cx, cy);
3182  }
3183  }
3184  }
3185  else
3186  {
3187  wxCoord cx0 = 0, cy0 = 0;
3188  bool first = TRUE;
3189  while (itX != m_trans_shape_xs.end())
3190  {
3191  wxCoord cx = w.x2p(*(itX++));
3192  wxCoord cy = w.y2p(*(itY++));
3193  if (first)
3194  {
3195  first = FALSE;
3196  cx0 = cx;
3197  cy0 = cy;
3198  }
3199  dc.DrawLine(cx0, cy0, cx, cy);
3200  cx0 = cx;
3201  cy0 = cy;
3202  }
3203  }
3204 
3205  if (!m_name.IsEmpty() && m_showName)
3206  {
3207  dc.SetFont(m_font);
3208 
3209  wxCoord tx, ty;
3210  dc.GetTextExtent(m_name, &tx, &ty);
3211 
3212  if (HasBBox())
3213  {
3214  wxCoord sx =
3215  (wxCoord)((m_bbox_max_x - w.GetPosX()) * w.GetScaleX());
3216  wxCoord sy =
3217  (wxCoord)((w.GetPosY() - m_bbox_max_y) * w.GetScaleY());
3218 
3219  tx = sx - tx - 8;
3220  ty = sy - 8 - ty;
3221  }
3222  else
3223  {
3224  const int sx = w.GetScrX() >> 1;
3225  const int sy = w.GetScrY() >> 1;
3226 
3227  if ((m_flags & mpALIGNMASK) == mpALIGN_NE)
3228  {
3229  tx = sx - tx - 8;
3230  ty = -sy + 8;
3231  }
3232  else if ((m_flags & mpALIGNMASK) == mpALIGN_NW)
3233  {
3234  tx = -sx + 8;
3235  ty = -sy + 8;
3236  }
3237  else if ((m_flags & mpALIGNMASK) == mpALIGN_SW)
3238  {
3239  tx = -sx + 8;
3240  ty = sy - 8 - ty;
3241  }
3242  else
3243  {
3244  tx = sx - tx - 8;
3245  ty = sy - 8 - ty;
3246  }
3247  }
3248 
3249  dc.DrawText(m_name, tx, ty);
3250  }
3251  }
3252 }
3253 
3254 //-----------------------------------------------------------------------------
3255 // mpCovarianceEllipse - provided by Jose Luis Blanco
3256 //-----------------------------------------------------------------------------
3257 
3258 // Called to update the m_shape_xs, m_shape_ys vectors, whenever a parameter
3259 // changes.
3260 void mpCovarianceEllipse::RecalculateShape()
3261 {
3262  m_shape_xs.clear();
3263  m_shape_ys.clear();
3264 
3265  // Preliminary checks:
3266  if (m_quantiles < 0)
3267  {
3268  wxLogError(
3269  wxT("[mpCovarianceEllipse] Error: quantiles must be non-negative"));
3270  return;
3271  }
3272  if (m_cov_00 < 0)
3273  {
3274  wxLogError(
3275  wxT("[mpCovarianceEllipse] Error: cov(0,0) must be non-negative"));
3276  return;
3277  }
3278  if (m_cov_11 < 0)
3279  {
3280  wxLogError(
3281  wxT("[mpCovarianceEllipse] Error: cov(1,1) must be non-negative"));
3282  return;
3283  }
3284 
3285  m_shape_xs.resize(m_segments, 0);
3286  m_shape_ys.resize(m_segments, 0);
3287 
3288  // Compute the two eigenvalues of the covariance:
3289  // -------------------------------------------------
3290  double b = -m_cov_00 - m_cov_11;
3291  double c = m_cov_00 * m_cov_11 - m_cov_01 * m_cov_01;
3292 
3293  double D = b * b - 4 * c;
3294 
3295  if (D < 0)
3296  {
3297  wxLogError(
3298  wxT("[mpCovarianceEllipse] Error: cov is not positive definite"));
3299  return;
3300  }
3301 
3302  double eigenVal0 = 0.5 * (-b + sqrt(D));
3303  double eigenVal1 = 0.5 * (-b - sqrt(D));
3304 
3305  // Compute the two corresponding eigenvectors:
3306  // -------------------------------------------------
3307  double eigenVec0_x, eigenVec0_y;
3308  double eigenVec1_x, eigenVec1_y;
3309 
3310  if (D == 0) // Square ellipse
3311  {
3312  eigenVec0_y = 0;
3313  eigenVec0_x = 1;
3314  }
3315  else if (fabs(eigenVal0 - m_cov_00) > 1e-6)
3316  {
3317  double k1x = m_cov_01 / (eigenVal0 - m_cov_00);
3318  eigenVec0_y = 1;
3319  eigenVec0_x = eigenVec0_y * k1x;
3320  }
3321  else
3322  {
3323  double k1y = m_cov_01 / (eigenVal0 - m_cov_11);
3324  eigenVec0_x = 1;
3325  eigenVec0_y = eigenVec0_x * k1y;
3326  }
3327 
3328  if (D == 0) // Square ellipse
3329  {
3330  eigenVec1_y = 1;
3331  eigenVec1_x = 0;
3332  }
3333  else if (fabs(eigenVal1 - m_cov_00) > 1e-6)
3334  {
3335  double k2x = m_cov_01 / (eigenVal1 - m_cov_00);
3336  eigenVec1_y = 1;
3337  eigenVec1_x = eigenVec1_y * k2x;
3338  }
3339  else
3340  {
3341  double k2y = m_cov_01 / (eigenVal1 - m_cov_11);
3342  eigenVec1_x = 1;
3343  eigenVec1_y = eigenVec1_x * k2y;
3344  }
3345 
3346  // Normalize the eigenvectors:
3347  double len = sqrt(eigenVec0_x * eigenVec0_x + eigenVec0_y * eigenVec0_y);
3348  eigenVec0_x /= len; // It *CANNOT* be zero
3349  eigenVec0_y /= len;
3350 
3351  len = sqrt(eigenVec1_x * eigenVec1_x + eigenVec1_y * eigenVec1_y);
3352  eigenVec1_x /= len; // It *CANNOT* be zero
3353  eigenVec1_y /= len;
3354 
3355  // Take the sqrt of the eigenvalues (required for the ellipse scale):
3356  eigenVal0 = sqrt(eigenVal0);
3357  eigenVal1 = sqrt(eigenVal1);
3358 
3359  // Compute the 2x2 matrix M = diag(eigVal) * (~eigVec) (each eigen vector
3360  // is a row):
3361  double M_00 = eigenVec0_x * eigenVal0;
3362  double M_01 = eigenVec0_y * eigenVal0;
3363 
3364  double M_10 = eigenVec1_x * eigenVal1;
3365  double M_11 = eigenVec1_y * eigenVal1;
3366 
3367  // The points of the 2D ellipse:
3368  double ang;
3369  double Aang = 6.283185308 / (m_segments - 1);
3370  int i;
3371  for (i = 0, ang = 0; i < m_segments; i++, ang += Aang)
3372  {
3373  double ccos = cos(ang);
3374  double csin = sin(ang);
3375 
3376  m_shape_xs[i] = m_quantiles * (ccos * M_00 + csin * M_10);
3377  m_shape_ys[i] = m_quantiles * (ccos * M_01 + csin * M_11);
3378  } // end for points on ellipse
3379 
3380  ShapeUpdated();
3381 }
3382 
3383 //-----------------------------------------------------------------------------
3384 // mpPolygon - provided by Jose Luis Blanco
3385 //-----------------------------------------------------------------------------
3386 void mpPolygon::setPoints(
3387  const std::vector<double>& points_xs, const std::vector<double>& points_ys,
3388  bool closedShape)
3389 {
3390  if (points_xs.size() != points_ys.size())
3391  {
3392  wxLogError(
3393  wxT("[mpPolygon] Error: points_xs and points_ys must have the same "
3394  "number of elements"));
3395  }
3396  else
3397  {
3398  m_shape_xs = points_xs;
3399  m_shape_ys = points_ys;
3400 
3401  if (closedShape && !points_xs.empty())
3402  {
3403  m_shape_xs.push_back(points_xs[0]);
3404  m_shape_ys.push_back(points_ys[0]);
3405  }
3406 
3407  ShapeUpdated();
3408  }
3409 }
3410 void mpPolygon::setPoints(
3411  const std::vector<float>& points_xs, const std::vector<float>& points_ys,
3412  bool closedShape)
3413 {
3414  if (points_xs.size() != points_ys.size())
3415  {
3416  wxLogError(
3417  wxT("[mpPolygon] Error: points_xs and points_ys must have the same "
3418  "number of elements"));
3419  }
3420  else
3421  {
3422  m_shape_xs.resize(points_xs.size());
3423  m_shape_ys.resize(points_xs.size());
3424 
3425  if (!points_xs.empty())
3426  {
3428  std::vector<double>::iterator itXo, itYo;
3429  for (itX = points_xs.begin(), itY = points_ys.begin(),
3430  itXo = m_shape_xs.begin(), itYo = m_shape_ys.begin();
3431  itX != points_xs.end(); ++itX, ++itY, ++itXo, ++itYo)
3432  {
3433  *itXo = (double)*itX;
3434  *itYo = (double)*itY;
3435  }
3436 
3437  if (closedShape)
3438  {
3439  m_shape_xs.push_back((double)points_xs[0]);
3440  m_shape_ys.push_back((double)points_ys[0]);
3441  }
3442  }
3443 
3444  ShapeUpdated();
3445  }
3446 }
3447 
3448 //-----------------------------------------------------------------------------
3449 // mpBitmapLayer - provided by Jose Luis Blanco
3450 //-----------------------------------------------------------------------------
3451 void mpBitmapLayer::GetBitmapCopy(wxImage& outBmp) const
3452 {
3453  if (m_validImg) outBmp = m_bitmap;
3454 }
3455 
3456 void mpBitmapLayer::SetBitmap(
3457  const wxImage& inBmp, double x, double y, double lx, double ly)
3458 {
3459  if (!inBmp.Ok())
3460  {
3461  wxLogError(wxT("[mpBitmapLayer] Assigned bitmap is not Ok()!"));
3462  }
3463  else
3464  {
3465  if (lx < 0)
3466  {
3467  wxLogError(
3468  wxT("[mpBitmapLayer::SetBitmap] Assigned lx is negative!!"));
3469  }
3470  if (ly < 0)
3471  {
3472  wxLogError(
3473  wxT("[mpBitmapLayer::SetBitmap] Assigned ly is negative!!"));
3474  }
3475 
3476  m_bitmap = inBmp; //.GetSubBitmap( wxRect(0, 0, inBmp.GetWidth(),
3477  // inBmp.GetHeight()));
3478  m_min_x = x;
3479  m_min_y = y;
3480  m_max_x = x + lx;
3481  m_max_y = y + ly;
3482  m_validImg = true;
3483  }
3484 }
3485 
3486 void mpBitmapLayer::Plot(wxDC& dc, mpWindow& w)
3487 {
3488  if (m_visible && m_validImg)
3489  {
3490  /* 1st: We compute (x0,y0)-(x1,y1), the pixel coordinates of the real
3491  outer limits
3492  of the image rectangle within the (screen) mpWindow. Note that
3493  these coordinates
3494  might fall well far away from the real view limits when the
3495  user zoom in.
3496 
3497  2nd: We compute (dx0,dy0)-(dx1,dy1), the pixel coordinates the
3498  rectangle that will
3499  be actually drawn into the mpWindow, i.e. the clipped real
3500  rectangle that
3501  avoids the non-visible parts. (offset_x,offset_y) are the pixel
3502  coordinates
3503  that correspond to the window point (dx0,dy0) within the image
3504  "m_bitmap", and
3505  (b_width,b_height) is the size of the bitmap patch that will be
3506  drawn.
3507 
3508  (x0,y0) ................. (x1,y0)
3509  . .
3510  . .
3511  (x0,y1) ................ (x1,y1)
3512  (In pixels!!)
3513  */
3514 
3515  // 1st step -------------------------------
3516  wxCoord x0 = w.x2p(m_min_x);
3517  wxCoord y0 = w.y2p(m_max_y);
3518  wxCoord x1 = w.x2p(m_max_x);
3519  wxCoord y1 = w.y2p(m_min_y);
3520 
3521  // 2nd step -------------------------------
3522  // Precompute the size of the actual bitmap pixel on the screen (e.g.
3523  // will be >1 if zoomed in)
3524  double screenPixelX = (x1 - x0) / (double)m_bitmap.GetWidth();
3525  double screenPixelY = (y1 - y0) / (double)m_bitmap.GetHeight();
3526 
3527  // The minimum number of pixels that the streched image will overpass
3528  // the actual mpWindow borders:
3529  wxCoord borderMarginX = (wxCoord)(screenPixelX + 1); // ceil
3530  wxCoord borderMarginY = (wxCoord)(screenPixelY + 1); // ceil
3531 
3532  // The actual drawn rectangle (dx0,dy0)-(dx1,dy1) is (x0,y0)-(x1,y1)
3533  // clipped:
3534  wxCoord dx0 = x0, dx1 = x1, dy0 = y0, dy1 = y1;
3535  if (dx0 < 0) dx0 = -borderMarginX;
3536  if (dy0 < 0) dy0 = -borderMarginY;
3537  if (dx1 > w.GetScrX()) dx1 = w.GetScrX() + borderMarginX;
3538  if (dy1 > w.GetScrY()) dy1 = w.GetScrY() + borderMarginY;
3539 
3540  // For convenience, compute the width/height of the rectangle to be
3541  // actually drawn:
3542  wxCoord d_width = dx1 - dx0 + 1;
3543  wxCoord d_height = dy1 - dy0 + 1;
3544 
3545  // Compute the pixel offsets in the internally stored bitmap:
3546  wxCoord offset_x = (wxCoord)((dx0 - x0) / screenPixelX);
3547  wxCoord offset_y = (wxCoord)((dy0 - y0) / screenPixelY);
3548 
3549  // and the size in pixel of the area to be actually drawn from the
3550  // internally stored bitmap:
3551  wxCoord b_width = (wxCoord)((dx1 - dx0 + 1) / screenPixelX);
3552  wxCoord b_height = (wxCoord)((dy1 - dy0 + 1) / screenPixelY);
3553 
3554 #ifdef MATHPLOT_DO_LOGGING
3555  wxLogMessage(
3556  _("[mpBitmapLayer::Plot] screenPixel: x=%f y=%f d_width=%ix%i"),
3557  screenPixelX, screenPixelY, d_width, d_height);
3558  wxLogMessage(
3559  _("[mpBitmapLayer::Plot] offset: x=%i y=%i bmpWidth=%ix%i"),
3560  offset_x, offset_y, b_width, b_height);
3561 #endif
3562 
3563  // Is there any visible region?
3564  if (d_width > 0 && d_height > 0)
3565  {
3566  // Build the scaled bitmap from the image, only if it has changed:
3567  if (m_scaledBitmap.GetWidth() != d_width ||
3568  m_scaledBitmap.GetHeight() != d_height ||
3569  m_scaledBitmap_offset_x != offset_x ||
3570  m_scaledBitmap_offset_y != offset_y)
3571  {
3572  wxRect r(wxRect(offset_x, offset_y, b_width, b_height));
3573  // Just for the case....
3574  if (r.x < 0) r.x = 0;
3575  if (r.y < 0) r.y = 0;
3576  if (r.width > m_bitmap.GetWidth())
3577  r.width = m_bitmap.GetWidth();
3578  if (r.height > m_bitmap.GetHeight())
3579  r.height = m_bitmap.GetHeight();
3580 
3581  m_scaledBitmap = wxBitmap(
3582  wxBitmap(m_bitmap).GetSubBitmap(r).ConvertToImage().Scale(
3583  d_width, d_height));
3584  m_scaledBitmap_offset_x = offset_x;
3585  m_scaledBitmap_offset_y = offset_y;
3586  }
3587 
3588  // Draw it:
3589  dc.DrawBitmap(m_scaledBitmap, dx0, dy0, true);
3590  }
3591  }
3592 
3593  // Draw the name label
3594  if (!m_name.IsEmpty() && m_showName)
3595  {
3596  dc.SetFont(m_font);
3597 
3598  wxCoord tx, ty;
3599  dc.GetTextExtent(m_name, &tx, &ty);
3600 
3601  if (HasBBox())
3602  {
3603  wxCoord sx = (wxCoord)((m_max_x - w.GetPosX()) * w.GetScaleX());
3604  wxCoord sy = (wxCoord)((w.GetPosY() - m_max_y) * w.GetScaleY());
3605 
3606  tx = sx - tx - 8;
3607  ty = sy - 8 - ty;
3608  }
3609  else
3610  {
3611  const int sx = w.GetScrX() >> 1;
3612  const int sy = w.GetScrY() >> 1;
3613 
3614  if ((m_flags & mpALIGNMASK) == mpALIGN_NE)
3615  {
3616  tx = sx - tx - 8;
3617  ty = -sy + 8;
3618  }
3619  else if ((m_flags & mpALIGNMASK) == mpALIGN_NW)
3620  {
3621  tx = -sx + 8;
3622  ty = -sy + 8;
3623  }
3624  else if ((m_flags & mpALIGNMASK) == mpALIGN_SW)
3625  {
3626  tx = -sx + 8;
3627  ty = sy - 8 - ty;
3628  }
3629  else
3630  {
3631  tx = sx - tx - 8;
3632  ty = sy - 8 - ty;
3633  }
3634  }
3635 
3636  dc.DrawText(m_name, tx, ty);
3637  }
3638 }
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei imageSize
Definition: glext.h:3743
Scalar * iterator
Definition: eigen_plugins.h:26
#define min(a, b)
GLint * first
Definition: glext.h:3827
GLenum GLsizei n
Definition: glext.h:5074
const int INVALID_CLICK_COORDS
Definition: mathplot.cpp:32
GLdouble s
Definition: glext.h:3676
GLenum GLsizei len
Definition: glext.h:4712
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:4178
T square(const T x)
Inline function for the square of a number.
#define mpMIN_Y_AXIS_LABEL_SEPARATION
Definition: mathplot.cpp:79
const GLubyte * c
Definition: glext.h:6313
GLbyte ty
Definition: glext.h:6092
GLuint GLuint end
Definition: glext.h:3528
GLubyte GLubyte b
Definition: glext.h:6279
GLdouble GLdouble GLdouble r
Definition: glext.h:3705
#define FALSE
Definition: xmlParser.h:231
_u8 status
Definition: rplidar_cmd.h:19
GLuint const GLchar * name
Definition: glext.h:4054
#define mpSCROLL_NUM_PIXELS_PER_LINE
Definition: mathplot.cpp:82
GLenum GLint GLint y
Definition: glext.h:3538
int sign(T x)
Returns the sign of X as "1" or "-1".
GLenum GLuint GLint GLint layer
Definition: glext.h:8192
#define mpMIN_X_AXIS_LABEL_SEPARATION
Definition: mathplot.cpp:78
GLsizeiptr size
Definition: glext.h:3923
GLenum GLint x
Definition: glext.h:3538
#define TRUE
Definition: xmlParser.h:234
EVT_RIGHT_DOWN(mpWindow::OnMouseRightDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1427
GLfloat GLfloat p
Definition: glext.h:6305
const Scalar * const_iterator
Definition: eigen_plugins.h:27
GLuint GLuint GLsizei GLenum type
Definition: glext.h:3528
#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: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020