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