1 // $Id: PixelatedGraph.cc 5748 2014-10-11 19:38:53Z flaterco $
2
3 /* PixelatedGraph Graphs that do not use scalable vectors.
4
5 Copyright (C) 2010 David Flater.
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "libxtide.hh"
22 #include "Graph.hh"
23 #include "PixelatedGraph.hh"
24
25 namespace libxtide {
26
27
28 // In drawing of line graphs, slope at which to abandon the thick line
29 // drawing algorithm.
30 static const double slopeLimit (5.0);
31
32
PixelatedGraph(unsigned xSize,unsigned ySize,GraphStyle style)33 PixelatedGraph::PixelatedGraph (unsigned xSize, unsigned ySize,
34 GraphStyle style):
35 Graph (xSize, ySize, style) {
36 }
37
38
setPixel(int x,int y,Colors::Colorchoice c,double opacity)39 void PixelatedGraph::setPixel (int x, int y,
40 Colors::Colorchoice c, double opacity) {
41 assert (c < (int)Colors::numColors);
42 if (opacity >= 0.5)
43 setPixel (x, y, c);
44 }
45
46
drawVerticalLineP(int x,int y1,int y2,Colors::Colorchoice c,double opacity)47 void PixelatedGraph::drawVerticalLineP (int x, int y1, int y2,
48 Colors::Colorchoice c,
49 double opacity) {
50 int ylo, yhi;
51 if (y1 < y2) {
52 ylo = y1; yhi = y2;
53 } else {
54 ylo = y2; yhi = y1;
55 }
56 if (opacity == 1)
57 for (int i=ylo; i<=yhi; ++i)
58 setPixel (x, i, c);
59 else
60 for (int i=ylo; i<=yhi; ++i)
61 setPixel (x, i, c, opacity);
62 }
63
64
drawVerticalLinePxSy(int x,double y1,double y2,Colors::Colorchoice c,double opacity)65 void PixelatedGraph::drawVerticalLinePxSy (int x, double y1, double y2,
66 Colors::Colorchoice c,
67 double opacity) {
68 double ylo, yhi;
69 if (y1 < y2) {
70 ylo = y1; yhi = y2;
71 } else {
72 ylo = y2; yhi = y1;
73 }
74 int ylo2 ((int) ceil (ylo));
75 int yhi2 ((int) floor (yhi));
76 if (ylo2 < yhi2)
77 drawVerticalLineP (x, ylo2, yhi2-1, c, opacity);
78 // What if they both fall within the same pixel: ylo2 > yhi2
79 if (ylo2 > yhi2) {
80 assert (yhi2 == ylo2 - 1);
81 setPixel (x, yhi2, c, opacity * (yhi - ylo));
82 } else {
83 // The normal case.
84 if (ylo < ylo2)
85 setPixel (x, ylo2-1, c, opacity * (ylo2 - ylo));
86 if (yhi > yhi2)
87 setPixel (x, yhi2, c, opacity * (yhi - yhi2));
88 }
89 }
90
91
drawVerticalLineS(double x,double y1,double y2,Colors::Colorchoice c)92 void PixelatedGraph::drawVerticalLineS (double x, double y1, double y2,
93 Colors::Colorchoice c) {
94 drawVerticalLinePxSy (Global::ifloor (x), y1, y2, c);
95 }
96
97
drawFunkyLine(double prevytide,double ytide,double nextytide,int x,Colors::Colorchoice c)98 void PixelatedGraph::drawFunkyLine (double prevytide,
99 double ytide,
100 double nextytide,
101 int x,
102 Colors::Colorchoice c) {
103 double dy, yleft, yright;
104 double slw (Global::settings["lw"].d);
105
106 // The fix for line slope breaks down when the slope gets nasty, so
107 // switch to a more conservative strategy when that happens. Line
108 // width becomes 1 no matter what.
109
110 #define dohalfline(yy) { \
111 double lw; \
112 if (fabs(dy) < slopeLimit) \
113 lw = (1.0 + (M_SQRT2 - 1.0) * fabs(dy)) * slw / 2.0; \
114 else \
115 lw = (fabs(dy) + slw) / 2.0; \
116 if (dy < 0.0) \
117 lw = -lw; \
118 yy = ytide - lw; \
119 }
120
121 dy = ytide - prevytide;
122 dohalfline (yleft);
123 dy = ytide - nextytide;
124 dohalfline (yright);
125
126 // Fix degenerate cases.
127 if (ytide > yleft && ytide > yright) {
128 if (yleft > yright)
129 yleft = ytide + slw / 2.0;
130 else
131 yright = ytide + slw / 2.0;
132 } else if (ytide < yleft && ytide < yright) {
133 if (yleft < yright)
134 yleft = ytide - slw / 2.0;
135 else
136 yright = ytide - slw / 2.0;
137 }
138 drawVerticalLinePxSy (x, yleft, yright, c);
139 }
140
141
drawX(double x,double y)142 void PixelatedGraph::drawX (double x, double y) {
143 int ix = Global::ifloor (x);
144 int iy = Global::ifloor (y);
145 drawVerticalLineP (ix, iy-4, iy+4, Colors::foreground);
146 drawHorizontalLineP (ix-4, ix+4, iy, Colors::foreground);
147 }
148
149
drawLevels(const SafeVector<double> & val,const SafeVector<double> & y,double yzulu,bool isCurrent,const SafeVector<BlendBlob> & blendBlobs)150 void PixelatedGraph::drawLevels (const SafeVector<double> &val,
151 const SafeVector<double> &y,
152 double yzulu,
153 bool isCurrent
154 #ifdef blendingTest
155 , const SafeVector<BlendBlob> &blendBlobs
156 #endif
157 ) {
158 const char gs (Global::settings["gs"].c);
159 const double opacity (gs == 's' ? Global::settings["to"].d : 1.0);
160
161 // Harmonize this with the quantized y coordinate of the 0 kt line to avoid
162 // anomalies like a gap between the flood curve and the line.
163 yzulu = Global::ifloor(yzulu);
164
165 for (int x=0; x<(int)_xSize; ++x) {
166
167 // Coloration is determined from the predicted heights, not from
168 // the eventTypes of surrounding tide events. Ideally the two
169 // would never disagree, but for pathological sub stations they
170 // can.
171 if (isCurrent) {
172 Colors::Colorchoice c = (val[x+1] > 0.0 ? Colors::flood : Colors::ebb);
173 switch (gs) {
174 case 'd':
175 drawVerticalLinePxSy (x, yzulu, y[x+1], c, opacity);
176 break;
177 case 'l':
178 drawFunkyLine (y[x], y[x+1], y[x+2], x, c);
179 break;
180 case 's':
181 drawVerticalLinePxSy (x, yzulu, y[x+1], c, opacity);
182 drawFunkyLine (y[x], y[x+1], y[x+2], x, Colors::foreground);
183 break;
184 default:
185 assert (false);
186 }
187 } else {
188 Colors::Colorchoice c = (val[x] < val[x+1] ? Colors::flood : Colors::ebb);
189 switch (gs) {
190 case 'd':
191 drawVerticalLinePxSy (x, _ySize, y[x+1], c, opacity);
192 break;
193 case 'l':
194 drawFunkyLine (y[x], y[x+1], y[x+2], x, c);
195 break;
196 case 's':
197 drawVerticalLinePxSy (x, _ySize, y[x+1], c, opacity);
198 drawFunkyLine (y[x], y[x+1], y[x+2], x, Colors::foreground);
199 break;
200 default:
201 assert (false);
202 }
203 }
204
205 #ifdef blendingTest
206 if (!blendBlobs[x+1].isNull) {
207 setPixel (x, (int)blendBlobs[x+1].firsty, Colors::mark);
208 setPixel (x, (int)blendBlobs[x+1].secondy, Colors::msl);
209 }
210 #endif
211 }
212 }
213
214
drawBoxS(double x1,double x2,double y1,double y2,Colors::Colorchoice c)215 void PixelatedGraph::drawBoxS (double x1, double x2, double y1, double y2,
216 Colors::Colorchoice c) {
217 int ix1 (Global::ifloor (x1)), ix2 (Global::ifloor (x2));
218 if (ix1 > ix2)
219 std::swap (ix1, ix2);
220 // Exclude upper limit so adjacent boxes don't overlap.
221 for (int x=ix1; x<ix2; ++x)
222 drawVerticalLinePxSy (x, y1, y2, c);
223 }
224
225
drawHorizontalLinePxSy(int xlo,int xhi,double y,Colors::Colorchoice c)226 void PixelatedGraph::drawHorizontalLinePxSy (int xlo, int xhi, double y,
227 Colors::Colorchoice c) {
228 drawHorizontalLineP (xlo, xhi, Global::ifloor(y), c);
229 }
230
231
drawHorizontalLineP(int xlo,int xhi,int y,Colors::Colorchoice c)232 void PixelatedGraph::drawHorizontalLineP (int xlo, int xhi, int y,
233 Colors::Colorchoice c) {
234 for (int i=xlo; i<=xhi; ++i)
235 setPixel (i, y, c);
236 }
237
238
239 // x quantization has to happen first to avoid an off-by-one error. It is
240 // most obvious in TTYGraph where the effective font size is 1 pixel. For
241 // example,
242 // Let x = 1.1
243 // Hour tick gets drawn at floor(1.1) = column 1
244 // Label of length 1 gets drawn at floor(1.1 - 0.5) = column 0
245 // So you get
246 // 8
247 // |
248 // Duh.
centerStringSxPy(double x,int y,const Dstr & s)249 void PixelatedGraph::centerStringSxPy (double x, int y, const Dstr &s) {
250 // int cast needed to prevent surprising promotion to unsigned
251 drawStringP (Global::ifloor(x)-(int)stringWidth(s)/2, y, s);
252 }
253
254
rightJustifyStringS(double x,double y,const Dstr & s)255 void PixelatedGraph::rightJustifyStringS (double x, double y, const Dstr &s) {
256 drawStringP (Global::ifloor(x-stringWidth(s)), Global::ifloor(y), s);
257 }
258
259 }
260