1 #include "wxMolGraph.h"
2 #include <stdexcept>
3 
4 using namespace std;
5 
6 IMPLEMENT_DYNAMIC_CLASS(wxMolGraph, wxControl)
7 
8 BEGIN_EVENT_TABLE(wxMolGraph, wxControl)
9     EVT_SIZE         (wxMolGraph::onSize)
10     EVT_PAINT        (wxMolGraph::onPaint)
11 
12     EVT_LEFT_DOWN    (wxMolGraph::onLeftClick)
13     EVT_LEFT_UP      (wxMolGraph::onLeftClick)
14     EVT_LEFT_DCLICK  (wxMolGraph::onLeftDblClick)
15     EVT_MOTION       (wxMolGraph::onMotion)
16     EVT_LEAVE_WINDOW (wxMolGraph::onLeaveWindow)
17 END_EVENT_TABLE()
18 
19 const int wxEVT_AXIS_DCLICK = wxNewEventType();
20 const int wxEVT_GRAPH_CLICK = wxNewEventType();
21 
22 const int BORDER = 2;
23 
wxMolGraph(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)24 wxMolGraph::wxMolGraph(wxWindow       *parent,
25                        wxWindowID      id,
26                        const wxPoint  &pos,
27                        const wxSize   &size,
28                        long            style) :
29             wxControl(parent, id, pos, size, style) {
30     reset();
31 }
32 
addXSet(vector<double> xData,bool selectable)33 int wxMolGraph::addXSet(vector<double> xData, bool selectable) {
34     int index = data.size();
35     int selected = 0;
36 
37     vector< YSettings > emptyYSettingsGroup;
38 
39     if(!selectable) selected = -1;
40 
41     vector< YSet > emptyYSetGroup;
42     XSet newXSet(make_pair(xData, selected));
43 
44     try {
45         data.push_back(make_pair(newXSet, emptyYSetGroup));
46     }
47     catch(const bad_alloc &e) {
48         return -1;
49     }
50 
51     try {
52         dataSettings.push_back(emptyYSettingsGroup);
53     }
54     catch(const bad_alloc &e) {
55         data.pop_back();
56         return -1;
57     }
58 
59     autoScaleX();
60 
61     Refresh();
62 
63     return index;
64 }
65 
addYSet(YSet yData,int xSet,int axis,int style,wxColour color,int shape,int size)66 int wxMolGraph::addYSet(YSet yData,
67                         int xSet,
68                         int axis,
69                         int style,
70                         wxColour color,
71                         int shape,
72                         int size) {
73     int index = data[xSet].second.size();
74     YSettings newYSettings;
75 
76     newYSettings.visible = true;
77     newYSettings.exists  = true;
78     newYSettings.axis    = axis;
79     newYSettings.style   = style;
80     newYSettings.color   = color;
81     newYSettings.shape   = shape;
82     newYSettings.size    = size;
83 
84     try {
85         data[xSet].second.push_back(yData);
86     }
87     catch(const bad_alloc &e) {
88         return -1;
89     }
90     try {
91         dataSettings[xSet].push_back(newYSettings);
92     }
93     catch(const bad_alloc &e) {
94         data[xSet].second.pop_back();
95         return -1;
96     }
97 
98     if(axis == MG_AXIS_Y1) {
99         numY1Graphs++;
100         numY1Visible++;
101     }
102     else if(axis == MG_AXIS_Y2) {
103         numY2Graphs++;
104         numY2Visible++;
105     }
106     else {
107         data[xSet].second.pop_back();
108         dataSettings[xSet].pop_back();
109         return -1;
110     }
111 
112     Refresh();
113 
114     return index;
115 }
116 
delXSet(int xSet)117 void wxMolGraph::delXSet(int xSet) {
118     vector< YSettings >::iterator pos;
119 
120     for(pos = dataSettings[xSet].begin();
121         pos != dataSettings[xSet].end();
122         ++pos) {
123 
124         if(pos->exists) {
125             switch(pos->axis) {
126                 case MG_AXIS_Y1:
127                     numY1Graphs--;
128                     if(pos->visible) numY1Visible--;
129                     break;
130                 case MG_AXIS_Y2:
131                     numY2Graphs--;
132                     if(pos->visible) numY2Visible--;
133                     break;
134             }
135         }
136     }
137 
138     data[xSet].first.first.clear();
139     data[xSet].first.second = -1;
140     data[xSet].second.clear();
141     dataSettings[xSet].clear();
142 
143     autoScaleX();
144 }
145 
delYSet(int xSet,int ySet)146 void wxMolGraph::delYSet(int xSet, int ySet) {
147     if(!dataSettings[xSet][ySet].exists) {
148         return;
149     }
150 
151     dataSettings[xSet][ySet].exists = false;
152     data[xSet].second.clear();
153 }
154 
getSelection(int xSet)155 int wxMolGraph::getSelection(int xSet) {
156     if(data[xSet].first.second == -1) return 0;
157     return data[xSet].first.second;
158 }
159 
setSelection(int xSet,int index)160 void wxMolGraph::setSelection(int xSet, int index) {
161     if(index < data[xSet].first.first.size()) {
162         data[xSet].first.second = index;
163     }
164 
165     Refresh();
166 }
167 
setYAxisMin(int axis,double val)168 void wxMolGraph::setYAxisMin(int axis, double val) {
169     switch(axis) {
170         case MG_AXIS_Y1:
171             y1Min = val;
172             break;
173         case MG_AXIS_Y2:
174             y2Min = val;
175             break;
176     }
177 }
178 
setYAxisMax(int axis,double val)179 void wxMolGraph::setYAxisMax(int axis, double val) {
180     switch(axis) {
181         case MG_AXIS_Y1:
182             y1Max = val;
183             break;
184         case MG_AXIS_Y2:
185             y2Max = val;
186             break;
187     }
188 }
189 
getYAxisMin(int axis)190 double wxMolGraph::getYAxisMin(int axis) {
191     switch(axis) {
192         case MG_AXIS_Y1:
193             return y1Min;
194             break;
195         case MG_AXIS_Y2:
196             return y2Min;
197             break;
198     }
199 
200     return 0.0;
201 }
202 
getYAxisMax(int axis)203 double wxMolGraph::getYAxisMax(int axis) {
204     switch(axis) {
205         case MG_AXIS_Y1:
206             return y1Max;
207             break;
208         case MG_AXIS_Y2:
209             return y2Max;
210             break;
211     }
212 
213     return 0.0;
214 }
215 
autoScaleY(int axis)216 void wxMolGraph::autoScaleY(int axis) {
217     double min = 0.0;
218     double max = 0.0;
219     double pad = 0.0;
220     int i = 0;
221     int j = 0;
222     int k = 0;
223     bool set = false;
224 
225     for(i = 0; i < data.size(); i++) {
226         for(j = 0; j < data[i].second.size(); j++) {
227             if(dataSettings[i][j].axis == axis && dataSettings[i][j].visible) {
228                 for(k = 0; k < data[i].second[j].size(); k++) {
229                     if(data[i].second[j][k].second < min || !set) {
230                         min = data[i].second[j][k].second;
231                     }
232                     if(data[i].second[j][k].second > max || !set) {
233                         max = data[i].second[j][k].second;
234                     }
235                     if(!set) {
236                         set = true;
237                     }
238                 }
239             }
240         }
241     }
242 
243     if(set) {
244         pad = (max - min) / 10.0;
245         max += pad;
246         min -= pad;
247         if((max - min)<=0.0) {	//The range just needs to be non-zero
248             max += 1.0;
249             min -= 1.0;
250         }
251         switch(axis) {
252             case MG_AXIS_Y1:
253                 y1Max = max;
254                 y1Min = min;
255                 break;
256             case MG_AXIS_Y2:
257                 y2Max = max;
258                 y2Min = min;
259                 break;
260         }
261     }
262 }
263 
setXAxisMin(double val)264 void wxMolGraph::setXAxisMin(double val) {
265     xMin = val;
266 }
267 
setXAxisMax(double val)268 void wxMolGraph::setXAxisMax(double val) {
269     xMax = val;
270 }
271 
getXAxisMin()272 double wxMolGraph::getXAxisMin() {
273     return xMin;
274 }
275 
getXAxisMax()276 double wxMolGraph::getXAxisMax() {
277     return xMax;
278 }
279 
autoScaleX(void)280 void wxMolGraph::autoScaleX(void) {
281     double min = 0.0;
282     double max = 0.0;
283     double pad = 0.0;
284     int i = 0;
285     int j = 0;
286     bool set = false;
287 
288     for(i = 0; i < data.size(); i++) {
289         for(j = 0; j < data[i].first.first.size(); j++) {
290             if(data[i].first.first[j] < min || !set) {
291                 min = data[i].first.first[j];
292             }
293             if(data[i].first.first[j] > max || !set) {
294                 max = data[i].first.first[j];
295             }
296             if(!set) {
297                 set = true;
298             }
299         }
300     }
301 
302     if(set) {
303         pad = (max - min) / 10.0;
304         max += pad;
305         min -= pad;
306         if((max - min)<=0.0) {	//The range just needs to be nonzero
307             max += 1.0;
308             min -= 1.0;
309         }
310         xMax = max;
311         xMin = min;
312     }
313 }
314 
setAxisLabel(int axis,const wxString & label)315 void wxMolGraph::setAxisLabel(int axis, const wxString &label) {
316     switch(axis) {
317         case MG_AXIS_X:
318             xAxisText = label;
319             break;
320         case MG_AXIS_Y1:
321             y1AxisText = label;
322             break;
323         case MG_AXIS_Y2:
324             y2AxisText = label;
325             break;
326     }
327 }
328 
setOffsetY(int axis,double offset)329 void wxMolGraph::setOffsetY(int axis, double offset) {
330     switch(axis) {
331         case MG_AXIS_Y1:
332             y1Offset = offset;
333             break;
334         case MG_AXIS_Y2:
335             y2Offset = offset;
336             break;
337     }
338 
339     Refresh();
340 }
341 
setVisible(int xSet,int ySet,bool visible)342 void wxMolGraph::setVisible(int xSet, int ySet, bool visible) {
343     if(dataSettings[xSet][ySet].visible != visible) {
344         switch(dataSettings[xSet][ySet].axis) {
345             case MG_AXIS_Y1:
346                 if(visible) numY1Visible++;
347                 else numY1Visible--;
348                 break;
349             case MG_AXIS_Y2:
350                 if(visible) numY2Visible++;
351                 else numY2Visible--;
352                 break;
353         }
354         dataSettings[xSet][ySet].visible = visible;
355     }
356 }
357 
reset()358 void wxMolGraph::reset() {
359     data.clear();
360     dataSettings.clear();
361     xAxisText.Empty();
362     y1AxisText.Empty();
363     y2AxisText.Empty();
364     xMax  = 1.0;
365     xMin  = 0.0;
366     y1Max = 1.0;
367     y1Min = 0.0;
368     y2Max = 1.0;
369     y2Min = 0.0;
370     numY1Graphs = 0;
371     numY1Visible = 0;
372     numY2Graphs = 0;
373     numY2Visible = 0;
374     precision = 4;
375     xAxisRegion.Clear();
376     y1AxisRegion.Clear();
377     y2AxisRegion.Clear();
378     graphRegion.Clear();
379     clickedX = 0;
380     clickedY = 0;
381     xConversion  = 0.0;
382     y1Conversion = 0.0;
383     y2Conversion = 0.0;
384     xScaleMin = 0;
385     xScaleMax = 0;
386     yScaleMin = 0;
387     yScaleMax = 0;
388     y1Offset = 0.0;
389     y2Offset = 0.0;
390     Refresh();
391     Update();
392 }
393 
setPrecision(int p)394 void wxMolGraph::setPrecision(int p) {
395     precision = p;
396 }
397 
DoGetBestSize() const398 wxSize wxMolGraph::DoGetBestSize() const {
399  //   wxASSERT_MSG( m_widget, wxT("wxMolGraph::DoGetBestSize called before creation") );
400 
401     wxClientDC dc(const_cast<wxMolGraph*> (this));
402     wxString  xMaxText;
403     wxString  xMinText;
404     wxString  y1MaxText;
405     wxString  y1MinText;
406     wxString  y2MaxText;
407     wxString  y2MinText;
408     wxCoord w = 0;
409     wxCoord h = 0;
410     wxSize x(0, 0);
411     wxSize xAxisTextSize(0, 0);
412     wxSize xMaxTextSize(0, 0);
413     wxSize xMinTextSize(0, 0);
414     wxSize y1(0, 0);
415     wxSize y1AxisTextSize(0, 0);
416     wxSize y1MaxTextSize(0, 0);
417     wxSize y1MinTextSize(0, 0);
418     wxSize y2(0, 0);
419     wxSize y2AxisTextSize(0, 0);
420     wxSize y2MaxTextSize(0, 0);
421     wxSize y2MinTextSize(0, 0);
422 
423 
424     dc.SetPen(*wxBLACK_PEN);
425     dc.SetBrush(*wxTRANSPARENT_BRUSH);
426     dc.SetBackground(*wxWHITE_BRUSH);
427     dc.SetFont(*wxSWISS_FONT);
428 
429     xMaxText = wxString::Format(wxT("%0.*f"), precision, xMax);
430     xMinText = wxString::Format(wxT("%0.*f"), precision, xMin);
431     dc.GetTextExtent(xAxisText, &w, &h);
432     xAxisTextSize.Set(w, h);
433     dc.GetTextExtent(xMaxText, &w, &h);
434     xMaxTextSize.Set(w, h);
435     dc.GetTextExtent(xMinText, &w, &h);
436     xMinTextSize.Set(w, h);
437 
438     y1MaxText = wxString::Format(wxT("%0.*f"), precision, y1Max - y1Offset);
439     y1MinText = wxString::Format(wxT("%0.*f"), precision, y1Min - y1Offset);
440     dc.GetTextExtent(y1AxisText, &w, &h);
441     y1AxisTextSize.Set(w, h);
442     dc.GetTextExtent(y1MaxText, &w, &h);
443     y1MaxTextSize.Set(w, h);
444     dc.GetTextExtent(y1MinText, &w, &h);
445     y1MinTextSize.Set(w, h);
446 
447     y2MaxText = wxString::Format(wxT("%0.*f"), precision, y2Max - y2Offset);
448     y2MinText = wxString::Format(wxT("%0.*f"), precision, y2Min - y2Offset);
449     dc.GetTextExtent(y2AxisText, &w, &h);
450     y2AxisTextSize.Set(w, h);
451     dc.GetTextExtent(y2MaxText, &w, &h);
452     y2MaxTextSize.Set(w, h);
453     dc.GetTextExtent(y2MinText, &w, &h);
454     y2MinTextSize.Set(w, h);
455 
456 
457     x.SetWidth(xMinTextSize.GetHeight() +
458                xAxisTextSize.GetWidth() +
459                xMaxTextSize.GetHeight());
460     x.SetHeight(wxMax(xMinTextSize.GetWidth(), xMaxTextSize.GetWidth()) +
461                 xAxisTextSize.GetHeight());
462 
463     if(numY1Visible > 0 || numY2Visible == 0) {
464         y1.SetWidth(wxMax(y1MinTextSize.GetWidth(), y1MaxTextSize.GetWidth()) +
465                     y1AxisTextSize.GetHeight());
466         y1.SetHeight(y1MinTextSize.GetHeight() +
467                      y1AxisTextSize.GetWidth() +
468                      y1MaxTextSize.GetHeight());
469     }
470     else {
471         y1.Set(0, 0);
472     }
473 
474     if(numY2Visible > 0) {
475         y2.SetWidth(wxMax(y2MinTextSize.GetWidth(), y2MaxTextSize.GetWidth()) +
476                     y2AxisTextSize.GetHeight());
477         y2.SetHeight(y2MinTextSize.GetHeight() +
478                      y2AxisTextSize.GetWidth() +
479                      y2MaxTextSize.GetHeight());
480     }
481     else {
482         y2.Set(0, 0);
483     }
484 
485     w = (2 * BORDER) +
486         y1.GetWidth() +
487         wxMax(x.GetWidth(), 300) +
488         y2.GetWidth();
489     h = (2 * BORDER) +
490         wxMax(wxMax(y1.GetHeight(), y2.GetHeight()), 200) +
491         x.GetHeight();
492 
493     return wxSize( w, h );
494 }
495 
onSize(wxSizeEvent &)496 void wxMolGraph::onSize(wxSizeEvent &/*event*/) {
497     Refresh();
498 }
499 
onPaint(wxPaintEvent &)500 void wxMolGraph::onPaint(wxPaintEvent &/*event*/) {
501     wxPaintDC dc(this);
502     draw(dc);
503 }
504 
draw(wxDC & dc)505 void wxMolGraph::draw(wxDC &dc) {
506     wxString  xMaxText;
507     wxString  xMinText;
508     wxString  y1MaxText;
509     wxString  y1MinText;
510     wxString  y2MaxText;
511     wxString  y2MinText;
512     wxCoord   x          = 0;
513     wxCoord   x2         = 0;
514     wxCoord   x3         = 0;
515     wxCoord   x4         = 0;
516     wxCoord   y          = 0;
517     wxCoord   y2         = 0;
518     wxCoord   y3         = 0;
519     wxCoord   lineX      = 0;
520     wxCoord   lineY      = 0;
521     wxSize    canvasSize(0, 0);
522     wxSize    xAxisTextSize(0, 0);
523     wxSize    xMaxTextSize(0, 0);
524     wxSize    xMinTextSize(0, 0);
525     wxSize    y1AxisTextSize(0, 0);
526     wxSize    y1MaxTextSize(0, 0);
527     wxSize    y1MinTextSize(0, 0);
528     wxSize    y2AxisTextSize(0, 0);
529     wxSize    y2MaxTextSize(0, 0);
530     wxSize    y2MinTextSize(0, 0);
531     double    spacing    = 0.0;
532     double    firstTick  = 0.0;
533     double    xCoord     = 0.0;
534     double    yCoord     = 0.0;
535     int       numTicks   = 0;
536     int       i          = 0;
537     int       j          = 0;
538     int       k          = 0;
539     DataSet::iterator pos;
540     wxPoint polyPoints[4];
541 
542     GetClientSize(&x, &y);
543     canvasSize.Set(x, y);
544 
545     dc.SetPen(*wxBLACK_PEN);
546     dc.SetBrush(*wxTRANSPARENT_BRUSH);
547     dc.SetBackground(*wxWHITE_BRUSH);
548     dc.SetFont(*wxSWISS_FONT);
549 
550     dc.Clear();
551 
552 
553     xMaxText = wxString::Format(wxT("%0.*f"), precision, xMax);
554     xMinText = wxString::Format(wxT("%0.*f"), precision, xMin);
555     dc.GetTextExtent(xAxisText, &x, &y);
556     xAxisTextSize.Set(x, y);
557     dc.GetTextExtent(xMaxText, &x, &y);
558     xMaxTextSize.Set(x, y);
559     dc.GetTextExtent(xMinText, &x, &y);
560     xMinTextSize.Set(x, y);
561 
562     y1MaxText = wxString::Format(wxT("%0.*f"), precision, y1Max - y1Offset);
563     y1MinText = wxString::Format(wxT("%0.*f"), precision, y1Min - y1Offset);
564     dc.GetTextExtent(y1AxisText, &x, &y);
565     y1AxisTextSize.Set(x, y);
566     dc.GetTextExtent(y1MaxText, &x, &y);
567     y1MaxTextSize.Set(x, y);
568     dc.GetTextExtent(y1MinText, &x, &y);
569     y1MinTextSize.Set(x, y);
570 
571     y2MaxText = wxString::Format(wxT("%0.*f"), precision, y2Max - y2Offset);
572     y2MinText = wxString::Format(wxT("%0.*f"), precision, y2Min - y2Offset);
573     dc.GetTextExtent(y2AxisText, &x, &y);
574     y2AxisTextSize.Set(x, y);
575     dc.GetTextExtent(y2MaxText, &x, &y);
576     y2MaxTextSize.Set(x, y);
577     dc.GetTextExtent(y2MinText, &x, &y);
578     y2MinTextSize.Set(x, y);
579 
580 
581     // Calculate axis and graph regions.
582     y2 = canvasSize.GetHeight() -
583          (xAxisTextSize.GetHeight() + BORDER +
584           wxMax(xMaxTextSize.GetWidth(), xMinTextSize.GetWidth()));
585     y3 = canvasSize.GetHeight() - BORDER;
586     if(numY1Visible > 0 || numY2Visible == 0) {
587         x2 = y1AxisTextSize.GetHeight() + BORDER +
588              wxMax(y1MaxTextSize.GetWidth(), y1MinTextSize.GetWidth());
589         y1AxisRegion = wxRegion(BORDER, BORDER, x2, y2);
590     }
591     else {
592         x2 = BORDER;
593         y1AxisRegion.Clear();
594     }
595     x4 = canvasSize.GetWidth() - BORDER;
596     if(numY2Visible > 0) {
597         x3 = canvasSize.GetWidth() -
598              (y2AxisTextSize.GetHeight() + BORDER +
599               wxMax(y2MaxTextSize.GetWidth(), y2MinTextSize.GetWidth()));
600         y2AxisRegion = wxRegion(x3, BORDER, (x4 - x3), y2);
601     }
602     else {
603         x3 = x4;
604         y2AxisRegion.Clear();
605     }
606 
607     xAxisRegion = wxRegion(x2, y2, (x3 - x2), (y3 - y2));
608     graphRegion = wxRegion(x2 + dc.GetCharHeight() / 2,
609                   BORDER,
610                   x3 - x2 - dc.GetCharHeight() + 1,
611                   y2 - dc.GetCharHeight() / 2);
612 
613 
614     // X Axis
615     x = x2 + ((x3 - x2) / 2) - (xAxisTextSize.GetWidth() / 2);
616     y = y3 - xAxisTextSize.GetHeight();
617     dc.DrawText(xAxisText, x, y);
618     x = x2;
619     y = y2 + xMinTextSize.GetWidth();
620     dc.DrawRotatedText(xMinText, x, y, 90.0);
621     x = x3 - xMaxTextSize.GetHeight();
622     y = y2 + xMaxTextSize.GetWidth();
623     dc.DrawRotatedText(xMaxText, x, y, 90.0);
624     x = x2 + 3;
625     y = y2 - (dc.GetCharHeight() / 2);
626     lineX = x3 - 2;
627     dc.DrawLine(x, y, lineX, y);
628     // Tick marks:
629     xScaleMin = x2 + (dc.GetCharHeight() / 2);
630     xScaleMax = x3 - (dc.GetCharHeight() / 2);
631     numTicks = (xScaleMax - xScaleMin) / 60;
632     spacing = (xMax - xMin) / (double)numTicks;
633     // TODO:  Round spacing
634     xConversion = (double)(xScaleMax - xScaleMin) / (xMax - xMin);
635     y = y2 - 3;
636     lineY = y2 - (dc.GetCharHeight() / 2);
637     // TODO:  Find first tick
638     firstTick = xMin;
639     for(i = 0; i * spacing + firstTick < xMax; i++) {
640         x = xScaleMin + (int)(((double)i * spacing + firstTick - xMin) * xConversion);
641         dc.DrawLine(x, y, x, lineY);
642     }
643 
644     // Y1 Axis
645     if(numY1Visible > 0 || numY2Visible == 0) {
646         x = BORDER;
647         y = BORDER + ((y2 - BORDER) / 2) + (y1AxisTextSize.GetWidth() / 2);
648         if(!y1AxisText.IsEmpty()) dc.DrawRotatedText(y1AxisText, x, y, 90.0);
649         x = x2 - y1MaxTextSize.GetWidth();
650         y = BORDER;
651         dc.DrawText(y1MaxText, x, y);
652         x = x2 - y1MinTextSize.GetWidth();
653         y = y2 - y1MinTextSize.GetHeight();
654         dc.DrawText(y1MinText, x, y);
655         x = x2 + (dc.GetCharHeight() / 2);
656         y = y2 - 3;
657         lineY = BORDER + (dc.GetCharHeight() / 2);
658         dc.DrawLine(x, y, x, lineY);
659         lineX = x2 + 3;
660         dc.DrawLine(lineX, lineY, x + 1, lineY);
661         // Tick marks:
662         yScaleMin = y2 - (dc.GetCharHeight() / 2);
663         yScaleMax = BORDER + (dc.GetCharHeight() / 2);
664         numTicks = (yScaleMin - yScaleMax) / 60;
665         spacing = (y1Max - y1Min) / (double)numTicks;
666         // TODO:  Round spacing
667         y1Conversion = (double)(yScaleMin - yScaleMax) / (y1Max - y1Min);
668         x = x2 + 3;
669         lineX = x2 + (dc.GetCharHeight() / 2);
670         // TODO:  Find first tick
671         firstTick = y1Min;
672         for(i = 0; i * spacing + firstTick < y1Max; i++) {
673             y = yScaleMin - (int)(((double)i * spacing + firstTick - y1Min) * y1Conversion);
674             dc.DrawLine(x, y, lineX, y);
675         }
676     }
677     else {
678         x = x2 + (dc.GetCharHeight() / 2);
679         y = y2 - 3;
680         lineY = y2 - (dc.GetCharHeight() / 2);
681         dc.DrawLine(x, y, x, lineY);
682     }
683 
684     // Y2 Axis
685     if(numY2Visible > 0) {
686         x = x4 - y2AxisTextSize.GetHeight();
687         y = BORDER + ((y2 - BORDER) / 2) + (y2AxisTextSize.GetWidth() / 2);
688         if(!y2AxisText.IsEmpty()) dc.DrawRotatedText(y2AxisText, x, y, 90.0);
689         x = x3;
690         y = BORDER;
691         dc.DrawText(y2MaxText, x, y);
692         x = x3;
693         y = y2 - y2MinTextSize.GetHeight();
694         dc.DrawText(y2MinText, x, y);
695         x = x3 - (dc.GetCharHeight() / 2);
696         y = y2 - 3;
697         lineY = BORDER + (dc.GetCharHeight() / 2);
698         dc.DrawLine(x, y, x, lineY);
699         lineX = x3 - 2;
700         dc.DrawLine(x, lineY, lineX, lineY);
701         // Tick marks:
702         yScaleMin = y2 - (dc.GetCharHeight() / 2);
703         yScaleMax = BORDER + (dc.GetCharHeight() / 2);
704         numTicks = (yScaleMin - yScaleMax) / 60;
705         spacing = (y2Max - y2Min) / (double)numTicks;
706         // TODO:  Round spacing
707         y2Conversion = (double)(yScaleMin - yScaleMax) / (y2Max - y2Min);
708         x = x3 - (dc.GetCharHeight() / 2);
709         lineX = x3 - 2;
710         // TODO:  Find first tick
711         firstTick = y2Min;
712         for(i = 0; i * spacing + firstTick < y2Max; i++) {
713             y = yScaleMin - (int)(((double)i * spacing + firstTick - y2Min) * y2Conversion);
714             dc.DrawLine(x, y, lineX, y);
715         }
716     }
717     else {
718         x = x3 - (dc.GetCharHeight() / 2);
719         y = y2 - 3;
720         lineY = y2 - (dc.GetCharHeight() / 2);
721         dc.DrawLine(x, y, x, lineY);
722     }
723 
724     // Data
725     dc.SetClippingRegion(graphRegion);
726     try {
727         for(i = 0; i < data.size(); i++) {
728             for(j = 0; j < data[i].second.size(); j++) {
729                 if(dataSettings[i][j].visible) {
730                     for(k = 0; k < data[i].second[j].size(); k++) {
731                         xCoord = data[i].first.first.at(data[i].second[j][k].first);
732                         yCoord = data[i].second[j][k].second;
733                         x = xScaleMin + (int)((xCoord - xMin) * xConversion);
734                         switch(dataSettings[i][j].axis) {
735                             case MG_AXIS_Y1:
736                                 y = yScaleMin - (int)((yCoord - y1Min) * y1Conversion);
737                                 break;
738                             case MG_AXIS_Y2:
739                                 y = yScaleMin - (int)((yCoord - y2Min) * y2Conversion);
740                                 break;
741                         }
742 
743                         dc.SetBrush(wxBrush(dataSettings[i][j].color));
744                         dc.SetPen(wxPen(*wxBLACK));
745                         if(dataSettings[i][j].style & MG_STYLE_LINE) {
746                             if(k + 1 < data[i].second[j].size()) {
747                                 xCoord = data[i].first.first.at(data[i].second[j][k+1].first);
748                                 yCoord = data[i].second[j][k+1].second;
749                                 lineX = xScaleMin + (int)((xCoord - xMin) * xConversion);
750                                 switch(dataSettings[i][j].axis) {
751                                     case MG_AXIS_Y1:
752                                         lineY = yScaleMin - (int)((yCoord - y1Min) * y1Conversion);
753                                         break;
754                                     case MG_AXIS_Y2:
755                                         lineY = yScaleMin - (int)((yCoord - y2Min) * y2Conversion);
756                                         break;
757                                 }
758                                 dc.DrawLine(x, y, lineX, lineY);
759                             }
760                         }
761                         if(dataSettings[i][j].style & MG_STYLE_BAR) {
762                             if(data[i].first.second == data[i].second[j][k].first) {
763                                 dc.SetPen(wxPen(*wxRED));
764                             }
765                             else {
766                                 dc.SetPen(wxPen(*wxBLACK));
767                             }
768                             dc.DrawLine(x, y, x, yScaleMin);
769                         }
770                         if(dataSettings[i][j].style & MG_STYLE_POINT) {
771                             if(data[i].first.second == data[i].second[j][k].first) {
772                                 dc.SetBrush(wxBrush(*wxRED));
773                             }
774                             else {
775                                 dc.SetBrush(wxBrush(dataSettings[i][j].color));
776                             }
777                             switch(dataSettings[i][j].shape) {
778                                 case MG_SHAPE_CIRCLE:
779                                     dc.DrawCircle(x, y, dataSettings[i][j].size / 2);
780                                     break;
781                                 case MG_SHAPE_DIAMOND:
782                                     polyPoints[0].x = 0;
783                                     polyPoints[0].y = dataSettings[i][j].size / 2 + 2;
784                                     polyPoints[1].x = dataSettings[i][j].size / 2 + 2;
785                                     polyPoints[1].y = 0;
786                                     polyPoints[2].x = 0;
787                                     polyPoints[2].y = -(dataSettings[i][j].size / 2 + 2);
788                                     polyPoints[3].x = -(dataSettings[i][j].size / 2 + 2);
789                                     polyPoints[3].y = 0;
790                                     dc.DrawPolygon(4, polyPoints, x, y);
791                                     break;
792                                 case MG_SHAPE_SQUARE:
793                                     polyPoints[0].x = dataSettings[i][j].size / 2;
794                                     polyPoints[0].y = dataSettings[i][j].size / 2;
795                                     polyPoints[1].x = dataSettings[i][j].size / 2;
796                                     polyPoints[1].y = -dataSettings[i][j].size / 2;
797                                     polyPoints[2].x = -(dataSettings[i][j].size / 2);
798                                     polyPoints[2].y = -(dataSettings[i][j].size / 2);
799                                     polyPoints[3].x = -(dataSettings[i][j].size / 2);
800                                     polyPoints[3].y = (dataSettings[i][j].size / 2);
801                                     dc.DrawPolygon(4, polyPoints, x, y);
802                                     break;
803                             }
804                         }
805                     }
806                 }
807             }
808         }
809     }
810     catch(const out_of_range &e) {
811     }
812 
813     dc.SetBrush(wxNullBrush);
814     dc.SetPen(wxNullPen);
815 }
816 
onLeftClick(wxMouseEvent & event)817 void wxMolGraph::onLeftClick(wxMouseEvent &event) {
818     wxCommandEvent event_graph(wxEVT_GRAPH_CLICK, GetId());
819     double x = 0.0;
820     int i = 0;
821     int j = 0;
822 
823     if(event.LeftDown()) {
824         event.GetPosition(&clickedX, &clickedY);
825         event.Skip();
826     }
827     else if(event.LeftUp()) {
828         wxPoint p((int)clickedX, (int)clickedY);
829         if(graphRegion.Contains(event.GetPosition()) == wxInRegion &&
830            graphRegion.Contains(p) == wxInRegion) {
831             clickedX = 0;
832             clickedY = 0;
833             x = (event.GetX() - xScaleMin) / xConversion + xMin;
834             for(i = 0; i < data.size(); i++) {
835                 if(x <= data[i].first.first[0]) {
836                     data[i].first.second = 0;
837                 }
838                 else if(x >= *(data[i].first.first.end() - 1)) {
839                     data[i].first.second = data[i].first.first.size() - 1;
840                 }
841                 else {
842                     for(j = 0; j < data[i].first.first.size() - 1; j++) {
843                         if(x > data[i].first.first[j] &&
844                            x < data[i].first.first[j + 1]) {
845                             if(x - data[i].first.first[j] <
846                                data[i].first.first[j + 1] - x) {
847                                 data[i].first.second = j;
848                             }
849                             else {
850                                 data[i].first.second = j + 1;
851                             }
852                             break;
853                         }
854                     }
855                 }
856             }
857             Refresh();
858             wxPostEvent(this, event_graph);
859         }
860     }
861 
862 }
863 
onLeftDblClick(wxMouseEvent & event)864 void wxMolGraph::onLeftDblClick(wxMouseEvent &event) {
865     wxCommandEvent event_axis(wxEVT_AXIS_DCLICK, GetId());
866     event_axis.SetEventObject(this);
867 
868     if(xAxisRegion.Contains(event.GetPosition()) == wxInRegion) {
869         event_axis.SetInt(MG_AXIS_X);
870         wxPostEvent(this, event_axis);
871     }
872     else if(y1AxisRegion.Contains(event.GetPosition()) == wxInRegion) {
873         event_axis.SetInt(MG_AXIS_Y1);
874         wxPostEvent(this, event_axis);
875     }
876     else if(y2AxisRegion.Contains(event.GetPosition()) == wxInRegion) {
877         event_axis.SetInt(MG_AXIS_Y2);
878         wxPostEvent(this, event_axis);
879     }
880 }
881 
onMotion(wxMouseEvent & event)882 void wxMolGraph::onMotion(wxMouseEvent &event) {
883     if(graphRegion.Contains(event.GetPosition()) == wxInRegion) {
884     }
885     else {
886     }
887 }
888 
onLeaveWindow(wxMouseEvent &)889 void wxMolGraph::onLeaveWindow(wxMouseEvent &/*event*/) {
890 }
891 
892