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