• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

renderingview.cpp

Go to the documentation of this file.
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         //    : QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
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: // grayscale
00096         return QColor(normValue * 255, normValue * 255, normValue * 255).rgba();
00097         break;
00098     case 1: // heat
00099         return QColor(normValue * 255, 255 - normValue * 255, 0).rgba();
00100         break;
00101     case 2: // HSV, with varying hue
00102         QColor color;
00103         color.setHsvF(normValue, 1.f, 1.f);
00104 
00105         return color.rgba();
00106         break;
00107     // TODO: other gradients
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     // check if images need resizing
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             // TODO: try QImage::Format_ARGB32_Premultiplied for better performance,
00180             // or painting on QGLWidget (with OpenGL backend) instead of QImage
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                     //float normValueX = channelX->normalizeValue(rawValueX);
00205                     float rawValueY = channelY->getValueNormPos(normPosX, normPosY);
00206                     //float normValueY = channelY->normalizeValue(rawValueY);
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                         // scale area, not length of vectors
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             //float dt = ui->streamlinesTimeStep->value();
00250             //bool tapering = ui->streamlinesTapering->isChecked(),
00251             //    glyphMapping = ui->streamlinesGlyphMapping->isChecked();
00252             //int glyphDistance = ui->streamlinesGlyphDistance->value();
00253 
00254             if (ui->streamlinesSpacing->currentIndex() == 0) // regular spacing
00255             {
00256                 int dist = ui->streamlinesDistance->value();
00257 
00258                 for (int y = dist / 2; y < h; y += dist)
00259                 {
00260                     //float normPosY = ((float) y) / h;
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     // length of path along streamline from last glyph to current position (in pixels)
00311     float pathLength = glyphDistance; // draw glyph at start of path
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                 // scale area, not length of vectors
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) // direction is either -1.0f or 1.0f
00375 {
00376     vec3 v = vec3(channelX->getValue(pos), channelY->getValue(pos)) * dt;
00377     vec3 newPos;
00378     if (ui->streamlinesIntegration->currentIndex() == 0) // Euler
00379     {
00380         newPos = pos + v * direction;
00381     }
00382     else
00383     { // 2nd order Runge-Kutta
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     // perform integration in geometry coordinates
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) // singularity
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) // singularity
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 }

Generated on Wed Jan 19 2011 13:25:19 for LU Visualisierung WS2010 - Beispiel 1 by  doxygen 1.7.2