geometry1dtransferfunctioneditor.cpp 15.9 KB
Newer Older
1
2
// ================================================================================================
// 
schultezub's avatar
schultezub committed
3
// This file is part of the CAMPVis Software Framework.
4
// 
5
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
schultezub's avatar
schultezub committed
6
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
7
//      Chair for Computer Aided Medical Procedures
8
9
//      Technische Universitaet Muenchen
//      Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
10
// 
schultezub's avatar
schultezub committed
11
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
12
// 
13
14
15
16
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
17
// 
18
19
20
21
// Unless required by applicable law or agreed to in writing, software distributed under the 
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
22
23
24
// 
// ================================================================================================

schultezub's avatar
schultezub committed
25
#include "geometry1dtransferfunctioneditor.h"
26
27
28

#include "tgt/assert.h"
#include "tgt/shadermanager.h"
29
#include "tgt/glcontextmanager.h"
30
31
32
#include "tgt/qt/qtthreadedcanvas.h"

#include "application/gui/qtcolortools.h"
33
34
#include "application/gui/properties/tfgeometrymanipulator.h"

schultezub's avatar
schultezub committed
35
36
#include "core/classification/geometry1dtransferfunction.h"
#include "core/classification/tfgeometry1d.h"
37
#include "core/datastructures/imagerepresentationlocal.h"
38
39
#include "core/properties/transferfunctionproperty.h"

40
#include <QCheckBox>
41
42
#include <QGridLayout>
#include <QLabel>
43
44
#include <QPushButton>
#include <QVBoxLayout>
45

schultezub's avatar
schultezub committed
46
namespace campvis {
47

48
49
    Geometry1DTransferFunctionEditor::Geometry1DTransferFunctionEditor(TransferFunctionProperty* prop, Geometry1DTransferFunction* tf, QWidget* parent /*= 0*/)
        : AbstractTransferFunctionEditor(prop, tf, parent)
50
        , _logScale(true)
51
52
        , _layout(0)
        , _canvas(0)
53
54
        , _lblIntensityLeft(0)
        , _lblIntensityRight(0)
55
        , _btnAddGeometry(0)
56
57
        , _btnRemoveGeometry(0)
        , _cbLogScale(0)
58
    {
59
        _selectedGeometry = 0;
60
        setupGUI();
61

schultezub's avatar
schultezub committed
62
        tf->s_geometryCollectionChanged.connect(this, &Geometry1DTransferFunctionEditor::onGeometryCollectionChanged);
63
64
        tf->s_aboutToBeDeleted.connect(this, &Geometry1DTransferFunctionEditor::onTfAboutToBeDeleted);

65
        updateManipulators();
66
        setEventTypes(tgt::Event::MOUSEPRESSEVENT);
67
68
    }

schultezub's avatar
schultezub committed
69
    Geometry1DTransferFunctionEditor::~Geometry1DTransferFunctionEditor() {
70
        disconnectFromTf();
71
72
73

        if (tgt::GlContextManager::isInited())
            tgt::GlContextManager::getRef().removeContext(_canvas);
74
75
    }

schultezub's avatar
schultezub committed
76
77
    void Geometry1DTransferFunctionEditor::updateWidgetFromProperty() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
78
79
        _lblIntensityLeft->setText(QString::number(gtf->getIntensityDomain().x));
        _lblIntensityRight->setText(QString::number(gtf->getIntensityDomain().y));
80
        invalidate();
81
82
    }

schultezub's avatar
schultezub committed
83
84
85
    void Geometry1DTransferFunctionEditor::paint() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
        const std::vector<TFGeometry1D*>& geometries = gtf->getGeometries();
86

87
        // TODO: get rid of intermediate mode?
88
        glPushAttrib(GL_ALL_ATTRIB_BITS);
89
90
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
91
92
        glViewport(0, 0, _canvas->width(), _canvas->height());

93
94
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
95
        glOrtho(0, 1 , 0, 1, -1, 1);
96
97
98
        glClearColor(1.f, 1.f, 1.f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);
        LGL_ERROR;
99
        
100
        // render histogram if existent
101
        const TransferFunctionProperty::IntensityHistogramType* ih = getIntensityHistogram();
102
103
104
105
        if (ih != 0) {
            size_t numBuckets = ih->getNumBuckets(0);
            if (numBuckets > 0) {
                float maxFilling = static_cast<float>(ih->getMaxFilling());
106
107
                if (_logScale)
                    maxFilling = log(maxFilling);
108
109

                float xl = static_cast<float>(0.f) / static_cast<float>(numBuckets);
110
111
112
                float yl = (_logScale 
                    ? log(static_cast<float>(ih->getNumElements(0))) / maxFilling
                    : static_cast<float>(ih->getNumElements(0)) / maxFilling);
113

114

115
116
117
                glBegin(GL_QUADS);
                glColor4f(1.f, .75f, 0.f, .5f);
                for (size_t i = 1; i < numBuckets; ++i) {
118
119
                    float xr = static_cast<float>(i) / static_cast<float>(numBuckets);
                    float yr = (_logScale 
schultezub's avatar
schultezub committed
120
                        ? std::max(0.f, static_cast<float>(log(static_cast<float>(ih->getNumElements(i)))) / maxFilling)
121
122
                        : static_cast<float>(ih->getNumElements(i)) / maxFilling);
                    
123
124
125
126
                    glVertex2f(xl, 0.f);
                    glVertex2f(xl, yl);
                    glVertex2f(xr, yr);
                    glVertex2f(xr, 0.f);
127
128
129

                    xl = xr;
                    yl = yr;
130
                }
131
                glEnd();
132
            }
133
134
        }

135
136
137
138
139
140
141
142
        // render TF geometries
        {
            tbb::mutex::scoped_lock lock(_localMutex);
            for (std::vector<TFGeometry1D*>::const_iterator it = geometries.begin(); it != geometries.end(); ++it) {
                (*it)->renderIntoEditor();
            }
        }

143
144
145
146
        {
            tbb::mutex::scoped_lock lock(_localMutex);

            // render selected geometry
schultezub's avatar
schultezub committed
147
148
149
150
            WholeTFGeometryManipulator* selectedGeometry = _selectedGeometry;
            if (selectedGeometry != 0) {
                // the copy is deliberate for improved thread safety (the whole design is a little messy here...)
                std::vector<tgt::vec2> helperPoints = selectedGeometry->getHelperPoints();
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
                glColor4ub(0, 0, 0, 196);
                glEnable(GL_LINE_STIPPLE);
                glLineStipple(1, 0xFAFA);
                glBegin(GL_LINE_LOOP);
                for (std::vector<tgt::vec2>::const_iterator it = helperPoints.begin(); it != helperPoints.end(); ++it)
                    glVertex2fv(it->elem);
                glEnd();
                glDisable(GL_LINE_STIPPLE);
            }

            glPopMatrix();

            glPushMatrix();
            glOrtho(0, _canvas->width(), 0, _canvas->height(), -1, 1);
            // render manipulators
            for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
                (*it)->render();
            }
            glPopMatrix();
170
171
172
173
174
175
        }

        LGL_ERROR;
        glPopAttrib();
    }

schultezub's avatar
schultezub committed
176
    void Geometry1DTransferFunctionEditor::sizeChanged(const tgt::ivec2& size) {
177
178
179
180
181
        {
            tbb::mutex::scoped_lock lock(_localMutex);
            for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
                (*it)->setViewportSize(size);
            }
182
        }
183
184
185
        invalidate();
    }

schultezub's avatar
schultezub committed
186
    void Geometry1DTransferFunctionEditor::mousePressEvent(tgt::MouseEvent* e) {
187
        if (_selectedGeometry != 0 && e->modifiers() & tgt::Event::CTRL) {
schultezub's avatar
schultezub committed
188
            TFGeometry1D* g = _selectedGeometry->getGeometry();
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
            {
                tbb::mutex::scoped_lock lock(_localMutex);

                // add a control point on CTRL+Click
                std::vector<TFGeometry1D::KeyPoint>& kpts = g->getKeyPoints();
                TFGeometry1D::KeyPoint kp(static_cast<float>(e->x()) / static_cast<float>(_canvas->width()), tgt::col4(255));
                std::vector<TFGeometry1D::KeyPoint>::iterator lb = std::upper_bound(kpts.begin(), kpts.end(), kp);
                if (lb != kpts.end()) {
                    kp._color = lb->_color;
                }
                else {
                    kp._color = kpts.back()._color;
                }
                float alpha = tgt::clamp(static_cast<float>(_canvas->height() - e->y()) / static_cast<float>(_canvas->height()), 0.f, 1.f);
                kp._color.a = static_cast<uint8_t>(alpha * 255.f);
                kpts.insert(lb, kp);
205
            }
206

207
            updateManipulators();
208
            g->s_changed.emitSignal();
209
210
211
        }
        else {
            _selectedGeometry = 0;
212
            invalidate();
213
214
215
216
            e->ignore();
        }
    }

217
218
219
220
    void Geometry1DTransferFunctionEditor::repaint() {
        invalidate();
    }

schultezub's avatar
schultezub committed
221
    void Geometry1DTransferFunctionEditor::invalidate() {
222
223
224
        // TODO: check, whether this should be done in an extra thread
        tgt::GLContextScopedLock lock(_canvas);
        paint();
225
226
    }

schultezub's avatar
schultezub committed
227
228
    void Geometry1DTransferFunctionEditor::setupGUI() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
229
230
231
232
233
234
235
236
237
238
239

        _layout = new QGridLayout(this);
        setLayout(_layout);

        QLabel* lblOpacityTop = new QLabel(tr("100%"), this);
        _layout->addWidget(lblOpacityTop, 1, 0, 1, 1, Qt::AlignRight);
        QLabel* lblOpacity = new QLabel(tr("Opacity"), this);
        _layout->addWidget(lblOpacity, 2, 0, 1, 1, Qt::AlignRight);
        QLabel* lblOpacityBottom = new QLabel(tr("0%"), this);
        _layout->addWidget(lblOpacityBottom, 3, 0, 1, 1, Qt::AlignRight);

240
        _canvas = new tgt::QtThreadedCanvas("", tgt::ivec2(256, 128), tgt::GLCanvas::RGBA_BUFFER, 0, false);
241
242
        GLCtxtMgr.registerContextAndInitGlew(_canvas, "Geometry1DTransferFunctionEditor");
        GLCtxtMgr.releaseContext(_canvas, false);
243

244
245
246
247
248
249
250
251
252
253
        _canvas->setPainter(this, false);
        _layout->addWidget(_canvas, 1, 1, 3, 3);

        _lblIntensityLeft = new QLabel(QString::number(gtf->getIntensityDomain().x), this);
        _layout->addWidget(_lblIntensityLeft, 4, 1, 1, 1, Qt::AlignLeft);
        QLabel* lblIntensity = new QLabel(tr("Intensity"), this);
        _layout->addWidget(lblIntensity, 4, 2, 1, 1, Qt::AlignHCenter);
        _lblIntensityRight = new QLabel(QString::number(gtf->getIntensityDomain().y), this);
        _layout->addWidget(_lblIntensityRight, 4, 3, 1, 1, Qt::AlignRight);

254
        QVBoxLayout* buttonLayout = new QVBoxLayout();
255
256
257
258
259
260
261
262
        _layout->addLayout(buttonLayout, 1, 4, 1, 3, Qt::AlignTop);

        _btnAddGeometry = new QPushButton(tr("Add Geometry"), this);
        buttonLayout->addWidget(_btnAddGeometry);
        connect(_btnAddGeometry, SIGNAL(clicked()), this, SLOT(onBtnAddGeometryClicked()));
        _btnRemoveGeometry = new QPushButton(tr("Remove Geometry"), this);
        buttonLayout->addWidget(_btnRemoveGeometry);
        connect(_btnRemoveGeometry, SIGNAL(clicked()), this, SLOT(onBtnRemoveGeometryClicked()));
263
264
265
266
        _cbLogScale = new QCheckBox(tr("Logarithmic Scale"), this);
        _cbLogScale->setChecked(true);
        buttonLayout->addWidget(_cbLogScale);
        connect(_cbLogScale, SIGNAL(stateChanged(int)), this, SLOT(onCbLogScaleStateChanged(int)));
267
268
269
270
        _layout->setColumnStretch(2, 1);
        _layout->setRowStretch(2, 1);
    }

schultezub's avatar
schultezub committed
271
    void Geometry1DTransferFunctionEditor::updateManipulators() {
272
273
        tbb::mutex::scoped_lock lock(_localMutex);

274
        // clearEventListeners and delete former stuff
275
        _selectedGeometry = 0;
276
        _canvas->getEventHandler()->clearEventListeners();
277
        for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
278
            if (WholeTFGeometryManipulator* tester = dynamic_cast<WholeTFGeometryManipulator*>(*it)) {
279
                tester->s_selected.disconnect(this);
280
            }
281
282
283
284
            delete *it;
        }
        _manipulators.clear();

schultezub's avatar
schultezub committed
285
286
287
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
        const std::vector<TFGeometry1D*>& geometries = gtf->getGeometries();
        for (std::vector<TFGeometry1D*>::const_iterator git = geometries.begin(); git != geometries.end(); ++git) {
288
            // Add manipulator for the whole geometry and register it as event handler:
289
            WholeTFGeometryManipulator* wtf = new WholeTFGeometryManipulator(_canvas->getSize(), *git);
290
            _manipulators.push_back(wtf);
291
            _canvas->getEventHandler()->addEventListenerToFront(wtf);
schultezub's avatar
schultezub committed
292
            wtf->s_selected.connect(this, &Geometry1DTransferFunctionEditor::onWholeTFGeometryManipulatorSelected);
293
294

            // Add a manipulator for each KeyPoint and register it as event handler:
schultezub's avatar
schultezub committed
295
            for (std::vector<TFGeometry1D::KeyPoint>::iterator kpit = (*git)->getKeyPoints().begin(); kpit != (*git)->getKeyPoints().end(); ++kpit) {
296
                _manipulators.push_back(new KeyPointManipulator(_canvas->getSize(), *git, kpit));
297
                _canvas->getEventHandler()->addEventListenerToFront(_manipulators.back());
298
299
300
            }
        }

301
        _canvas->getEventHandler()->addEventListenerToFront(this);
302
303
    }

schultezub's avatar
schultezub committed
304
    void Geometry1DTransferFunctionEditor::onGeometryCollectionChanged() {
305
306
307
        updateManipulators();
    }

schultezub's avatar
schultezub committed
308
    void Geometry1DTransferFunctionEditor::onWholeTFGeometryManipulatorSelected(WholeTFGeometryManipulator* wtf /* :) */) {
309
        _selectedGeometry = wtf;
310
        invalidate();
311
312
    }

schultezub's avatar
schultezub committed
313
314
315
    void Geometry1DTransferFunctionEditor::onBtnAddGeometryClicked() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
        gtf->addGeometry(TFGeometry1D::createQuad(tgt::vec2(.4f, .6f), tgt::col4(196), tgt::col4(196)));
316
317
    }

schultezub's avatar
schultezub committed
318
    void Geometry1DTransferFunctionEditor::onBtnRemoveGeometryClicked() {
319
320
321
        if (_selectedGeometry != 0) {
            // to get the signal-slots disconnected in the correct order and avoid double deletion,
            // this is getting a little messy and cumbersome:
schultezub's avatar
schultezub committed
322
323
            Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
            TFGeometry1D* geometryToRemove = _selectedGeometry->getGeometry();
324
325
326
327
328
329
330
331
332

            {
                tbb::mutex::scoped_lock lock(_localMutex);

                for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
                    if (*it == _selectedGeometry) {
                        _manipulators.erase(it);
                        break;
                    }
333
                }
334
335
                delete _selectedGeometry;
                _selectedGeometry = 0;
336
            }
337

338
339
340
341
            gtf->removeGeometry(geometryToRemove);
        }
    }

342
343
344
345
346
    void Geometry1DTransferFunctionEditor::onCbLogScaleStateChanged(int state) {
        _logScale = (state & Qt::Checked);
        invalidate();
    }

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
    void Geometry1DTransferFunctionEditor::onTfAboutToBeDeleted() {
        disconnectFromTf();
    }

    void Geometry1DTransferFunctionEditor::disconnectFromTf() {
        tbb::mutex::scoped_lock lock(_localMutex);

        // clearEventListeners and delete former stuff
        _selectedGeometry = 0;
        for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
            if (WholeTFGeometryManipulator* tester = dynamic_cast<WholeTFGeometryManipulator*>(*it)) {
                tester->s_selected.disconnect(this);
            }
            delete *it;
        }
        _manipulators.clear();

        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
        if (gtf != nullptr) {
            gtf->s_geometryCollectionChanged.disconnect(this);
            gtf->s_aboutToBeDeleted.disconnect(this);
            _transferFunction->s_changed.disconnect(this);
            _transferFunction = nullptr;
        }
    }

373

374
}