Commit 60e29112 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'refactor_tf_property' of /mnt/bigone/git/repositories/berge/campvis into development

parents b77f179e f211c483
......@@ -241,7 +241,7 @@ namespace campvis {
_lblTimestamp->setText("Timestamp: " + QString::number(handles.front().second.getTimestamp()));
if (const ImageData* tester = dynamic_cast<const ImageData*>(handles.front().second.getData())) {
_canvas->p_transferFunction.getTF()->setImageHandle(handles.front().second);
_canvas->p_transferFunction.setImageHandle(handles.front().second);
std::ostringstream ss;
ss << tester->getSize();
......@@ -296,7 +296,7 @@ namespace campvis {
_lblName->setText(QString::number(handles.size()) + " DataHandles selected");
_lblTimestamp->setText("Timestamp: n/a");
_canvas->p_transferFunction.getTF()->setImageHandle(DataHandle(0));
_canvas->p_transferFunction.setImageHandle(DataHandle(0));
}
_lblLocalMemoryFootprint->setText("Local Memory Footprint: " + humanizeBytes(_localFootprint));
_lblVideoMemoryFootprint->setText("Video Memory Footprint: " + humanizeBytes(_videoFootprint));
......
......@@ -24,11 +24,13 @@
#include "abstracttransferfunctioneditor.h"
#include "core/classification/abstracttransferfunction.h"
#include "core/properties/transferfunctionproperty.h"
namespace campvis {
AbstractTransferFunctionEditor::AbstractTransferFunctionEditor(AbstractTransferFunction* tf, QWidget* parent /*= 0*/)
AbstractTransferFunctionEditor::AbstractTransferFunctionEditor(TransferFunctionProperty* prop, AbstractTransferFunction* tf, QWidget* parent /*= 0*/)
: QWidget(parent)
, _tfProperty(prop)
, _transferFunction(tf)
{
_ignorePropertyUpdates = 0;
......@@ -43,4 +45,9 @@ namespace campvis {
if (_ignorePropertyUpdates == 0)
updateWidgetFromProperty();
}
const TransferFunctionProperty::IntensityHistogramType* AbstractTransferFunctionEditor::getIntensityHistogram() const {
return _tfProperty->getIntensityHistogram();
}
}
\ No newline at end of file
......@@ -26,6 +26,9 @@
#define ABSTRACTTRANSFERFUNCTIONEDITOR_H__
#include "sigslot/sigslot.h"
#include "core/properties/transferfunctionproperty.h"
#include <QBoxLayout>
#include <QLabel>
#include <QWidget>
......@@ -43,10 +46,11 @@ namespace campvis {
public:
/**
* Creates a new editor widget for the for the AbstractTransferFunction \a tf.
* \param prop TransferFunctionProperty to generate the editor for.
* \param tf The transfer function the editor shall handle.
* \param parent Parent Qt widget
*/
AbstractTransferFunctionEditor(AbstractTransferFunction* tf, QWidget* parent = 0);
AbstractTransferFunctionEditor(TransferFunctionProperty* prop, AbstractTransferFunction* tf, QWidget* parent = 0);
/**
* Destructor
......@@ -59,6 +63,13 @@ namespace campvis {
*/
virtual void updateWidgetFromProperty() = 0;
/**
* Tries to get the intensity histogram of the image stored in the Tf's property. May return 0.
* \return The intensity histogram of the image stored in the TF's property's data handle.
*/
const TransferFunctionProperty::IntensityHistogramType* getIntensityHistogram() const;
TransferFunctionProperty* _tfProperty; ///< The parent TransferFunctionProperty of this editor
AbstractTransferFunction* _transferFunction; ///< The transfer function this widget handles
/// Semaphore acts as flag whether the widget shall ignore incoming signals from properties being updated.
......
......@@ -46,8 +46,8 @@
namespace campvis {
Geometry1DTransferFunctionEditor::Geometry1DTransferFunctionEditor(Geometry1DTransferFunction* tf, QWidget* parent /*= 0*/)
: AbstractTransferFunctionEditor(tf, parent)
Geometry1DTransferFunctionEditor::Geometry1DTransferFunctionEditor(TransferFunctionProperty* prop, Geometry1DTransferFunction* tf, QWidget* parent /*= 0*/)
: AbstractTransferFunctionEditor(prop, tf, parent)
, _logScale(true)
, _layout(0)
, _canvas(0)
......@@ -111,7 +111,7 @@ namespace campvis {
LGL_ERROR;
// render histogram if existent
const AbstractTransferFunction::IntensityHistogramType* ih = gtf->getIntensityHistogram();
const TransferFunctionProperty::IntensityHistogramType* ih = getIntensityHistogram();
if (ih != 0) {
size_t numBuckets = ih->getNumBuckets(0);
if (numBuckets > 0) {
......
......@@ -56,10 +56,11 @@ namespace campvis {
public:
/**
* Creates a new editor widget for the for the TransferFunctionProperty \a property.
* \param prop TransferFunctionProperty to generate the editor for.
* \param tf The transfer function the editor shall handle.
* \param parent Parent Qt widget
*/
Geometry1DTransferFunctionEditor(Geometry1DTransferFunction* tf, QWidget* parent = 0);
Geometry1DTransferFunctionEditor(TransferFunctionProperty* prop, Geometry1DTransferFunction* tf, QWidget* parent = 0);
/**
* Destructor
......
......@@ -45,8 +45,8 @@
namespace campvis {
Geometry2DTransferFunctionEditor::Geometry2DTransferFunctionEditor(Geometry2DTransferFunction* tf, QWidget* parent /*= 0*/)
: AbstractTransferFunctionEditor(tf, parent)
Geometry2DTransferFunctionEditor::Geometry2DTransferFunctionEditor(TransferFunctionProperty* prop, Geometry2DTransferFunction* tf, QWidget* parent /*= 0*/)
: AbstractTransferFunctionEditor(prop, tf, parent)
, _layout(0)
, _canvas(0)
, _lblIntensityLeft(0)
......@@ -115,7 +115,7 @@ namespace campvis {
}
// render histogram if existent
const AbstractTransferFunction::IntensityHistogramType* ih = gtf->getIntensityHistogram();
const TransferFunctionProperty::IntensityHistogramType* ih = getIntensityHistogram();
if (ih != 0) {
size_t numBuckets = ih->getNumBuckets(0);
if (numBuckets > 0) {
......
......@@ -55,10 +55,11 @@ namespace campvis {
public:
/**
* Creates a new editor widget for the for the TransferFunctionProperty \a property.
* \param prop TransferFunctionProperty to generate the editor for.
* \param tf The transfer function the editor shall handle.
* \param parent Parent Qt widget
*/
Geometry2DTransferFunctionEditor(Geometry2DTransferFunction* tf, QWidget* parent = 0);
Geometry2DTransferFunctionEditor(TransferFunctionProperty* prop, Geometry2DTransferFunction* tf, QWidget* parent = 0);
/**
* Destructor
......
......@@ -33,8 +33,8 @@
namespace campvis {
SimpleTransferFunctionEditor::SimpleTransferFunctionEditor(SimpleTransferFunction* tf, QWidget* parent /*= 0*/)
: AbstractTransferFunctionEditor(tf, parent)
SimpleTransferFunctionEditor::SimpleTransferFunctionEditor(TransferFunctionProperty* prop, SimpleTransferFunction* tf, QWidget* parent /*= 0*/)
: AbstractTransferFunctionEditor(prop, tf, parent)
, _layout(0)
, _lblColorLeft(0)
, _lblColorRight(0)
......
......@@ -44,10 +44,11 @@ namespace campvis {
public:
/**
* Creates a new editor widget for the for the TransferFunctionProperty \a property.
* \param prop TransferFunctionProperty to generate the editor for.
* \param tf The transfer function the editor shall handle.
* \param parent Parent Qt widget
*/
SimpleTransferFunctionEditor(SimpleTransferFunction* tf, QWidget* parent = 0);
SimpleTransferFunctionEditor(TransferFunctionProperty* prop, SimpleTransferFunction* tf, QWidget* parent = 0);
/**
* Destructor
......
......@@ -34,21 +34,26 @@
#include "core/classification/geometry2dtransferfunction.h"
#include "core/classification/simpletransferfunction.h"
#include "core/properties/transferfunctionproperty.h"
namespace campvis {
AbstractTransferFunctionEditor* TransferFunctionEditorFactory::createEditor(AbstractTransferFunction* tf) {
AbstractTransferFunctionEditor* TransferFunctionEditorFactory::createEditor(TransferFunctionProperty* prop) {
tgtAssert(prop != 0, "Property must not be 0.");
AbstractTransferFunction* tf = prop->getTF();
tgtAssert(tf != 0, "Transfer function must not be 0.");
if (SimpleTransferFunction* tester = dynamic_cast<SimpleTransferFunction*>(tf)) {
return new SimpleTransferFunctionEditor(tester);
return new SimpleTransferFunctionEditor(prop, tester);
}
if (Geometry1DTransferFunction* tester = dynamic_cast<Geometry1DTransferFunction*>(tf)) {
return new Geometry1DTransferFunctionEditor(tester);
return new Geometry1DTransferFunctionEditor(prop, tester);
}
if (Geometry2DTransferFunction* tester = dynamic_cast<Geometry2DTransferFunction*>(tf)) {
return new Geometry2DTransferFunctionEditor(tester);
return new Geometry2DTransferFunctionEditor(prop, tester);
}
return 0;
......
......@@ -28,6 +28,7 @@
namespace campvis {
class AbstractTransferFunction;
class AbstractTransferFunctionEditor;
class TransferFunctionProperty;
/**
* Factory class offering the static method createEditor to create transfer function editors
......@@ -38,10 +39,10 @@ namespace campvis {
/**
* Creates the corresponding TransferFunctionEditor for the given transfer function \a tf.
* \note The callee has to take the ownership of the returned pointer.
* \param tf Transfer function to generate the editor for.
* \param prop TransferFunctionProperty to generate the editor for.
* \return A new transfer function editor for the given transfer function (depending on its type).
*/
static AbstractTransferFunctionEditor* createEditor(AbstractTransferFunction* tf);
static AbstractTransferFunctionEditor* createEditor(TransferFunctionProperty* prop);
private:
/// Shall not instantiate
......
......@@ -34,6 +34,7 @@
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QCheckBox>
namespace campvis {
TransferFunctionPropertyWidget::TransferFunctionPropertyWidget(TransferFunctionProperty* property, QWidget* parent /*= 0*/)
......@@ -43,6 +44,8 @@ namespace campvis {
, _lblDomain(0)
, _spinDomainLeft(0)
, _spinDomainRight(0)
, _cbAutoFitDomainToImage(0)
, _btnFitDomainToImage(0)
, _btnEditTF(0)
, _dockWidget(0)
, _editor(0)
......@@ -51,7 +54,7 @@ namespace campvis {
_gridLayout = new QGridLayout(_widget);
_widget->setLayout(_gridLayout);
_lblDomain = new QLabel("Intensity Domain: ", _widget);
_lblDomain = new QLabel("Window:", _widget);
_gridLayout->addWidget(_lblDomain, 0, 0);
......@@ -70,38 +73,30 @@ namespace campvis {
_btnFitDomainToImage = new QPushButton("Fit", _widget);
_gridLayout->addWidget(_btnFitDomainToImage, 0, 3);
_cbAutoFitDomainToImage = new QCheckBox("Auto", _widget);
_gridLayout->addWidget(_cbAutoFitDomainToImage, 0, 4);
_btnEditTF = new QPushButton("Edit Transfer Function", _widget);
_gridLayout->addWidget(_btnEditTF, 1, 1, 1, 3);
_gridLayout->addWidget(_btnEditTF, 1, 1, 1, 4);
addWidget(_widget);
onTransferFunctionImageHandleChanged();
updateWidgetFromProperty();
property->getTF()->s_imageHandleChanged.connect(this, &TransferFunctionPropertyWidget::onTransferFunctionImageHandleChanged);
connect(_spinDomainLeft, SIGNAL(valueChanged(double)), this, SLOT(onDomainChanged(double)));
connect(_spinDomainRight, SIGNAL(valueChanged(double)), this, SLOT(onDomainChanged(double)));
connect(_btnEditTF, SIGNAL(clicked(bool)), this, SLOT(onEditClicked(bool)));
connect(_btnFitDomainToImage, SIGNAL(clicked(bool)), this, SLOT(onFitClicked(bool)));
connect(_cbAutoFitDomainToImage, SIGNAL(stateChanged(int)), this, SLOT(onAutoFitDomainToImageChanged(int)));
property->s_autoFitWindowToDataChanged.connect(this, &TransferFunctionPropertyWidget::onTransferFunctionAutoFitWindowToDataChanged);
}
TransferFunctionPropertyWidget::~TransferFunctionPropertyWidget() {
static_cast<TransferFunctionProperty*>(_property)->getTF()->s_imageHandleChanged.disconnect(this);
static_cast<TransferFunctionProperty*>(_property)->s_autoFitWindowToDataChanged.disconnect(this);
delete _dockWidget;
}
void TransferFunctionPropertyWidget::onTransferFunctionImageHandleChanged() {
DataHandle dh = static_cast<TransferFunctionProperty*>(_property)->getTF()->getImageHandle();
if (dh.getData() != 0) {
const ImageRepresentationLocal* idl = dynamic_cast<const ImageRepresentationLocal*>(dh.getData());
if (idl != 0) {
Interval<float> intensityInterval = idl->getNormalizedIntensityRange();
// _spinDomainLeft->setMinimum(intensityInterval.getLeft());
// _spinDomainRight->setMaximum(intensityInterval.getRight());
}
}
}
void TransferFunctionPropertyWidget::updateWidgetFromProperty() {
TransferFunctionProperty* prop = static_cast<TransferFunctionProperty*>(_property);
AbstractTransferFunction* tf = prop->getTF();
......@@ -116,6 +111,10 @@ namespace campvis {
_spinDomainRight->setMinimum(domain.x);
_spinDomainRight->setValue(domain.y);
_spinDomainRight->blockSignals(false);
_cbAutoFitDomainToImage->blockSignals(true);
_cbAutoFitDomainToImage->setChecked(prop->getAutoFitWindowToData());
_cbAutoFitDomainToImage->blockSignals(false);
}
void TransferFunctionPropertyWidget::onDomainChanged(double value) {
......@@ -131,7 +130,7 @@ namespace campvis {
void TransferFunctionPropertyWidget::onEditClicked(bool checked) {
if (_editor == 0) {
TransferFunctionProperty* prop = static_cast<TransferFunctionProperty*>(_property);
_editor = TransferFunctionEditorFactory::createEditor(prop->getTF());
_editor = TransferFunctionEditorFactory::createEditor(prop);
_dockWidget = new QDockWidget("Transfer Function Editor");
_dockWidget->setWidget(_editor);
......@@ -146,7 +145,7 @@ namespace campvis {
TransferFunctionProperty* prop = static_cast<TransferFunctionProperty*>(_property);
AbstractTransferFunction* tf = prop->getTF();
DataHandle dh = tf->getImageHandle();
DataHandle dh = prop->getImageHandle();
if (dh.getData() != 0) {
const ImageRepresentationLocal* idl = static_cast<const ImageData*>(dh.getData())->getRepresentation<ImageRepresentationLocal>();
if (idl != 0) {
......@@ -156,5 +155,14 @@ namespace campvis {
}
}
void TransferFunctionPropertyWidget::onAutoFitDomainToImageChanged(int state) {
TransferFunctionProperty* prop = static_cast<TransferFunctionProperty*>(_property);
prop->setAutoFitWindowToData(state != Qt::Unchecked);
}
void TransferFunctionPropertyWidget::onTransferFunctionAutoFitWindowToDataChanged() {
emit s_propertyChanged(_property);
}
}
\ No newline at end of file
......@@ -33,6 +33,7 @@ class QDoubleSpinBox;
class QGridLayout;
class QLabel;
class QPushButton;
class QCheckBox;
namespace campvis {
class AbstractTransferFunctionEditor;
......@@ -56,17 +57,17 @@ namespace campvis {
*/
virtual ~TransferFunctionPropertyWidget();
/**
* Slot to be called when the propertie's TF changed its image DataHandle.
* Resets the intensity domain borders.
*/
void onTransferFunctionImageHandleChanged();
protected:
/**
* Gets called when the property has changed, so that widget can update its state.
*/
virtual void updateWidgetFromProperty();
/**
* Slot to be called from property when the property's flag whether to auto fit the TF window has changed.
*/
void onTransferFunctionAutoFitWindowToDataChanged();
private slots:
/// slot called when one of the intensity domain spin edits has changed
......@@ -75,6 +76,8 @@ namespace campvis {
void onEditClicked(bool checked);
/// slot called when _btnFitDomainToImage clicked
void onFitClicked(bool checked);
/// slot called when _cbAutoFitDomainToImage changed
void onAutoFitDomainToImageChanged(int state);
private:
QWidget* _widget; ///< Widget grouping the widgets together
......@@ -83,6 +86,7 @@ namespace campvis {
QLabel* _lblDomain; ///< intensity domain label
QDoubleSpinBox* _spinDomainLeft; ///< spin edit for intensity domain lower bound
QDoubleSpinBox* _spinDomainRight; ///< spin edit for intensity domain upper bound
QCheckBox* _cbAutoFitDomainToImage; ///< Checkbox for the flag whether to automatically fit the TF domain to new image data
QPushButton* _btnFitDomainToImage; ///< button for fitting the intensity domain to the image
QPushButton* _btnEditTF; ///< button for showing the TF editor widget
......
......@@ -41,30 +41,24 @@ namespace campvis {
: _size(size)
, _intensityDomain(intensityDomain)
, _texture(0)
, _imageHandle(0)
, _intensityHistogram(0)
{
_dirtyTexture = false;
_dirtyHistogram = false;
}
AbstractTransferFunction::~AbstractTransferFunction() {
if (_texture != 0)
LWARNING("Called AbstractTransferFunction dtor without proper deinitialization - you just wasted resources!");
delete _intensityHistogram;
}
void AbstractTransferFunction::deinit() {
delete _texture;
_texture = 0;
_imageHandle = DataHandle(0);
}
void AbstractTransferFunction::bind(tgt::Shader* shader, const tgt::TextureUnit& texUnit, const std::string& transFuncUniform /*= "_transferFunction"*/, const std::string& transFuncParamsUniform /*= "_transferFunctionParameters"*/) {
tgtAssert(shader != 0, "Shader must not be 0.");
{
// TODO: lock here or in createTexture?
tbb::mutex::scoped_lock lock(_localMutex);
if (_texture == 0 || _dirtyTexture) {
shader->deactivate();
......@@ -93,23 +87,14 @@ namespace campvis {
shader->setIgnoreUniformLocationError(tmp);
}
void AbstractTransferFunction::uploadTexture() {
{
tbb::mutex::scoped_lock lock(_localMutex);
if (_texture == 0 || _dirtyTexture) {
createTexture();
}
}
}
void AbstractTransferFunction::setIntensityDomain(const tgt::vec2& newDomain) {
tgtAssert(newDomain.x <= newDomain.y, "Intensity domain is not a valid interval.");
{
tbb::mutex::scoped_lock lock(_localMutex);
_intensityDomain = newDomain;
}
_dirtyHistogram = true;
s_intensityDomainChanged();
s_changed();
}
......@@ -118,7 +103,6 @@ namespace campvis {
}
const tgt::Texture* AbstractTransferFunction::getTexture() {
// TODO: lock here or in createTexture?
{
tbb::mutex::scoped_lock lock(_localMutex);
if (_texture == 0 || _dirtyTexture) {
......@@ -127,49 +111,5 @@ namespace campvis {
}
return _texture;
}
DataHandle AbstractTransferFunction::getImageHandle() const {
return _imageHandle;
}
void AbstractTransferFunction::setImageHandle(DataHandle imageHandle) {
tgtAssert(
imageHandle.getData() == 0 || dynamic_cast<const ImageData*>(imageHandle.getData()) != 0,
"The data in the image handle must either be 0 or point to a valid ImageData object!");
_imageHandle = imageHandle;
_dirtyHistogram = true;
s_imageHandleChanged();
}
void AbstractTransferFunction::computeIntensityHistogram() const {
delete _intensityHistogram;
_intensityHistogram = 0;
ImageRepresentationLocal::ScopedRepresentation repLocal(_imageHandle);
if (repLocal != 0) {
float mins = _intensityDomain.x;
float maxs = _intensityDomain.y;
size_t numBuckets = std::min(WeaklyTypedPointer::numBytes(repLocal->getWeaklyTypedPointer()._baseType) << 8, static_cast<size_t>(512));
_intensityHistogram = new IntensityHistogramType(&mins, &maxs, &numBuckets);
tbb::parallel_for(tbb::blocked_range<size_t>(0, repLocal->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
float value = repLocal->getElementNormalized(i, 0);
_intensityHistogram->addSample(&value);
}
});
}
_dirtyHistogram = false;
}
const AbstractTransferFunction::IntensityHistogramType* AbstractTransferFunction::getIntensityHistogram() const {
if (_dirtyHistogram) {
computeIntensityHistogram();
}
return _intensityHistogram;
}
}
\ No newline at end of file
......@@ -55,8 +55,6 @@ namespace campvis {
* b) All OpenGL-related methods must be called by a thread with a valid and locked OpenGL
* context. Even though other internals might be changed meanwhile, this ensures that
* the OpenGL stuff (e.g. the texture) stays valid for this time.
*
* \todo Check thread-safety, the private local lock is probably not the best design.
*/
class AbstractTransferFunction {
public:
......@@ -101,12 +99,6 @@ namespace campvis {
*/
void bind(tgt::Shader* shader, const tgt::TextureUnit& texUnit, const std::string& transFuncUniform = "_transferFunction", const std::string& transFuncParamsUniform = "_transferFunctionParams");
/**
* Creates the OpenGL texture.
* \note Calling thread must have a valid OpenGL context.
*/
void uploadTexture();
/**
* Sets the intensity domain where the transfer function is mapped to during classification.
* \param newDomain new intensity domain
......@@ -125,34 +117,11 @@ namespace campvis {
* \return _texture
*/
const tgt::Texture* getTexture();
/**
* Returns a DataHandle to the image for this transfer function, its pointer may be 0.
* \note If the data in \a imageHandle is not 0, it points to a valid ImageData object.
* \return _imageHandle, its pointer may be 0.
*/
DataHandle getImageHandle() const;
/**
* Sets the DataHandle for this transfer function, its pointer may be 0.
* \note If the data in \a imageHandle is not 0, it must point to a valid ImageData object.
* \param imageHandle The new DataHandle for this transfer function, if its pointer is
* not 0 it must point to a valid ImageData object.
*/
void setImageHandle(DataHandle imageHandle);
/**
* Returns the intensity histogram
* \todo This is NOT thread-safe!
* \return _intensityHistogram
*/
const IntensityHistogramType* getIntensityHistogram() const;
/// Signal emitted when transfer function has changed.
sigslot::signal0<> s_changed;
/// Signal emitted when the image DataHandle for this TF has changed.
sigslot::signal0<> s_imageHandleChanged;
/// Signal emitted when the intensity domain has changed
sigslot::signal0<> s_intensityDomainChanged;
protected:
/**
......@@ -171,11 +140,7 @@ namespace campvis {
tgt::Texture* _texture; ///< OpenGL lookup texture storing the TF
tbb::atomic<bool> _dirtyTexture; ///< Flag whether the OpenGL texture has to be updated
DataHandle _imageHandle; ///< DataHandle to the image for this transfer function. May be 0.
mutable IntensityHistogramType* _intensityHistogram; ///< Intensity histogram of the intensity in _imageHandle for the current _intensityDomain
mutable tbb::atomic<bool> _dirtyHistogram; ///< Flag whether the intensity histogram has to be updated.
mutable tbb::mutex _localMutex; ///< mutex protecting the local members
mutable tbb::mutex _localMutex; ///< mutex protecting the local members
static const std::string loggerCat_;
......
......@@ -46,13 +46,12 @@ namespace campvis {
ImageRepresentationLocal::ImageRepresentationLocal(ImageData* parent, WeaklyTypedPointer::BaseType baseType)
: GenericAbstractImageRepresentation<ImageRepresentationLocal>(parent)
, _baseType(baseType)
, _intensityHistogram(0)
{
_intensityRangeDirty = true;
}
ImageRepresentationLocal::~ImageRepresentationLocal() {
delete _intensityHistogram;
}