1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2013 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
12 */
13
14 #include "TransferFunctionWidget.h"
15
16 #include <osg/Geode>
17 #include <osg/Geometry>
18 #include <osg/TexGen>
19 #include <osg/AlphaFunc>
20 #include <osg/Texture2D>
21 #include <osg/MatrixTransform>
22 #include <osg/io_utils>
23
24 #include <osgGA/EventVisitor>
25 #include <osgDB/ReadFile>
26 #include <osgDB/WriteFile>
27 #include <osgViewer/View>
28
29 using namespace osgUI;
30
TransferFunctionWidget(osg::TransferFunction1D * tf)31 TransferFunctionWidget::TransferFunctionWidget(osg::TransferFunction1D* tf):
32 _min(FLT_MAX),
33 _max(-FLT_MAX),
34 _left(FLT_MAX),
35 _right(-FLT_MAX),
36 _startedDrag(false),
37 _previousDragPosition(0.0f)
38 {
39 setNumChildrenRequiringEventTraversal(1);
40 setExtents(osg::BoundingBox(0.0,0.0,0.0,1.0,1.0,0.0));
41 setTransferFunction(tf);
42 }
43
TransferFunctionWidget(const TransferFunctionWidget & tfw,const osg::CopyOp & copyop)44 TransferFunctionWidget::TransferFunctionWidget(const TransferFunctionWidget& tfw, const osg::CopyOp& copyop):
45 Widget(tfw,copyop)
46 {
47 setExtents(tfw.getExtents());
48 setTransferFunction(tfw.getTransferFunction());
49 }
50
setTransferFunction(const osg::TransferFunction1D * tf)51 void TransferFunctionWidget::setTransferFunction(const osg::TransferFunction1D* tf)
52 {
53 if (_transferFunction==tf) return;
54
55 _transferFunction = const_cast<osg::TransferFunction1D*>(tf);
56
57 if (_transferFunction.valid())
58 {
59 osg::TransferFunction1D::ColorMap& colorMap = _transferFunction->getColorMap();
60 if (colorMap.empty())
61 {
62 _min = FLT_MAX;
63 _max = -FLT_MAX;
64 }
65 else
66 {
67 _min = colorMap.begin()->first;
68 _max = colorMap.rbegin()->first;
69 }
70 }
71
72 resetVisibleRange();
73 }
74
resetVisibleRange()75 void TransferFunctionWidget::resetVisibleRange()
76 {
77 setVisibleRange(_min, _max);
78 }
79
setVisibleRange(float left,float right)80 void TransferFunctionWidget::setVisibleRange(float left, float right)
81 {
82 if (left<_min) left = _min;
83 if (right>_max) right = _max;
84
85 _left = left;
86 _right = right;
87 // OSG_NOTICE<<"setVisibleRange("<<_left<<", "<<_right<<")"<<std::endl;
88 createGraphics();
89 }
90
translateVisibleRange(float delta)91 void TransferFunctionWidget::translateVisibleRange(float delta)
92 {
93 float new_left = _left+(_right-_left)*delta;
94 float new_right = _right+(_right-_left)*delta;
95 if (delta<0.0)
96 {
97 if (new_left<_min)
98 {
99 new_right += (_min-new_left);
100 new_left = _min;
101 }
102 }
103 else
104 {
105 if (new_right>_max)
106 {
107 new_left += (_max-new_right);
108 new_right = _max;
109 }
110 }
111
112 setVisibleRange(new_left, new_right);
113 }
114
scaleVisibleRange(float center,float delta)115 void TransferFunctionWidget::scaleVisibleRange(float center, float delta)
116 {
117 float scale = powf(2.0, delta);
118 setVisibleRange(center+(_left-center)*scale,
119 center+(_right-center)*scale);
120 }
121
122
traverseImplementation(osg::NodeVisitor & nv)123 void TransferFunctionWidget::traverseImplementation(osg::NodeVisitor& nv)
124 {
125 Widget::traverseImplementation(nv);
126 }
127
handleImplementation(osgGA::EventVisitor * ev,osgGA::Event * event)128 bool TransferFunctionWidget::handleImplementation(osgGA::EventVisitor* ev, osgGA::Event* event)
129 {
130 osgGA::GUIEventAdapter* ea = event->asGUIEventAdapter();
131 if (!ea) return false;
132
133 switch(ea->getEventType())
134 {
135 case(osgGA::GUIEventAdapter::PUSH):
136 // OSG_NOTICE<<"Pressed button "<<ea->getButton()<<std::endl;
137 _startedDrag = false;
138 if (ea->getButtonMask()==osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
139 {
140 osg::Vec3d position;
141 if (computePositionInLocalCoordinates(ev, ea, position))
142 {
143 _startedDrag = true;
144 _previousDragPosition = position.x();
145 }
146 }
147 break;
148 case(osgGA::GUIEventAdapter::RELEASE):
149 // OSG_NOTICE<<"Released button "<<ea->getButton()<<std::endl;
150 _startedDrag = false;
151 break;
152 case(osgGA::GUIEventAdapter::DRAG):
153 // OSG_NOTICE<<"Dragged "<<std::endl;
154 if (_startedDrag)
155 {
156 osg::Vec3d position;
157 if (computePositionInLocalCoordinates(ev, ea, position))
158 {
159 float delta = -(position.x()-_previousDragPosition);
160
161 _previousDragPosition = position.x();
162
163 translateVisibleRange(delta);
164 }
165 }
166 break;
167 case(osgGA::GUIEventAdapter::SCROLL):
168 {
169 osg::Vec3d position;
170 if (computePositionInLocalCoordinates(ev, ea, position))
171 {
172 float translation = 0.0;
173 float increment = 0.1;
174 float scale = 1.0;
175 float ratio = (1.0f+increment);
176 switch(ea->getScrollingMotion())
177 {
178 case(osgGA::GUIEventAdapter::SCROLL_NONE):
179 break;
180 case(osgGA::GUIEventAdapter::SCROLL_LEFT):
181 translation -= increment;
182 break;
183 case(osgGA::GUIEventAdapter::SCROLL_RIGHT):
184 translation += increment;
185 break;
186 case(osgGA::GUIEventAdapter::SCROLL_UP):
187 scale /= ratio;
188 break;
189 case(osgGA::GUIEventAdapter::SCROLL_DOWN):
190 scale *= ratio;
191 break;
192 case(osgGA::GUIEventAdapter::SCROLL_2D):
193 translation = increment*ea->getScrollingDeltaX();
194 scale = powf(ratio, increment*ea->getScrollingDeltaY());
195 break;
196 }
197 float center = _left+(_right-_left)*position.x();
198 // OSG_NOTICE<<"translation = "<<translation<<", scale = "<<scale<<", x="<<position.x()<<", center="<<center<<std::endl;
199 setVisibleRange(translation+center+(_left-center)*scale,
200 translation+center+(_right-center)*scale);
201
202 }
203 break;
204 }
205 case(osgGA::GUIEventAdapter::KEYDOWN):
206 {
207 // OSG_NOTICE<<"Pressed key"<<ea->getKey()<<std::endl;
208 float delta = 0.02;
209 if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Left) translateVisibleRange(-delta);
210 else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Right) translateVisibleRange(delta);
211 else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Up) scaleVisibleRange((_left+_right)*0.5f, -delta);
212 else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Down) scaleVisibleRange((_left+_right)*0.5f, delta);
213 break;
214 }
215 case(osgGA::GUIEventAdapter::KEYUP):
216 // OSG_NOTICE<<"Released key"<<ea->getKey()<<std::endl;
217 if (ea->getKey()==' ' ||ea->getKey()==osgGA::GUIEventAdapter::KEY_Home) resetVisibleRange();
218 break;
219 default:
220 break;
221 }
222 return false;
223 }
224
createGraphicsImplementation()225 void TransferFunctionWidget::createGraphicsImplementation()
226 {
227 // OSG_NOTICE<<"Create graphics"<<std::endl;
228
229 typedef osg::TransferFunction1D::ColorMap ColorMap;
230 ColorMap& colorMap = _transferFunction->getColorMap();
231 if (colorMap.empty()) return;
232
233 float depth = 0.0f;
234 float yMax = 0.0f;
235
236 // find yMax
237 for(ColorMap::iterator itr = colorMap.begin();
238 itr != colorMap.end();
239 ++itr)
240 {
241 float y = itr->second[3];
242 if (y>yMax) yMax = y;
243 }
244
245 float xScale = 1.0f/(_right-_left);
246 float xOffset = -_left;
247 float yScale = 1.0f/yMax;
248
249 if (!_geode)
250 {
251 _geode = new osg::Geode;
252 addChild(_geode.get());
253 }
254
255 {
256
257 if (!_geometry)
258 {
259 _geometry = new osg::Geometry;
260 _geometry->setDataVariance(osg::Geometry::DYNAMIC);
261 _geometry->setUseDisplayList(false);
262 _geometry->setUseVertexBufferObjects(false);
263
264 _geode->addDrawable(_geometry.get());
265
266 osg::ref_ptr<osg::StateSet> stateset = _geometry->getOrCreateStateSet();
267
268 stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
269 stateset->setMode(GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
270
271 osg::ref_ptr<osg::AlphaFunc> alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.0f);
272 stateset->setAttributeAndModes(alphaFunc.get(), osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
273
274 osg::ref_ptr<osg::Image> image = new osg::Image;
275 image->allocateImage(1,1,1, GL_RGBA, GL_UNSIGNED_BYTE);
276 unsigned char* data = image->data();
277 data[0] = 255;
278 data[1] = 255;
279 data[2] = 255;
280 data[3] = 255;
281
282 osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
283 texture->setImage(image.get());
284 texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
285 texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
286 texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_BORDER);
287 texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_BORDER);
288 texture->setBorderColor(osg::Vec4(1.0f,1.0f,0.0f,0.0f));
289
290 stateset->setTextureAttribute(0, texture.get());
291
292 osg::ref_ptr<osg::TexGen> texgen = new osg::TexGen;
293 texgen->setMode(osg::TexGen::OBJECT_LINEAR);
294 texgen->setPlane(osg::TexGen::S, osg::Plane(1.0,0.0,0.0,0.0));
295 texgen->setPlane(osg::TexGen::T, osg::Plane(0.0,1.0,0.0,0.0));
296
297 stateset->setTextureAttribute(0, texgen.get());
298 stateset->setTextureMode(0, GL_TEXTURE_GEN_S, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
299 stateset->setTextureMode(0, GL_TEXTURE_GEN_T, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
300 stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);
301 }
302
303
304 if (!_vertices)
305 {
306 _vertices = new osg::Vec3Array;
307 _geometry->setVertexArray(_vertices.get());
308 }
309
310 if (!_colours)
311 {
312 _colours = new osg::Vec4Array;
313 _geometry->setColorArray(_colours.get(), osg::Array::BIND_PER_VERTEX);
314 }
315
316 osg::Vec4 background_color(1.0f, 1.0f, 1.0f, 0.1f);
317
318 unsigned numColumnsRequired = colorMap.size();
319 _vertices->resize(0);
320 _vertices->reserve(numColumnsRequired*3);
321 for(ColorMap::iterator itr = colorMap.begin();
322 itr != colorMap.end();
323 ++itr)
324 {
325 float x = itr->first;
326 osg::Vec4 color = itr->second;
327
328 float y = itr->second[3];
329 color[3] = 1.0f;
330
331 _vertices->push_back(osg::Vec3((x+xOffset)*xScale, 0.0f, depth));
332 _colours->push_back(color);
333
334 _vertices->push_back(osg::Vec3((x+xOffset)*xScale, y*yScale, depth));
335 _colours->push_back(color);
336
337 _vertices->push_back(osg::Vec3((x+xOffset)*xScale, y*yScale, depth));
338 _colours->push_back(background_color);
339
340 _vertices->push_back(osg::Vec3((x+xOffset)*xScale, yMax*yScale, depth));
341 _colours->push_back(background_color);
342 }
343
344 if (!_background_primitives)
345 {
346 _background_primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
347 _geometry->addPrimitiveSet(_background_primitives.get());
348 }
349
350 if (!_historgram_primitives)
351 {
352 _historgram_primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
353 _geometry->addPrimitiveSet(_historgram_primitives.get());
354 }
355
356 if (!_outline_primitives)
357 {
358 _outline_primitives = new osg::DrawElementsUShort(GL_LINE_STRIP);
359 _geometry->addPrimitiveSet(_outline_primitives.get());
360 }
361
362 _background_primitives->resize(0);
363 _historgram_primitives->resize(0);
364 _outline_primitives->resize(0);
365
366 for(unsigned int i=0; i<numColumnsRequired; ++i)
367 {
368 int iv = i*4;
369
370 _background_primitives->push_back(iv+3);
371 _background_primitives->push_back(iv+2);
372
373 _historgram_primitives->push_back(iv+1);
374 _historgram_primitives->push_back(iv+0);
375
376 _outline_primitives->push_back(iv+1);
377 }
378
379 }
380
381 #if 0
382 static bool first = true;
383 if (first)
384 {
385 osgDB::writeNodeFile(*_geode, "test.osgt");
386 first = false;
387 }
388 #endif
389
390 _geometry->dirtyBound();
391
392 // make sure the general widget geometry/state is created and _graphicsInitialized reset to false
393 Widget::createGraphicsImplementation();
394 }
395