meshlab/src/fgt/edit_quality/qualitymapperdialog.cpp
Paolo Cignoni cignoni ccbe67eeca -changed folders\files organizations. Added Common folder to contain files used by both projects
-many minor changed due to update of incude file paths

- changed representation of brightness level value in csv files
- fixed bug in apply method of filter

-other minor changes
2008-02-20 17:23:00 +00:00

1314 lines
44 KiB
C++
Raw Blame History

#include "qualitymapperdialog.h"
#include <limits>
#include <QPen>
#include <QBrush>
#include <vcg/complex/trimesh/update/color.h>
using namespace vcg;
//returns true if relative x of h1 is < then x of h2.
//if x values of h1 and h2 are the same, true is returned if relative y of h1 is < then relative y of h2
bool TfHandleCompare(TFHandle*h1, TFHandle*h2)
{ return (h1->getRelativeX() <= h2->getRelativeX()); }
//callback to manage the double-click of TFDoubleClickCatcher object
void TFDoubleClickCatcher::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{ emit TFdoubleClicked(event->scenePos()); }
//class constructor
QualityMapperDialog::QualityMapperDialog(QWidget *parent, MeshModel& m, GLArea *gla) : QDockWidget(parent), mesh(m)
{
ui.setupUi(this);
this->setWidget(ui.frame);
/*this->setFeatures(QDockWidget::AllDockWidgetFeatures);
this->setAllowedAreas(Qt::LeftDockWidgetArea);*/
this->setFloating(true);
// Setting dialog position in top right corner
QPoint p=parent->mapToGlobal(QPoint(0,0));
this->setGeometry(p.x()+(parent->width()-width()),p.y()+40,width(),height() );
this->gla = gla;
/* MOVED TO drawEqualizerHistogram
//building up histogram...
int numberOfBins = 100;
_equalizer_histogram = new Histogramf();
Frange mmmq(tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(mesh->cm));
this->ComputePerVertexQualityHistogram(mesh->cm, mmmq, _equalizer_histogram, numberOfBins);
//...histogram built*/
_histogram_info = 0;
for (int i=0; i<NUMBER_OF_EQHANDLES; i++)
_equalizerHandles[i] = 0;
//building default Transfer Function
_transferFunction = new TransferFunction( STARTUP_TF_TYPE );
_isTransferFunctionInitialized = false;
_transferFunction_info = 0;
_currentTfHandle = 0;
//initializing Transfer Function
this->initTF();
//building a catcher for double click in empty areas of transfer function view
//and adding it to the scene
_tfCatcher = new TFDoubleClickCatcher(_transferFunction_info);
_transferFunctionScene.addItem(_tfCatcher);
connect(_tfCatcher, SIGNAL(TFdoubleClicked(QPointF)), this, SLOT(on_TF_view_doubleClicked(QPointF)));
//connect(this, SIGNAL(closing()),gla,SLOT(endEdit()) );
// toggle Trackball button (?)
connect(this, SIGNAL(suspendEditToggle()),gla,SLOT(suspendEditToggle()) );
suspendEditToggle();
//gla->suspendEditToggle();
_equalizer_histogram = 0;
}
//class destructor
QualityMapperDialog::~QualityMapperDialog()
{
//removing from scene and deleting each graphical object
this->clearItems(REMOVE_ALL_ITEMS | DELETE_REMOVED_ITEMS);
//destroying info about histogram chart
if ( _histogram_info )
{
delete _histogram_info;
_histogram_info = 0;
}
//destroying transfer function object
if ( _transferFunction )
{
delete _transferFunction;
_transferFunction = 0;
}
//destroying info about histogram chart
if ( _transferFunction_info )
{
delete _transferFunction_info;
_transferFunction_info = 0;
}
//destroying transfer function double-click catcher
if ( _tfCatcher )
{
delete _tfCatcher;
_tfCatcher = 0;
}
// this->disconnect();
//sending closing dialog, to call EndEdit of plugin
emit closingDialog();
}
void QualityMapperDialog::ComputePerVertexQualityHistogram( CMeshO &m, Frange range, Histogramf *h, int bins ) // V1.0
{
h->Clear();
h->SetRange( range.minV, range.maxV, bins);
CMeshO::VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if(!(*vi).IsD()) h->Add((*vi).Q());
}
// ATTUALMENTE NON VIENE MAI UTILIZZATA (UCCIO)
GRAPHICS_ITEMS_LIST* QualityMapperDialog::clearScene(QGraphicsScene *scene, int cleanFlag)
{
// _removed_items.clear();
//deleting scene items
GRAPHICS_ITEMS_LIST allItems = _transferFunctionScene.items();
QGraphicsItem *item = 0;
foreach (item, allItems)
{
scene->removeItem( item );
_removed_items << item;
}
if ((cleanFlag & DELETE_REMOVED_ITEMS) == DELETE_REMOVED_ITEMS)
{
foreach (item, _removed_items)
{
delete item;
item = 0;
}
}
return &_removed_items;
}
//this method clears some particular types of graphical items from dialog.
//toClear value represents the logical OR of some macros that define what to clear
//returns a list of pointers to Items removed (NOT DELETED!!)
//If DELETE_REMOVED_ITEMS is passed in toClear flag, the items are destroyed too and the list returned is empty
GRAPHICS_ITEMS_LIST* QualityMapperDialog::clearItems(int toClear)
{
_removed_items.clear();
QGraphicsItem *item = 0;
if ((toClear & REMOVE_TF_HANDLE) == REMOVE_TF_HANDLE)
{
//removing TF Handles
for (int i=0; i<NUMBER_OF_CHANNELS; i++)
{
foreach( item, _transferFunctionHandles[i] )
{
((TFHandle*)item)->disconnect();
_transferFunctionScene.removeItem( item );
_removed_items << item;
}
_transferFunctionHandles[i].clear();
}
}
if ((toClear & REMOVE_EQ_HANDLE) == REMOVE_EQ_HANDLE)
{
//removing EQ Handles
//just removing! NOT DELETING!!
for (int i=0; i<NUMBER_OF_EQHANDLES; i++)
{
if ( _equalizerHandles[i] )
{
_equalizerHandles[i]->disconnect();
_equalizerHistogramScene.removeItem( (QGraphicsItem*)_equalizerHandles[i] );
_removed_items << _equalizerHandles[i];
}
}
if ((toClear & DELETE_REMOVED_ITEMS) == DELETE_REMOVED_ITEMS)
{
for (int i=0; i<NUMBER_OF_EQHANDLES; i++)
{
if ( _equalizerHandles[i] )
{
delete _equalizerHandles[i];
_equalizerHandles[i] = 0;
_removed_items.removeLast();
}
}
}
}
if ((toClear & REMOVE_TF_BG) == REMOVE_TF_BG)
{
//removing background Histogram bars of the TF
foreach( item, _transferFunctionBg )
{
//disconnecting everything connected to TF handle before removing it
_transferFunctionScene.removeItem( item );
_removed_items << item;
}
_transferFunctionBg.clear();
}
if ((toClear & REMOVE_TF_LINES) == REMOVE_TF_LINES)
{
//removing TF lines
foreach( item, _transferFunctionLines )
{
//disconnecting everything connected to TF handle before removing it
_transferFunctionScene.removeItem( item );
_removed_items << item;
}
_transferFunctionLines.clear();
}
if ((toClear & REMOVE_EQ_HISTOGRAM) == REMOVE_EQ_HISTOGRAM)
{
//removing Histogram
foreach( item, _equalizerHistogramBars )
{
//disconnecting everything connected to TF handle before removing it
_equalizerHistogramScene.removeItem( item );
_removed_items << item;
}
_equalizerHistogramBars.clear();
//_removed_items = *(this->clearScene( &_equalizerHistogramScene, toClear & DO_NOT_DELETE_REMOVED_ITEMS ));
}
if ((toClear & DELETE_REMOVED_ITEMS) == DELETE_REMOVED_ITEMS)
{
//deleting removed items
foreach(item, _removed_items)
{
if (item != 0)
{
delete item;
item = 0;
}
}
_removed_items.clear();
}
return &_removed_items;
}
//draws the charts basics (axis)
void QualityMapperDialog::drawChartBasics(QGraphicsScene& scene, CHART_INFO *chart_info)
{
//a valid chart_info must be passed
assert( chart_info != 0 );
QPen p( Qt::black, 2 );
QGraphicsItem *current_item = 0;
//drawing axis
//x axis
current_item = scene.addLine( chart_info->leftBorder, chart_info->lowerBorder, chart_info->rightBorder, chart_info->lowerBorder, p );
current_item->setZValue( 0 );
if (chart_info == _transferFunction_info)
_transferFunctionLines << current_item;
else
_equalizerHistogramBars << current_item;
//y axis
current_item = scene.addLine( chart_info->leftBorder, chart_info->upperBorder, chart_info->leftBorder, chart_info->lowerBorder, p );
current_item->setZValue( 0 );
if (chart_info == _transferFunction_info)
_transferFunctionLines << current_item;
else
_equalizerHistogramBars << current_item;
}
// Initializes equalizerHistogramView
// (This method is called only once)
bool QualityMapperDialog::initEqualizerHistogram()
{
if (_equalizer_histogram) //added by MAL 17\02\08
{
// _equalizer_histogram->disconnect(); //added by MAL 17\02\08
delete _equalizer_histogram; //added by MAL 17\02\08
_equalizer_histogram = 0;
}
_leftHandleWasInsideHistogram = true;
_rightHandleWasInsideHistogram = true;
if ( !drawEqualizerHistogram(true, true) )
return false;
//DRAWING HANDLES
QDoubleSpinBox* spinboxes[] = { ui.minSpinBox, ui.midSpinBox, ui.maxSpinBox };
qreal xStart = _histogram_info->leftBorder;
qreal xPos = 0.0f;
qreal yPos = _histogram_info->lowerBorder;
_equalizerMidHandlePercentilePosition = 0.5f;
for (int i=0; i<NUMBER_OF_EQHANDLES; i++)
{
xPos = xStart + _histogram_info->chartWidth/2.0f*i;
// _equalizerHandles[i].setColor(colors[i]);
_equalizerHandles[i] = new EqHandle(_histogram_info, Qt::black, QPointF(xPos, yPos),
(EQUALIZER_HANDLE_TYPE)i, _equalizerHandles, &_equalizerMidHandlePercentilePosition, spinboxes[i],
1, 5);
_equalizerHistogramScene.addItem((QGraphicsItem*)_equalizerHandles[i]);
}
// SETTING SPNIBOX VALUES
// (Se venissero inizializzati prima di impostare setHistogramInfo sulle handles darebbero errore nello SLOT setX delle handles.)
double singleStep = (_histogram_info->maxX - _histogram_info->minX) / _histogram_info->chartWidth;
int decimals = 0;
if (singleStep > std::numeric_limits<float>::epsilon())
{
double temp = singleStep;
while (temp < 0.1)
{
decimals++;
temp *= 10;
}
}
decimals+=2;
ui.minSpinBox->setValue(_histogram_info->minX);
//ui.minSpinBox->setRange(_histogram_info->minX, _histogram_info->maxX);
ui.minSpinBox->setRange(2*_histogram_info->minX - _histogram_info->maxX, 2*_histogram_info->maxX - _histogram_info->minX);
ui.minSpinBox->setSingleStep(singleStep);
ui.minSpinBox->setDecimals(decimals);
ui.midSpinBox->setValue((_histogram_info->maxX + _histogram_info->minX) / 2.0f);
ui.midSpinBox->setRange(_histogram_info->minX, _histogram_info->maxX);
ui.midSpinBox->setSingleStep(singleStep);
ui.midSpinBox->setDecimals(decimals);
ui.maxSpinBox->setValue(_histogram_info->maxX);
//ui.maxSpinBox->setRange(_histogram_info->minX, _histogram_info->maxX);
ui.maxSpinBox->setRange(2*_histogram_info->minX - _histogram_info->maxX, 2*_histogram_info->maxX - _histogram_info->minX);
ui.maxSpinBox->setSingleStep(singleStep);
ui.maxSpinBox->setDecimals(decimals);
//SETTING UP CONNECTIONS
// Connecting spinboxes to handles
connect(ui.minSpinBox, SIGNAL(valueChanged(double)), _equalizerHandles[LEFT_HANDLE], SLOT(setXBySpinBoxValueChanged(double)));
connect(ui.midSpinBox, SIGNAL(valueChanged(double)), _equalizerHandles[MID_HANDLE], SLOT(setXBySpinBoxValueChanged(double)));
connect(ui.maxSpinBox, SIGNAL(valueChanged(double)), _equalizerHandles[RIGHT_HANDLE], SLOT(setXBySpinBoxValueChanged(double)));
// Connecting handles to spinboxes
connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(positionChangedToSpinBox(double)), ui.minSpinBox, SLOT(setValue(double)));
connect(_equalizerHandles[MID_HANDLE], SIGNAL(positionChangedToSpinBox(double)), ui.midSpinBox, SLOT(setValue(double)));
connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(positionChangedToSpinBox(double)), ui.maxSpinBox, SLOT(setValue(double)));
// Connecting left and right handles to mid handle
connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(positionChanged()), _equalizerHandles[MID_HANDLE], SLOT(moveMidHandle()));
connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(positionChanged()), _equalizerHandles[MID_HANDLE], SLOT(moveMidHandle()));
// Making spinboxes and handles changes redrawing transferFunctionScene
// Nota: non <20> necessario connettere anche le spinbox (UCCIO)
connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(positionChanged()), this, SLOT(on_EQHandle_moved()));
connect(_equalizerHandles[MID_HANDLE], SIGNAL(positionChanged()), this, SLOT(on_EQHandle_moved()));
connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(positionChanged()), this, SLOT(on_EQHandle_moved()));
// Connecting mid equalizerHistogram handle to gammaCorrectionLabel
connect(_equalizerHandles[MID_HANDLE], SIGNAL(positionChanged()), this, SLOT(drawGammaCorrection()) );
connect(ui.midSpinBox, SIGNAL(valueChanged(double)), this, SLOT(drawGammaCorrection()) );
// Connecting eqHandles to histogram drawing
connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(insideHistogram(EqHandle*,bool)), this, SLOT(on_EqHandle_crossing_histogram(EqHandle*,bool)) );
connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(insideHistogram(EqHandle*,bool)), this, SLOT(on_EqHandle_crossing_histogram(EqHandle*,bool)) );
// Connecting handles to preview method
connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(handleReleased()), this, SLOT(on_Handle_released()));
connect(_equalizerHandles[MID_HANDLE], SIGNAL(handleReleased()), this, SLOT(on_Handle_released()));
connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(handleReleased()), this, SLOT(on_Handle_released()));
connect(ui.brightnessSlider, SIGNAL(sliderReleased()), this, SLOT(on_Handle_released()));
ui.equalizerGraphicsView->setScene(&_equalizerHistogramScene);
drawGammaCorrection();
drawTransferFunctionBG();
return true;
}
// Add histogram bars to equalizerHistogram Scene
bool QualityMapperDialog::drawEqualizerHistogram(bool leftHandleIsInsideHistogram, bool rightHandleIsInsideHistogram)
{
//building up histogram...
int numberOfBins = 200;
if (_equalizer_histogram == 0)
{
// This block is called only the first time
_equalizer_histogram = new Histogramf();
Frange histogramRange(tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(mesh.cm));
this->ComputePerVertexQualityHistogram(mesh.cm, histogramRange, _equalizer_histogram, numberOfBins);
if (histogramRange.minV == histogramRange.maxV)
{
int ret = QMessageBox::warning(this, tr("Quality Mapper"), tr("The model has no vertex quality"), QMessageBox::Ok);
return false;
}
//building histogram chart informations
//processing minY and maxY values for histogram
pair<int,int> minMaxY = computeHistogramMinMaxY(_equalizer_histogram);
_histogram_info = new CHART_INFO( ui.equalizerGraphicsView->width(), ui.equalizerGraphicsView->height(), _equalizer_histogram->n, _equalizer_histogram->minv, _equalizer_histogram->maxv, minMaxY.first, minMaxY.second );
//drawing axis and other basic items
this->drawChartBasics( _equalizerHistogramScene, _histogram_info );
}
else
{
// if histogram doesn't need to be redrawn, return
if ( (leftHandleIsInsideHistogram && _leftHandleWasInsideHistogram) && (rightHandleIsInsideHistogram && _rightHandleWasInsideHistogram) )
return true;
_leftHandleWasInsideHistogram = leftHandleIsInsideHistogram;
_rightHandleWasInsideHistogram = rightHandleIsInsideHistogram;
this->clearItems( REMOVE_EQ_HISTOGRAM | DELETE_REMOVED_ITEMS );
//_equalizer_histogram->Clear();
float minX = (_histogram_info->minX<ui.minSpinBox->value())?_histogram_info->minX:ui.minSpinBox->value();
float maxX = (_histogram_info->maxX>ui.maxSpinBox->value())?_histogram_info->maxX:ui.maxSpinBox->value();
Frange mmmq(minX, maxX);
this->ComputePerVertexQualityHistogram(mesh.cm, mmmq, _equalizer_histogram, numberOfBins);
pair<int,int> minMaxY = computeHistogramMinMaxY(_equalizer_histogram);
_histogram_info->minY = minMaxY.first;
_histogram_info->maxY = minMaxY.second;
}
//...histogram built
//drawing histogram bars
this->drawHistogramBars (_equalizerHistogramScene, _histogram_info, 0, _histogram_info->numOfItems, QColor(128,128,128));
// this->drawGammaCorrection();
this->drawTransferFunctionBG();
return true;
}
// Add histogramBars to destinationScene with GAMMA-STRETCHING
void QualityMapperDialog::drawHistogramBars (QGraphicsScene& destinationScene, CHART_INFO *chartInfo, int minIndex, int maxIndex, QColor color)
{
// Questro controllo <20> necessario perch<63> se si cerca in Histogram il valore minimo viene restituito l'indice -1. Forse deve essere corretto? (UCCIO)
if (minIndex<0)
minIndex = 0;
float barHeight = 0.0f; //initializing height of the histogram bars
float barWidth = chartInfo->chartWidth / (float)(maxIndex-minIndex); //processing width of the histogram bars (4\5 of dX)
// float barSeparator = dX - barWidth; //processing space between consecutive bars of the histogram bars (1\5 of dX)
int numberOfItems = maxIndex - minIndex;
float exp = log10(0.5f) / log10((float)_equalizerMidHandlePercentilePosition);
QPen drawingPen(color);
QBrush drawingBrush (color);
QPointF startBarPt;
//drawing histogram bars
QGraphicsItem *current_item = 0;
for (int i = minIndex; i < maxIndex; i++)
{
barHeight = (float)(chartInfo->chartHeight * _equalizer_histogram->H[i]) / (float)_histogram_info->maxY;
startBarPt.setY( (float)chartInfo->lowerBorder - barHeight );
//drawing histogram bar
if ( &destinationScene == &_transferFunctionScene )
{
startBarPt.setX( chartInfo->leftBorder + relative2AbsoluteValf(pow( absolute2RelativeValf(i-minIndex,numberOfItems ), exp ), chartInfo->chartWidth) );
current_item = destinationScene.addLine(startBarPt.x(), startBarPt.y(), startBarPt.x(), (float)chartInfo->lowerBorder, drawingPen);
_transferFunctionBg << current_item;
}
else
{
startBarPt.setX( chartInfo->leftBorder + ( barWidth * (i-minIndex) ) );
current_item = destinationScene.addRect(startBarPt.x(), startBarPt.y(), barWidth, barHeight, drawingPen, drawingBrush);
_equalizerHistogramBars << current_item;
}
current_item->setZValue(-1);
}
}
//this method prepares the Transfer Function unit to work
void QualityMapperDialog::initTF()
{
assert(_transferFunction != 0);
//UPDATE OF PRESET COMBO BOX STATE
//blocking comboBox signals
ui.presetComboBox->blockSignals( true );
//adding default TFs text to comboBox
QString itemText;
for (int i=0; i<NUMBER_OF_DEFAULT_TF; i++ )
{
//fetching default TF text
itemText = TransferFunction::defaultTFs[(STARTUP_TF_TYPE + i)%NUMBER_OF_DEFAULT_TF];
//items are added to the list only if they're not present yet
if ( -1 == ui.presetComboBox->findText( itemText ) )
ui.presetComboBox->addItem( itemText );
}
//adding to comboBox "Known External TFs", i.e. TFs previously loaded from external TF Files
//for each external file TF the system stores the path of the file and its name (this one is added to comboBox)
for (int i=0; i<_knownExternalTFs.size(); i++)
{
//fetching KETF from KETFs list
itemText = _knownExternalTFs.at(i).name;
//items are added to the list only if they're not present yet
if ( -1 == ui.presetComboBox->findText( itemText ) )
ui.presetComboBox->insertItem( 0, itemText );
}
//comboBox operations complete. Re-enabling signals for comboBox
ui.presetComboBox->blockSignals( false );
//building transfer function chart informations (if necessary)
if ( _transferFunction_info == 0 )
_transferFunction_info = new CHART_INFO( ui.transferFunctionView->width(), ui.transferFunctionView->height(), _transferFunction->size(), 0.0f, 1.0f, 0.0f, 1.0f );
//removing and deleting any old TF graphics item (TF lines and handles)
this->clearItems( REMOVE_TF_ALL | DELETE_REMOVED_ITEMS );
//passing to TFHandles the pointer to the current Transfer Function
assert(_transferFunction != 0);
TFHandle::setTransferFunction(_transferFunction);
//setting up handles
TF_KEY *val = 0;
QColor channelColor;
qreal zValue = 0;
for (int c=0; c<NUMBER_OF_CHANNELS; c++)
{
// //fetching channelType
// channelType = (*_transferFunction)[c].getType();
//processing z value
zValue = (((*_transferFunction)[c].getType() + 1)*2.0f) + 1;
TYPE_2_COLOR( c, channelColor );
for (int i=0; i<_transferFunction->getChannel(c).size(); i++)
{
val = _transferFunction->getChannel(c)[i];
this->addTfHandle( c,
QPointF(_transferFunction_info->leftBorder + relative2AbsoluteValf( val->x, (float)_transferFunction_info->chartWidth ), _transferFunction_info->lowerBorder - relative2AbsoluteValf( val->y, (float)_transferFunction_info->chartHeight )),
val,
zValue );
}
}
//adding to TF Scene TFHandles
//if handles are not already in the transferFunctionScene, they're added to it
if ( ! _transferFunctionScene.items().contains(_transferFunctionHandles[0][0]) )
for (int i=0; i<NUMBER_OF_CHANNELS; i++)
if ( _transferFunctionHandles[i].size() > 0 )
for (int h=0; h<_transferFunctionHandles[i].size(); h++)
_transferFunctionScene.addItem( _transferFunctionHandles[i][h] );
//no TfHande selected yet
_currentTfHandle = 0;
//all done
_isTransferFunctionInitialized = true;
//refreshing TF BG
this->drawTransferFunctionBG();
//initializing forward channel to BLUE
ui.blueButton->setChecked( true );
}
void QualityMapperDialog::drawGammaCorrection()
{
int width = ui.gammaCorrectionLabel->width();
int height = ui.gammaCorrectionLabel->height();
QPixmap *pixmap = new QPixmap(width, height);
QPainter painter(pixmap);
painter.setPen(QColor(128,128,128));
painter.drawLine(0,height-1,width-1,0);
painter.setPen(Qt::black);
painter.drawRect(0,0,width-1,height-1);
int c = _equalizerMidHandlePercentilePosition*width;
QPainterPath path;
path.moveTo(0, height);
path.quadTo(c, c, width, 0);
//path.cubicTo(c,c,c,c,width,0);
painter.drawPath(path);
//painter.drawArc(0, 0, pixmap->width(), pixmap->height(), 0, -90*16);
ui.gammaCorrectionLabel->setPixmap(*pixmap);
painter.end();
delete pixmap;
}
//draws the Transfer Function in the transfer function view
void QualityMapperDialog::drawTransferFunction()
{
//before drawing, old TF lines are removed and destroyed
this->clearItems( REMOVE_TF_LINES | DELETE_REMOVED_ITEMS );
//TF must exist!
assert(_transferFunction != 0);
//building transfer function chart informations
if ( _transferFunction_info == 0 )
_transferFunction_info = new CHART_INFO( ui.transferFunctionView->width(), ui.transferFunctionView->height(), _transferFunction->size(), 0.0f, 1.0f, 0.0f, 1.0f );
//is necessary, initialize TF
if ( !_isTransferFunctionInitialized )
this->initTF();
//drawing axis and other basic items
this->drawChartBasics( _transferFunctionScene, _transferFunction_info );
QColor channelColor;
QPen drawingPen(Qt::black, 3);
QGraphicsItem *item = 0;
QGraphicsItem *handle1 = 0;
QGraphicsItem *handle2 = 0;
qreal zValue = 0;
int channelType = 0;
//for each channel
for(int c=0; c<NUMBER_OF_CHANNELS; c++)
{
//processing correct channel type (the correct order is obtained through the [] operator of TransferFunction class)
channelType = (*_transferFunction)[c].getType();
//converting channel code in the proper color
TYPE_2_COLOR(channelType, channelColor);
//pen colo is set to proper color
drawingPen.setColor( channelColor );
//z order for lines
zValue = ((c + 1)*2.0f);
QPointF pos1;
QPointF pos2;
//for each TfHandle of the current channel
for (int i=0; i<_transferFunctionHandles[channelType].size(); i++)
{
//fetch the i-th handle
handle1 = _transferFunctionHandles[channelType][i];
//setting z-order in the scene
handle1->setZValue( zValue);
//if there's another handle after this in the sequence
if ( (i+1)<_transferFunctionHandles[channelType].size() )
{
//fetching 2nd handle
handle2 = _transferFunctionHandles[channelType][i+1];
//setting z-order
handle1->setZValue( zValue+1 ); //modified by MAL 15/02/08
//fetching positions of handles
pos1 = handle1->scenePos();
pos2 = handle2->scenePos();
//hilighting line if it touches the currently selected handle
if (( handle1 == _currentTfHandle ) || (handle2 == _currentTfHandle) )
drawingPen.setColor( channelColor.lighter() );
else
drawingPen.setColor( channelColor );
//tracing line between the 2 handles
item = _transferFunctionScene.addLine( handle1->scenePos().x(), handle1->scenePos().y(), handle2->scenePos().x(), handle2->scenePos().y(), drawingPen );
//setting z-order of line
item->setZValue( zValue );
//adding line to lines list
_transferFunctionLines << item;
}
}
}
// updating Color Band
this->updateColorBand();
ui.transferFunctionView->setScene( &_transferFunctionScene );
}
void QualityMapperDialog::updateColorBand()
{
QColor* colors = _transferFunction->buildColorBand();
QImage image(ui.colorbandLabel->width(), 1, QImage::Format_RGB32);
float step = ((float)COLOR_BAND_SIZE) / ((float)ui.colorbandLabel->width());
for (int i=0; i<image.width(); i++)
image.setPixel (i, 0, colors[(int)(i*step)].rgb());
ui.colorbandLabel->setPixmap(QPixmap::fromImage(image));
}
void QualityMapperDialog::drawTransferFunctionBG ()
{
this->clearItems( REMOVE_TF_BG | DELETE_REMOVED_ITEMS );
/*// JUST FOR TEST
float minspinboxvalue = ui.minSpinBox->value();
float maxspinboxvalue = ui.maxSpinBox->value();*/
if (_histogram_info !=0)
{
int minIndex = _equalizer_histogram->Interize((float)ui.minSpinBox->value());
int maxIndex = _equalizer_histogram->Interize((float)ui.maxSpinBox->value());
drawHistogramBars (_transferFunctionScene, _transferFunction_info, minIndex, maxIndex, QColor(192,192,192));
}
}
void QualityMapperDialog::on_savePresetButton_clicked()
{
//setting default save name
QString tfName = ui.presetComboBox->currentText();
//user chooses the file to save and saves it onto disk
EQUALIZER_INFO eqInfo;
eqInfo.minQualityVal = ui.minSpinBox->value();
eqInfo.midQualityPercentage = _equalizerMidHandlePercentilePosition;
eqInfo.maxQualityVal = ui.maxSpinBox->value();
eqInfo.brightness = (1.0f - (float)(ui.brightnessSlider->value())/(float)(ui.brightnessSlider->maximum()) )*2.0;
QString tfPath = _transferFunction->saveColorBand( tfName, eqInfo );
//user didn't select anything. Nothing to do.
if (tfPath.isNull())
return ;
//building file info
QFileInfo fi(tfPath);
tfName = fi.fileName();
QString ext = CSV_FILE_EXSTENSION;
if ( tfName.endsWith( ext ) )
tfName.remove( tfName.size() - ext.size(), ext.size() );
//adding external file to the list of known ones
KNOWN_EXTERNAL_TFS newTF( tfPath, tfName );
_knownExternalTFs << newTF;
// FORSE QUANDO SI SALVA IL PRESET NON C'E' BISOGNO DI CANCELLARE TUTTO (UCCIO)
this->clearItems( REMOVE_TF_ALL | DELETE_REMOVED_ITEMS );
//preparing TF to work
_isTransferFunctionInitialized = false;
this->initTF();
ui.presetComboBox->setCurrentIndex( 0 );
}
//callback for loading a new TF from an external CSV file
void QualityMapperDialog::on_loadPresetButton_clicked()
{
//user chooses the file to load
QString csvFileName = QFileDialog::getOpenFileName(0, "Open Transfer Function File", QDir::currentPath(), "CSV File (*.csv)");
//user didn't select anything. Nothing to do.
if (csvFileName.isNull())
return ;
//deleting any previous TF object
if ( _transferFunction )
delete _transferFunction;
//building new TF object from external file
_transferFunction = new TransferFunction( csvFileName );
//building file info
QFileInfo fi(csvFileName);
QString tfName = fi.fileName();
QString ext = CSV_FILE_EXSTENSION;
if ( tfName.endsWith( ext ) )
tfName.remove( tfName.size() - ext.size(), ext.size() );
//adding external file to the list of known ones
KNOWN_EXTERNAL_TFS newTF( csvFileName, tfName );
_knownExternalTFs << newTF;
//preparing TF to work
_isTransferFunctionInitialized = false;
this->initTF();
//setting combo box to TF just built
ui.presetComboBox->setCurrentIndex( 0 );
//setting equalizer values
EQUALIZER_INFO eqData;
loadEqualizerInfo(csvFileName, &eqData);
this->setEqualizerParameters(eqData);
/* on_resetButton_clicked(csvFileName, &eqData);*/
//drawing new TF
this->drawTransferFunction();
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
}
//callback to manage the user selection of a TF from combo box
//builds a new TF joined to newValue. It searches first among the default ones, then among the external file ones
void QualityMapperDialog::on_presetComboBox_textChanged(const QString &newValue)
{
//searching newValue among default TFs
for (int i=0; i<NUMBER_OF_DEFAULT_TF; i++)
{
//found it!
if ( TransferFunction::defaultTFs[i] == newValue )
{
//deleting any previous TF object
if ( _transferFunction )
delete _transferFunction;
//building a new one
_transferFunction = new TransferFunction( (DEFAULT_TRANSFER_FUNCTIONS)i );
//preparing TF to work
this->initTF(); //added by MAL 04\02\08
//drawing new TF
this->drawTransferFunction();
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
return ;
}
}
//TF selected is not a default one. Maybe it's a loaded or saved one. Searching among known external TFs
KNOWN_EXTERNAL_TFS external_tf;
for (int i=0; i<_knownExternalTFs.size(); i++)
{
external_tf = _knownExternalTFs.at(i);
//found it!
if ( newValue == external_tf.name )
{
//deleting any previous TF object
if ( _transferFunction )
delete _transferFunction;
//building new TF object from external file
_transferFunction = new TransferFunction( external_tf.path );
//preparing TF to work
this->initTF(); //added by MAL 04\02\08
//drawing new TF
this->drawTransferFunction();
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
return ;
}
}
}
//callback for RED radio button
void QualityMapperDialog::on_redButton_toggled(bool checked)
{
//if checked moves ahead the RED CHANNEL TF items
if (checked)
this->moveAheadChannel( RED_CHANNEL );
}
//callback for GREEN radio button
void QualityMapperDialog::on_greenButton_toggled(bool checked)
{
//if checked moves ahead the GREEN CHANNEL TF items
if (checked)
this->moveAheadChannel( GREEN_CHANNEL );
}
//callback for BLUE radio button
void QualityMapperDialog::on_blueButton_toggled(bool checked)
{
//if checked moves ahead the BLUE CHANNEL TF items
if (checked)
this->moveAheadChannel( BLUE_CHANNEL );
}
//bring in first plane the TfHandles and Tf lines of the channel passed to it
void QualityMapperDialog::moveAheadChannel( TF_CHANNELS channelCode )
{
//if Transfer Function object instanced
if ( _transferFunction )
{
//changing drawing order for channel lines
_transferFunction->moveChannelAhead( channelCode );
//changing z order of TF handles
QGraphicsItem *item = 0;
for (int i=0; i<NUMBER_OF_CHANNELS; i++)
foreach( item, _transferFunctionHandles[i] )
item->setZValue( ((i + 1)*2.0f) + 1 );
//all done. Drawing updated TF
this->drawTransferFunction();
}
}
void QualityMapperDialog::on_EQHandle_moved()
{
if ( _transferFunction )
this->drawTransferFunctionBG();
if ( _currentTfHandle )
//updating x-quality Label
this->updateXQualityLabel(_currentTfHandle->getRelativeX());
}
//callback to manage move of a TfHandle object
//updates the object position in the position spinboxes, restores the correct order among the TfHandle objects and refreshes the TF scene
void QualityMapperDialog::on_TfHandle_moved(TFHandle *sender)
{
//suspending signals from sender
sender->blockSignals( true );
//setting position spinboxes to Handle position
ui.xSpinBox->blockSignals( true );
ui.xSpinBox->setValue(sender->getRelativeX());
ui.xSpinBox->blockSignals( false );
ui.ySpinBox->blockSignals( true );
ui.ySpinBox->setValue(sender->getRelativeY());
ui.ySpinBox->blockSignals( false );
this->manageBorderTfHandles(sender);
//updating correct order among TF Handle objects
this->updateTfHandlesOrder(sender->getChannel());
//refreshing TF scene
this->drawTransferFunction();
//all done. Unlocking sender signals
sender->blockSignals( false );
this->updateXQualityLabel(sender->getRelativeX());
}
void QualityMapperDialog::manageBorderTfHandles(TFHandle *handle)
{
TF_KEY *firstKey = 0;
TF_KEY *newKey = 0;
if ( _transferFunction->getChannel(handle->getChannel()).size() > 0 )
{
firstKey = _transferFunction->getChannel(handle->getChannel())[0];
if ( handle->getMyKey() == firstKey )
{
if ( ! _transferFunction->getChannel(handle->getChannel()).isHead(/*firstKey*/handle->getMyKey()) )
{
newKey = new TF_KEY(0.0f, handle->getRelativeY());
_transferFunction->getChannel(handle->getChannel()).addKey(newKey);
this->addTfHandle( handle->getChannel(),
QPointF(_transferFunction_info->leftBorder + relative2AbsoluteValf( 0.0f, (float)_transferFunction_info->chartWidth ), _transferFunction_info->lowerBorder - relative2AbsoluteValf( handle->getRelativeY(), (float)_transferFunction_info->chartHeight )),
newKey,
((handle->getChannel() + 1)*2.0f) + 1 );
}
}
}
TF_KEY *lastKey = 0;
if ( _transferFunction->getChannel(handle->getChannel()).size() > 0 )
{
lastKey = _transferFunction->getChannel(handle->getChannel())[_transferFunction->getChannel(handle->getChannel()).size()-1];
if ( handle->getMyKey() == lastKey )
{
if ( ! _transferFunction->getChannel(handle->getChannel()).isTail(/*lastKey*/handle->getMyKey()) )
{
newKey = new TF_KEY(1.0f, handle->getRelativeY());
_transferFunction->getChannel(handle->getChannel()).addKey(newKey);
this->addTfHandle( handle->getChannel(),
QPointF(_transferFunction_info->leftBorder + relative2AbsoluteValf( 1.0f, (float)_transferFunction_info->chartWidth ), _transferFunction_info->lowerBorder - relative2AbsoluteValf( handle->getRelativeY(), (float)_transferFunction_info->chartHeight )),
newKey,
((handle->getChannel() + 1)*2.0f) + 1);
}
}
}
}
//callback to manage click on a TfHandle object
//updates the currenttfHandle attribute and refresh the position spinboxes
void QualityMapperDialog::on_TfHandle_clicked(TFHandle *sender)
{
if (_currentTfHandle)
_currentTfHandle->setCurrentlSelected( false );
//updating currentTfHandle to sender
_currentTfHandle = sender;
_currentTfHandle->setCurrentlSelected( true );
//setting position spinboxes to Handle position
ui.xSpinBox->setValue(_currentTfHandle->getRelativeX());
ui.ySpinBox->setValue(_currentTfHandle->getRelativeY());
this->updateXQualityLabel(_currentTfHandle->getRelativeX());
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
}
//callback to manage double-click on a TfHandle object
//updates the currenttfHandle attribute and refresh the position spinboxes
void QualityMapperDialog::on_TfHandle_doubleClicked(TFHandle *sender)
{
//updating currentTfHandle to sender
_currentTfHandle = sender;
//removing sender
_currentTfHandle = this->removeTfHandle(_currentTfHandle);
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
}
/*void QualityMapperDialog::on_applyButton_clicked()
{
// Colorazione della mesh
float rangeMin = ui.minSpinBox->value();
float rangeMax = ui.maxSpinBox->value();
//tri::UpdateColor<CMeshO>::VertexQuality(mesh->cm,RangeMin,RangeMax);
CMeshO::VertexIterator vi;
float percentageQuality;
// brightness value between 0 and 2
float brightness = (1.0f - (float)(ui.brightnessSlider->value())/(float)(ui.brightnessSlider->maximum()) )*2.0;
Color4b currentColor;
for(vi=mesh->cm.vert.begin(); vi!=mesh->cm.vert.end(); ++vi)
if(!(*vi).IsD())
{
//(*vi).C().ColorRamp(minq,maxq,(*vi).Q());
float vertexQuality = (*vi).Q();
if (vertexQuality < rangeMin)
percentageQuality = 0.0;
else
if (vertexQuality > rangeMax)
percentageQuality = 1.0;
else
percentageQuality = pow( ((*vi).Q() - rangeMin) / (rangeMax - rangeMin) , (float)(2.0*_equalizerMidHandlePercentilePosition));
currentColor = _transferFunction->getColorByQuality(percentageQuality);
if (brightness!=1.0f) //Applying brightness to each color channel
if (brightness<1.0f)
for (int i=0; i<3; i++)
//currentColor[i] = relative2AbsoluteVali(pow(absolute2RelativeValf(currentColor[i],255.0f),brightness), 255.0f);
currentColor[i] = relative2AbsoluteVali(pow(absolute2RelativeValf(currentColor[i]+1,257.0f),brightness), 255.0f);
else
for (int i=0; i<3; i++)
//currentColor[i] = relative2AbsoluteVali(1.0f-pow(1.0f-absolute2RelativeValf(currentColor[i],255.0f),2-brightness), 255.0f);
currentColor[i] = relative2AbsoluteVali(1.0f-pow(1.0f-absolute2RelativeValf(currentColor[i]+1,257.0f),2-brightness), 255.0f);
(*vi).C() = currentColor;
}
gla->update();
}*/
void QualityMapperDialog::on_applyButton_clicked()
{
float minQuality = ui.minSpinBox->value();
float maxQuality = ui.maxSpinBox->value();
// brightness value between 0 and 2
float brightness = (1.0f - (float)(ui.brightnessSlider->value())/(float)(ui.brightnessSlider->maximum()) )*2.0;
applyColorByVertexQuality((MeshModel&)mesh, _transferFunction, minQuality, maxQuality, (float)_equalizerMidHandlePercentilePosition, brightness);
gla->update();
}
void QualityMapperDialog::on_Handle_released()
{
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
}
void QualityMapperDialog::on_previewButton_clicked()
{
on_applyButton_clicked();
}
//callback that manages the value change of current Handle y position
void QualityMapperDialog::on_xSpinBox_valueChanged(double newX)
{
// if any handle is selected
if (_currentTfHandle)
{
//updating handle position in the scene
_currentTfHandle->setPos(_transferFunction_info->leftBorder+relative2AbsoluteValf(newX,_transferFunction_info->chartWidth), _currentTfHandle->scenePos().y());
//updating the Tf Handle position at logical level (update of joined TF_KEY)
_currentTfHandle->updateTfHandlesState(_currentTfHandle->scenePos());
this->manageBorderTfHandles(_currentTfHandle);
//restoring the correct order for TfHandles (they're drawn in the same order as they're stored)
this->updateTfHandlesOrder(_currentTfHandle->getChannel());
//updating x-quality Label
this->updateXQualityLabel(_currentTfHandle->getRelativeX());
//refresh of TF
this->drawTransferFunction();
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
}
}
//callback that manages the value change of current Handle y position
void QualityMapperDialog::on_ySpinBox_valueChanged(double newY)
{
// if any handle is selected
if (_currentTfHandle)
{
//updating handle position in the scene
_currentTfHandle->setPos(_currentTfHandle->scenePos().x(), _transferFunction_info->chartHeight+_transferFunction_info->upperBorder-relative2AbsoluteValf(newY,_transferFunction_info->chartHeight));
//updating the Tf Handle position at logical level (update of joined TF_KEY)
_currentTfHandle->updateTfHandlesState(_currentTfHandle->scenePos());
//restoring the correct order for TfHandles (they're drawn in the same order as they're stored)
this->manageBorderTfHandles(_currentTfHandle);
this->updateTfHandlesOrder(_currentTfHandle->getChannel());
//refresh of TF
this->drawTransferFunction();
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
}
}
//orders the TfHandles by the x value of the joined TF_KEY
void QualityMapperDialog::updateTfHandlesOrder(int channelCode)
{
//ordering TfHandles list (sort is used because of the tiny number of elements to manage)
qSort(_transferFunctionHandles[channelCode].begin(), _transferFunctionHandles[channelCode].end(), TfHandleCompare);
}
void QualityMapperDialog::on_resetButton_clicked()
{
assert(_histogram_info != 0);
EQUALIZER_INFO data;
data.brightness = 50;
data.minQualityVal = _histogram_info->minX;
data.maxQualityVal = _histogram_info->maxX;
data.midQualityPercentage = 0.5f;
this->setEqualizerParameters(data);
}
// Method invoked when moving left/right EqHandles,
void QualityMapperDialog::on_EqHandle_crossing_histogram(EqHandle* sender, bool insideHistogram)
{
if (sender = _equalizerHandles[LEFT_HANDLE])
drawEqualizerHistogram(insideHistogram, _rightHandleWasInsideHistogram);
else
drawEqualizerHistogram(_leftHandleWasInsideHistogram, insideHistogram);
}
TFHandle* QualityMapperDialog::addTfHandle(int channelCode, QPointF handlePos, TF_KEY *key, int zOrder )
{
QColor channelColor;
TYPE_2_COLOR(channelCode, channelColor);
return this->addTfHandle( new TFHandle( _transferFunction_info, channelColor, handlePos, key, zOrder ) );
}
TFHandle* QualityMapperDialog::addTfHandle(TFHandle *handle)
{
_transferFunctionHandles[handle->getChannel()] << handle;
connect(handle, SIGNAL(positionChanged(TFHandle*)), this, SLOT(on_TfHandle_moved(TFHandle*)));
connect(handle, SIGNAL(clicked(TFHandle*)), this, SLOT(on_TfHandle_clicked(TFHandle*)));
connect(handle, SIGNAL(doubleClicked(TFHandle*)), this, SLOT(on_TfHandle_doubleClicked(TFHandle*)));
connect(handle, SIGNAL(handleReleased()), this, SLOT(on_Handle_released()));
_transferFunctionScene.addItem((QGraphicsItem*)handle);
return handle;
}
//remove a TFHandle from the scene (and at logical level too)
TFHandle* QualityMapperDialog::removeTfHandle(TFHandle *handle)
{
//no handle. Nothing to do.
if ( handle == 0)
return handle;
//removing TF Handle item from scene
_transferFunctionScene.removeItem( handle );
//removing it from TfHandles list
for (int i=0; i<_transferFunctionHandles[handle->getChannel()].size(); i++)
{
if ( _transferFunctionHandles[handle->getChannel()][i] == handle )
{
_transferFunctionHandles[handle->getChannel()].removeAt(i);
break;
}
}
//destroying joined logical key
_transferFunction->getChannel(_currentTfHandle->getChannel()).removeKey(handle->getMyKey());
//disconnecting and destroying handle
handle->disconnect();
delete handle;
handle = 0;
//no more traces of handle...
//refreshing TF
this->drawTransferFunction();
return handle;
}
//callback that manages the value change of current Handle y position
void QualityMapperDialog::on_TF_view_doubleClicked(QPointF pos)
{
//getting channel for new handle:
int channelCode = -1;
if ( _currentTfHandle != 0)
//if an handle was already selected let's use the same channel of the selected one
channelCode = _currentTfHandle->getChannel();
else
//else, let's use the more ahead channel in TF
channelCode = _transferFunction->getFirstPlaneChanel();
float xPos = pos.x() - _transferFunction_info->leftBorder;
float yPos = pos.y() - _transferFunction_info->upperBorder;
TF_KEY *val = new TF_KEY(absolute2RelativeValf(xPos, _transferFunction_info->chartWidth),
absolute2RelativeValf(yPos, _transferFunction_info->chartHeight));
_transferFunction->getChannel(channelCode).addKey(val);
TFHandle *newHandle = this->addTfHandle(channelCode, pos, val, ((channelCode + 1)*2.0f) + 1 );
_currentTfHandle = newHandle;
//updating correct order among TF Handle objects
this->updateTfHandlesOrder(newHandle->getChannel());
//refreshing TF scene
this->drawTransferFunction();
this->updateXQualityLabel(_currentTfHandle->getRelativeX());
//applying preview if necessary
if (ui.previewButton->isChecked()) //added by FB 07\02\08
on_applyButton_clicked();
}
void QualityMapperDialog::updateXQualityLabel(float xPos)
{
float exp = log10((float)_equalizerMidHandlePercentilePosition) / log10(0.5f);
_currentTfHandleQualityValue.setNum(relative2QualityValf(xPos, ui.minSpinBox->value(), ui.maxSpinBox->value(), exp));
ui.xQualityLabel->setText(_currentTfHandleQualityValue);
}
void QualityMapperDialog::setEqualizerParameters(EQUALIZER_INFO data)
{
// Resetting brightnessSlider position
ui.brightnessSlider->setSliderPosition(data.brightness);
// Resetting equalizerHistogram spinboxes values
ui.minSpinBox->setValue(data.minQualityVal);
ui.minSpinBox->setRange(2*data.minQualityVal - data.maxQualityVal, 2*data.maxQualityVal - data.minQualityVal);
ui.maxSpinBox->setValue(data.maxQualityVal);
ui.maxSpinBox->setRange(2*data.minQualityVal - data.maxQualityVal, 2*data.maxQualityVal - data.minQualityVal);
ui.midSpinBox->setValue(((data.maxQualityVal - data.minQualityVal) * data.midQualityPercentage) + data.minQualityVal);
ui.midSpinBox->setRange(data.minQualityVal, data.maxQualityVal);
// Because of approximation error it is necessary to directly update handles position, transferFunctionBG and gammaCorrection
qreal xStart = _histogram_info->leftBorder;
qreal xPos = 0.0f;
qreal yPos = _histogram_info->lowerBorder;
_equalizerMidHandlePercentilePosition = data.midQualityPercentage;
for (int i=0; i<NUMBER_OF_EQHANDLES; i++)
{
xPos = xStart + _histogram_info->chartWidth/2.0f*i;
_equalizerHandles[i]->setPos(xPos,yPos);
}
drawGammaCorrection();
drawTransferFunctionBG();
if (ui.previewButton->isChecked())
on_applyButton_clicked();
}