geometrytransferfunctioneditor.cpp 14.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// ================================================================================================
// 
// This file is part of the TUMVis Visualization Framework.
// 
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
//      Christian Schulte zu Berge (christian.szb@in.tum.de)
//      Chair for Computer Aided Medical Procedures
//      Technische Universitt Mnchen
//      Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
// 
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
// 
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// 
// ================================================================================================

#include "geometrytransferfunctioneditor.h"

#include "tgt/assert.h"
#include "tgt/shadermanager.h"
#include "tgt/qt/qtcontextmanager.h"
#include "tgt/qt/qtthreadedcanvas.h"

#include "application/gui/qtcolortools.h"
37
38
#include "application/gui/properties/tfgeometrymanipulator.h"

39
40
#include "core/classification/geometrytransferfunction.h"
#include "core/classification/tfgeometry.h"
41
#include "core/datastructures/imagedatalocal.h"
42
43
44
45
46
#include "core/properties/transferfunctionproperty.h"
#include "core/tools/opengljobprocessor.h"

#include <QGridLayout>
#include <QLabel>
47
48
#include <QPushButton>
#include <QVBoxLayout>
49
50
51
52
53
54
55

namespace TUMVis {

    GeometryTransferFunctionEditor::GeometryTransferFunctionEditor(GeometryTransferFunction* tf, QWidget* parent /*= 0*/)
        : AbstractTransferFunctionEditor(tf, parent)
        , _layout(0)
        , _canvas(0)
56
57
        , _lblIntensityLeft(0)
        , _lblIntensityRight(0)
58
        , _btnAddGeometry(0)
59
    {
60
        _selectedGeometry = 0;
61
62
63
        setupGUI();
        tf->s_geometryCollectionChanged.connect(this, &GeometryTransferFunctionEditor::onGeometryCollectionChanged);
        updateManipulators();
64
        setEventTypes(tgt::Event::MOUSEPRESSEVENT);
65
66
67
    }

    GeometryTransferFunctionEditor::~GeometryTransferFunctionEditor() {
68
69
70
71
72
73
74
75
76
77
78
        tbb::mutex::scoped_lock lock(_localMutex);

        // clear 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;
        }

79
80
        GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
        gtf->s_geometryCollectionChanged.disconnect(this);
81
        // TODO: this needs to be done, but we can not ensure that GLJobProc is still existant during deconstruction...
82
83
84
85
86
        //GLJobProc.deregisterContext(_canvas);
    }

    void GeometryTransferFunctionEditor::updateWidgetFromProperty() {
        GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
87
88
        _lblIntensityLeft->setText(QString::number(gtf->getIntensityDomain().x));
        _lblIntensityRight->setText(QString::number(gtf->getIntensityDomain().y));
89
        invalidate();
90
91
92
93
    }

    void GeometryTransferFunctionEditor::paint() {
        GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
94
        gtf->lock();
95
96
97
        const std::vector<TFGeometry*>& geometries = gtf->getGeometries();
        const tgt::vec2& intensityDomain = gtf->getIntensityDomain();

98
        // TODO: get rid of intermediate mode?
99
        glPushAttrib(GL_ALL_ATTRIB_BITS);
100
101
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
102
103
        glViewport(0, 0, _canvas->width(), _canvas->height());

104
105
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
106
        glOrtho(0, 1 , 0, 1, -1, 1);
107
108
109
110
        glClearColor(1.f, 1.f, 1.f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);
        LGL_ERROR;

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
        // render TF geometries
        for (std::vector<TFGeometry*>::const_iterator it = geometries.begin(); it != geometries.end(); ++it) {
            (*it)->render();
        }

        // render histogram if existent
        const AbstractTransferFunction::IntensityHistogramType* ih = gtf->getIntensityHistogram();
        if (ih != 0) {
            size_t numBuckets = ih->getNumBuckets(0);
            if (numBuckets > 0) {
                float maxFilling = static_cast<float>(ih->getMaxFilling());

                float xl = static_cast<float>(0.f) / static_cast<float>(numBuckets);
                float xr = 0.f;
                float yl = static_cast<float>(ih->getNumElements(0)) / maxFilling;
                float yr = 0.f;

                glBegin(GL_QUADS);
                glColor4f(1.f, .75f, 0.f, .5f);
                for (size_t i = 1; i < numBuckets; ++i) {
                    xr = static_cast<float>(i) / static_cast<float>(numBuckets);
                    yr = static_cast<float>(ih->getNumElements(i)) / maxFilling;

                    glVertex2f(xl, 0.f);
                    glVertex2f(xl, yl);
                    glVertex2f(xr, yr);
                    glVertex2f(xr, 0.f);

                    xl = xr;
                    yl = yr;
141
                }
142
                glEnd();
143
            }
144
145
        }

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        {
            tbb::mutex::scoped_lock lock(_localMutex);

            // render selected geometry
            if (_selectedGeometry != 0) {
                const std::vector<tgt::vec2>& helperPoints = _selectedGeometry->getHelperPoints();
                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();
171
172
173
174
        }

        LGL_ERROR;
        glPopAttrib();
175
176

        gtf->unlock();
177
178
    }

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

187
188
189
190
191
    void GeometryTransferFunctionEditor::mousePressEvent(tgt::MouseEvent* e) {
        if (_selectedGeometry != 0 && e->modifiers() & tgt::Event::CTRL) {
            TFGeometry* g = _selectedGeometry->getGeometry();
            std::vector<TFGeometry::KeyPoint>& kpts = g->getKeyPoints();
            TFGeometry::KeyPoint kp(static_cast<float>(e->x()) / static_cast<float>(_canvas->width()), tgt::col4(255));
192
            std::vector<TFGeometry::KeyPoint>::const_iterator lb = std::upper_bound(kpts.begin(), kpts.end(), kp);
193
194
195
196
197
198
199
200
201
202
203
204
205
206
            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);
            updateManipulators();
            g->s_changed();
        }
        else {
            _selectedGeometry = 0;
207
            invalidate();
208
209
210
211
            e->ignore();
        }
    }

212
213
214
215
    void GeometryTransferFunctionEditor::invalidate() {
        GLJobProc.enqueueJob(_canvas, new CallMemberFuncJob<GeometryTransferFunctionEditor>(this, &GeometryTransferFunctionEditor::paint), OpenGLJobProcessor::PaintJob);
    }

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
    void GeometryTransferFunctionEditor::setupGUI() {
        GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);

        _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);

        _canvas = CtxtMgr.createContext("tfcanvas", "", tgt::ivec2(256, 128), tgt::GLCanvas::RGBA_BUFFER, 0, false);
        _canvas->doneCurrent();
        GLJobProc.registerContext(_canvas);
        _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);

242
243
244
245
246
247
248
249
250
251
        QVBoxLayout* buttonLayout = new QVBoxLayout(); // TODO: check whether buttonLayout will be deleted by Qt's GC!
        _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()));

252
253
254
255
256
        _layout->setColumnStretch(2, 1);
        _layout->setRowStretch(2, 1);
    }

    void GeometryTransferFunctionEditor::updateManipulators() {
257
258
        tbb::mutex::scoped_lock lock(_localMutex);

259
        // clear and delete former stuff
260
        _selectedGeometry = 0;
261
262
        _canvas->getEventHandler()->clear();
        for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
263
264
265
            if (WholeTFGeometryManipulator* tester = dynamic_cast<WholeTFGeometryManipulator*>(*it)) {
            	tester->s_selected.disconnect(this);
            }
266
267
268
269
270
271
272
273
            delete *it;
        }
        _manipulators.clear();

        GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
        const std::vector<TFGeometry*>& geometries = gtf->getGeometries();
        for (std::vector<TFGeometry*>::const_iterator git = geometries.begin(); git != geometries.end(); ++git) {
            // Add manipulator for the whole geometry and register it as event handler:
274
275
276
277
            WholeTFGeometryManipulator* wtf = new WholeTFGeometryManipulator(_canvas->getSize(), gtf, *git);
            _manipulators.push_back(wtf);
            _canvas->getEventHandler()->addListenerToFront(wtf);
            wtf->s_selected.connect(this, &GeometryTransferFunctionEditor::onWholeTFGeometryManipulatorSelected);
278
279
280
281

            // Add a manipulator for each KeyPoint and register it as event handler:
            for (std::vector<TFGeometry::KeyPoint>::iterator kpit = (*git)->getKeyPoints().begin(); kpit != (*git)->getKeyPoints().end(); ++kpit) {
                _manipulators.push_back(new KeyPointManipulator(_canvas->getSize(), gtf, *git, kpit));
282
                _canvas->getEventHandler()->addListenerToFront(_manipulators.back());
283
284
285
            }
        }

286
        _canvas->getEventHandler()->addListenerToFront(this);
287
288
289
290
291
292
    }

    void GeometryTransferFunctionEditor::onGeometryCollectionChanged() {
        updateManipulators();
    }

293
294
    void GeometryTransferFunctionEditor::onWholeTFGeometryManipulatorSelected(WholeTFGeometryManipulator* wtf /* :) */) {
        _selectedGeometry = wtf;
295
        invalidate();
296
297
298
299
300
301
302
303
304
305
306
    }

    void GeometryTransferFunctionEditor::onBtnAddGeometryClicked() {
        GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
        gtf->addGeometry(TFGeometry::createQuad(tgt::vec2(.4f, .6f), tgt::col4(196), tgt::col4(196)));
    }

    void GeometryTransferFunctionEditor::onBtnRemoveGeometryClicked() {
        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:
307
            GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
308
            TFGeometry* geometryToRemove = _selectedGeometry->getGeometry();
309
310
311
312
313
314
315
316
317

            {
                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;
                    }
318
                }
319
320
                delete _selectedGeometry;
                _selectedGeometry = 0;
321
            }
322

323
324
325
326
            gtf->removeGeometry(geometryToRemove);
        }
    }

327
328

}