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