geometry1dtransferfunctioneditor.cpp 14.7 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, all rights reserved,
schultezub's avatar
schultezub committed
6
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
7
8
9
//      Chair for Computer Aided Medical Procedures
//      Technische Universitt Mnchen
//      Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
schultezub's avatar
schultezub committed
10
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 
// 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.
// 
// ================================================================================================

schultezub's avatar
schultezub committed
30
#include "geometry1dtransferfunctioneditor.h"
31
32
33
34
35
36
37

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

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

schultezub's avatar
schultezub committed
40
41
#include "core/classification/geometry1dtransferfunction.h"
#include "core/classification/tfgeometry1d.h"
42
#include "core/datastructures/imagerepresentationlocal.h"
43
44
45
46
47
#include "core/properties/transferfunctionproperty.h"
#include "core/tools/opengljobprocessor.h"

#include <QGridLayout>
#include <QLabel>
48
49
#include <QPushButton>
#include <QVBoxLayout>
50

schultezub's avatar
schultezub committed
51
namespace campvis {
52

schultezub's avatar
schultezub committed
53
    Geometry1DTransferFunctionEditor::Geometry1DTransferFunctionEditor(Geometry1DTransferFunction* tf, QWidget* parent /*= 0*/)
54
55
56
        : AbstractTransferFunctionEditor(tf, parent)
        , _layout(0)
        , _canvas(0)
57
58
        , _lblIntensityLeft(0)
        , _lblIntensityRight(0)
59
        , _btnAddGeometry(0)
60
    {
61
        _selectedGeometry = 0;
62
        setupGUI();
schultezub's avatar
schultezub committed
63
        tf->s_geometryCollectionChanged.connect(this, &Geometry1DTransferFunctionEditor::onGeometryCollectionChanged);
64
        updateManipulators();
65
        setEventTypes(tgt::Event::MOUSEPRESSEVENT);
66
67
    }

schultezub's avatar
schultezub committed
68
    Geometry1DTransferFunctionEditor::~Geometry1DTransferFunctionEditor() {
69
70
71
72
73
74
75
76
77
78
79
        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;
        }

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

schultezub's avatar
schultezub committed
86
87
    void Geometry1DTransferFunctionEditor::updateWidgetFromProperty() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
88
89
        _lblIntensityLeft->setText(QString::number(gtf->getIntensityDomain().x));
        _lblIntensityRight->setText(QString::number(gtf->getIntensityDomain().y));
90
        invalidate();
91
92
    }

schultezub's avatar
schultezub committed
93
94
    void Geometry1DTransferFunctionEditor::paint() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
95
        gtf->lock();
schultezub's avatar
schultezub committed
96
        const std::vector<TFGeometry1D*>& geometries = gtf->getGeometries();
97
98
        const tgt::vec2& intensityDomain = gtf->getIntensityDomain();

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

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

112
        // renderIntoEditor TF geometries
schultezub's avatar
schultezub committed
113
        for (std::vector<TFGeometry1D*>::const_iterator it = geometries.begin(); it != geometries.end(); ++it) {
114
            (*it)->renderIntoEditor();
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
141
        }

        // 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;
142
                }
143
                glEnd();
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
171
        {
            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();
172
173
174
175
        }

        LGL_ERROR;
        glPopAttrib();
176
177

        gtf->unlock();
178
179
    }

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

schultezub's avatar
schultezub committed
188
    void Geometry1DTransferFunctionEditor::mousePressEvent(tgt::MouseEvent* e) {
189
        if (_selectedGeometry != 0 && e->modifiers() & tgt::Event::CTRL) {
schultezub's avatar
schultezub committed
190
191
192
193
            TFGeometry1D* g = _selectedGeometry->getGeometry();
            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>::const_iterator lb = std::upper_bound(kpts.begin(), kpts.end(), kp);
194
195
196
197
198
199
200
201
202
203
204
205
206
207
            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;
208
            invalidate();
209
210
211
212
            e->ignore();
        }
    }

schultezub's avatar
schultezub committed
213
214
    void Geometry1DTransferFunctionEditor::invalidate() {
        GLJobProc.enqueueJob(_canvas, new CallMemberFuncJob<Geometry1DTransferFunctionEditor>(this, &Geometry1DTransferFunctionEditor::paint), OpenGLJobProcessor::PaintJob);
215
216
    }

schultezub's avatar
schultezub committed
217
218
    void Geometry1DTransferFunctionEditor::setupGUI() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

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

243
244
245
246
247
248
249
250
251
252
        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()));

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

schultezub's avatar
schultezub committed
257
    void Geometry1DTransferFunctionEditor::updateManipulators() {
258
259
        tbb::mutex::scoped_lock lock(_localMutex);

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

schultezub's avatar
schultezub committed
271
272
273
        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) {
274
            // Add manipulator for the whole geometry and register it as event handler:
275
            WholeTFGeometryManipulator* wtf = new WholeTFGeometryManipulator(_canvas->getSize(), *git);
276
277
            _manipulators.push_back(wtf);
            _canvas->getEventHandler()->addListenerToFront(wtf);
schultezub's avatar
schultezub committed
278
            wtf->s_selected.connect(this, &Geometry1DTransferFunctionEditor::onWholeTFGeometryManipulatorSelected);
279
280

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

287
        _canvas->getEventHandler()->addListenerToFront(this);
288
289
    }

schultezub's avatar
schultezub committed
290
    void Geometry1DTransferFunctionEditor::onGeometryCollectionChanged() {
291
292
293
        updateManipulators();
    }

schultezub's avatar
schultezub committed
294
    void Geometry1DTransferFunctionEditor::onWholeTFGeometryManipulatorSelected(WholeTFGeometryManipulator* wtf /* :) */) {
295
        _selectedGeometry = wtf;
296
        invalidate();
297
298
    }

schultezub's avatar
schultezub committed
299
300
301
    void Geometry1DTransferFunctionEditor::onBtnAddGeometryClicked() {
        Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
        gtf->addGeometry(TFGeometry1D::createQuad(tgt::vec2(.4f, .6f), tgt::col4(196), tgt::col4(196)));
302
303
    }

schultezub's avatar
schultezub committed
304
    void Geometry1DTransferFunctionEditor::onBtnRemoveGeometryClicked() {
305
306
307
        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
308
309
            Geometry1DTransferFunction* gtf = static_cast<Geometry1DTransferFunction*>(_transferFunction);
            TFGeometry1D* geometryToRemove = _selectedGeometry->getGeometry();
310
311
312
313
314
315
316
317
318

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

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

328
329

}