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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019