Commit 75cd24be authored by schultezub's avatar schultezub
Browse files

Further work on GeometryTransferFunctionEditor:

 * introducing TFGeometryManipulator for managing GUI stuff and manipulating a TFGeometry
 * added TFGeometry::s_changed signal

git-svn-id: https://camplinux.in.tum.de/svn/campvis/trunk@313 bb408c1c-ae56-11e1-83d9-df6b3e0c105e
parent 3e903cba
......@@ -34,6 +34,8 @@
#include "tgt/qt/qtthreadedcanvas.h"
#include "application/gui/qtcolortools.h"
#include "application/gui/properties/tfgeometrymanipulator.h"
#include "core/classification/geometrytransferfunction.h"
#include "core/classification/tfgeometry.h"
#include "core/datastructures/imagedatalocal.h"
......@@ -49,110 +51,99 @@ namespace TUMVis {
: AbstractTransferFunctionEditor(tf, parent)
, _layout(0)
, _canvas(0)
, _shader(0)
, _lblIntensityLeft(0)
, _lblIntensityRight(0)
{
_layout = new QGridLayout(this);
setLayout(_layout);
_canvas = CtxtMgr.createContext("tfcanvas", "", tgt::ivec2(256, 128), tgt::GLCanvas::RGBA_BUFFER, this, false);
_canvas->doneCurrent();
GLJobProc.registerContext(_canvas);
_canvas->getEventHandler()->addListenerToBack(this);
_canvas->setPainter(this, false);
_layout->addWidget(_canvas, 0, 0);
GLJobProc.enqueueJob(_canvas, new CallMemberFuncJob<GeometryTransferFunctionEditor>(this, &GeometryTransferFunctionEditor::init), OpenGLJobProcessor::SerialJob);
setupGUI();
tf->s_geometryCollectionChanged.connect(this, &GeometryTransferFunctionEditor::onGeometryCollectionChanged);
updateManipulators();
}
GeometryTransferFunctionEditor::~GeometryTransferFunctionEditor() {
GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
gtf->s_geometryCollectionChanged.disconnect(this);
// TODO: this needs to be done, but we can not ensure that GLJobProc is still existant during deconstruction...
//GLJobProc.deregisterContext(_canvas);
}
void GeometryTransferFunctionEditor::updateWidgetFromProperty() {
GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
_lblIntensityLeft->setText(QString::number(gtf->getIntensityDomain().x));
_lblIntensityRight->setText(QString::number(gtf->getIntensityDomain().y));
invalidate();
}
void GeometryTransferFunctionEditor::init() {
tgtAssert(_shader == 0, "GeometryTransferFunctionEditor already inited.");
}
void GeometryTransferFunctionEditor::paint() {
GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
const std::vector<TFGeometry*>& geometries = gtf->getGeometries();
const tgt::vec2& intensityDomain = gtf->getIntensityDomain();
// TODO: get rid of intermediate mode?
glPushAttrib(GL_ALL_ATTRIB_BITS);
glViewport(0, 0, _canvas->width(), _canvas->height());
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glViewport(0, 0, _canvas->width(), _canvas->height());
glOrtho(0, 1 , 0, 1, -1, 1);
glClearColor(1.f, 1.f, 1.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
LGL_ERROR;
// const DataHandle* dh = gtf->getImageHandle();
// if (dh != 0) {
// const ImageDataLocal* idl = dynamic_cast<const ImageDataLocal*>(dh->getData());
// if (idl != 0) {
// const ImageDataLocal::IntensityHistogramType& ih = idl->getIntensityHistogram();
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;
glPushMatrix();
glOrtho(0,1 , 0, 1, -1, 1);
glBegin(GL_QUADS);
glColor4f(1.f, .75f, 0.f, .25f);
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;
}
glEnd();
glPopMatrix();
// 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;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
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;
}
glEnd();
glDisable(GL_BLEND);
}
// }
// glBegin(GL_QUADS);
// glColor3f(1.f, 0.f, 0.f);
// glVertex2f(0.f, 0.f);
// glColor3f(1.f, 1.f, 0.f);
// glVertex2f(1.f, 0.f);
// glColor3f(0.f, 1.f, 0.f);
// glVertex2f(1.f, 1.f);
// glColor3f(0.f, 0.f, 1.f);
// glVertex2f(0.f, 1.f);
// glEnd();
glOrtho(0, 1, 0, 1, -1, 1);
for (std::vector<TFGeometry*>::const_iterator it = geometries.begin(); it != geometries.end(); ++it) {
}
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();
LGL_ERROR;
glPopMatrix();
glPopAttrib();
}
void GeometryTransferFunctionEditor::sizeChanged(const tgt::ivec2&) {
void GeometryTransferFunctionEditor::sizeChanged(const tgt::ivec2& size) {
for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
(*it)->setViewportSize(size);
}
invalidate();
}
......@@ -160,5 +151,63 @@ namespace TUMVis {
GLJobProc.enqueueJob(_canvas, new CallMemberFuncJob<GeometryTransferFunctionEditor>(this, &GeometryTransferFunctionEditor::paint), OpenGLJobProcessor::PaintJob);
}
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);
_layout->setColumnStretch(2, 1);
_layout->setRowStretch(2, 1);
}
void GeometryTransferFunctionEditor::updateManipulators() {
// clear and delete former stuff
_canvas->getEventHandler()->clear();
for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
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:
_manipulators.push_back(new WholeTFGeometryManipulator(_canvas->getSize(), gtf, *git));
_canvas->getEventHandler()->addListenerToBack(_manipulators.back());
// 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));
_canvas->getEventHandler()->addListenerToBack(_manipulators.back());
}
}
}
void GeometryTransferFunctionEditor::onGeometryCollectionChanged() {
updateManipulators();
}
}
\ No newline at end of file
......@@ -31,6 +31,7 @@
#include "tgt/painter.h"
#include "tgt/event/eventlistener.h"
#include "core/classification/tfgeometry.h"
#include "application/gui/properties/abstracttransferfunctioneditor.h"
class QGridLayout;
......@@ -44,11 +45,12 @@ namespace tgt {
namespace TUMVis {
class ColorPickerWidget;
class GeometryTransferFunction;
class AbstractTFGeometryManipulator;
/**
* Editor widget for a GeometryTransferFunction.
*/
class GeometryTransferFunctionEditor : public AbstractTransferFunctionEditor, public tgt::EventListener, public tgt::Painter {
class GeometryTransferFunctionEditor : public AbstractTransferFunctionEditor, public tgt::Painter {
Q_OBJECT;
public:
......@@ -64,11 +66,6 @@ namespace TUMVis {
*/
virtual ~GeometryTransferFunctionEditor();
/**
* Initializes the shader for the OpenGL canvas.
*/
void init();
/**
* Performs the painting.
......@@ -78,26 +75,41 @@ namespace TUMVis {
/// \see tgt::Painter::sizeChanged
virtual void sizeChanged(const tgt::ivec2&);
/**
* Slot to be called when the geometry vector of the transfer function has changed.
*/
void onGeometryCollectionChanged();
protected:
/**
* Gets called when the property has changed, so that widget can update its state.
*/
virtual void updateWidgetFromProperty();
/**
* Updates the GeometryManipulator vector.
* Invalidates all of its iterators.
*/
void updateManipulators();
/**
* To be called when the canvas is invalidated, issues new paint job.
*/
void invalidate();
/**
* Sets up the GUI stuff.
*/
void setupGUI();
private slots:
std::vector<AbstractTFGeometryManipulator*> _manipulators;
private:
QGridLayout* _layout;
tgt::QtThreadedCanvas* _canvas;
tgt::Shader* _shader;
QLabel* _lblIntensityLeft;
QLabel* _lblIntensityRight;
};
}
......
// ================================================================================================
//
// 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 "tfgeometrymanipulator.h"
#include "tgt/assert.h"
#include "tgt/tgt_gl.h"
#include "tgt/event/mouseevent.h"
#include "application/gui/qtcolortools.h"
#include "core/classification/geometrytransferfunction.h"
#include "core/classification/tfgeometry.h"
#include <QColorDialog>
namespace TUMVis {
AbstractTFGeometryManipulator::AbstractTFGeometryManipulator(const tgt::ivec2& viewportSize, GeometryTransferFunction* tf)
: _viewportSize(viewportSize)
, _tf(tf)
{
tgtAssert(tf != 0, "Transfer Function must not be 0.");
}
void AbstractTFGeometryManipulator::setViewportSize(const tgt::ivec2& viewportSize) {
_viewportSize = viewportSize;
}
tgt::ivec2 AbstractTFGeometryManipulator::tfToViewport(const tgt::vec2& pos) const {
// TF coordinate system is expected to be [0, 1]^n
return tgt::ivec2(tgt::iround(pos.x * _viewportSize.x), tgt::iround(pos.y * _viewportSize.y));
}
tgt::vec2 AbstractTFGeometryManipulator::viewportToTF(const tgt::ivec2& pos) const {
// TF coordinate system is expected to be [0, 1]^n
return tgt::vec2(static_cast<float>(pos.x) / static_cast<float>(_viewportSize.x), static_cast<float>(pos.y) / static_cast<float>(_viewportSize.y));
}
// ================================================================================================
KeyPointManipulator::KeyPointManipulator(const tgt::ivec2& viewportSize, GeometryTransferFunction* tf, TFGeometry* geometry, const std::vector<TFGeometry::KeyPoint>::iterator& keyPoint)
: AbstractTFGeometryManipulator(viewportSize, tf)
, _geometry(geometry)
, _keyPoint(keyPoint)
, _mousePressed(false)
//, _pressedPosition(0, 0)
{
tgtAssert(geometry != 0, "Geometry must not be 0.");
}
void KeyPointManipulator::render() {
tgt::ivec2 pos = tfToViewport(tgt::vec2(_keyPoint->_position, static_cast<float>(_keyPoint->_color.a) / 255.f));
glColor3ub(0, 0, 0);
glBegin(GL_LINE_LOOP);
glVertex2i(pos.x - MANIPULATOR_SIZE, pos.y - MANIPULATOR_SIZE);
glVertex2i(pos.x + MANIPULATOR_SIZE, pos.y - MANIPULATOR_SIZE);
glVertex2i(pos.x + MANIPULATOR_SIZE, pos.y + MANIPULATOR_SIZE);
glVertex2i(pos.x - MANIPULATOR_SIZE, pos.y + MANIPULATOR_SIZE);
glEnd();
}
void KeyPointManipulator::mousePressEvent(tgt::MouseEvent* e) {
tgt::ivec2 kppos = tfToViewport(tgt::vec2(_keyPoint->_position, static_cast<float>(_keyPoint->_color.a) / 255.f));
if ((abs(kppos.x - e->coord().x) < MANIPULATOR_SIZE) && (abs(kppos.y - _viewportSize.y + e->coord().y) < MANIPULATOR_SIZE)) {
//_valueWhenPressed = *_keyPoint;
_mousePressed = true;
e->accept();
}
}
void KeyPointManipulator::mouseReleaseEvent(tgt::MouseEvent* e) {
_mousePressed = false;
// no accept here, because other listeners probably need this signal as well
}
void KeyPointManipulator::mouseMoveEvent(tgt::MouseEvent* e) {
if (_mousePressed) {
tgt::ivec2 currentPosition = tgt::clamp(tgt::ivec2(e->coord().x, _viewportSize.y - e->coord().y), tgt::ivec2(0, 0), _viewportSize);
//tgt::vec2 displacement = viewportToTF(currentPosition - _pressedPosition);
tgt::vec2 tfCoords = viewportToTF(currentPosition);
_keyPoint->_position = tfCoords.x;
_keyPoint->_color.a = static_cast<uint8_t>(tfCoords.y * 255.f);
_geometry->s_changed();
// no accept here, because other listeners probably need this signal as well
}
}
void KeyPointManipulator::mouseDoubleClickEvent(tgt::MouseEvent* e) {
tgt::ivec2 kppos = tfToViewport(tgt::vec2(_keyPoint->_position, static_cast<float>(_keyPoint->_color.a) / 255.f));
if ((abs(kppos.x - e->coord().x) < MANIPULATOR_SIZE) && (abs(kppos.y - _viewportSize.y + e->coord().y) < MANIPULATOR_SIZE)) {
// launch a color picker dialog and set new color on success
QColor newColor = QColorDialog::getColor(QtColorTools::toQColor(_keyPoint->_color), 0, "Select New Color");
if(newColor.isValid()) {
tgt::col4 tmp = QtColorTools::toTgtColor(newColor);
_keyPoint->_color = tgt::col4(tmp.xyz(), _keyPoint->_color.a);
_geometry->s_changed();
}
e->accept();
}
}
const int KeyPointManipulator::MANIPULATOR_SIZE = 6;
// ================================================================================================
WholeTFGeometryManipulator::WholeTFGeometryManipulator(const tgt::ivec2& viewportSize, GeometryTransferFunction* tf, TFGeometry* geometry)
: AbstractTFGeometryManipulator(viewportSize, tf)
, _geometry(geometry)
{
tgtAssert(geometry != 0, "Geometry must not be 0.");
}
void WholeTFGeometryManipulator::render() {
}
}
\ No newline at end of file
// ================================================================================================
//
// 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 Universität München
// Boltzmannstr. 3, 85748 Garching b. München, 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.
//
// ================================================================================================
#ifndef TFGEOMETRYMANIPULATOR_H__
#define TFGEOMETRYMANIPULATOR_H__
#include "tgt/vector.h"
#include "tgt/matrix.h"
#include "tgt/event/eventlistener.h"
#include "core/classification/tfgeometry.h"
namespace TUMVis {
class GeometryTransferFunction;
/**
* Abstract base class for TFGeometry manipulators.
* TFGeometry manipulators encapsulate a TF Geometry (or a part of it) and offer methods
* for manipulating the TFGeometry (which is handy e.g. for TF editors...). Therefore, it
* extends the tgt::EventListener class and should implement its methods as necessary.
*/
class AbstractTFGeometryManipulator : public tgt::EventListener {
public:
/**
* Creates a new AbstractTFGeometryManipulator for the given GeometryTransferFunction.
* \param viewportSize Viewport size (extent of the OpenGL rendering canvas)
* \param tf Parent TF of the geometries to manipulate
*/
AbstractTFGeometryManipulator(const tgt::ivec2& viewportSize, GeometryTransferFunction* tf);
/**
* Pure virtual Destructor
*/
virtual ~AbstractTFGeometryManipulator() = 0 {};
/**
* Sets the new viewport size.
* \param viewportSize The new Viewport size (extent of the OpenGL rendering canvas)
*/
void setViewportSize(const tgt::ivec2& viewportSize);
/**
* Renders the manipulator to the current OpenGL context.
* \note Must be called from an active and valid OpenGL context.
*/
virtual void render() = 0;
protected:
/**
* Transforms TF coordinates to viewport coordinates.
* \param pos Coordinates in TF coordinate system.
* \return \a pos converted to viewport coordinates.
*/
tgt::ivec2 tfToViewport(const tgt::vec2& pos) const;
/**
* Transforms viewport coordinates to TF coordinates.
* \param pos Coordnates in viewport coordinate system
* \return \a pos converted to TF coordinates
*/
tgt::vec2 viewportToTF(const tgt::ivec2& pos) const;
tgt::ivec2 _viewportSize; ///< Viewport size (extent of the OpenGL rendering canvas)
GeometryTransferFunction* _tf; ///< Parent TF of the geometries to manipulate (might be handy somewhere later...)
};
// ================================================================================================
/**
* Class for manipulating the KeyPoints of a TFGeometry.
*/
class KeyPointManipulator : public AbstractTFGeometryManipulator {
public:
/**
* Creates a new KeyPointManipulator
* \param viewportSize Viewport size (extent of the OpenGL rendering canvas)
* \param tf Parent TF of the geometries to manipulate
* \param geometry Parent geometry of the KeyPoint to manipulate
* \param keyPoint Iterator to the KeyPoint to manipulate
*/
KeyPointManipulator(const tgt::ivec2& viewportSize, GeometryTransferFunction* tf, TFGeometry* geometry, const std::vector<TFGeometry::KeyPoint>::iterator& keyPoint);
/// \see AbstractTFGeometryManipulator::render
void render();
/// \see tgt::EventListener::mousePressEvent
virtual void mousePressEvent(tgt::MouseEvent* e);
<