395 lines
13 KiB
C++
395 lines
13 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2013 Robert Osfield
|
|
*
|
|
* This library is open source and may be redistributed and/or modified under
|
|
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
|
|
* (at your option) any later version. The full license is in LICENSE file
|
|
* included with this distribution, and on the openscenegraph.org website.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* OpenSceneGraph Public License for more details.
|
|
*/
|
|
|
|
#include "TransferFunctionWidget.h"
|
|
|
|
#include <osg/Geode>
|
|
#include <osg/Geometry>
|
|
#include <osg/TexGen>
|
|
#include <osg/AlphaFunc>
|
|
#include <osg/Texture2D>
|
|
#include <osg/MatrixTransform>
|
|
#include <osg/io_utils>
|
|
|
|
#include <osgGA/EventVisitor>
|
|
#include <osgDB/ReadFile>
|
|
#include <osgDB/WriteFile>
|
|
#include <osgViewer/View>
|
|
|
|
using namespace osgUI;
|
|
|
|
TransferFunctionWidget::TransferFunctionWidget(osg::TransferFunction1D* tf):
|
|
_min(FLT_MAX),
|
|
_max(-FLT_MAX),
|
|
_left(FLT_MAX),
|
|
_right(-FLT_MAX),
|
|
_startedDrag(false),
|
|
_previousDragPosition(0.0f)
|
|
{
|
|
setNumChildrenRequiringEventTraversal(1);
|
|
setExtents(osg::BoundingBox(0.0,0.0,0.0,1.0,1.0,0.0));
|
|
setTransferFunction(tf);
|
|
}
|
|
|
|
TransferFunctionWidget::TransferFunctionWidget(const TransferFunctionWidget& tfw, const osg::CopyOp& copyop):
|
|
Widget(tfw,copyop)
|
|
{
|
|
setExtents(tfw.getExtents());
|
|
setTransferFunction(tfw.getTransferFunction());
|
|
}
|
|
|
|
void TransferFunctionWidget::setTransferFunction(const osg::TransferFunction1D* tf)
|
|
{
|
|
if (_transferFunction==tf) return;
|
|
|
|
_transferFunction = const_cast<osg::TransferFunction1D*>(tf);
|
|
|
|
if (_transferFunction.valid())
|
|
{
|
|
osg::TransferFunction1D::ColorMap& colorMap = _transferFunction->getColorMap();
|
|
if (colorMap.empty())
|
|
{
|
|
_min = FLT_MAX;
|
|
_max = -FLT_MAX;
|
|
}
|
|
else
|
|
{
|
|
_min = colorMap.begin()->first;
|
|
_max = colorMap.rbegin()->first;
|
|
}
|
|
}
|
|
|
|
resetVisibleRange();
|
|
}
|
|
|
|
void TransferFunctionWidget::resetVisibleRange()
|
|
{
|
|
setVisibleRange(_min, _max);
|
|
}
|
|
|
|
void TransferFunctionWidget::setVisibleRange(float left, float right)
|
|
{
|
|
if (left<_min) left = _min;
|
|
if (right>_max) right = _max;
|
|
|
|
_left = left;
|
|
_right = right;
|
|
// OSG_NOTICE<<"setVisibleRange("<<_left<<", "<<_right<<")"<<std::endl;
|
|
createGraphics();
|
|
}
|
|
|
|
void TransferFunctionWidget::translateVisibleRange(float delta)
|
|
{
|
|
float new_left = _left+(_right-_left)*delta;
|
|
float new_right = _right+(_right-_left)*delta;
|
|
if (delta<0.0)
|
|
{
|
|
if (new_left<_min)
|
|
{
|
|
new_right += (_min-new_left);
|
|
new_left = _min;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (new_right>_max)
|
|
{
|
|
new_left += (_max-new_right);
|
|
new_right = _max;
|
|
}
|
|
}
|
|
|
|
setVisibleRange(new_left, new_right);
|
|
}
|
|
|
|
void TransferFunctionWidget::scaleVisibleRange(float center, float delta)
|
|
{
|
|
float scale = powf(2.0, delta);
|
|
setVisibleRange(center+(_left-center)*scale,
|
|
center+(_right-center)*scale);
|
|
}
|
|
|
|
|
|
void TransferFunctionWidget::traverse(osg::NodeVisitor& nv)
|
|
{
|
|
Widget::traverse(nv);
|
|
}
|
|
|
|
bool TransferFunctionWidget::handle(osgGA::EventVisitor* ev, osgGA::Event* event)
|
|
{
|
|
osgGA::GUIEventAdapter* ea = event->asGUIEventAdapter();
|
|
if (!ea) return false;
|
|
|
|
switch(ea->getEventType())
|
|
{
|
|
case(osgGA::GUIEventAdapter::PUSH):
|
|
// OSG_NOTICE<<"Pressed button "<<ea->getButton()<<std::endl;
|
|
_startedDrag = false;
|
|
if (ea->getButtonMask()==osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
|
|
{
|
|
osg::Vec3 position;
|
|
if (computePositionInLocalCoordinates(ev, ea, position))
|
|
{
|
|
_startedDrag = true;
|
|
_previousDragPosition = position.x();
|
|
}
|
|
}
|
|
break;
|
|
case(osgGA::GUIEventAdapter::RELEASE):
|
|
// OSG_NOTICE<<"Released button "<<ea->getButton()<<std::endl;
|
|
_startedDrag = false;
|
|
break;
|
|
case(osgGA::GUIEventAdapter::DRAG):
|
|
// OSG_NOTICE<<"Dragged "<<std::endl;
|
|
if (_startedDrag)
|
|
{
|
|
osg::Vec3 position;
|
|
if (computePositionInLocalCoordinates(ev, ea, position))
|
|
{
|
|
float delta = -(position.x()-_previousDragPosition);
|
|
|
|
_previousDragPosition = position.x();
|
|
|
|
translateVisibleRange(delta);
|
|
}
|
|
}
|
|
break;
|
|
case(osgGA::GUIEventAdapter::SCROLL):
|
|
{
|
|
osg::Vec3 position;
|
|
if (computePositionInLocalCoordinates(ev, ea, position))
|
|
{
|
|
float translation = 0.0;
|
|
float increment = 0.1;
|
|
float scale = 1.0;
|
|
float ratio = (1.0f+increment);
|
|
switch(ea->getScrollingMotion())
|
|
{
|
|
case(osgGA::GUIEventAdapter::SCROLL_NONE):
|
|
break;
|
|
case(osgGA::GUIEventAdapter::SCROLL_LEFT):
|
|
translation -= increment;
|
|
break;
|
|
case(osgGA::GUIEventAdapter::SCROLL_RIGHT):
|
|
translation += increment;
|
|
break;
|
|
case(osgGA::GUIEventAdapter::SCROLL_UP):
|
|
scale /= ratio;
|
|
break;
|
|
case(osgGA::GUIEventAdapter::SCROLL_DOWN):
|
|
scale *= ratio;
|
|
break;
|
|
case(osgGA::GUIEventAdapter::SCROLL_2D):
|
|
translation = increment*ea->getScrollingDeltaX();
|
|
scale = powf(ratio, increment*ea->getScrollingDeltaY());
|
|
break;
|
|
}
|
|
float center = _left+(_right-_left)*position.x();
|
|
// OSG_NOTICE<<"translation = "<<translation<<", scale = "<<scale<<", x="<<position.x()<<", center="<<center<<std::endl;
|
|
setVisibleRange(translation+center+(_left-center)*scale,
|
|
translation+center+(_right-center)*scale);
|
|
|
|
}
|
|
break;
|
|
}
|
|
case(osgGA::GUIEventAdapter::KEYDOWN):
|
|
{
|
|
// OSG_NOTICE<<"Pressed key"<<ea->getKey()<<std::endl;
|
|
float delta = 0.02;
|
|
if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Left) translateVisibleRange(-delta);
|
|
else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Right) translateVisibleRange(delta);
|
|
else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Up) scaleVisibleRange((_left+_right)*0.5f, -delta);
|
|
else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Down) scaleVisibleRange((_left+_right)*0.5f, delta);
|
|
break;
|
|
}
|
|
case(osgGA::GUIEventAdapter::KEYUP):
|
|
// OSG_NOTICE<<"Released key"<<ea->getKey()<<std::endl;
|
|
if (ea->getKey()==' ' ||ea->getKey()==osgGA::GUIEventAdapter::KEY_Home) resetVisibleRange();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TransferFunctionWidget::createGraphics()
|
|
{
|
|
// OSG_NOTICE<<"Create graphics"<<std::endl;
|
|
|
|
typedef osg::TransferFunction1D::ColorMap ColorMap;
|
|
ColorMap& colorMap = _transferFunction->getColorMap();
|
|
if (colorMap.empty()) return;
|
|
|
|
float depth = 0.0f;
|
|
float yMax = 0.0f;
|
|
|
|
// find yMax
|
|
for(ColorMap::iterator itr = colorMap.begin();
|
|
itr != colorMap.end();
|
|
++itr)
|
|
{
|
|
float y = itr->second[3];
|
|
if (y>yMax) yMax = y;
|
|
}
|
|
|
|
float xScale = 1.0f/(_right-_left);
|
|
float xOffset = -_left;
|
|
float yScale = 1.0f/yMax;
|
|
|
|
if (!_geode)
|
|
{
|
|
_geode = new osg::Geode;
|
|
addChild(_geode.get());
|
|
}
|
|
|
|
{
|
|
|
|
if (!_geometry)
|
|
{
|
|
_geometry = new osg::Geometry;
|
|
_geometry->setDataVariance(osg::Geometry::DYNAMIC);
|
|
_geometry->setUseDisplayList(false);
|
|
_geometry->setUseVertexBufferObjects(false);
|
|
|
|
_geode->addDrawable(_geometry.get());
|
|
|
|
osg::ref_ptr<osg::StateSet> stateset = _geometry->getOrCreateStateSet();
|
|
|
|
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
|
|
stateset->setMode(GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
|
|
|
|
osg::ref_ptr<osg::AlphaFunc> alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.0f);
|
|
stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
|
|
|
|
osg::ref_ptr<osg::Image> image = new osg::Image;
|
|
image->allocateImage(1,1,1, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
unsigned char* data = image->data();
|
|
data[0] = 255;
|
|
data[1] = 255;
|
|
data[2] = 255;
|
|
data[3] = 255;
|
|
|
|
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
|
|
texture->setImage(image.get());
|
|
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
|
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
|
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_BORDER);
|
|
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_BORDER);
|
|
texture->setBorderColor(osg::Vec4(1.0f,1.0f,0.0f,0.0f));
|
|
|
|
stateset->setTextureAttribute(0, texture.get());
|
|
|
|
osg::ref_ptr<osg::TexGen> texgen = new osg::TexGen;
|
|
texgen->setMode(osg::TexGen::OBJECT_LINEAR);
|
|
texgen->setPlane(osg::TexGen::S, osg::Plane(1.0,0.0,0.0,0.0));
|
|
texgen->setPlane(osg::TexGen::T, osg::Plane(0.0,1.0,0.0,0.0));
|
|
|
|
stateset->setTextureAttribute(0, texgen.get());
|
|
stateset->setTextureMode(0, GL_TEXTURE_GEN_S, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
|
|
stateset->setTextureMode(0, GL_TEXTURE_GEN_T, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
|
|
stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
|
|
}
|
|
|
|
|
|
if (!_vertices)
|
|
{
|
|
_vertices = new osg::Vec3Array;
|
|
_geometry->setVertexArray(_vertices.get());
|
|
}
|
|
|
|
if (!_colours)
|
|
{
|
|
_colours = new osg::Vec4Array;
|
|
_geometry->setColorArray(_colours.get(), osg::Array::BIND_PER_VERTEX);
|
|
}
|
|
|
|
osg::Vec4 background_color(1.0f, 1.0f, 1.0f, 0.1f);
|
|
|
|
unsigned numColumnsRequired = colorMap.size();
|
|
_vertices->resize(0);
|
|
_vertices->reserve(numColumnsRequired*3);
|
|
for(ColorMap::iterator itr = colorMap.begin();
|
|
itr != colorMap.end();
|
|
++itr)
|
|
{
|
|
float x = itr->first;
|
|
osg::Vec4 color = itr->second;
|
|
|
|
float y = itr->second[3];
|
|
color[3] = 1.0f;
|
|
|
|
_vertices->push_back(osg::Vec3((x+xOffset)*xScale, 0.0f, depth));
|
|
_colours->push_back(color);
|
|
|
|
_vertices->push_back(osg::Vec3((x+xOffset)*xScale, y*yScale, depth));
|
|
_colours->push_back(color);
|
|
|
|
_vertices->push_back(osg::Vec3((x+xOffset)*xScale, y*yScale, depth));
|
|
_colours->push_back(background_color);
|
|
|
|
_vertices->push_back(osg::Vec3((x+xOffset)*xScale, yMax*yScale, depth));
|
|
_colours->push_back(background_color);
|
|
}
|
|
|
|
if (!_background_primitives)
|
|
{
|
|
_background_primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
|
|
_geometry->addPrimitiveSet(_background_primitives.get());
|
|
}
|
|
|
|
if (!_historgram_primitives)
|
|
{
|
|
_historgram_primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
|
|
_geometry->addPrimitiveSet(_historgram_primitives.get());
|
|
}
|
|
|
|
if (!_outline_primitives)
|
|
{
|
|
_outline_primitives = new osg::DrawElementsUShort(GL_LINE_STRIP);
|
|
_geometry->addPrimitiveSet(_outline_primitives.get());
|
|
}
|
|
|
|
_background_primitives->resize(0);
|
|
_historgram_primitives->resize(0);
|
|
_outline_primitives->resize(0);
|
|
|
|
for(unsigned int i=0; i<numColumnsRequired; ++i)
|
|
{
|
|
int iv = i*4;
|
|
|
|
_background_primitives->push_back(iv+3);
|
|
_background_primitives->push_back(iv+2);
|
|
|
|
_historgram_primitives->push_back(iv+1);
|
|
_historgram_primitives->push_back(iv+0);
|
|
|
|
_outline_primitives->push_back(iv+1);
|
|
}
|
|
|
|
}
|
|
|
|
#if 0
|
|
static bool first = true;
|
|
if (first)
|
|
{
|
|
osgDB::writeNodeFile(*_geode, "test.osgt");
|
|
first = false;
|
|
}
|
|
#endif
|
|
|
|
_geometry->dirtyBound();
|
|
|
|
// make sure the general widget geometry/state is created and _graphicsInitialized reset to false
|
|
Widget::createGraphics();
|
|
}
|