3 @description functions in class plot_t is implemented here
4 licensed under (open-source) QPL v1.0
5 which accompanies this distribution in the file QPL
16 #include "verbosity.hpp"
18 #include "line_window_onevar_t.hpp"
19 #include "line_real_set_t.hpp"
20 #include "line_real_t.hpp"
22 /** ultimate window-coord class: holds SEVERAL vars' info and can plot them in different colors */
25 typedef std::list<line_window_onevar_t> data_manyexecs_t;
27 plot_t::plot_t(line_real_set_t& LRS, int WIDTH, int HEIGHT) {
28 // can also take padding arguments maybe?
29 // we are a friend of line_real_set_t
30 plotName = "garden variety plot";
31 XAxisName = "x-units";
32 YAxisName = "y-units";
33 rMinX = LRS.getMinX();
34 rMaxX = LRS.getMaxX();
35 rMinY = LRS.getMinY();
36 rMaxY = LRS.getMaxY();
37 // calculate instance variables, mainly to make the right label notches on axes
38 sd_line_t::WIDTH = WIDTH;
39 (this->myWidth) = WIDTH;
40 sd_line_t::HEIGHT = HEIGHT;
41 (this->myHeight) = HEIGHT; // insurance
42 // perform fudging defensively
43 if (0 < sd_line_t::X_HALF_FUDGE_INCREMENT) {
44 rMinX -= sd_line_t::X_HALF_FUDGE_INCREMENT;
45 rMaxX += sd_line_t::X_HALF_FUDGE_INCREMENT;
47 xr_range = rMaxX - rMinX; // underscore between r's for emphasis
48 yr_range = rMaxY - rMinY;
49 if (0 < sd_line_t::Y_HALF_FUDGE_FACTOR) {
50 double y_half_fudge = yr_range * sd_line_t::Y_HALF_FUDGE_FACTOR;
51 rMinY -= y_half_fudge;
52 rMaxY += y_half_fudge;
53 yr_range = rMaxY - rMinY; // calculate post-fudge yr_range
55 ywmin = sd_line_t::YWINPAD;
56 ywmax = (this->myHeight) - 1 - sd_line_t::YWINPAD;
57 ywrange = ywmax - ywmin;
58 xwmin = sd_line_t::XWINPAD;
59 xwrange = (this->myWidth) - 1 - 2 * sd_line_t::XWINPAD;
60 ywmin = sd_line_t::YWINPAD;
61 ywmax = (this->myHeight) - 1 - sd_line_t::YWINPAD;
62 xscalar = static_cast<double> (xwrange) / static_cast<double> (xr_range);
63 yscalar = static_cast<double> (ywrange) / yr_range;
64 data.clear(); // JUST in case
65 // LRS has a bunch of line_real_t's inside
66 line_real_set_t::line_real_list_t::const_iterator it = LRS.data.begin();
67 for (; it != LRS.data.end(); it++) {
68 line_real_t LR = (*it);
69 line_window_onevar_t LWO(LR);
70 if (std::verbose > 2) {
77 /** give a name to this plot_t object */
78 void plot_t::setName(QString& inName) {
80 } // --plot_t::setName()
82 void plot_t::setXAxisName(QString& inNameX) {
84 } // --plot_t::setXAxisName()
86 void plot_t::setYAxisName(QString& inNameY) {
88 } // --plot_t::setYAxisName()
90 /** calculate and make appropriate and human-oriented range increment for Y Axis:
91 * 1, 2, or 5 times a power of 10
92 * such there are usually about 10 to 20 marks on the axis
93 * this calibration can be fudged to about halve or double the number of notches
95 void plot_t::makeNotchesOnYAxis(QPainter& P) {
96 double rmin = this->rMinY; // minimum as a real
97 double rmax = this->rMaxY; // maximum as a real
98 double range = rmax - rmin; // range: real
99 int xPosOfYAxis = xwmin;
100 // this code is for calculating the values at which we make notches on the axes
101 double logbase10ofrange = log(range) / log(10);
102 int characteristic = static_cast<int> (floor(logbase10ofrange));
103 double mantissa = logbase10ofrange - characteristic;
104 double log2 = log(2) / log(10);
105 double log5 = log(5) / log(10);
106 int multiplierTimes10;
107 /* the whole idea is to partition numbers like a partition of frequencies \in |R+
108 * by musical note-based half-open intervals, e.g.: { [A,C#) , [C#,F) , [F,A) }
109 * we partition all ranges \in |R+ into three representatives: {1,2,5}.
110 * so we work on the mantissa of the range's log base 10, by definition \in [0,1).
111 * unlike the base 12-partitioned base 2 musical octaves, we throw away and add back in powers of 10,
112 * which the snark14Display user tribe feels comfortable with.
113 * ( we really tripartition each interval [r, 10r) where r \in |R+ )
114 * since our representatives are {1, 2, 5}.10^n (does not form a perfect geometric sequence when sorted),
115 * we partition [0,1) into { [0,log2) , [log2,log5) , [log5, 1) } => represented as {1,2,5}
116 * rather than using the perfectly even tri-partition of notes shown above.
117 * say range is 763, then the mantissa will be log(7.63), which falls in the third partition,
118 * so all intervals are 50 (5 times the appropriate power of 10 remembered by characteristic)
119 * and range/notchinterval = 763/50 is indeed in [10,20). get either 15 or 16 notches, depending on extrema.
120 * in the case of something like range = 4.9, we have 0.2 be the representative: 24 or 25 notches.
121 * (that is the absolute maximum of the number of notches.)
123 if (mantissa < log2) {
124 multiplierTimes10 = 1;
125 } else if (mantissa < log5) {
126 multiplierTimes10 = 2;
128 multiplierTimes10 = 5;
130 // double range_inc = multiplier * pow(10, characteristic);
131 double range_inc = multiplierTimes10 * pow(10, characteristic - 1);
132 // double from = range_inc * ceil(rmin/range_inc);
133 // handle "weird zero" (bug #114) this way...
134 int from_idx = static_cast<int> (ceil(rmin / range_inc));
135 // not idiomatic 'C' but it is easier for me to think about inclusive bounds
136 // double to = range_inc * floor(rmax/range_inc);
137 int to_idx = static_cast<int> (floor(rmax / range_inc));
138 // int num_intervals = static_cast<int>(floor((to-from)/range_inc));
140 QFont f("times", 12);
142 // for(int notch = 0; notch <= num_intervals; notch++) {
143 for (int idx = from_idx; idx <= to_idx; idx++) {
144 double markreal = idx * range_inc; // if idx==0, markreal will be 0.0
145 int markwinY = static_cast<int> (floor(ywmax - (markreal - rMinY) * yscalar)); // mark position in window
146 P.drawLine(xPosOfYAxis - 6, markwinY, xPosOfYAxis + 1, markwinY);
148 labelToDraw.setNum(markreal);
149 P.drawText(xPosOfYAxis - sd_line_t::YAXISNOTCHLABELOFFSET, markwinY, labelToDraw);
151 } // --plot_t::makeNotchesOnYAxis()
153 /** just like the above, slightly trickier since we only want integers
155 void plot_t::makeNotchesOnXAxis(QPainter& P) {
156 int& imin = (this->rMinX);
157 int& imax = (this->rMaxX);
158 int yPosOfXAxis = ywmax;
159 double rmin = static_cast<double> (imin); // minimum as a real
160 double rmax = static_cast<double> (imax); // maximum as a real
161 double range = rmax - rmin; // range: real
162 // this code is for calculating the values at which we make notches on the axes
163 double logbase10ofrange = log(range) / log(10);
164 int characteristic = static_cast<int> (floor(logbase10ofrange));
165 double mantissa = logbase10ofrange - characteristic;
166 double log2 = log(2) / log(10);
167 double log5 = log(5) / log(10);
168 int multiplierTimes10; // i like using integral types.
169 // the logic for this whole operation is the same as above,
170 // save some measures taken to ensure that the interval as well as notches are always integers.
171 if (mantissa < log2) {
172 multiplierTimes10 = 1;
173 } else if (mantissa < log5) {
174 multiplierTimes10 = 2;
176 multiplierTimes10 = 5;
178 // the following line really means: // double range_inc = multiplier * pow(10, characteristic);
179 double range_inc = multiplierTimes10 * pow(10, characteristic - 1);
180 if (range_inc <= 1) range_inc = 1.0; // x-axis should not have fractional labels (>1 guarantees it isn't)
181 int range_inc_int = static_cast<int> (floor(range_inc));
182 if (range_inc_int <= 0) range_inc_int = 1; // paranoia: don't want loop increment to be 0
183 int from_int = static_cast<int> (floor(range_inc * ceil(rmin / range_inc)));
184 // not idiomatic 'C' but it is easier for me to think about inclusive bounds
185 int to = static_cast<int> (floor(range_inc * floor(rmax / range_inc)));
186 P.setPen(Qt::black); // darkish blue notches and axis labels [as was in Bruno's code]
187 QFont f("times", 12);
189 for (int notch = from_int; notch <= to; notch += range_inc_int) {
190 // safe to increment by integer in for loop
191 int markreal = notch; // really!
192 int markwinX = xwmin + static_cast<int> (floor((markreal - rMinX) * xscalar));
193 P.drawLine(markwinX, yPosOfXAxis - 1, markwinX, yPosOfXAxis + 6);
195 labelToDraw.setNum(markreal);
196 P.drawText(markwinX - 5, yPosOfXAxis + 19, labelToDraw);
198 } // --plot_t::makeNotchesOnXAxis()
200 // void plot_t::labelTheXAxis() {
201 // } // --plot_t::labelTheXAxis()
202 // void plot_t::labelTheYAxis() {
203 // } // --plot_t::labelTheYAxis()
205 /** plot the plot_t object */
206 void plot_t::plot(QPainter& P) {
207 static const int numCanned = 15;
208 static QColor cannedColors[numCanned] = {
225 static const int numPenStyles = 5;
226 static Qt::PenStyle cannedPenStyles[numPenStyles] = {
233 int RectWidth = 16; // swatch rectangles' (for algo color/name association) dimensions
235 int xPadForRectCtrs = RectWidth;
236 int yPadForRectCtrs = RectHeight;
237 int xCtrDistance = myWidth/3;
238 int yCtrDistance = ywmin/4; // so it has to be that ywmin > 4*RectHeight
239 // for drawing legend-swatches: 2x3 on top, 3x3 at bottom, total of 15
240 static point_window_t RectCtrPositions[numCanned] = {
241 // swatches above the plot
242 {xPadForRectCtrs + 0 * xCtrDistance, yPadForRectCtrs + 0 * yCtrDistance},
243 {xPadForRectCtrs + 1 * xCtrDistance, yPadForRectCtrs + 0 * yCtrDistance},
244 {xPadForRectCtrs + 2 * xCtrDistance, yPadForRectCtrs + 0 * yCtrDistance},
245 {xPadForRectCtrs + 0 * xCtrDistance, yPadForRectCtrs + 1 * yCtrDistance},
246 {xPadForRectCtrs + 1 * xCtrDistance, yPadForRectCtrs + 1 * yCtrDistance},
247 {xPadForRectCtrs + 2 * xCtrDistance, yPadForRectCtrs + 1 * yCtrDistance},
248 // leave room above the plot for plot name... and swatches below the plot
249 {xPadForRectCtrs + 0 * xCtrDistance, myHeight - yPadForRectCtrs - 0 * yCtrDistance},
250 {xPadForRectCtrs + 1 * xCtrDistance, myHeight - yPadForRectCtrs - 0 * yCtrDistance},
251 {xPadForRectCtrs + 2 * xCtrDistance, myHeight - yPadForRectCtrs - 0 * yCtrDistance},
252 {xPadForRectCtrs + 0 * xCtrDistance, myHeight - yPadForRectCtrs - 1 * yCtrDistance},
253 {xPadForRectCtrs + 1 * xCtrDistance, myHeight - yPadForRectCtrs - 1 * yCtrDistance},
254 {xPadForRectCtrs + 2 * xCtrDistance, myHeight - yPadForRectCtrs - 1 * yCtrDistance},
255 // really shouldn't plot this many... these could clash a bit with x axis label.
256 {xPadForRectCtrs + 0 * xCtrDistance, myHeight - yPadForRectCtrs - 2 * yCtrDistance},
257 {xPadForRectCtrs + 1 * xCtrDistance, myHeight - yPadForRectCtrs - 2 * yCtrDistance},
258 {xPadForRectCtrs + 2 * xCtrDistance, myHeight - yPadForRectCtrs - 2 * yCtrDistance}
260 int colorInd = 0; // don't want this to be static!
262 for(data_manyexecs_t::iterator it = data.begin(); it != data.end(); it++) {
263 QColor& myColor = cannedColors[ (colorInd % numCanned) ];
264 Qt::PenStyle myPenStyle = cannedPenStyles[ penStyleInd % numPenStyles ];
266 //pen.setStyle(myPenStyle);
270 it->plot(P, myColor, myPenStyle);
271 if(colorInd < numCanned) {
272 int RectCtrX = RectCtrPositions[colorInd].x;
273 int RectCtrY = RectCtrPositions[colorInd].y;
274 // fill in rectangle with corresponding color (same as the line just plotted)
275 P.fillRect(RectCtrX - RectWidth/2, RectCtrY - RectHeight/2, RectWidth, RectHeight, myColor);
276 P.setPen(Qt::black); // then draw the boundary
277 P.drawRect(RectCtrX - RectWidth/2, RectCtrY - RectHeight/2, RectWidth, RectHeight);
278 QString algName = (it->getName());
279 int nameLengthInChars = 40; // max
280 QFont savedFont = P.font();
281 QFont f("Times", 11);
283 P.drawText(RectCtrX + RectWidth*3/4, RectCtrY+5, algName, nameLengthInChars);
284 P.setFont(savedFont);
286 // perhaps generate some kind of warning about too many plots,
287 // not enough colors being used / not enough space for swatches etc
289 // no one in their right mind would run 16 different execs (?), but just covering our base (no segfault).
290 // here could also be where label associating color with variable name is plotted...
294 sd_line_t::drawBoundary(P); // by doing this after, we make sure the frame is not painted over
295 makeNotchesOnXAxis(P);
296 makeNotchesOnYAxis(P);
297 // now label the plot just above the plot window
303 P.drawText(xwmin, ywmin-15, plotName);
304 P.setFont(F); // restore original font to QPainter
306 P.drawText( (sd_line_t::WIDTH - sd_line_t::XWINPAD)/2, ywmax+45, XAxisName);
307 // label the Y axis (sideways)
309 P.drawText( -(sd_line_t::HEIGHT)/2, sd_line_t::XWINPAD - sd_line_t::YAXISLABELOFFSET, YAxisName);
310 P.rotate(90); // restore orientation of painter
311 } // --plot_t::plot()
313 /** plot the plot_t object in Grayscale*/
314 // jk 1/13/2009 adding grayScale option for graphs
315 void plot_t::plotGray(QPainter& P) {
317 static const int numCanned = 9;
318 static QColor cannedColors[numCanned] = {
329 static const int numPenStyles = 9;
330 static Qt::PenStyle cannedPenStyles[numPenStyles] = {
341 //Qt::DashDotDotLine,
343 int RectWidth = 16; // swatch rectangles' (for algo color/name association) dimensions
345 int xPadForRectCtrs = RectWidth;
346 int yPadForRectCtrs = RectHeight;
347 int xCtrDistance = myWidth / 3;
348 int yCtrDistance = ywmin / 4; // so it has to be that ywmin > 4*RectHeight
349 // for drawing legend-swatches: 2x3 on top, 3x3 at bottom, total of 15
350 static point_window_t RectCtrPositions[numCanned] = {
351 // swatches above the plot
352 {xPadForRectCtrs + 0 * xCtrDistance, yPadForRectCtrs + 0 * yCtrDistance},
353 {xPadForRectCtrs + 1 * xCtrDistance, yPadForRectCtrs + 0 * yCtrDistance},
354 {xPadForRectCtrs + 2 * xCtrDistance, yPadForRectCtrs + 0 * yCtrDistance},
355 {xPadForRectCtrs + 0 * xCtrDistance, yPadForRectCtrs + 1 * yCtrDistance},
356 {xPadForRectCtrs + 1 * xCtrDistance, yPadForRectCtrs + 1 * yCtrDistance},
357 {xPadForRectCtrs + 2 * xCtrDistance, yPadForRectCtrs + 1 * yCtrDistance},
358 // leave room above the plot for plot name... and swatches below the plot
359 {xPadForRectCtrs + 0 * xCtrDistance, myHeight - yPadForRectCtrs - 0 * yCtrDistance},
360 {xPadForRectCtrs + 1 * xCtrDistance, myHeight - yPadForRectCtrs - 0 * yCtrDistance},
361 {xPadForRectCtrs + 2 * xCtrDistance, myHeight - yPadForRectCtrs - 0 * yCtrDistance}
362 // {xPadForRectCtrs + 0 * xCtrDistance, myHeight - yPadForRectCtrs - 1 * yCtrDistance},
363 // {xPadForRectCtrs + 1 * xCtrDistance, myHeight - yPadForRectCtrs - 1 * yCtrDistance},
364 // {xPadForRectCtrs + 2 * xCtrDistance, myHeight - yPadForRectCtrs - 1 * yCtrDistance},
365 // // really shouldn't plot this many... these could clash a bit with x axis label.
366 // {xPadForRectCtrs + 0 * xCtrDistance, myHeight - yPadForRectCtrs - 2 * yCtrDistance},
367 // {xPadForRectCtrs + 1 * xCtrDistance, myHeight - yPadForRectCtrs - 2 * yCtrDistance},
368 // {xPadForRectCtrs + 2 * xCtrDistance, myHeight - yPadForRectCtrs - 2 * yCtrDistance}
370 int colorInd = 0; // don't want this to be static!
372 for (data_manyexecs_t::iterator it = data.begin(); it != data.end(); it++) {
373 QColor& myColor = cannedColors[ (colorInd % numCanned) ];
374 Qt::PenStyle myPenStyle = cannedPenStyles[ penStyleInd % numPenStyles ];
376 it->plot(P, myColor, myPenStyle);
377 if (colorInd < numCanned) {
378 int RectCtrX = RectCtrPositions[colorInd].x;
379 int RectCtrY = RectCtrPositions[colorInd].y;
380 // fill in rectangle with corresponding color (same as the line just plotted)
381 //P.fillRect(RectCtrX - RectWidth/2, RectCtrY - RectHeight/2, RectWidth, RectHeight, myColor);
382 QPen savedPen = P.pen();
383 QPen pen; // = P.pen();
384 pen.setStyle(myPenStyle);
386 pen.setColor(myColor);
389 //P.setPen(myColor); // then draw the boundary
390 //P.setPen(myPenStyle);
391 //P.drawRect(RectCtrX - RectWidth/2, RectCtrY - RectHeight/2, RectWidth, RectHeight);
392 P.drawLine(RectCtrX - RectWidth / 2, RectCtrY, RectCtrX + RectWidth / 2, RectCtrY);
393 //change pen color and style back to black solid for printing the text
394 //P.setPen( savedPen );
395 pen.setStyle(Qt::SolidLine);
397 pen.setColor(Qt::black);
400 QString algName = (it->getName());
401 int nameLengthInChars = 40; // max
402 QFont savedFont = P.font();
403 QFont f("Times", 11);
405 P.drawText(RectCtrX + RectWidth * 3 / 4, RectCtrY + 5, algName, nameLengthInChars);
406 P.setFont(savedFont);
409 // perhaps generate some kind of warning about too many plots,
410 // not enough colors being used / not enough space for swatches etc
412 // no one in their right mind would run 16 different execs (?), but just covering our base (no segfault).
413 // here could also be where label associating color with variable name is plotted...
417 sd_line_t::drawBoundary(P); // by doing this after, we make sure the frame is not painted over
418 makeNotchesOnXAxis(P);
419 makeNotchesOnYAxis(P);
420 // now label the plot just above the plot window
426 P.drawText(xwmin, ywmin - 15, plotName);
427 P.setFont(F); // restore original font to QPainter
429 P.drawText((sd_line_t::WIDTH - sd_line_t::XWINPAD) / 2, ywmax + 45, XAxisName);
430 // label the Y axis (sideways)
432 P.drawText(-(sd_line_t::HEIGHT) / 2, sd_line_t::XWINPAD - sd_line_t::YAXISLABELOFFSET, YAxisName);
433 P.rotate(90); // restore orientation of painter
434 } // --plot_t::plot()