This diff has been collapsed as it changes many lines, (619 lines changed) Show them Hide them | |||
@@ -0,0 +1,619 | |||
|
1 | #include "qcustomplotvect.h" | |
|
2 | #include <QVector> | |
|
3 | #include <QVectorIterator> | |
|
4 | QCustomPlotVect::QCustomPlotVect(QWidget *parent) | |
|
5 | :QCustomPlot(parent) | |
|
6 | { | |
|
7 | ||
|
8 | } | |
|
9 | ||
|
10 | QCustomPlotVect::~QCustomPlotVect() | |
|
11 | { | |
|
12 | ||
|
13 | } | |
|
14 | ||
|
15 | QCPGraphVect *QCustomPlotVect::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) | |
|
16 | { | |
|
17 | if (!keyAxis) keyAxis = xAxis; | |
|
18 | if (!valueAxis) valueAxis = yAxis; | |
|
19 | if (!keyAxis || !valueAxis) | |
|
20 | { | |
|
21 | qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)"; | |
|
22 | return 0; | |
|
23 | } | |
|
24 | if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this) | |
|
25 | { | |
|
26 | qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent"; | |
|
27 | return 0; | |
|
28 | } | |
|
29 | ||
|
30 | QCPGraphVect *newGraph = new QCPGraphVect(keyAxis, valueAxis); | |
|
31 | if (addPlottable(newGraph)) | |
|
32 | { | |
|
33 | newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size())); | |
|
34 | return newGraph; | |
|
35 | } else | |
|
36 | { | |
|
37 | delete newGraph; | |
|
38 | return 0; | |
|
39 | } | |
|
40 | } | |
|
41 | ||
|
42 | ||
|
43 | QCPGraphVect::QCPGraphVect(QCPAxis *keyAxis, QCPAxis *valueAxis) | |
|
44 | :QCPGraph(keyAxis,valueAxis) | |
|
45 | { | |
|
46 | mData = new QVector<QCPData>(); | |
|
47 | } | |
|
48 | ||
|
49 | QCPGraphVect::~QCPGraphVect() | |
|
50 | { | |
|
51 | ||
|
52 | } | |
|
53 | ||
|
54 | void QCPGraphVect::setData(QVector<QCPData> *data) | |
|
55 | { | |
|
56 | if(data!=mData) | |
|
57 | { | |
|
58 | delete this->mData; | |
|
59 | this->mData = data; | |
|
60 | } | |
|
61 | } | |
|
62 | ||
|
63 | void QCPGraphVect::draw(QCPPainter *painter) | |
|
64 | { | |
|
65 | if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } | |
|
66 | int test = mKeyAxis.data()->range().size(); | |
|
67 | test = mData->count(); | |
|
68 | if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return; | |
|
69 | if (mLineStyle == lsNone && mScatterStyle.isNone()) return; | |
|
70 | ||
|
71 | // allocate line and (if necessary) point vectors: | |
|
72 | QVector<QPointF> *lineData = new QVector<QPointF>; | |
|
73 | QVector<QCPData> *scatterData = 0; | |
|
74 | if (!mScatterStyle.isNone()) | |
|
75 | scatterData = new QVector<QCPData>; | |
|
76 | ||
|
77 | // fill vectors with data appropriate to plot style: | |
|
78 | getPlotData(lineData, scatterData); | |
|
79 | ||
|
80 | // check data validity if flag set: | |
|
81 | #ifdef QCUSTOMPLOT_CHECK_DATA | |
|
82 | QCPDataMap::const_iterator it; | |
|
83 | for (it = mData->constBegin(); it != mData->constEnd(); ++it) | |
|
84 | { | |
|
85 | if (QCP::isInvalidData(it.value().key, it.value().value) || | |
|
86 | QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) || | |
|
87 | QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus)) | |
|
88 | qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name(); | |
|
89 | } | |
|
90 | #endif | |
|
91 | ||
|
92 | // draw fill of graph: | |
|
93 | drawFill(painter, lineData); | |
|
94 | ||
|
95 | // draw line: | |
|
96 | if (mLineStyle == lsImpulse) | |
|
97 | drawImpulsePlot(painter, lineData); | |
|
98 | else if (mLineStyle != lsNone) | |
|
99 | drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot | |
|
100 | ||
|
101 | // draw scatters: | |
|
102 | if (scatterData) | |
|
103 | drawScatterPlot(painter, scatterData); | |
|
104 | ||
|
105 | // free allocated line and point vectors: | |
|
106 | delete lineData; | |
|
107 | if (scatterData) | |
|
108 | delete scatterData; | |
|
109 | } | |
|
110 | ||
|
111 | void QCPGraphVect::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const | |
|
112 | { | |
|
113 | switch(mLineStyle) | |
|
114 | { | |
|
115 | case lsNone: getScatterPlotData(scatterData); break; | |
|
116 | case lsLine: getLinePlotData(lineData, scatterData); break; | |
|
117 | case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break; | |
|
118 | case lsStepRight: getStepRightPlotData(lineData, scatterData); break; | |
|
119 | case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break; | |
|
120 | case lsImpulse: getImpulsePlotData(lineData, scatterData); break; | |
|
121 | } | |
|
122 | } | |
|
123 | ||
|
124 | void QCPGraphVect::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const | |
|
125 | { | |
|
126 | QCPAxis *keyAxis = mKeyAxis.data(); | |
|
127 | QCPAxis *valueAxis = mValueAxis.data(); | |
|
128 | if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } | |
|
129 | if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; } | |
|
130 | ||
|
131 | QVector<QCPData> lineData; | |
|
132 | getPreparedData(&lineData, scatterData); | |
|
133 | linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill | |
|
134 | linePixelData->resize(lineData.size()); | |
|
135 | ||
|
136 | // transform lineData points to pixels: | |
|
137 | if (keyAxis->orientation() == Qt::Vertical) | |
|
138 | { | |
|
139 | for (int i=0; i<lineData.size(); ++i) | |
|
140 | { | |
|
141 | (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value)); | |
|
142 | (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key)); | |
|
143 | } | |
|
144 | } else // key axis is horizontal | |
|
145 | { | |
|
146 | for (int i=0; i<lineData.size(); ++i) | |
|
147 | { | |
|
148 | (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key)); | |
|
149 | (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value)); | |
|
150 | } | |
|
151 | } | |
|
152 | } | |
|
153 | ||
|
154 | QCPRange QCPGraphVect::getKeyRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain, bool includeErrors) const | |
|
155 | { | |
|
156 | ||
|
157 | QCPRange range; | |
|
158 | bool haveLower = false; | |
|
159 | bool haveUpper = false; | |
|
160 | ||
|
161 | double current, currentErrorMinus, currentErrorPlus; | |
|
162 | ||
|
163 | if (inSignDomain == sdBoth) // range may be anywhere | |
|
164 | { | |
|
165 | QVector<QCPData>::const_iterator it = mData->constBegin(); | |
|
166 | while (it != mData->constEnd()) | |
|
167 | { | |
|
168 | current = (*it).key; | |
|
169 | currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); | |
|
170 | currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); | |
|
171 | if (current-currentErrorMinus < range.lower || !haveLower) | |
|
172 | { | |
|
173 | range.lower = current-currentErrorMinus; | |
|
174 | haveLower = true; | |
|
175 | } | |
|
176 | if (current+currentErrorPlus > range.upper || !haveUpper) | |
|
177 | { | |
|
178 | range.upper = current+currentErrorPlus; | |
|
179 | haveUpper = true; | |
|
180 | } | |
|
181 | ++it; | |
|
182 | } | |
|
183 | } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain | |
|
184 | { | |
|
185 | QVector<QCPData>::const_iterator it = mData->constBegin(); | |
|
186 | while (it != mData->constEnd()) | |
|
187 | { | |
|
188 | current = (*it).key; | |
|
189 | currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); | |
|
190 | currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); | |
|
191 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) | |
|
192 | { | |
|
193 | range.lower = current-currentErrorMinus; | |
|
194 | haveLower = true; | |
|
195 | } | |
|
196 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) | |
|
197 | { | |
|
198 | range.upper = current+currentErrorPlus; | |
|
199 | haveUpper = true; | |
|
200 | } | |
|
201 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. | |
|
202 | { | |
|
203 | if ((current < range.lower || !haveLower) && current < 0) | |
|
204 | { | |
|
205 | range.lower = current; | |
|
206 | haveLower = true; | |
|
207 | } | |
|
208 | if ((current > range.upper || !haveUpper) && current < 0) | |
|
209 | { | |
|
210 | range.upper = current; | |
|
211 | haveUpper = true; | |
|
212 | } | |
|
213 | } | |
|
214 | ++it; | |
|
215 | } | |
|
216 | } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain | |
|
217 | { | |
|
218 | QVector<QCPData>::const_iterator it = mData->constBegin(); | |
|
219 | while (it != mData->constEnd()) | |
|
220 | { | |
|
221 | current = (*it).key; | |
|
222 | currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); | |
|
223 | currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); | |
|
224 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) | |
|
225 | { | |
|
226 | range.lower = current-currentErrorMinus; | |
|
227 | haveLower = true; | |
|
228 | } | |
|
229 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) | |
|
230 | { | |
|
231 | range.upper = current+currentErrorPlus; | |
|
232 | haveUpper = true; | |
|
233 | } | |
|
234 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. | |
|
235 | { | |
|
236 | if ((current < range.lower || !haveLower) && current > 0) | |
|
237 | { | |
|
238 | range.lower = current; | |
|
239 | haveLower = true; | |
|
240 | } | |
|
241 | if ((current > range.upper || !haveUpper) && current > 0) | |
|
242 | { | |
|
243 | range.upper = current; | |
|
244 | haveUpper = true; | |
|
245 | } | |
|
246 | } | |
|
247 | ++it; | |
|
248 | } | |
|
249 | } | |
|
250 | ||
|
251 | foundRange = haveLower && haveUpper; | |
|
252 | return range; | |
|
253 | } | |
|
254 | ||
|
255 | QCPRange QCPGraphVect::getValueRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain, bool includeErrors) const | |
|
256 | { | |
|
257 | QCPRange range; | |
|
258 | bool haveLower = false; | |
|
259 | bool haveUpper = false; | |
|
260 | ||
|
261 | double current, currentErrorMinus, currentErrorPlus; | |
|
262 | ||
|
263 | if (inSignDomain == sdBoth) // range may be anywhere | |
|
264 | { | |
|
265 | QVector<QCPData>::const_iterator it = mData->constBegin(); | |
|
266 | while (it != mData->constEnd()) | |
|
267 | { | |
|
268 | current = (*it).value; | |
|
269 | currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); | |
|
270 | currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); | |
|
271 | if (current-currentErrorMinus < range.lower || !haveLower) | |
|
272 | { | |
|
273 | range.lower = current-currentErrorMinus; | |
|
274 | haveLower = true; | |
|
275 | } | |
|
276 | if (current+currentErrorPlus > range.upper || !haveUpper) | |
|
277 | { | |
|
278 | range.upper = current+currentErrorPlus; | |
|
279 | haveUpper = true; | |
|
280 | } | |
|
281 | ++it; | |
|
282 | } | |
|
283 | } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain | |
|
284 | { | |
|
285 | QVector<QCPData>::const_iterator it = mData->constBegin(); | |
|
286 | while (it != mData->constEnd()) | |
|
287 | { | |
|
288 | current = (*it).value; | |
|
289 | currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); | |
|
290 | currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); | |
|
291 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) | |
|
292 | { | |
|
293 | range.lower = current-currentErrorMinus; | |
|
294 | haveLower = true; | |
|
295 | } | |
|
296 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) | |
|
297 | { | |
|
298 | range.upper = current+currentErrorPlus; | |
|
299 | haveUpper = true; | |
|
300 | } | |
|
301 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. | |
|
302 | { | |
|
303 | if ((current < range.lower || !haveLower) && current < 0) | |
|
304 | { | |
|
305 | range.lower = current; | |
|
306 | haveLower = true; | |
|
307 | } | |
|
308 | if ((current > range.upper || !haveUpper) && current < 0) | |
|
309 | { | |
|
310 | range.upper = current; | |
|
311 | haveUpper = true; | |
|
312 | } | |
|
313 | } | |
|
314 | ++it; | |
|
315 | } | |
|
316 | } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain | |
|
317 | { | |
|
318 | QVector<QCPData>::const_iterator it = mData->constBegin(); | |
|
319 | while (it != mData->constEnd()) | |
|
320 | { | |
|
321 | current = (*it).value; | |
|
322 | currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); | |
|
323 | currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); | |
|
324 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) | |
|
325 | { | |
|
326 | range.lower = current-currentErrorMinus; | |
|
327 | haveLower = true; | |
|
328 | } | |
|
329 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) | |
|
330 | { | |
|
331 | range.upper = current+currentErrorPlus; | |
|
332 | haveUpper = true; | |
|
333 | } | |
|
334 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. | |
|
335 | { | |
|
336 | if ((current < range.lower || !haveLower) && current > 0) | |
|
337 | { | |
|
338 | range.lower = current; | |
|
339 | haveLower = true; | |
|
340 | } | |
|
341 | if ((current > range.upper || !haveUpper) && current > 0) | |
|
342 | { | |
|
343 | range.upper = current; | |
|
344 | haveUpper = true; | |
|
345 | } | |
|
346 | } | |
|
347 | ++it; | |
|
348 | } | |
|
349 | } | |
|
350 | ||
|
351 | foundRange = haveLower && haveUpper; | |
|
352 | return range; | |
|
353 | } | |
|
354 | ||
|
355 | void QCPGraphVect::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const | |
|
356 | { | |
|
357 | QCPAxis *keyAxis = mKeyAxis.data(); | |
|
358 | QCPAxis *valueAxis = mValueAxis.data(); | |
|
359 | if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } | |
|
360 | // get visible data range: | |
|
361 | QVector<QCPData>::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point | |
|
362 | getVisibleDataBounds(lower, upper); | |
|
363 | if (lower == mData->constEnd() || upper == mData->constEnd()) | |
|
364 | return; | |
|
365 | ||
|
366 | // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling: | |
|
367 | int maxCount = std::numeric_limits<int>::max(); | |
|
368 | if (mAdaptiveSampling) | |
|
369 | { | |
|
370 | int keyPixelSpan = qAbs(keyAxis->coordToPixel((*lower).key)-keyAxis->coordToPixel((*upper).key)); | |
|
371 | maxCount = 2*keyPixelSpan+2; | |
|
372 | } | |
|
373 | int dataCount = countDataInBounds(lower, upper, maxCount); | |
|
374 | ||
|
375 | if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average | |
|
376 | { | |
|
377 | if (lineData) | |
|
378 | { | |
|
379 | QVector<QCPData>::const_iterator it = lower; | |
|
380 | QVector<QCPData>::const_iterator upperEnd = upper+1; | |
|
381 | double minValue = (*it).value; | |
|
382 | double maxValue = (*it).value; | |
|
383 | QVector<QCPData>::const_iterator currentIntervalFirstPoint = it; | |
|
384 | int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction | |
|
385 | int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey | |
|
386 | double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*lower).key)+reversedRound)); | |
|
387 | double lastIntervalEndKey = currentIntervalStartKey; | |
|
388 | double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates | |
|
389 | bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) | |
|
390 | int intervalDataCount = 1; | |
|
391 | ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect | |
|
392 | while (it != upperEnd) | |
|
393 | { | |
|
394 | if ((*it).key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary | |
|
395 | { | |
|
396 | if ((*it).value < minValue) | |
|
397 | minValue = (*it).value; | |
|
398 | else if ((*it).value > maxValue) | |
|
399 | maxValue = (*it).value; | |
|
400 | ++intervalDataCount; | |
|
401 | } else // new pixel interval started | |
|
402 | { | |
|
403 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster | |
|
404 | { | |
|
405 | if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point | |
|
406 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, (*currentIntervalFirstPoint).value)); | |
|
407 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); | |
|
408 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); | |
|
409 | if ((*it).key > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point | |
|
410 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (*(it-1)).value)); | |
|
411 | } else | |
|
412 | lineData->append(QCPData((*currentIntervalFirstPoint).key, (*currentIntervalFirstPoint).value)); | |
|
413 | lastIntervalEndKey = (*(it-1)).key; | |
|
414 | minValue = (*it).value; | |
|
415 | maxValue = (*it).value; | |
|
416 | currentIntervalFirstPoint = it; | |
|
417 | currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*it).key)+reversedRound)); | |
|
418 | if (keyEpsilonVariable) | |
|
419 | keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); | |
|
420 | intervalDataCount = 1; | |
|
421 | } | |
|
422 | ++it; | |
|
423 | } | |
|
424 | // handle last interval: | |
|
425 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster | |
|
426 | { | |
|
427 | if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point | |
|
428 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, (*currentIntervalFirstPoint).value)); | |
|
429 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); | |
|
430 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); | |
|
431 | } else | |
|
432 | lineData->append(QCPData((*currentIntervalFirstPoint).key, (*currentIntervalFirstPoint).value)); | |
|
433 | } | |
|
434 | ||
|
435 | if (scatterData) | |
|
436 | { | |
|
437 | double valueMaxRange = valueAxis->range().upper; | |
|
438 | double valueMinRange = valueAxis->range().lower; | |
|
439 | QVector<QCPData>::const_iterator it = lower; | |
|
440 | QVector<QCPData>::const_iterator upperEnd = upper+1; | |
|
441 | double minValue = (*it).value; | |
|
442 | double maxValue = (*it).value; | |
|
443 | QVector<QCPData>::const_iterator minValueIt = it; | |
|
444 | QVector<QCPData>::const_iterator maxValueIt = it; | |
|
445 | QVector<QCPData>::const_iterator currentIntervalStart = it; | |
|
446 | int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction | |
|
447 | int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey | |
|
448 | double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*lower).key)+reversedRound)); | |
|
449 | double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates | |
|
450 | bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) | |
|
451 | int intervalDataCount = 1; | |
|
452 | ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect | |
|
453 | while (it != upperEnd) | |
|
454 | { | |
|
455 | if ((*it).key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary | |
|
456 | { | |
|
457 | if ((*it).value < minValue && (*it).value > valueMinRange && (*it).value < valueMaxRange) | |
|
458 | { | |
|
459 | minValue = (*it).value; | |
|
460 | minValueIt = it; | |
|
461 | } else if ((*it).value > maxValue && (*it).value > valueMinRange && (*it).value < valueMaxRange) | |
|
462 | { | |
|
463 | maxValue = (*it).value; | |
|
464 | maxValueIt = it; | |
|
465 | } | |
|
466 | ++intervalDataCount; | |
|
467 | } else // new pixel started | |
|
468 | { | |
|
469 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them | |
|
470 | { | |
|
471 | // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): | |
|
472 | double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); | |
|
473 | int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average | |
|
474 | QVector<QCPData>::const_iterator intervalIt = currentIntervalStart; | |
|
475 | int c = 0; | |
|
476 | while (intervalIt != it) | |
|
477 | { | |
|
478 | if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && (*intervalIt).value > valueMinRange && (*intervalIt).value < valueMaxRange) | |
|
479 | scatterData->append((*intervalIt)); | |
|
480 | ++c; | |
|
481 | ++intervalIt; | |
|
482 | } | |
|
483 | } else if ((*currentIntervalStart).value > valueMinRange && (*currentIntervalStart).value < valueMaxRange) | |
|
484 | scatterData->append((*currentIntervalStart)); | |
|
485 | minValue = (*it).value; | |
|
486 | maxValue = (*it).value; | |
|
487 | currentIntervalStart = it; | |
|
488 | currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*it).key)+reversedRound)); | |
|
489 | if (keyEpsilonVariable) | |
|
490 | keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); | |
|
491 | intervalDataCount = 1; | |
|
492 | } | |
|
493 | ++it; | |
|
494 | } | |
|
495 | // handle last interval: | |
|
496 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them | |
|
497 | { | |
|
498 | // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): | |
|
499 | double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); | |
|
500 | int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average | |
|
501 | QVector<QCPData>::const_iterator intervalIt = currentIntervalStart; | |
|
502 | int c = 0; | |
|
503 | while (intervalIt != it) | |
|
504 | { | |
|
505 | if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && (*intervalIt).value > valueMinRange && (*intervalIt).value < valueMaxRange) | |
|
506 | scatterData->append((*intervalIt)); | |
|
507 | ++c; | |
|
508 | ++intervalIt; | |
|
509 | } | |
|
510 | } else if ((*currentIntervalStart).value > valueMinRange && (*currentIntervalStart).value < valueMaxRange) | |
|
511 | scatterData->append(*currentIntervalStart); | |
|
512 | } | |
|
513 | } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters | |
|
514 | { | |
|
515 | QVector<QCPData> *dataVector = 0; | |
|
516 | if (lineData) | |
|
517 | { | |
|
518 | dataVector = lineData; | |
|
519 | } | |
|
520 | else if (scatterData) | |
|
521 | dataVector = scatterData; | |
|
522 | if (dataVector) | |
|
523 | { | |
|
524 | QVector<QCPData>::const_iterator it = lower; | |
|
525 | QVector<QCPData>::const_iterator upperEnd = upper+1; | |
|
526 | dataVector->reserve(dataCount+2); // +2 for possible fill end points | |
|
527 | while (it != upperEnd) | |
|
528 | { | |
|
529 | dataVector->append(*it); | |
|
530 | ++it; | |
|
531 | } | |
|
532 | } | |
|
533 | if (lineData && scatterData) | |
|
534 | *scatterData = *dataVector; | |
|
535 | } | |
|
536 | } | |
|
537 | ||
|
538 | QVector<QCPData>::const_iterator __lowerBoundDico_vect(QVector<QCPData>* vector,double key) | |
|
539 | { | |
|
540 | int DX=vector->size()/2; | |
|
541 | int pos=DX; | |
|
542 | // double test=(*vector)[vector->length()-1].key; | |
|
543 | if(key>((*vector)[vector->length()-1].key)) | |
|
544 | return vector->constEnd(); | |
|
545 | if(key<((*vector)[0].key)) | |
|
546 | return vector->constBegin(); | |
|
547 | while (DX>1) | |
|
548 | { | |
|
549 | DX=DX/2; | |
|
550 | if((*vector)[pos].key > key) | |
|
551 | { | |
|
552 | pos-=DX; | |
|
553 | } | |
|
554 | else | |
|
555 | { | |
|
556 | pos+=DX; | |
|
557 | } | |
|
558 | } | |
|
559 | if((*vector)[pos].key >= key) | |
|
560 | return vector->constBegin()+pos; | |
|
561 | return vector->constBegin()+pos+1; | |
|
562 | } | |
|
563 | ||
|
564 | ||
|
565 | QVector<QCPData>::const_iterator __upperBoundDico_vect(QVector<QCPData>* vector,double key) | |
|
566 | { | |
|
567 | int DX=vector->size()/2; | |
|
568 | int pos=DX; | |
|
569 | if(key>((*vector)[vector->length()-1].key)) | |
|
570 | return vector->constEnd(); | |
|
571 | if(key<((*vector)[0].key)) | |
|
572 | return vector->constBegin(); | |
|
573 | while (DX>1) | |
|
574 | { | |
|
575 | DX=DX/2; | |
|
576 | if((*vector)[pos].key > key) | |
|
577 | { | |
|
578 | pos-=DX; | |
|
579 | } | |
|
580 | else | |
|
581 | { | |
|
582 | pos+=DX; | |
|
583 | } | |
|
584 | } | |
|
585 | return vector->constBegin()+pos+1; | |
|
586 | } | |
|
587 | ||
|
588 | ||
|
589 | void QCPGraphVect::getVisibleDataBounds(QVector<QCPData>::const_iterator &lower, QVector<QCPData>::const_iterator &upper) const | |
|
590 | { | |
|
591 | if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } | |
|
592 | if (mData->isEmpty()) | |
|
593 | { | |
|
594 | lower = mData->constEnd(); | |
|
595 | upper = mData->constEnd(); | |
|
596 | return; | |
|
597 | } | |
|
598 | QVector<QCPData>::const_iterator lbound = __lowerBoundDico_vect(mData,mKeyAxis.data()->range().lower); | |
|
599 | QVector<QCPData>::const_iterator ubound = __upperBoundDico_vect(mData,mKeyAxis.data()->range().upper); | |
|
600 | bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range | |
|
601 | bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range | |
|
602 | ||
|
603 | lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn | |
|
604 | upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn | |
|
605 | } | |
|
606 | ||
|
607 | int QCPGraphVect::countDataInBounds(const QVector<QCPData>::const_iterator &lower, const QVector<QCPData>::const_iterator &upper, int maxCount) const | |
|
608 | { | |
|
609 | if (upper == mData->constEnd() && lower == mData->constEnd()) | |
|
610 | return 0; | |
|
611 | QVector<QCPData>::const_iterator it = lower; | |
|
612 | int count = 1; | |
|
613 | while (it != upper && count < maxCount) | |
|
614 | { | |
|
615 | ++it; | |
|
616 | ++count; | |
|
617 | } | |
|
618 | return count; | |
|
619 | } |
@@ -0,0 +1,38 | |||
|
1 | #ifndef QCUSTOMPLOTVECT_H | |
|
2 | #define QCUSTOMPLOTVECT_H | |
|
3 | ||
|
4 | #include <QWidget> | |
|
5 | #include <qcustomplot.h> | |
|
6 | ||
|
7 | class QCPGraphVect : public QCPGraph | |
|
8 | { | |
|
9 | Q_OBJECT | |
|
10 | public: | |
|
11 | explicit QCPGraphVect(QCPAxis *keyAxis, QCPAxis *valueAxis); | |
|
12 | ~QCPGraphVect(); | |
|
13 | QVector<QCPData> *data() const { return mData; } | |
|
14 | void setData(QVector<QCPData> *data); | |
|
15 | protected: | |
|
16 | QVector<QCPData>* mData; | |
|
17 | virtual void draw(QCPPainter *painter); | |
|
18 | virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface | |
|
19 | virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; | |
|
20 | void getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const; | |
|
21 | void getVisibleDataBounds(QVector<QCPData>::const_iterator &lower, QVector<QCPData>::const_iterator &upper) const; | |
|
22 | int countDataInBounds(const QVector<QCPData>::const_iterator &lower, const QVector<QCPData>::const_iterator &upper, int maxCount) const; | |
|
23 | ||
|
24 | void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const; | |
|
25 | void getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const; | |
|
26 | }; | |
|
27 | ||
|
28 | class QCustomPlotVect : public QCustomPlot | |
|
29 | { | |
|
30 | Q_OBJECT | |
|
31 | public: | |
|
32 | QCustomPlotVect(QWidget *parent = 0); | |
|
33 | ~QCustomPlotVect(); | |
|
34 | QCPGraphVect *addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0); | |
|
35 | protected: | |
|
36 | }; | |
|
37 | ||
|
38 | #endif // QCUSTOMPLOTVECT_H |
@@ -30,9 +30,7 SOURCES += src/main.cpp\ | |||
|
30 | 30 | src/mainwindow.cpp \ |
|
31 | 31 | src/SocExplorerPlot.cpp \ |
|
32 | 32 | src/QCustomPlot/qcustomplot.cpp \ |
|
33 | src/folderview.cpp \ | |
|
34 | 33 | src/toolbarcontainer.cpp \ |
|
35 | src/folderlistwidget.cpp \ | |
|
36 | 34 | src/Core/abstractfileloader.cpp \ |
|
37 | 35 | src/Core/filedownloader.cpp \ |
|
38 | 36 | src/Core/filedownloadertask.cpp \ |
@@ -52,14 +50,13 SOURCES += src/main.cpp\ | |||
|
52 | 50 | src/Core/Widgets/PyWdgt/pythonqtscriptingconsoledandd.cpp \ |
|
53 | 51 | src/QCustomPlot/qcpdocumentobject.cpp \ |
|
54 | 52 | src/Core/Widgets/filebrowser.cpp \ |
|
55 | src/Core/Widgets/filesystemmodel.cpp | |
|
53 | src/Core/Widgets/filesystemmodel.cpp \ | |
|
54 | src/Core/Widgets/qcustomplotvect.cpp | |
|
56 | 55 | |
|
57 | 56 | HEADERS += src/mainwindow.h \ |
|
58 | 57 | src/SocExplorerPlot.h \ |
|
59 | 58 | src/QCustomPlot/qcustomplot.h \ |
|
60 | src/folderview.h \ | |
|
61 | 59 | src/toolbarcontainer.h \ |
|
62 | src/folderlistwidget.h \ | |
|
63 | 60 | src/Core/abstractfileloader.h \ |
|
64 | 61 | src/Core/filedownloader.h \ |
|
65 | 62 | src/Core/filedownloadertask.h \ |
@@ -81,10 +78,10 HEADERS += src/mainwindow.h \ | |||
|
81 | 78 | src/Core/pyqlop.h \ |
|
82 | 79 | src/QCustomPlot/qcpdocumentobject.h \ |
|
83 | 80 | src/Core/Widgets/filebrowser.h \ |
|
84 | src/Core/Widgets/filesystemmodel.h | |
|
81 | src/Core/Widgets/filesystemmodel.h \ | |
|
82 | src/Core/Widgets/qcustomplotvect.h | |
|
85 | 83 | |
|
86 | 84 | FORMS += src/mainwindow.ui \ |
|
87 | src/folderview.ui \ | |
|
88 | 85 | src/Core/Widgets/downloadhistory.ui \ |
|
89 | 86 | src/Core/Widgets/downloadhistoryelement.ui \ |
|
90 | 87 | src/Cassini/cassinidatadownloader.ui \ |
@@ -55,7 +55,7 void CassiniTools::makePlot() | |||
|
55 | 55 | plot->setXaxisTickLabelType(QCPAxis::ltDateTime); |
|
56 | 56 | plot->setXaxisDateTimeFormat("hh:mm:ss.zzz"); |
|
57 | 57 | plot->setContextMenuPolicy(Qt::ActionsContextMenu); |
|
58 |
|
|
|
58 | SocExplorerPlotActions* action=new SocExplorerPlotActions("export view",plot->PID(),_self); | |
|
59 | 59 | plot->addAction(action); |
|
60 | 60 | QObject::connect(action,SIGNAL(triggered()),_self,SLOT(export_view())); |
|
61 | 61 | } |
@@ -104,15 +104,11 void CassiniTools::plot_TAB_File(const Q | |||
|
104 | 104 | plotFile(fileName); |
|
105 | 105 | } |
|
106 | 106 | |
|
107 | void CassiniTools::export_view() | |
|
108 | { | |
|
109 | SocExplorerPlot* plot = QLopPlots::getPlot(m_defaultPlot); | |
|
110 | if(plot==NULL) | |
|
107 | void CassiniTools::export_view(int PID) | |
|
111 | 108 | { |
|
112 | makePlot(); | |
|
113 | plot = QLopPlots::getPlot(m_defaultPlot); | |
|
114 | } | |
|
115 | if(plot) | |
|
109 | SocExplorerPlot* plot = QLopPlots::getPlot(PID); | |
|
110 | if(plot==NULL) | |
|
111 | return; | |
|
116 | 112 | { |
|
117 | 113 | QString fileName = QFileDialog::getSaveFileName(); |
|
118 | 114 | } |
@@ -31,7 +31,7 public: | |||
|
31 | 31 | static void plotFile(const QString &File); |
|
32 | 32 | public slots: |
|
33 | 33 | void plot_TAB_File(const QString& fileName); |
|
34 | void export_view(); | |
|
34 | void export_view(int PID); | |
|
35 | 35 | private slots: |
|
36 | 36 | void dataReady(QLopDataList data); |
|
37 | 37 | }; |
@@ -2,7 +2,6 | |||
|
2 | 2 | #define CASSINITOOLSGUI_H |
|
3 | 3 | |
|
4 | 4 | #include <QWidget> |
|
5 | #include <folderview.h> | |
|
6 | 5 | #include <filebrowser.h> |
|
7 | 6 | |
|
8 | 7 | namespace Ui { |
@@ -79,6 +79,7 int QLopPlots::addPlot() | |||
|
79 | 79 | if(pid!=-1) |
|
80 | 80 | { |
|
81 | 81 | SocExplorerPlot* plot=new SocExplorerPlot(); |
|
82 | plot->setPID(pid); | |
|
82 | 83 | m_plots->insert(pid,plot); |
|
83 | 84 | QDockWidget* dock = new QDockWidget(); |
|
84 | 85 | dock->setWidget(plot); |
@@ -4629,7 +4629,7 void PythonQtWrapper_QCPGraph::setErrorP | |||
|
4629 | 4629 | |
|
4630 | 4630 | void PythonQtWrapper_QCPGraph::setUseFastVectors(QCPGraph* theWrappedObject, bool useFastVectors) |
|
4631 | 4631 | { |
|
4632 | ( theWrappedObject->setUseFastVectors(useFastVectors)); | |
|
4632 | // ( theWrappedObject->setUseFastVectors(useFastVectors)); | |
|
4633 | 4633 | } |
|
4634 | 4634 | |
|
4635 | 4635 | QPointF PythonQtWrapper_QCPGraph::upperFillBasePoint(QCPGraph* theWrappedObject, double upperKey) const |
@@ -4639,7 +4639,7 QPointF PythonQtWrapper_QCPGraph::upper | |||
|
4639 | 4639 | |
|
4640 | 4640 | bool PythonQtWrapper_QCPGraph::useFastVectors(QCPGraph* theWrappedObject) const |
|
4641 | 4641 | { |
|
4642 |
return |
|
|
4642 | return true;//( theWrappedObject->useFastVectors()); | |
|
4643 | 4643 | } |
|
4644 | 4644 | |
|
4645 | 4645 |
This diff has been collapsed as it changes many lines, (507 lines changed) Show them Hide them | |||
@@ -9369,7 +9369,6 void QCustomPlot::setMultiSelectModifier | |||
|
9369 | 9369 | mMultiSelectModifier = modifier; |
|
9370 | 9370 | } |
|
9371 | 9371 | |
|
9372 | ||
|
9373 | 9372 | /*! |
|
9374 | 9373 | Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout |
|
9375 | 9374 | (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect. |
@@ -10685,13 +10684,6 void QCustomPlot::resizeEvent(QResizeEve | |||
|
10685 | 10684 | mPaintBuffer = QPixmap(event->size()); |
|
10686 | 10685 | setViewport(rect()); |
|
10687 | 10686 | replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts |
|
10688 | // resize and repaint the buffer: | |
|
10689 | // QSize pbSize = event->size(); | |
|
10690 | // pbSize *= devicePixelRatio(); | |
|
10691 | // mPaintBuffer = QPixmap(pbSize); | |
|
10692 | // mPaintBuffer.setDevicePixelRatio(devicePixelRatio()); | |
|
10693 | // setViewport(rect()); | |
|
10694 | // replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts | |
|
10695 | 10687 | } |
|
10696 | 10688 | |
|
10697 | 10689 | /*! \internal |
@@ -14510,7 +14502,7 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCP | |||
|
14510 | 14502 | QCPAbstractPlottable(keyAxis, valueAxis) |
|
14511 | 14503 | { |
|
14512 | 14504 | mData = new QCPDataMap; |
|
14513 | mDataVector = new QVector<QCPData>(); | |
|
14505 | ||
|
14514 | 14506 | setPen(QPen(Qt::blue, 0)); |
|
14515 | 14507 | setErrorPen(QPen(Qt::black)); |
|
14516 | 14508 | setBrush(Qt::NoBrush); |
@@ -14523,13 +14515,11 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCP | |||
|
14523 | 14515 | setErrorBarSkipSymbol(true); |
|
14524 | 14516 | setChannelFillGraph(0); |
|
14525 | 14517 | setAdaptiveSampling(true); |
|
14526 | setUseFastVectors(false); | |
|
14527 | 14518 | } |
|
14528 | 14519 | |
|
14529 | 14520 | QCPGraph::~QCPGraph() |
|
14530 | 14521 | { |
|
14531 | 14522 | delete mData; |
|
14532 | delete mDataVector; | |
|
14533 | 14523 | } |
|
14534 | 14524 | |
|
14535 | 14525 | /*! |
@@ -14579,15 +14569,6 void QCPGraph::setData(const QVector<dou | |||
|
14579 | 14569 | } |
|
14580 | 14570 | } |
|
14581 | 14571 | |
|
14582 | void QCPGraph::setData(QVector<QCPData> *data) | |
|
14583 | { | |
|
14584 | if(data!=mDataVector) | |
|
14585 | { | |
|
14586 | delete this->mDataVector; | |
|
14587 | this->mDataVector = data; | |
|
14588 | } | |
|
14589 | } | |
|
14590 | ||
|
14591 | 14572 | /*! |
|
14592 | 14573 | Replaces the current data with the provided points in \a key and \a value pairs. Additionally the |
|
14593 | 14574 | symmetrical value error of the data points are set to the values in \a valueError. |
@@ -14888,11 +14869,6 void QCPGraph::setAdaptiveSampling(bool | |||
|
14888 | 14869 | mAdaptiveSampling = enabled; |
|
14889 | 14870 | } |
|
14890 | 14871 | |
|
14891 | void QCPGraph::setUseFastVectors(bool useFastVectors) | |
|
14892 | { | |
|
14893 | mUseFastVectors=useFastVectors; | |
|
14894 | } | |
|
14895 | ||
|
14896 | 14872 | /*! |
|
14897 | 14873 | Adds the provided data points in \a dataMap to the current data. |
|
14898 | 14874 | |
@@ -15119,7 +15095,9 void QCPGraph::rescaleValueAxis(bool onl | |||
|
15119 | 15095 | void QCPGraph::draw(QCPPainter *painter) |
|
15120 | 15096 | { |
|
15121 | 15097 | if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } |
|
15122 | if (mKeyAxis.data()->range().size() <= 0 || (mData->isEmpty() && mDataVector->isEmpty())) return; | |
|
15098 | int test = mKeyAxis.data()->range().size(); | |
|
15099 | test = mData->count(); | |
|
15100 | if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return; | |
|
15123 | 15101 | if (mLineStyle == lsNone && mScatterStyle.isNone()) return; |
|
15124 | 15102 | |
|
15125 | 15103 | // allocate line and (if necessary) point vectors: |
@@ -15263,9 +15241,6 void QCPGraph::getLinePlotData(QVector<Q | |||
|
15263 | 15241 | if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; } |
|
15264 | 15242 | |
|
15265 | 15243 | QVector<QCPData> lineData; |
|
15266 | if(mUseFastVectors) | |
|
15267 | getPreparedDataVector(&lineData, scatterData); | |
|
15268 | else | |
|
15269 | 15244 | getPreparedData(&lineData, scatterData); |
|
15270 | 15245 | linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill |
|
15271 | 15246 | linePixelData->resize(lineData.size()); |
@@ -15856,9 +15831,7 void QCPGraph::getPreparedData(QVector<Q | |||
|
15856 | 15831 | { |
|
15857 | 15832 | QVector<QCPData> *dataVector = 0; |
|
15858 | 15833 | if (lineData) |
|
15859 | { | |
|
15860 | 15834 | dataVector = lineData; |
|
15861 | } | |
|
15862 | 15835 | else if (scatterData) |
|
15863 | 15836 | dataVector = scatterData; |
|
15864 | 15837 | if (dataVector) |
@@ -15877,190 +15850,6 void QCPGraph::getPreparedData(QVector<Q | |||
|
15877 | 15850 | } |
|
15878 | 15851 | } |
|
15879 | 15852 | |
|
15880 | ||
|
15881 | void QCPGraph::getPreparedDataVector(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const | |
|
15882 | { | |
|
15883 | QCPAxis *keyAxis = mKeyAxis.data(); | |
|
15884 | QCPAxis *valueAxis = mValueAxis.data(); | |
|
15885 | if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } | |
|
15886 | // get visible data range: | |
|
15887 | QVector<QCPData>::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point | |
|
15888 | getVisibleDataBoundsVector(lower, upper); | |
|
15889 | if (lower == mDataVector->constEnd() || upper == mDataVector->constEnd()) | |
|
15890 | return; | |
|
15891 | ||
|
15892 | // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling: | |
|
15893 | int maxCount = std::numeric_limits<int>::max(); | |
|
15894 | if (mAdaptiveSampling) | |
|
15895 | { | |
|
15896 | int keyPixelSpan = qAbs(keyAxis->coordToPixel((*lower).key)-keyAxis->coordToPixel((*upper).key)); | |
|
15897 | maxCount = 2*keyPixelSpan+2; | |
|
15898 | } | |
|
15899 | int dataCount = countDataInBoundsVector(lower, upper, maxCount); | |
|
15900 | ||
|
15901 | if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average | |
|
15902 | { | |
|
15903 | if (lineData) | |
|
15904 | { | |
|
15905 | QVector<QCPData>::const_iterator it = lower; | |
|
15906 | QVector<QCPData>::const_iterator upperEnd = upper+1; | |
|
15907 | double minValue = (*it).value; | |
|
15908 | double maxValue = (*it).value; | |
|
15909 | QVector<QCPData>::const_iterator currentIntervalFirstPoint = it; | |
|
15910 | int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction | |
|
15911 | int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey | |
|
15912 | double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*lower).key)+reversedRound)); | |
|
15913 | double lastIntervalEndKey = currentIntervalStartKey; | |
|
15914 | double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates | |
|
15915 | bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) | |
|
15916 | int intervalDataCount = 1; | |
|
15917 | ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect | |
|
15918 | while (it != upperEnd) | |
|
15919 | { | |
|
15920 | if ((*it).key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary | |
|
15921 | { | |
|
15922 | if ((*it).value < minValue) | |
|
15923 | minValue = (*it).value; | |
|
15924 | else if ((*it).value > maxValue) | |
|
15925 | maxValue = (*it).value; | |
|
15926 | ++intervalDataCount; | |
|
15927 | } else // new pixel interval started | |
|
15928 | { | |
|
15929 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster | |
|
15930 | { | |
|
15931 | if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point | |
|
15932 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, (*currentIntervalFirstPoint).value)); | |
|
15933 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); | |
|
15934 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); | |
|
15935 | if ((*it).key > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point | |
|
15936 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (*(it-1)).value)); | |
|
15937 | } else | |
|
15938 | lineData->append(QCPData((*currentIntervalFirstPoint).key, (*currentIntervalFirstPoint).value)); | |
|
15939 | lastIntervalEndKey = (*(it-1)).key; | |
|
15940 | minValue = (*it).value; | |
|
15941 | maxValue = (*it).value; | |
|
15942 | currentIntervalFirstPoint = it; | |
|
15943 | currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*it).key)+reversedRound)); | |
|
15944 | if (keyEpsilonVariable) | |
|
15945 | keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); | |
|
15946 | intervalDataCount = 1; | |
|
15947 | } | |
|
15948 | ++it; | |
|
15949 | } | |
|
15950 | // handle last interval: | |
|
15951 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster | |
|
15952 | { | |
|
15953 | if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point | |
|
15954 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, (*currentIntervalFirstPoint).value)); | |
|
15955 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); | |
|
15956 | lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); | |
|
15957 | } else | |
|
15958 | lineData->append(QCPData((*currentIntervalFirstPoint).key, (*currentIntervalFirstPoint).value)); | |
|
15959 | } | |
|
15960 | ||
|
15961 | if (scatterData) | |
|
15962 | { | |
|
15963 | double valueMaxRange = valueAxis->range().upper; | |
|
15964 | double valueMinRange = valueAxis->range().lower; | |
|
15965 | QVector<QCPData>::const_iterator it = lower; | |
|
15966 | QVector<QCPData>::const_iterator upperEnd = upper+1; | |
|
15967 | double minValue = (*it).value; | |
|
15968 | double maxValue = (*it).value; | |
|
15969 | QVector<QCPData>::const_iterator minValueIt = it; | |
|
15970 | QVector<QCPData>::const_iterator maxValueIt = it; | |
|
15971 | QVector<QCPData>::const_iterator currentIntervalStart = it; | |
|
15972 | int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction | |
|
15973 | int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey | |
|
15974 | double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*lower).key)+reversedRound)); | |
|
15975 | double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates | |
|
15976 | bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) | |
|
15977 | int intervalDataCount = 1; | |
|
15978 | ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect | |
|
15979 | while (it != upperEnd) | |
|
15980 | { | |
|
15981 | if ((*it).key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary | |
|
15982 | { | |
|
15983 | if ((*it).value < minValue && (*it).value > valueMinRange && (*it).value < valueMaxRange) | |
|
15984 | { | |
|
15985 | minValue = (*it).value; | |
|
15986 | minValueIt = it; | |
|
15987 | } else if ((*it).value > maxValue && (*it).value > valueMinRange && (*it).value < valueMaxRange) | |
|
15988 | { | |
|
15989 | maxValue = (*it).value; | |
|
15990 | maxValueIt = it; | |
|
15991 | } | |
|
15992 | ++intervalDataCount; | |
|
15993 | } else // new pixel started | |
|
15994 | { | |
|
15995 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them | |
|
15996 | { | |
|
15997 | // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): | |
|
15998 | double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); | |
|
15999 | int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average | |
|
16000 | QVector<QCPData>::const_iterator intervalIt = currentIntervalStart; | |
|
16001 | int c = 0; | |
|
16002 | while (intervalIt != it) | |
|
16003 | { | |
|
16004 | if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && (*intervalIt).value > valueMinRange && (*intervalIt).value < valueMaxRange) | |
|
16005 | scatterData->append((*intervalIt)); | |
|
16006 | ++c; | |
|
16007 | ++intervalIt; | |
|
16008 | } | |
|
16009 | } else if ((*currentIntervalStart).value > valueMinRange && (*currentIntervalStart).value < valueMaxRange) | |
|
16010 | scatterData->append((*currentIntervalStart)); | |
|
16011 | minValue = (*it).value; | |
|
16012 | maxValue = (*it).value; | |
|
16013 | currentIntervalStart = it; | |
|
16014 | currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*it).key)+reversedRound)); | |
|
16015 | if (keyEpsilonVariable) | |
|
16016 | keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); | |
|
16017 | intervalDataCount = 1; | |
|
16018 | } | |
|
16019 | ++it; | |
|
16020 | } | |
|
16021 | // handle last interval: | |
|
16022 | if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them | |
|
16023 | { | |
|
16024 | // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): | |
|
16025 | double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); | |
|
16026 | int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average | |
|
16027 | QVector<QCPData>::const_iterator intervalIt = currentIntervalStart; | |
|
16028 | int c = 0; | |
|
16029 | while (intervalIt != it) | |
|
16030 | { | |
|
16031 | if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && (*intervalIt).value > valueMinRange && (*intervalIt).value < valueMaxRange) | |
|
16032 | scatterData->append((*intervalIt)); | |
|
16033 | ++c; | |
|
16034 | ++intervalIt; | |
|
16035 | } | |
|
16036 | } else if ((*currentIntervalStart).value > valueMinRange && (*currentIntervalStart).value < valueMaxRange) | |
|
16037 | scatterData->append(*currentIntervalStart); | |
|
16038 | } | |
|
16039 | } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters | |
|
16040 | { | |
|
16041 | QVector<QCPData> *dataVector = 0; | |
|
16042 | if (lineData) | |
|
16043 | { | |
|
16044 | dataVector = lineData; | |
|
16045 | } | |
|
16046 | else if (scatterData) | |
|
16047 | dataVector = scatterData; | |
|
16048 | if (dataVector) | |
|
16049 | { | |
|
16050 | QVector<QCPData>::const_iterator it = lower; | |
|
16051 | QVector<QCPData>::const_iterator upperEnd = upper+1; | |
|
16052 | dataVector->reserve(dataCount+2); // +2 for possible fill end points | |
|
16053 | while (it != upperEnd) | |
|
16054 | { | |
|
16055 | dataVector->append(*it); | |
|
16056 | ++it; | |
|
16057 | } | |
|
16058 | } | |
|
16059 | if (lineData && scatterData) | |
|
16060 | *scatterData = *dataVector; | |
|
16061 | } | |
|
16062 | } | |
|
16063 | ||
|
16064 | 15853 | /*! \internal |
|
16065 | 15854 | |
|
16066 | 15855 | called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data |
@@ -16199,77 +15988,6 void QCPGraph::getVisibleDataBounds(QCPD | |||
|
16199 | 15988 | upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn |
|
16200 | 15989 | } |
|
16201 | 15990 | |
|
16202 | ||
|
16203 | ||
|
16204 | ||
|
16205 | QVector<QCPData>::const_iterator __lowerBoundDico(QVector<QCPData>* vector,double key) | |
|
16206 | { | |
|
16207 | int DX=vector->size()/2; | |
|
16208 | int pos=DX; | |
|
16209 | double test=(*vector)[vector->length()-1].key; | |
|
16210 | if(key>((*vector)[vector->length()-1].key)) | |
|
16211 | return vector->constEnd(); | |
|
16212 | if(key<((*vector)[0].key)) | |
|
16213 | return vector->constBegin(); | |
|
16214 | while (DX>1) | |
|
16215 | { | |
|
16216 | DX=DX/2; | |
|
16217 | if((*vector)[pos].key > key) | |
|
16218 | { | |
|
16219 | pos-=DX; | |
|
16220 | } | |
|
16221 | else | |
|
16222 | { | |
|
16223 | pos+=DX; | |
|
16224 | } | |
|
16225 | } | |
|
16226 | if((*vector)[pos].key >= key) | |
|
16227 | return vector->constBegin()+pos; | |
|
16228 | return vector->constBegin()+pos+1; | |
|
16229 | } | |
|
16230 | ||
|
16231 | ||
|
16232 | QVector<QCPData>::const_iterator __upperBoundDico(QVector<QCPData>* vector,double key) | |
|
16233 | { | |
|
16234 | int DX=vector->size()/2; | |
|
16235 | int pos=DX; | |
|
16236 | if(key>((*vector)[vector->length()-1].key)) | |
|
16237 | return vector->constEnd(); | |
|
16238 | if(key<((*vector)[0].key)) | |
|
16239 | return vector->constBegin(); | |
|
16240 | while (DX>1) | |
|
16241 | { | |
|
16242 | DX=DX/2; | |
|
16243 | if((*vector)[pos].key > key) | |
|
16244 | { | |
|
16245 | pos-=DX; | |
|
16246 | } | |
|
16247 | else | |
|
16248 | { | |
|
16249 | pos+=DX; | |
|
16250 | } | |
|
16251 | } | |
|
16252 | return vector->constBegin()+pos+1; | |
|
16253 | } | |
|
16254 | ||
|
16255 | void QCPGraph::getVisibleDataBoundsVector(QVector<QCPData>::const_iterator &lower, QVector<QCPData>::const_iterator &upper) const | |
|
16256 | { | |
|
16257 | if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } | |
|
16258 | if (mDataVector->isEmpty()) | |
|
16259 | { | |
|
16260 | lower = mDataVector->constEnd(); | |
|
16261 | upper = mDataVector->constEnd(); | |
|
16262 | return; | |
|
16263 | } | |
|
16264 | QVector<QCPData>::const_iterator lbound = __lowerBoundDico(mDataVector,mKeyAxis.data()->range().lower); | |
|
16265 | QVector<QCPData>::const_iterator ubound = __upperBoundDico(mDataVector,mKeyAxis.data()->range().upper); | |
|
16266 | bool lowoutlier = lbound != mDataVector->constBegin(); // indicates whether there exist points below axis range | |
|
16267 | bool highoutlier = ubound != mDataVector->constEnd(); // indicates whether there exist points above axis range | |
|
16268 | ||
|
16269 | lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn | |
|
16270 | upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn | |
|
16271 | } | |
|
16272 | ||
|
16273 | 15991 | /*! \internal |
|
16274 | 15992 | |
|
16275 | 15993 | Counts the number of data points between \a lower and \a upper (including them), up to a maximum |
@@ -16294,20 +16012,6 int QCPGraph::countDataInBounds(const QC | |||
|
16294 | 16012 | return count; |
|
16295 | 16013 | } |
|
16296 | 16014 | |
|
16297 | int QCPGraph::countDataInBoundsVector(const QVector<QCPData>::const_iterator &lower, const QVector<QCPData>::const_iterator &upper, int maxCount) const | |
|
16298 | { | |
|
16299 | if (upper == mDataVector->constEnd() && lower == mDataVector->constEnd()) | |
|
16300 | return 0; | |
|
16301 | QVector<QCPData>::const_iterator it = lower; | |
|
16302 | int count = 1; | |
|
16303 | while (it != upper && count < maxCount) | |
|
16304 | { | |
|
16305 | ++it; | |
|
16306 | ++count; | |
|
16307 | } | |
|
16308 | return count; | |
|
16309 | } | |
|
16310 | ||
|
16311 | 16015 | /*! \internal |
|
16312 | 16016 | |
|
16313 | 16017 | The line data vector generated by e.g. getLinePlotData contains only the line that connects the |
@@ -16793,8 +16497,6 QCPRange QCPGraph::getKeyRange(bool &fou | |||
|
16793 | 16497 | { |
|
16794 | 16498 | // just call the specialized version which takes an additional argument whether error bars |
|
16795 | 16499 | // should also be taken into consideration for range calculation. We set this to true here. |
|
16796 | if(mUseFastVectors) | |
|
16797 | return getKeyRangeVector(foundRange, inSignDomain, true); | |
|
16798 | 16500 | return getKeyRange(foundRange, inSignDomain, true); |
|
16799 | 16501 | } |
|
16800 | 16502 | |
@@ -16803,8 +16505,6 QCPRange QCPGraph::getValueRange(bool &f | |||
|
16803 | 16505 | { |
|
16804 | 16506 | // just call the specialized version which takes an additional argument whether error bars |
|
16805 | 16507 | // should also be taken into consideration for range calculation. We set this to true here. |
|
16806 | if(mUseFastVectors) | |
|
16807 | return getValueRangeVector(foundRange, inSignDomain, true); | |
|
16808 | 16508 | return getValueRange(foundRange, inSignDomain, true); |
|
16809 | 16509 | } |
|
16810 | 16510 | |
@@ -16914,106 +16614,6 QCPRange QCPGraph::getKeyRange(bool &fou | |||
|
16914 | 16614 | return range; |
|
16915 | 16615 | } |
|
16916 | 16616 | |
|
16917 | QCPRange QCPGraph::getKeyRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const | |
|
16918 | { | |
|
16919 | QCPRange range; | |
|
16920 | bool haveLower = false; | |
|
16921 | bool haveUpper = false; | |
|
16922 | ||
|
16923 | double current, currentErrorMinus, currentErrorPlus; | |
|
16924 | ||
|
16925 | if (inSignDomain == sdBoth) // range may be anywhere | |
|
16926 | { | |
|
16927 | QVector<QCPData>::const_iterator it = mDataVector->constBegin(); | |
|
16928 | while (it != mDataVector->constEnd()) | |
|
16929 | { | |
|
16930 | current = (*it).key; | |
|
16931 | currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); | |
|
16932 | currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); | |
|
16933 | if (current-currentErrorMinus < range.lower || !haveLower) | |
|
16934 | { | |
|
16935 | range.lower = current-currentErrorMinus; | |
|
16936 | haveLower = true; | |
|
16937 | } | |
|
16938 | if (current+currentErrorPlus > range.upper || !haveUpper) | |
|
16939 | { | |
|
16940 | range.upper = current+currentErrorPlus; | |
|
16941 | haveUpper = true; | |
|
16942 | } | |
|
16943 | ++it; | |
|
16944 | } | |
|
16945 | } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain | |
|
16946 | { | |
|
16947 | QVector<QCPData>::const_iterator it = mDataVector->constBegin(); | |
|
16948 | while (it != mDataVector->constEnd()) | |
|
16949 | { | |
|
16950 | current = (*it).key; | |
|
16951 | currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); | |
|
16952 | currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); | |
|
16953 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) | |
|
16954 | { | |
|
16955 | range.lower = current-currentErrorMinus; | |
|
16956 | haveLower = true; | |
|
16957 | } | |
|
16958 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) | |
|
16959 | { | |
|
16960 | range.upper = current+currentErrorPlus; | |
|
16961 | haveUpper = true; | |
|
16962 | } | |
|
16963 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. | |
|
16964 | { | |
|
16965 | if ((current < range.lower || !haveLower) && current < 0) | |
|
16966 | { | |
|
16967 | range.lower = current; | |
|
16968 | haveLower = true; | |
|
16969 | } | |
|
16970 | if ((current > range.upper || !haveUpper) && current < 0) | |
|
16971 | { | |
|
16972 | range.upper = current; | |
|
16973 | haveUpper = true; | |
|
16974 | } | |
|
16975 | } | |
|
16976 | ++it; | |
|
16977 | } | |
|
16978 | } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain | |
|
16979 | { | |
|
16980 | QVector<QCPData>::const_iterator it = mDataVector->constBegin(); | |
|
16981 | while (it != mDataVector->constEnd()) | |
|
16982 | { | |
|
16983 | current = (*it).key; | |
|
16984 | currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); | |
|
16985 | currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); | |
|
16986 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) | |
|
16987 | { | |
|
16988 | range.lower = current-currentErrorMinus; | |
|
16989 | haveLower = true; | |
|
16990 | } | |
|
16991 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) | |
|
16992 | { | |
|
16993 | range.upper = current+currentErrorPlus; | |
|
16994 | haveUpper = true; | |
|
16995 | } | |
|
16996 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. | |
|
16997 | { | |
|
16998 | if ((current < range.lower || !haveLower) && current > 0) | |
|
16999 | { | |
|
17000 | range.lower = current; | |
|
17001 | haveLower = true; | |
|
17002 | } | |
|
17003 | if ((current > range.upper || !haveUpper) && current > 0) | |
|
17004 | { | |
|
17005 | range.upper = current; | |
|
17006 | haveUpper = true; | |
|
17007 | } | |
|
17008 | } | |
|
17009 | ++it; | |
|
17010 | } | |
|
17011 | } | |
|
17012 | ||
|
17013 | foundRange = haveLower && haveUpper; | |
|
17014 | return range; | |
|
17015 | } | |
|
17016 | ||
|
17017 | 16617 | /*! \overload |
|
17018 | 16618 | |
|
17019 | 16619 | Allows to specify whether the error bars should be included in the range calculation. |
@@ -17120,105 +16720,6 QCPRange QCPGraph::getValueRange(bool &f | |||
|
17120 | 16720 | return range; |
|
17121 | 16721 | } |
|
17122 | 16722 | |
|
17123 | QCPRange QCPGraph::getValueRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const | |
|
17124 | { | |
|
17125 | QCPRange range; | |
|
17126 | bool haveLower = false; | |
|
17127 | bool haveUpper = false; | |
|
17128 | ||
|
17129 | double current, currentErrorMinus, currentErrorPlus; | |
|
17130 | ||
|
17131 | if (inSignDomain == sdBoth) // range may be anywhere | |
|
17132 | { | |
|
17133 | QVector<QCPData>::const_iterator it = mDataVector->constBegin(); | |
|
17134 | while (it != mDataVector->constEnd()) | |
|
17135 | { | |
|
17136 | current = (*it).value; | |
|
17137 | currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); | |
|
17138 | currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); | |
|
17139 | if (current-currentErrorMinus < range.lower || !haveLower) | |
|
17140 | { | |
|
17141 | range.lower = current-currentErrorMinus; | |
|
17142 | haveLower = true; | |
|
17143 | } | |
|
17144 | if (current+currentErrorPlus > range.upper || !haveUpper) | |
|
17145 | { | |
|
17146 | range.upper = current+currentErrorPlus; | |
|
17147 | haveUpper = true; | |
|
17148 | } | |
|
17149 | ++it; | |
|
17150 | } | |
|
17151 | } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain | |
|
17152 | { | |
|
17153 | QVector<QCPData>::const_iterator it = mDataVector->constBegin(); | |
|
17154 | while (it != mDataVector->constEnd()) | |
|
17155 | { | |
|
17156 | current = (*it).value; | |
|
17157 | currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); | |
|
17158 | currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); | |
|
17159 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) | |
|
17160 | { | |
|
17161 | range.lower = current-currentErrorMinus; | |
|
17162 | haveLower = true; | |
|
17163 | } | |
|
17164 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) | |
|
17165 | { | |
|
17166 | range.upper = current+currentErrorPlus; | |
|
17167 | haveUpper = true; | |
|
17168 | } | |
|
17169 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. | |
|
17170 | { | |
|
17171 | if ((current < range.lower || !haveLower) && current < 0) | |
|
17172 | { | |
|
17173 | range.lower = current; | |
|
17174 | haveLower = true; | |
|
17175 | } | |
|
17176 | if ((current > range.upper || !haveUpper) && current < 0) | |
|
17177 | { | |
|
17178 | range.upper = current; | |
|
17179 | haveUpper = true; | |
|
17180 | } | |
|
17181 | } | |
|
17182 | ++it; | |
|
17183 | } | |
|
17184 | } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain | |
|
17185 | { | |
|
17186 | QVector<QCPData>::const_iterator it = mDataVector->constBegin(); | |
|
17187 | while (it != mDataVector->constEnd()) | |
|
17188 | { | |
|
17189 | current = (*it).value; | |
|
17190 | currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); | |
|
17191 | currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); | |
|
17192 | if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) | |
|
17193 | { | |
|
17194 | range.lower = current-currentErrorMinus; | |
|
17195 | haveLower = true; | |
|
17196 | } | |
|
17197 | if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) | |
|
17198 | { | |
|
17199 | range.upper = current+currentErrorPlus; | |
|
17200 | haveUpper = true; | |
|
17201 | } | |
|
17202 | if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. | |
|
17203 | { | |
|
17204 | if ((current < range.lower || !haveLower) && current > 0) | |
|
17205 | { | |
|
17206 | range.lower = current; | |
|
17207 | haveLower = true; | |
|
17208 | } | |
|
17209 | if ((current > range.upper || !haveUpper) && current > 0) | |
|
17210 | { | |
|
17211 | range.upper = current; | |
|
17212 | haveUpper = true; | |
|
17213 | } | |
|
17214 | } | |
|
17215 | ++it; | |
|
17216 | } | |
|
17217 | } | |
|
17218 | ||
|
17219 | foundRange = haveLower && haveUpper; | |
|
17220 | return range; | |
|
17221 | } | |
|
17222 | 16723 | |
|
17223 | 16724 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
|
17224 | 16725 | //////////////////// QCPCurveData |
@@ -1862,7 +1862,6 protected: | |||
|
1862 | 1862 | QCPLayer *mCurrentLayer; |
|
1863 | 1863 | QCP::PlottingHints mPlottingHints; |
|
1864 | 1864 | Qt::KeyboardModifier mMultiSelectModifier; |
|
1865 | bool mUseFastVectors; | |
|
1866 | 1865 | |
|
1867 | 1866 | // non-property members: |
|
1868 | 1867 | QPixmap mPaintBuffer; |
@@ -2539,12 +2538,10 public: | |||
|
2539 | 2538 | bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; } |
|
2540 | 2539 | QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); } |
|
2541 | 2540 | bool adaptiveSampling() const { return mAdaptiveSampling; } |
|
2542 | bool useFastVectors() const { return mUseFastVectors; } | |
|
2543 | 2541 | |
|
2544 | 2542 | // setters: |
|
2545 | 2543 | void setData(QCPDataMap *data, bool copy=false); |
|
2546 | 2544 | void setData(const QVector<double> &key, const QVector<double> &value); |
|
2547 | void setData(QVector<QCPData> *data); | |
|
2548 | 2545 | void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError); |
|
2549 | 2546 | void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus); |
|
2550 | 2547 | void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError); |
@@ -2559,7 +2556,6 public: | |||
|
2559 | 2556 | void setErrorBarSkipSymbol(bool enabled); |
|
2560 | 2557 | void setChannelFillGraph(QCPGraph *targetGraph); |
|
2561 | 2558 | void setAdaptiveSampling(bool enabled); |
|
2562 | void setUseFastVectors(bool useFastVectors); | |
|
2563 | 2559 | |
|
2564 | 2560 | // non-property methods: |
|
2565 | 2561 | void addData(const QCPDataMap &dataMap); |
@@ -2584,7 +2580,6 public: | |||
|
2584 | 2580 | protected: |
|
2585 | 2581 | // property members: |
|
2586 | 2582 | QCPDataMap *mData; |
|
2587 | QVector<QCPData>* mDataVector; | |
|
2588 | 2583 | QPen mErrorPen; |
|
2589 | 2584 | LineStyle mLineStyle; |
|
2590 | 2585 | QCPScatterStyle mScatterStyle; |
@@ -2593,7 +2588,6 protected: | |||
|
2593 | 2588 | bool mErrorBarSkipSymbol; |
|
2594 | 2589 | QPointer<QCPGraph> mChannelFillGraph; |
|
2595 | 2590 | bool mAdaptiveSampling; |
|
2596 | bool mUseFastVectors; | |
|
2597 | 2591 | |
|
2598 | 2592 | // reimplemented virtual methods: |
|
2599 | 2593 | virtual void draw(QCPPainter *painter); |
@@ -2601,9 +2595,8 protected: | |||
|
2601 | 2595 | virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; |
|
2602 | 2596 | virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; |
|
2603 | 2597 | virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface |
|
2604 | virtual QCPRange getKeyRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface | |
|
2605 | 2598 | virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface |
|
2606 | virtual QCPRange getValueRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; | |
|
2599 | ||
|
2607 | 2600 |
|
|
2608 | 2601 | virtual void drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const; |
|
2609 | 2602 | virtual void drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const; |
@@ -2612,7 +2605,6 protected: | |||
|
2612 | 2605 | |
|
2613 | 2606 | // non-virtual methods: |
|
2614 | 2607 | void getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const; |
|
2615 | void getPreparedDataVector(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const; | |
|
2616 | 2608 | void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const; |
|
2617 | 2609 | void getScatterPlotData(QVector<QCPData> *scatterData) const; |
|
2618 | 2610 | void getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const; |
@@ -2622,9 +2614,7 protected: | |||
|
2622 | 2614 | void getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const; |
|
2623 | 2615 | void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const; |
|
2624 | 2616 | void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const; |
|
2625 | void getVisibleDataBoundsVector(QVector<QCPData>::const_iterator &lower, QVector<QCPData>::const_iterator &upper) const; | |
|
2626 | 2617 | int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const; |
|
2627 | int countDataInBoundsVector(const QVector<QCPData>::const_iterator &lower, const QVector<QCPData>::const_iterator &upper, int maxCount) const; | |
|
2628 | 2618 | void addFillBasePoints(QVector<QPointF> *lineData) const; |
|
2629 | 2619 | void removeFillBasePoints(QVector<QPointF> *lineData) const; |
|
2630 | 2620 | QPointF lowerFillBasePoint(double lowerKey) const; |
@@ -28,7 +28,7 | |||
|
28 | 28 | SocExplorerPlot::SocExplorerPlot(QWidget *parent) : |
|
29 | 29 | QWidget(parent), mRubberBand(new QRubberBand(QRubberBand::Rectangle, this)) |
|
30 | 30 | { |
|
31 | this->m_plot = new QCustomPlot(this); | |
|
31 | this->m_plot = new QCustomPlotVect(this); | |
|
32 | 32 | this->m_plot->setInteractions(QCP::iRangeDrag | QCP::iSelectAxes | |
|
33 | 33 | QCP::iSelectLegend | QCP::iSelectPlottables); |
|
34 | 34 | this->m_plot->axisRect()->setRangeDrag(Qt::Horizontal|Qt::Vertical); |
@@ -85,6 +85,18 void SocExplorerPlot::exportToPDF(const | |||
|
85 | 85 | qcpPainter.end(); |
|
86 | 86 | } |
|
87 | 87 | |
|
88 | void SocExplorerPlot::addAction(SocExplorerPlotActions *action) | |
|
89 | { | |
|
90 | this->m_actions.append(action); | |
|
91 | QWidget::addAction((QAction*)action); | |
|
92 | } | |
|
93 | ||
|
94 | QVector<QCPData> *SocExplorerPlot::getVisibleData(int graphIndex) | |
|
95 | { | |
|
96 | QVector<QCPData> *wholeData=((QCPGraphVect*)m_plot->graph(graphIndex))->data(); | |
|
97 | // m_plot->xAxis-> | |
|
98 | } | |
|
99 | ||
|
88 | 100 | void SocExplorerPlot::setTitle(QString title) |
|
89 | 101 | { |
|
90 | 102 | Q_UNUSED(title) |
@@ -144,7 +156,8 void SocExplorerPlot::setAdaptativeSampl | |||
|
144 | 156 | |
|
145 | 157 | void SocExplorerPlot::setUseFastVector(int graphIndex, bool enable) |
|
146 | 158 | { |
|
147 | this->m_plot->graph(graphIndex)->setUseFastVectors(enable); | |
|
159 | // TODO deprecated | |
|
160 | // this->m_plot->graph(graphIndex)->setUseFastVectors(enable); | |
|
148 | 161 | } |
|
149 | 162 | |
|
150 | 163 | int SocExplorerPlot::addGraph() |
@@ -158,6 +171,11 bool SocExplorerPlot::removeGraph(int gr | |||
|
158 | 171 | return this->m_plot->removeGraph(graphIndex); |
|
159 | 172 | } |
|
160 | 173 | |
|
174 | int SocExplorerPlot::graphCount() | |
|
175 | { | |
|
176 | return m_plot->graphCount(); | |
|
177 | } | |
|
178 | ||
|
161 | 179 | void SocExplorerPlot::removeAllGraphs() |
|
162 | 180 | { |
|
163 | 181 | int graphCount=this->m_plot->graphCount(); |
@@ -226,7 +244,7 void SocExplorerPlot::setGraphData(int g | |||
|
226 | 244 | { |
|
227 | 245 | if((graphIndex<this->m_plot->graphCount()))// && (x.at(0).type()==QVariant::Double)) |
|
228 | 246 | { |
|
229 | this->m_plot->graph(graphIndex)->setData(data); | |
|
247 | ((QCPGraphVect*)this->m_plot->graph(graphIndex))->setData(data); | |
|
230 | 248 | } |
|
231 | 249 | if(replot) |
|
232 | 250 | this->m_plot->replot(); |
@@ -25,9 +25,37 | |||
|
25 | 25 | #include <QWidget> |
|
26 | 26 | #include <QGridLayout> |
|
27 | 27 | #include <qcustomplot.h> |
|
28 | #include <qcustomplotvect.h> | |
|
28 | 29 | #include <QRubberBand> |
|
29 | 30 | #include <QPoint> |
|
30 | 31 | |
|
32 | class SocExplorerPlotActions : public QAction | |
|
33 | { | |
|
34 | Q_OBJECT | |
|
35 | public: | |
|
36 | SocExplorerPlotActions(const QString &text,int PID,QObject* parent=0) | |
|
37 | :QAction(text,parent) | |
|
38 | { | |
|
39 | setPID(PID); | |
|
40 | connect(this,SIGNAL(triggered()),this,SLOT(trigger())); | |
|
41 | } | |
|
42 | SocExplorerPlotActions(const QIcon &icon, const QString &text,int PID, QObject* parent) | |
|
43 | :QAction(icon,text,parent) | |
|
44 | { | |
|
45 | setPID(PID); | |
|
46 | connect(this,SIGNAL(triggered()),this,SLOT(trigger())); | |
|
47 | } | |
|
48 | ~SocExplorerPlotActions(){} | |
|
49 | void setPID(int PID){this->m_PID=PID;} | |
|
50 | int PID(){return m_PID;} | |
|
51 | private slots: | |
|
52 | void trigger(){emit triggered(m_PID);} | |
|
53 | signals: | |
|
54 | void triggered(int PID); | |
|
55 | private: | |
|
56 | int m_PID; | |
|
57 | }; | |
|
58 | ||
|
31 | 59 | class SocExplorerPlot : public QWidget |
|
32 | 60 | { |
|
33 | 61 | Q_OBJECT |
@@ -46,6 +74,7 public: | |||
|
46 | 74 | void setUseFastVector(int graphIndex,bool enable); |
|
47 | 75 | int addGraph(); |
|
48 | 76 | bool removeGraph(int graphIndex); |
|
77 | int graphCount(); | |
|
49 | 78 | void removeAllGraphs(); |
|
50 | 79 | void setGraphName(int graphIndex,QString name); |
|
51 | 80 | void setGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y); |
@@ -63,6 +92,10 public: | |||
|
63 | 92 | void replot(); |
|
64 | 93 | void exportToSVG(const QString& fileName); |
|
65 | 94 | void exportToPDF(const QString& fileName); |
|
95 | void addAction(SocExplorerPlotActions* action); | |
|
96 | int PID(){return m_PID;} | |
|
97 | void setPID(int PID){m_PID = PID;} | |
|
98 | QVector<QCPData>* getVisibleData(int graphIndex); | |
|
66 | 99 | signals: |
|
67 | 100 | void titleChanged(const QString& newTitle); |
|
68 | 101 | public slots: |
@@ -78,7 +111,7 protected: | |||
|
78 | 111 | private: |
|
79 | 112 | void zoom(double factor, int center, Qt::Orientation orientation); |
|
80 | 113 | void move(double factor, Qt::Orientation orientation); |
|
81 | QCustomPlot* m_plot; | |
|
114 | QCustomPlotVect* m_plot; | |
|
82 | 115 | QGridLayout* m_mainlayout; |
|
83 | 116 | bool ctrl_hold; |
|
84 | 117 | bool shift_hold; |
@@ -89,6 +122,8 private: | |||
|
89 | 122 | bool mZoomMode; |
|
90 | 123 | QRubberBand * mRubberBand; |
|
91 | 124 | QPoint mOrigin; |
|
125 | QList<SocExplorerPlotActions*> m_actions; | |
|
126 | int m_PID; | |
|
92 | 127 | }; |
|
93 | 128 | |
|
94 | 129 | #endif // SOCEXPLORERPLOT_H |
@@ -78,15 +78,15 MainWindow::~MainWindow() | |||
|
78 | 78 | delete ui; |
|
79 | 79 | } |
|
80 | 80 | |
|
81 | QString MainWindow::getFilePath(const QString &name) | |
|
82 | { | |
|
83 | // for(int i=0;i<this->folderViews.count();i++) | |
|
81 | //QString MainWindow::getFilePath(const QString &name) | |
|
84 | 82 |
// |
|
85 | // if(folderViews.at(i)->isDraging(name)) | |
|
86 | // return folderViews.at(i)->currentFolder(); | |
|
83 | //// for(int i=0;i<this->folderViews.count();i++) | |
|
84 | //// { | |
|
85 | //// if(folderViews.at(i)->isDraging(name)) | |
|
86 | //// return folderViews.at(i)->currentFolder(); | |
|
87 | //// } | |
|
88 | // return ""; | |
|
87 | 89 |
// |
|
88 | return ""; | |
|
89 | } | |
|
90 | 90 | |
|
91 | 91 | |
|
92 | 92 |
@@ -30,7 +30,6 | |||
|
30 | 30 | #include <QListWidgetItem> |
|
31 | 31 | #include <QVBoxLayout> |
|
32 | 32 | #include <QWidget> |
|
33 | #include "folderview.h" | |
|
34 | 33 | #include "abstractfileloader.h" |
|
35 | 34 | #include "cassiniindexfileviewer.h" |
|
36 | 35 | #include <downloadhistory.h> |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now