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



Page generated by Doxygen 1.8.14 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at lun oct 28 01:39:17 CET 2019