00001 #include <QtGui>
00002 #include <QtOpenGL>
00003
00004 #include <math.h>
00005 #include <iostream>
00006
00007
00008 #include "renderingview.h"
00009 #include "ui_mainwindow.h"
00010
00011 RenderingView::RenderingView(Ui::MainWindow *ui, QWidget *parent)
00012
00013 : QWidget(parent)
00014 {
00015 this->ui = ui;
00016 flowData = 0;
00017
00018 channelVectorLength = -1;
00019
00020 colorCodingNeedsUpdate = true;
00021 arrowPlotNeedsUpdate = true;
00022 streamlinesNeedsUpdate = true;
00023 }
00024
00025 RenderingView::~RenderingView()
00026 {
00027
00028 }
00029
00030 void RenderingView::setDataset(FlowData* dataset)
00031 {
00032 flowData = dataset;
00033
00034 channelVectorLength = -1;
00035
00036 updateDerivedChannels();
00037
00038 update();
00039 }
00040
00041 void RenderingView::updateDerivedChannels()
00042 {
00043 if (channelVectorLength >= 0)
00044 flowData->deleteChannel(channelVectorLength);
00045
00046 channelVectorLength = flowData->createChannelVectorLength(
00047 ui->arrowPlotChannelX->value(), ui->arrowPlotChannelY->value(), -1);
00048
00049 ui->arrowPlotChannelLength->setText(QString::number(channelVectorLength));
00050
00051 colorCodingNeedsUpdate = true;
00052 arrowPlotNeedsUpdate = true;
00053 streamlinesNeedsUpdate = true;
00054
00055 update();
00056 }
00057
00058 void RenderingView::updateColorCoding()
00059 {
00060 colorCodingNeedsUpdate = true;
00061 update();
00062 }
00063
00064 void RenderingView::updateArrowPlot()
00065 {
00066 arrowPlotNeedsUpdate = true;
00067 update();
00068 }
00069
00070 void RenderingView::updateStreamlines()
00071 {
00072 streamlinesNeedsUpdate = true;
00073 update();
00074 }
00075
00076
00077
00078 QSize RenderingView::minimumSizeHint() const
00079 {
00080 return QSize(200, 200);
00081 }
00082
00083 QSize RenderingView::sizeHint() const
00084 {
00085 return QSize(400, 400);
00086 }
00087
00088 QRgb RenderingView::normValueToRGB(float normValue)
00089 {
00090 int gradient = ui->colorCodingGradient->currentIndex();
00091
00092 switch (gradient)
00093 {
00094 default:
00095 case 0:
00096 return QColor(normValue * 255, normValue * 255, normValue * 255).rgba();
00097 break;
00098 case 1:
00099 return QColor(normValue * 255, 255 - normValue * 255, 0).rgba();
00100 break;
00101 case 2:
00102 QColor color;
00103 color.setHsvF(normValue, 1.f, 1.f);
00104
00105 return color.rgba();
00106 break;
00107
00108 }
00109 }
00110
00111 void RenderingView::paintEvent(QPaintEvent *e)
00112 {
00113 if (! flowData)
00114 return;
00115
00116 float widgetAspectRatio = ((float) width()) / height();
00117 float dataAspectRatio = (flowData->getMaxX() - flowData->getMinX()) /
00118 (flowData->getMaxY() - flowData->getMinY());
00119
00120 int w, h;
00121 if ( widgetAspectRatio > dataAspectRatio)
00122 {
00123 h = height();
00124 w = h * dataAspectRatio;
00125 } else {
00126 w = width();
00127 h = w / dataAspectRatio;
00128 }
00129
00130
00131 static int prevW = 0, prevH = 0;
00132 if (w != prevW || h != prevH)
00133 {
00134 colorCodingImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
00135 arrowPlotImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
00136 streamlinesImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
00137
00138 colorCodingNeedsUpdate = true;
00139 arrowPlotNeedsUpdate = true;
00140 streamlinesNeedsUpdate = true;
00141
00142 prevW = w;
00143 prevH = h;
00144 }
00145
00146 QPainter painter(this);
00147
00148 if (ui->colorCodingActive->isChecked())
00149 {
00150 FlowChannel *channel = flowData->getChannel(ui->colorCodingChannel->value());
00151
00152 if (colorCodingNeedsUpdate && channel && channel->getRange() > 0)
00153 {
00154 colorCodingImage.fill(0x0000FF00);
00155
00156 for (int y = 0; y < h; ++y)
00157 {
00158 for (int x = 0; x < w; ++x)
00159 {
00160 float rawValue = channel->getValueNormPos(((float) x) / w, ((float) y) / h);
00161 float normValue = channel->normalizeValue(rawValue);
00162
00163 colorCodingImage.setPixel(x, y, normValueToRGB(normValue));
00164 }
00165 }
00166
00167 colorCodingNeedsUpdate = false;
00168 }
00169
00170 painter.drawImage((width() - w) / 2, (height() - h) / 2, colorCodingImage);
00171 }
00172
00173 if (ui->arrowPlotActive->isChecked())
00174 {
00175 FlowChannel *channelX = flowData->getChannel(ui->arrowPlotChannelX->value());
00176 FlowChannel *channelY = flowData->getChannel(ui->arrowPlotChannelY->value());
00177
00178 if (arrowPlotNeedsUpdate && channelX && channelY) {
00179
00180
00181 arrowPlotImage.fill(0x00000000);
00182
00183 QPainter arrowPlotPainter(&arrowPlotImage);
00184 arrowPlotPainter.setRenderHint(QPainter::Antialiasing);
00185 arrowPlotPainter.setBrush(Qt::black);
00186
00187 int dist = ui->arrowPlotDistance->value();
00188 int size = dist * ui->arrowPlotSize->value() / 100;
00189 const QPointF arrowPoints[] = {
00190 QPointF(size / 2.f, 0),
00191 QPointF(-size / 2.f, -size / 3.f),
00192 QPointF(-size / 2.f, size / 3.f)
00193 };
00194
00195 for (int y = dist / 2; y < h; y += dist)
00196 {
00197 float normPosY = ((float) y) / h;
00198
00199 for (int x = dist / 2; x < w; x += dist)
00200 {
00201 float normPosX = ((float) x) / w;
00202
00203 float rawValueX = channelX->getValueNormPos(normPosX, normPosY);
00204
00205 float rawValueY = channelY->getValueNormPos(normPosX, normPosY);
00206
00207
00208 arrowPlotPainter.save();
00209
00210 arrowPlotPainter.translate(x, y);
00211 arrowPlotPainter.rotate(atan2(rawValueY, rawValueX) * 180 / PI);
00212
00213 if (ui->arrowPlotScale->isChecked())
00214 {
00215 FlowChannel *channelLength = flowData->getChannel(channelVectorLength);
00216
00217 float rawValueLength = channelLength->getValueNormPos(normPosX, normPosY);
00218 float normValueLength = channelLength->normalizeValue(rawValueLength);
00219
00220
00221 float scale = sqrt(normValueLength);
00222 arrowPlotPainter.scale(scale, scale);
00223 }
00224
00225 arrowPlotPainter.drawPolygon(arrowPoints, 3);
00226 arrowPlotPainter.restore();
00227 }
00228 }
00229
00230 arrowPlotNeedsUpdate = false;
00231 }
00232
00233 painter.drawImage((width() - w) / 2, (height() - h) / 2, arrowPlotImage);
00234 }
00235
00236 if (ui->streamlinesActive->isChecked())
00237 {
00238 FlowChannel *channelX = flowData->getChannel(ui->arrowPlotChannelX->value());
00239 FlowChannel *channelY = flowData->getChannel(ui->arrowPlotChannelY->value());
00240
00241 if (streamlinesNeedsUpdate && channelX && channelY) {
00242 streamlinesImage.fill(0x00000000);
00243
00244 QPainter streamlinesPainter(&streamlinesImage);
00245 streamlinesPainter.setRenderHint(QPainter::Antialiasing);
00246 QBrush brush = Qt::black;
00247 streamlinesPainter.setBrush(brush);
00248
00249
00250
00251
00252
00253
00254 if (ui->streamlinesSpacing->currentIndex() == 0)
00255 {
00256 int dist = ui->streamlinesDistance->value();
00257
00258 for (int y = dist / 2; y < h; y += dist)
00259 {
00260
00261
00262 for (int x = dist / 2; x < w; x += dist)
00263 {
00264 Streamline sl = computeStreamline(vec3(x, y), w, h, false);
00265 drawStreamline(sl, streamlinesPainter, w, h);
00266 }
00267 }
00268 } else {
00269 dSep = ui->streamlinesDSep->value();
00270 dTest = ui->streamlinesDTest->value();
00271 lookupW = w / dSep;
00272 lookupH = h / dSep;
00273 lookupGrid = new QList<vec3>[lookupW * lookupH];
00274 QQueue<Streamline> streamlineQueue;
00275 Streamline currentStreamline = computeStreamline(vec3(w/2,h/2), w, h);
00276 drawStreamline(currentStreamline, streamlinesPainter, w, h);
00277 bool finished = false;
00278 do {
00279 bool valid;
00280 vec3 seedPoint = selectSeedPoint(currentStreamline, valid);
00281 if (valid) {
00282 qDebug() << "Valid seedpoint: (" << seedPoint.v[0] << "," << seedPoint.v[1] << ")";
00283 streamlineQueue.append(computeStreamline(seedPoint, w, h));
00284 } else {
00285 qDebug() << "No valid seedpoint, next streamline (queue length: " << streamlineQueue.size() << ")";
00286 if (streamlineQueue.empty()) {
00287 finished = true;
00288 } else {
00289 currentStreamline = streamlineQueue.dequeue();
00290 drawStreamline(currentStreamline, streamlinesPainter, w, h);
00291 }
00292 }
00293 } while (!finished);
00294 delete[] lookupGrid;
00295 }
00296
00297 streamlinesNeedsUpdate = false;
00298 }
00299
00300 painter.drawImage((width() - w) / 2, (height() - h) / 2, streamlinesImage);
00301 }
00302 }
00303
00304 void RenderingView::drawStreamline(const Streamline& streamline, QPainter& painter, int w, int h)
00305 {
00306 bool tapering = ui->streamlinesTapering->isChecked();
00307 bool glyphMapping = ui->streamlinesGlyphMapping->isChecked();
00308 int glyphDistance = ui->streamlinesGlyphDistance->value();
00309
00310
00311 float pathLength = glyphDistance;
00312
00313 for (int i=0; i<streamline.size()-1; i++) {
00314 const vec3& p0 = streamline[i];
00315 const vec3& p1 = streamline[i+1];
00316
00317 if (tapering)
00318 {
00319 FlowChannel *channelLength = flowData->getChannel(channelVectorLength);
00320
00321 float rawValueLength = channelLength->getValueNormPos(p0.v[0] / w, p0.v[1] / h);
00322 float normValueLength = channelLength->normalizeValue(rawValueLength);
00323
00324 float width = 0.1f + normValueLength *
00325 (ui->streamlinesMaximumWidth->value() - 0.1f);
00326 QPen pen(painter.brush(), width);
00327 painter.setPen(pen);
00328 }
00329
00330 painter.drawLine(QPointF(p0.v[0], p0.v[1]), QPointF(p1.v[0], p1.v[1]));
00331
00332 if (glyphMapping)
00333 {
00334 vec3 d = p1 - p0;
00335 float newPathLength = pathLength + d.length();
00336
00337 if (newPathLength > glyphDistance)
00338 {
00339 int size = ui->streamlinesGlyphDistance->value() *
00340 ui->streamlinesGlyphSize->value() / 100;
00341 const QPointF arrowPoints[] = {
00342 QPointF(size / 2.f, 0),
00343 QPointF(-size / 2.f, -size / 3.f),
00344 QPointF(-size / 2.f, size / 3.f)
00345 };
00346
00347 float f = (newPathLength - glyphDistance) / (newPathLength - pathLength);
00348 vec3 glyphPos = p0 * f + p1 * (1 - f);
00349
00350 painter.save();
00351
00352 painter.translate(glyphPos.v[0], glyphPos.v[1]);
00353 painter.rotate(atan2(d.v[1], d.v[0]) * 180 / PI);
00354
00355 FlowChannel *channelLength = flowData->getChannel(channelVectorLength);
00356
00357 float rawValueLength = channelLength->getValueNormPos(p0.v[0] / w, p0.v[1] / h);
00358 float normValueLength = channelLength->normalizeValue(rawValueLength);
00359
00360
00361 float scale = sqrt(normValueLength);
00362 painter.scale(scale, scale);
00363
00364 painter.drawPolygon(arrowPoints, 3);
00365 painter.restore();
00366
00367 newPathLength -= glyphDistance;
00368 }
00369 pathLength = newPathLength;
00370 }
00371 }
00372 }
00373
00374 vec3 RenderingView::integratePoint(vec3 pos, FlowChannel* channelX, FlowChannel* channelY, float dt, float direction)
00375 {
00376 vec3 v = vec3(channelX->getValue(pos), channelY->getValue(pos)) * dt;
00377 vec3 newPos;
00378 if (ui->streamlinesIntegration->currentIndex() == 0)
00379 {
00380 newPos = pos + v * direction;
00381 }
00382 else
00383 {
00384 vec3 midPoint = pos + v * 0.5f;
00385
00386 if (midPoint.v[0] >= flowData->getMinX() && midPoint.v[0] <= flowData->getMaxX()
00387 && midPoint.v[1] >= flowData->getMinY() && midPoint.v[1] <= flowData->getMaxY())
00388 {
00389 float newRawValueX = channelX->getValue(midPoint);
00390 float newRawValueY = channelY->getValue(midPoint);
00391
00392 v = vec3(newRawValueX, newRawValueY, 0) * dt;
00393 newPos = pos + v * direction;
00394 } else
00395 newPos = pos;
00396 }
00397 return newPos;
00398 }
00399
00400 RenderingView::Streamline RenderingView::computeStreamline(vec3 p, int w, int h, bool lookup)
00401 {
00402 int steps;
00403 vec3 pos;
00404 vec3 posPixel;
00405 FlowChannel *channelX = flowData->getChannel(ui->arrowPlotChannelX->value());
00406 FlowChannel *channelY = flowData->getChannel(ui->arrowPlotChannelY->value());
00407 float dt = ui->streamlinesTimeStep->value();
00408 vec3 normPos(p.v[0] / w, p.v[1] / h);
00409
00410 vec3 initialPos = flowData->unNormalizeCoords(normPos);
00411 Streamline streamlinePlus;
00412 streamlinePlus.append(p);
00413 if (lookup) addPointToLookup(p);
00414 steps = ui->streamlinesSteps->value();
00415 pos = initialPos;
00416 posPixel = p;
00417 while (pos.v[0] >= flowData->getMinX() && pos.v[0] <= flowData->getMaxX()
00418 && pos.v[1] >= flowData->getMinY() && pos.v[1] <= flowData->getMaxY()
00419 && steps > 0)
00420 {
00421 steps--;
00422 vec3 newPos = integratePoint(pos, channelX, channelY, dt, 1.0f);
00423
00424 if (pos == newPos)
00425 break;
00426
00427 vec3 newPosNormalized = flowData->normalizeCoords(newPos);
00428 vec3 newPosPixel(newPosNormalized.v[0] * w, newPosNormalized.v[1] * h);
00429 if (lookup && newPosPixel.dist(posPixel) < dTest) {
00430 pos = newPos;
00431 continue;
00432 }
00433
00434 if (lookup && !isPointValid(newPosPixel, dTest)) break;
00435 streamlinePlus.append(newPosPixel);
00436 if (lookup) addPointToLookup(newPosPixel);
00437 pos = newPos;
00438 posPixel = newPosPixel;
00439 }
00440 Streamline streamlineMinus;
00441 steps = ui->streamlinesSteps->value();
00442 pos = initialPos;
00443 posPixel = p;
00444 while (pos.v[0] >= flowData->getMinX() && pos.v[0] <= flowData->getMaxX()
00445 && pos.v[1] >= flowData->getMinY() && pos.v[1] <= flowData->getMaxY()
00446 && steps > 0)
00447 {
00448 steps--;
00449 vec3 newPos = integratePoint(pos, channelX, channelY, dt, -1.0f);
00450
00451 if (pos == newPos)
00452 break;
00453
00454 vec3 newPosNormalized = flowData->normalizeCoords(newPos);
00455 vec3 newPosPixel(newPosNormalized.v[0] * w, newPosNormalized.v[1] * h);
00456 if (lookup && newPosPixel.dist(posPixel) < dTest) {
00457 pos = newPos;
00458 continue;
00459 }
00460
00461 if (lookup && !isPointValid(newPosPixel, dTest)) break;
00462 streamlineMinus.append(newPosPixel);
00463 if (lookup) addPointToLookup(newPosPixel);
00464 pos = newPos;
00465 posPixel = newPosPixel;
00466 }
00467
00468 Streamline streamline;
00469 for (int i=streamlineMinus.size() - 1; i>=0; i--) {
00470 streamline.append(streamlineMinus[i]);
00471 }
00472 for (int i=0; i<streamlinePlus.size(); i++) {
00473 streamline.append(streamlinePlus[i]);
00474 }
00475
00476 return streamline;
00477 }
00478
00479 vec3 RenderingView::selectSeedPoint(Streamline streamLine, bool& valid)
00480 {
00481 if (streamLine.size() < 2) {
00482 valid = false;
00483 return vec3();
00484 }
00485 valid = true;
00486 for (int i=1; i<streamLine.size(); i++) {
00487 vec3& p0 = streamLine[i-1];
00488 vec3& p1 = streamLine[i];
00489 vec3 p0p1 = p1 - p0;
00490 vec3 mid = p0 + p0p1 / 2.0f;
00491 vec3 normal1(-p0p1.v[1], p0p1.v[0]);
00492 normal1 /= normal1.length();
00493 vec3 normal2(p0p1.v[1], -p0p1.v[0]);
00494 normal2 /= normal2.length();
00495 vec3 candidate1 = mid + normal1 * dSep;
00496 if (isPointValid(candidate1, dSep)) return candidate1;
00497 vec3 candidate2 = mid + normal2 * dSep;
00498 if (isPointValid(candidate2, dSep)) return candidate2;
00499 }
00500 valid = false;
00501 return vec3();
00502 }
00503
00504 bool RenderingView::isPointValid(vec3 p, int testDistance)
00505 {
00506 int x = p.v[0] / dSep;
00507 int y = p.v[1] / dSep;
00508 int idx = y * lookupW + x;
00509 if (idx < 0 || idx >= lookupW * lookupH) return false;
00510 const int indices[] = {
00511 idx, idx - 1, idx + 1,
00512 idx + lookupW, idx + lookupW - 1, idx + lookupW + 1,
00513 idx - lookupW, idx - lookupW - 1, idx - lookupW + 1,
00514 };
00515 for (int j = 0; j < 9; j++) {
00516 if (indices[j] < 0 || indices[j] >= lookupW * lookupH) continue;
00517 QList<vec3>& cell = lookupGrid[indices[j]];
00518 for (int i=0; i<cell.size(); i++) {
00519 vec3& cellPoint = cell[i];
00520 vec3 dv = cellPoint - p;
00521 float dist = sqrt(dv.v[0] * dv.v[0] + dv.v[1] * dv.v[1]);
00522 if (dist < (float)testDistance) return false;
00523 }
00524 }
00525 return true;
00526 }
00527
00528 void RenderingView::addPointToLookup(vec3 p)
00529 {
00530 int x = p.v[0] / dSep;
00531 int y = p.v[1] / dSep;
00532 int idx = y * lookupW + x;
00533 QList<vec3>& cell = lookupGrid[idx];
00534 cell.append(p);
00535 }