Hide
Siafoo is here to make coding less frustrating and to save you time. Join Siafoo Now or Learn More

FTLK Transfer Function Atom Feed 0

In Brief A transfer function widget that allows you to map a value range to RGBA colors. It is useful for data visualization applications, especially volume rendering. I wrote this code for a volume data visualization class... in a couple of days so the code quality is fairly low. I no longer use FLTK so it is unlikely that I'll further improve this thing, so you are encouraged to edit and improve the code, here. There is also a wxPython version... more
# 's
  1/**************************************************************************\
2 * Volume Data Visualizer for ECS 177
3 * Author: Stou Sandalski
4 * Date: 21 January 2005
5 *
6 * File: transfer_function.hpp - Implements the color transfer graph and transfer
7 * function. This is a merge of the transfer-function and transfer-function-widget
8 * files.
9 *
10 * Copyright (c) 2005, Stou Sandalski
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * * Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * * Neither the name of the <organization> nor the
21 * names of its contributors may be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY Stou Sandalski ''AS IS'' AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 **************************************************************************/
36
37
38#if _MSC_VER > 1000
39#pragma once
40#endif
41
42#ifndef HPP_UI_TRANSFER_GRAPH
43#define HPP_UI_TRANSFER_GRAPH
44
45#if defined(__APPLE__) && defined(__MACH__)
46# include <OpenGL/gl.h> // Header File For The OpenGL32 Library
47# include <OpenGL/glu.h> // Header File For The GLu32 Library
48#else
49# ifndef WIN32
50# define GL_GLEXT_PROTOTYPES
51# endif
52# include <GL/gl.h> // Header File For The OpenGL32 Library
53# include <GL/glu.h> // Header File For The GLu32 Library
54# ifndef WIN32
55# define GL_GLEXT_PROTOTYPES
56# include <GL/glext.h>
57# endif // WIN32
58#endif
59
60#include <FL/Fl.H>
61# include <FL/Fl_Widget.h>
62# include <FL/fl_draw.H>
63# include <FL/Fl_Color_Chooser.H>
64
65#include <iostream>
66#include <list>
67#include <string>
68#include <string.h>
69
70inline static float InterpolLinF(const float target, const float *v)
71{
72 return (target*(v[1])+ (1 - target)*(v[0]));
73}
74
75using namespace std;
76
77class TransferPoint
78{
79public:
80 TransferPoint(GLfloat value, GLfloat opacity, bool endNode = false)
81 : m_Value(value), m_endNode(endNode), m_Selected(false)
82
83 {
84 m_RGBA[0] = 1.0f;
85 m_RGBA[1] = 0.0f;
86 m_RGBA[2] = 0.0f;
87 m_RGBA[3] = opacity;
88 };
89
90 TransferPoint(GLfloat *data)
91 : m_endNode(false), m_Selected(false)
92
93 {
94 m_Value = data[0];
95 m_RGBA[0] = data[1];
96 m_RGBA[1] = data[2];
97 m_RGBA[2] = data[3];
98 m_RGBA[3] = data[4];
99 };
100
101public:
102 // Returns an array of GLfloats containg r,g,b,a
103 // clamped to [0,1]
104 GLfloat *getColor4f() { return m_RGBA; };
105
106 void setAlpha(float a) { m_RGBA[3] = a; };
107 void setRGBf(float r, float g, float b)
108 {
109 m_RGBA[0] = r;
110 m_RGBA[1] = g;
111 m_RGBA[2] = b;
112 }
113 void setRGBAf(float r, float g, float b, float a)
114 {
115 m_RGBA[0] = r;
116 m_RGBA[1] = g;
117 m_RGBA[2] = b;
118 m_RGBA[3] = a;
119 }
120 void getRGBAf(float &r, float &g, float &b, float &a)
121 {
122 r = m_RGBA[0];
123 g = m_RGBA[1];
124 b = m_RGBA[2];
125 a = m_RGBA[3];
126 }
127 void setRGBb(unsigned char r, unsigned char g, unsigned char b)
128 {
129 m_RGBA[0] = (float)r/255.0f;
130 m_RGBA[1] = (float)g/255.0f;
131 m_RGBA[2] = (float)b/255.0f;
132 }
133
134 void getRGBb(unsigned char &r, unsigned char &g, unsigned char &b)
135 {
136 r = (unsigned char)(m_RGBA[0]*255.0f);
137 g = (unsigned char)(m_RGBA[1]*255.0f);
138 b = (unsigned char)(m_RGBA[2]*255.0f);
139 }
140 void getRGBb(unsigned char *rgb)
141 {
142 for(int i = 0; i < 3; ++i)
143 rgb[i] = (unsigned char)(m_RGBA[i]*255.0f);
144 }
145
146 void getRGBAf(float *rgba)
147 {
148 memcpy(rgba, m_RGBA, 4*sizeof(float));
149 }
150
151 bool isSelected() { return m_Selected; };
152 void setSelect(bool select) { m_Selected = select; };
153 bool isEndNode() { return m_endNode; };
154
155 GLfloat getValue() { return m_Value; };
156 GLfloat getOpacity() { return m_RGBA[3]; };
157
158 void setValue(GLfloat value) { m_Value = value; };
159 void setOpacity(GLfloat opacity ) { m_RGBA[3] = opacity; };
160
161 void getPointData(float *data)
162 {
163 data[0] = m_Value;
164 data[1] = m_RGBA[0];
165 data[2] = m_RGBA[1];
166 data[3] = m_RGBA[2];
167 data[4] = m_RGBA[3];
168 }
169
170private:
171 GLfloat m_RGBA[4];
172 GLfloat m_Value;
173 bool m_Selected;
174 bool m_endNode;
175
176public:
177 static const int m_PixSize = 4;
178};
179
180
181class TransferFunction
182{
183public: // Setter Getters
184 void setValueRange(float *minmax)
185 {
186 m_MinMax[0] = minmax[0];
187 m_MinMax[1] = minmax[1];
188 destroyPoints();
189 initPoints();
190
191 }
192public: // Translation
193 float getValueFromX(int xc)
194 {
195 if((xc < m_X + m_BorderLeft)
196 || (xc > m_X + r_fieldWidth + m_BorderLeft))
197 {
198 cout<<"getValueFromX: "<<xc<<" out of range: "<<endl
199 <<xc<<", "<<m_X<<endl
200 <<m_BorderRight<<", "<<r_fieldWidth<<endl;
201 return -1.0f; // Out of range
202 }
203
204 float temp = (float)(xc - m_X - m_BorderLeft)/(float)r_fieldWidth;
205
206 return ((float)m_MinMax[0]) + r_rangeWidth*temp;
207 }
208
209 float getOpacityFromY(int yc)
210 {
211 if((yc < m_Y + m_BorderUp)
212 || (yc > m_Y + m_BorderUp + r_fieldHeight))
213 {
214 cout<<"getOpacityFromY: "<<yc<<" out of range: "<<endl;
215 return -1.0f; // Out of range
216 }
217
218 return 1.0f - (float)(yc - m_Y - m_BorderUp)/(float)r_fieldHeight;
219 }
220
221 int getXfromValue(float value)
222 {
223 float temp = r_fieldWidth*((value-m_MinMax[0])/r_rangeWidth);
224 return m_BorderLeft + m_X + static_cast<int>(temp);
225 }
226
227 int getYfromOpacity(float opacity)
228 {
229 if(opacity < 0)
230 {
231 cout<<"ERROR: negative opacity"<<endl;
232 return 0;
233 }
234
235 return (int)(m_BorderUp + m_Y + static_cast<int>(r_fieldHeight*(1 - opacity)));
236 }
237
238 void getRGBFromValue(float value, unsigned char *rgb)
239 {
240 float temp = 0.0f;
241 getRGBAFromValue(value, rgb, temp);
242 }
243
244 void getRGBAFromValue(float value, unsigned char *rgb, float &a)
245 {
246 float rgba[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
247 getRGBAFromValuef(value, rgba);
248
249 for(int i = 0; i < 3; ++i)
250 rgb[i] = (unsigned char)(rgba[i]*255.0f);
251
252 a = rgba[3];
253 }
254
255 void getRGBAFromValuef(float value, float *rgba)
256 {
257 if(m_PreviewMode)
258 {
259 for(int i = 0; i < 4; ++i)
260 rgba[i] = 0.0f;
261
262 if((m_PreviewValue <= value + 3) && (m_PreviewValue >= value - 3))
263 {
264 rgba[0] = 1.0f;
265 rgba[3] = 1.0f;
266 }
267 return;
268 }
269 float tempTarget = 0.0f;
270 float ra[2], ga[2], ba[2], aa[2];
271
272 if((value < m_MinMax[0]) || (value > m_MinMax[1]))
273 return;
274
275 list<TransferPoint*>::iterator p_next;
276 for( list<TransferPoint*>::iterator p_cur = m_Points.begin(); p_cur != m_Points.end(); p_cur++ )
277 {
278 p_next = p_cur;
279 p_next++;
280 if(p_next == m_Points.end())
281 { break; }
282
283 if((*p_cur)->getValue() < value)
284 {
285 if((*p_next)->getValue() > value)
286 {
287 tempTarget = (value - (*p_cur)->getValue())/((*p_next)->getValue() - (*p_cur)->getValue());
288 (*p_cur)->getRGBAf(ra[0], ga[0], ba[0], aa[0]);
289 (*p_next)->getRGBAf(ra[1], ga[1], ba[1], aa[1]);
290
291 //TODO this can be vectorized
292 rgba[0] = InterpolLinF(tempTarget, (float*)ra);
293 rgba[1] = InterpolLinF(tempTarget, (float*)ga);
294 rgba[2] = InterpolLinF(tempTarget, (float*)ba);
295 rgba[3] = InterpolLinF(tempTarget, (float*)aa);
296 break;
297 }
298 else if((*p_next)->getValue() == value) // Rare
299 {
300 (*p_next)->getRGBAf(rgba);
301 break;
302 }
303 }
304 else if((*p_cur)->getValue() == value) // Rare
305 {
306 (*p_cur)->getRGBAf(rgba);
307 break;
308 }
309 else
310 {
311 cout<<"getRGBAFromValue: ERROR"<<endl;
312 }
313 }
314 }
315
316public: // Utility functions
317 bool hitTest(int x, int y, TransferPoint *pt)
318 {
319 if((x <= getXfromValue(pt->getValue()) + TransferPoint::m_PixSize/2)
320 && (x >= getXfromValue(pt->getValue()) - TransferPoint::m_PixSize/2))
321 {
322 if((y <= getYfromOpacity(pt->getOpacity()) + TransferPoint::m_PixSize/2)
323 && (y >= getYfromOpacity(pt->getOpacity()) - TransferPoint::m_PixSize/2))
324 {
325 return true;
326 }
327 else { return false; }
328 }
329 else { return false; }
330 }
331
332 void setFunctionPoints(float *points, int count)
333 {
334 //TODO fix memory leak
335 if(m_Points.size() > 0)
336 {
337 m_Points.clear();
338 }
339
340 // Add all points
341 for(int i = 0; i < count; ++i)
342 m_Points.push_back(new TransferPoint(points + 5*i));
343 }
344
345 int getFunctionPoints(float **points)
346 {
347 *points = new float[5*m_Points.size()];
348
349 // Iterate over points
350
351 return m_Points.size();
352 }
353
354protected: // Misc
355 void destroyPoints()
356 {
357 for( list<TransferPoint*>::iterator p_cur = m_Points.begin(); p_cur != m_Points.end(); p_cur++ )
358 {
359 delete (*p_cur);
360 }
361 m_Points.clear();
362 }
363
364 void initPoints()
365 {
366 char buff[10];
367 m_selectedIter = m_Points.end();
368 // TODO Change these to use actual range values
369 TransferPoint *pt = new TransferPoint(m_MinMax[0], 0.0f, true);
370 pt->setRGBb(255, 255, 255);
371 m_Points.push_back(pt);
372 pt = new TransferPoint(m_MinMax[1], 1.0f, true);
373 pt->setRGBb(0,0,0);
374 m_Points.push_back(pt);
375
376 memset(buff, 0, 10*sizeof(char));
377 sprintf(buff, "%.2f", m_MinMax[0]);
378 m_strRangeFrom = buff;
379
380 memset(buff, 0, 10*sizeof(char));
381 sprintf(buff, "%.2f", m_MinMax[1]);
382 m_strRangeTo = buff;
383
384 r_rangeWidth = (float)(m_MinMax[1] - m_MinMax[0]);
385 increment = r_rangeWidth/(float)r_fieldWidth;
386 }
387
388public: // Construction Destruction
389 TransferFunction(int x,int y,int w,int h) :
390 m_Changed(false), m_PreviewMode(false), m_PreviewValue(0.0f)
391 {
392 m_MinMax[0] = 0.0f;
393 m_MinMax[1] = 0.0f;
394
395 m_X = x;
396 m_Y = y;
397 m_W = w;
398 m_H = h;
399
400 r_fieldHeight = (m_H - (m_BorderUp + m_BorderDown));
401 r_fieldWidth = (m_W - (m_BorderRight + m_BorderLeft));
402 r_rangeWidth = 0.0f;
403 }
404
405 ~TransferFunction(void) { };
406
407 bool m_PreviewMode;
408 float m_PreviewValue;
409
410protected:
411 list<TransferPoint*> m_Points;
412 list<TransferPoint*>::iterator m_selectedIter;
413
414 bool m_hasSelection;
415 bool m_Changed;
416
417 float m_MinMax[2];
418 float increment;
419
420 int m_X;
421 int m_Y;
422 int m_H;
423 int m_W;
424
425 string m_strRangeFrom;
426 string m_strRangeTo;
427
428 static const int m_BorderLeft = 20;
429 static const int m_BorderUp = 5;
430 static const int m_BorderRight = 5;
431 static const int m_BorderDown = 13;
432
433 static const int m_GridLines = 4;
434 static const int m_TickSize = 4;
435 static const int m_TickBorder = 2;
436
437 static const int m_labelFontPt = 10;
438
439protected: // Data used for calculations
440 int r_fieldHeight;
441 int r_fieldWidth;
442 float r_rangeWidth;
443
444};
445
446
447class TransferGraph : public Fl_Widget, public TransferFunction
448{
449
450public: // Fl_Widget overrides
451 void draw()
452 {
453 TransferPoint* tpoint;
454 list<TransferPoint*>::iterator m_pointIter;
455
456 fl_color(255, 255, 255);
457 fl_rectf(x(),y(), w(), h()); // Check
458
459 fl_color(0,0,0);
460
461 int x, y, x_c, y_c;
462 x = getXfromValue(m_MinMax[0]); //((*m_Points.begin())->getValue());
463 y = getYfromOpacity(0.0f); //((*m_Points.begin())->getOpacity());
464
465
466 if(m_PreviewMode)
467 {
468 y = getYfromOpacity(1.0f);
469 drawGrid();
470 drawAxis();
471
472 fl_color(255,0,0);
473 // Vertical
474 float v = getXfromValue(m_PreviewValue);
475 fl_line(v, y, v, y + r_fieldHeight);
476
477 if(m_Changed)
478 {
479 Fl_Widget::do_callback();
480 m_Changed = false;
481 }
482 return;
483 }
484
485 drawFill();
486 drawGrid();
487 drawAxis();
488
489
490
491 for( m_pointIter = m_Points.begin(); m_pointIter != m_Points.end(); m_pointIter++ )
492 {
493 tpoint = (TransferPoint*) *m_pointIter;
494
495 fl_color(255, 0, 0);
496 x_c = getXfromValue(tpoint->getValue());
497 y_c = getYfromOpacity(tpoint->getOpacity());
498
499 fl_line(x, y, x_c, y_c);
500
501 if(tpoint->isSelected())
502 {
503 fl_color(0, 255, 0);
504 fl_rectf(x_c - 3, y_c - 3 , 6, 6);
505 }
506
507 fl_color(255, 0, 0);
508 fl_rectf(x_c - 2, y_c - 2 , 4, 4);
509
510 if(getXfromValue(tpoint->getValue()) < x)
511 x = x_c;
512 else
513 x = getXfromValue(tpoint->getValue());
514
515 y = getYfromOpacity(tpoint->getOpacity());
516 }
517
518 if(m_Changed)
519 {
520 Fl_Widget::do_callback();
521 m_Changed = false;
522 }
523
524 }
525
526 int handle(int event)
527 {
528 int tX = 0;
529 int tY = 0;
530
531 TransferPoint* tpoint;
532 list<TransferPoint*>::iterator tempIter1;
533 list<TransferPoint*>::iterator tempIter2;
534 list<TransferPoint*>::iterator m_pointIter;
535
536 switch(event)
537 {
538
539 case FL_PUSH:
540 tX = Fl::event_x(); // To prevent weird shit
541 tY = Fl::event_y();
542
543 if((tY < y() + m_BorderUp) || (tY > y() + m_BorderUp + r_fieldHeight)
544 || (tX < x() + m_BorderLeft) || (tX > x() + m_BorderLeft + r_fieldWidth))
545 {
546 return 0;
547 }
548
549 m_selectedIter = m_Points.end();
550 // cout<<"X,Y: "<<Fl::event_x()<<", "<<Fl::event_y()<<endl;
551 for( m_pointIter = m_Points.begin(); m_pointIter != m_Points.end(); m_pointIter++ )
552 {
553 tpoint = (TransferPoint*) *m_pointIter;
554 if(hitTest(Fl::event_x(), Fl::event_y(), tpoint))
555 {
556 m_selectedIter = m_pointIter;
557 tpoint->setSelect(true);
558 }
559 else
560 {
561 tpoint->setSelect(false);
562 }
563 }
564 redraw();
565
566 return 1;
567
568 case FL_DRAG: // TODO Fix This
569 return handleDrag(Fl::event_x(), Fl::event_y());
570
571 case FL_RELEASE:
572 m_hasSelection = false;
573 m_selectedIter = m_Points.end(); //TODO use the selection pointer here
574
575 tX = Fl::event_x();
576 tY = Fl::event_y();
577 if((tY < y() + m_BorderUp) || (tY > y() + m_BorderUp + r_fieldHeight)
578 || (tX < x() + m_BorderLeft) || (tX > x() + m_BorderLeft + r_fieldWidth))
579 {
580 // cout<<"TransferGraph: Out of Bounds "<<tX<<", "<<tY<<endl;
581
582 return 1;
583 }
584 /// cout<<"Value "<<getValueFromX(tX)<<endl;
585 for( m_pointIter = m_Points.begin(); m_pointIter != m_Points.end(); m_pointIter++ )
586 {
587 tpoint = (TransferPoint*) *m_pointIter;
588 if(tpoint->isSelected())
589 {
590 m_hasSelection = true;
591 m_selectedIter = m_pointIter;
592 m_Changed = true;
593 }
594 }
595 if(!m_hasSelection)
596 {
597 tpoint = new TransferPoint(0.0f, 0.0f);
598 getRGBAFromValuef(getValueFromX(tX), temp_RGBf);
599
600 for(int i = 0; i < 3; ++i)
601 temp_RGB[i] = (unsigned char)(temp_RGBf[i]*255.0f);
602
603 if(!fl_color_chooser("Pick a Color", temp_RGB[0], temp_RGB[1], temp_RGB[2]))
604 return 1;
605
606 tpoint->setRGBb(temp_RGB[0], temp_RGB[1],temp_RGB[2]);
607
608 tpoint->setSelect(true);
609 tpoint->setValue(getValueFromX(tX));
610 tpoint->setOpacity(getOpacityFromY(tY));
611
612 // Find the two points in between where I clicked
613 tempIter1 = m_Points.end();
614
615 for( m_pointIter = m_Points.begin(); m_pointIter != m_Points.end(); m_pointIter++ )
616 {
617 if(tX < getXfromValue((*m_pointIter)->getValue()))
618 {
619 tempIter1 = m_pointIter;
620 break;
621 }
622 }
623 // Insert the thing there
624 m_selectedIter = m_Points.insert(tempIter1, tpoint);
625 m_Changed = true;
626 redraw();
627 return 1;
628 }
629
630 case FL_FOCUS:
631 return 1;
632
633 case FL_KEYUP:
634 if(Fl::event_key() == FL_Delete)
635 {
636 if(m_selectedIter != m_Points.end())
637 {
638 tpoint = (TransferPoint*) *m_selectedIter;
639 if(tpoint->isEndNode())
640 {
641 //cout<<"I am sorry, I can not delete the end points"<<endl;
642 return 1;
643 }
644
645 tempIter1 = m_selectedIter;
646 --tempIter1;
647
648 m_selectedIter = m_Points.erase(m_selectedIter);
649 m_selectedIter = tempIter1;
650
651 // This should always be true but its a sanity check
652 if(m_selectedIter != m_Points.end())
653 (*m_selectedIter)->setSelect(true);
654
655 delete tpoint;
656 m_Changed = true;
657 redraw();
658 }
659 }
660 return 1;
661
662 default:
663 return Fl_Widget::handle(event);
664 }
665
666
667 }
668
669
670public: // Setter / Getters
671 //TransferFunction* getTransferFunction() { return this; };
672
673private: // Helper functions
674
675 // Event Handling
676 int handleDrag(int tX, int tY)
677 {
678 // Do we have selected points?
679 if(m_selectedIter != m_Points.end())
680 {
681 m_Changed = true;
682 // Maybe move these declarations globaly? to save on allocations
683 TransferPoint *tpoint = (TransferPoint*) *m_selectedIter;
684 list<TransferPoint*>::iterator tempIter1;
685 list<TransferPoint*>::iterator tempIter2;
686
687
688 tempIter1 = m_selectedIter;
689 tempIter2 = m_selectedIter;
690
691 // Y Direction handling
692 if(tY < y() + m_BorderUp)
693 (*m_selectedIter)->setOpacity(1.0f);
694 else if(tY > y() + m_BorderUp + r_fieldHeight)
695 (*m_selectedIter)->setOpacity(0.0f);
696 else
697 tpoint->setOpacity(getOpacityFromY(tY));
698
699 if(tempIter1 == m_Points.begin())
700 {
701 redraw();
702 return 1;
703 }
704
705 --tempIter1;
706
707 if(tempIter2 == m_Points.end())
708 {
709 redraw();
710 return 1;
711 }
712
713 ++tempIter2;
714
715 if((*m_selectedIter)->isEndNode())
716 {
717 redraw();
718 return 1;
719 }
720
721 if(tX < x() + m_BorderLeft)
722 (*m_selectedIter)->setValue(this->m_MinMax[0]);
723 else if (tX > x() + m_BorderLeft + r_fieldWidth)
724 (*m_selectedIter)->setValue(this->m_MinMax[1]);
725 else
726 if((getXfromValue((*tempIter1)->getValue()) <= tX) &&
727 (getXfromValue((*tempIter2)->getValue()) >= tX))
728 {
729 tpoint->setValue(getValueFromX(tX));
730 }
731
732 redraw();
733 }
734
735 return 1;
736 }
737
738 // Drawing
739 void drawGrid()
740 {
741 int spacing = h()/(m_GridLines + 1);
742 fl_color(218,218,218);
743
744 for(int i = 0; i < m_GridLines; ++i)
745 {
746 fl_line(x() + m_BorderLeft, y() + (1 + i)*spacing, x() + m_BorderLeft + r_fieldWidth, y() + (1 + i)*spacing);
747 }
748 }
749
750 void drawAxis()
751 {
752 fl_color(218,218,218);
753 // Horizontal
754 fl_line(x()+ m_BorderLeft - 2, y() + m_BorderUp, x() + m_BorderLeft + r_fieldWidth, y() + m_BorderUp);
755 fl_line(x()+ m_BorderLeft - 2, y() + r_fieldHeight + m_BorderUp, x() + m_BorderLeft + r_fieldWidth, y() + r_fieldHeight + m_BorderUp);
756
757 // Vertical
758 fl_line(x() + m_BorderLeft, y() + m_BorderUp, x() + m_BorderLeft, y() + m_BorderUp + r_fieldHeight);
759 fl_line(x() + m_BorderLeft + r_fieldWidth, y() + m_BorderUp, x() + m_BorderLeft + r_fieldWidth, y() + m_BorderUp + r_fieldHeight);
760
761 fl_font(1, m_labelFontPt);
762 fl_draw("1.0", x() + 1, y() + m_BorderUp + m_labelFontPt/2 );
763 fl_draw("0.0", x() + 1, y() + m_BorderUp + r_fieldHeight);
764
765 fl_draw("O", x() + 1, y() + m_BorderUp + 2*m_labelFontPt );
766 fl_draw("p", x() + 1, y() + m_BorderUp + 3*m_labelFontPt );
767 fl_draw("a", x() + 1, y() + m_BorderUp + 4*m_labelFontPt );
768 fl_draw("c", x() + 1, y() + m_BorderUp + 5*m_labelFontPt );
769 fl_draw("i", x() + 1, y() + m_BorderUp + 6*m_labelFontPt );
770 fl_draw("t", x() + 1, y() + m_BorderUp + 7*m_labelFontPt );
771 fl_draw("y", x() + 1, y() + m_BorderUp + 8*m_labelFontPt );
772
773 int strw = 0;
774 int strh = 0;
775
776 fl_measure(m_strRangeFrom.c_str(), strw, strh);
777 fl_draw(m_strRangeFrom.c_str(), x() + m_BorderLeft - strw/2, y() + m_BorderUp + r_fieldHeight + m_labelFontPt);
778 fl_measure(m_strRangeTo.c_str(), strw, strh);
779 fl_draw(m_strRangeTo.c_str(), x() + m_BorderLeft + r_fieldWidth - strw , y() + m_BorderUp + r_fieldHeight + m_labelFontPt);
780 fl_draw("Values", x() + m_BorderLeft + r_fieldWidth/2, y() + m_BorderUp + r_fieldHeight + m_labelFontPt);
781
782 }
783
784 void drawFill()
785 {
786 float rgba[4] = {0, 0, 0, 0};
787 fl_color(0,0,0);
788
789 float val = m_MinMax[0];
790
791 int yat = getYfromOpacity(0.0f);
792
793 for(int i = 0; i < r_fieldWidth; ++i)
794 {
795 getRGBAFromValuef(val, rgba);
796 fl_color((unsigned char)(rgba[0]*255.0f),
797 (unsigned char)(rgba[1]*255.0f),
798 (unsigned char)(rgba[2]*255.0f));
799
800 fl_line(getXfromValue(val), getYfromOpacity(rgba[3]), getXfromValue(val), yat);
801 val += increment;
802 }
803 }
804
805public: // Construction / Destruction
806 TransferGraph(int x,int y,int w,int h,const char *l=0) :
807 Fl_Widget(x,y,w,h,l), TransferFunction(x, y, w, h)
808 {
809
810 }
811
812 ~TransferGraph(void) { };
813
814private: // Data Members
815 int y_drag, x_drag;
816 GLubyte temp_RGB[3];
817 GLfloat temp_RGBf[3];
818
819};
820
821#endif // HPP_UI_TRANSFER_GRAPH

A transfer function widget that allows you to map a value range to RGBA colors. It is useful for data visualization applications, especially volume rendering. I wrote this code for a volume data visualization class... in a couple of days so the code quality is fairly low. I no longer use FLTK so it is unlikely that I'll further improve this thing, so you are encouraged to edit and improve the code, here. There is also a wxPython version

If you happen to use the widget for something cool, send me a screenshot.

http://www.siafoo.net/image/125

The simplest application that uses this transfer function is:

# 's
 1#include "transfer_graph.hpp"
2#include <iostream>
3#include <FL/Fl.H>
4#include <FL/Fl_Double_Window.H>
5
6using namespace std;
7
8// Transfer function callback
9static void cb_colorChange(TransferGraph* o, void*)
10{
11 unsigned char rgba[4] = {0, 0, 0, 0};
12
13 o->getRGBFromValue(3.0f, rgba);
14
15 cout<<"Colors for Value 3.0f\t";
16
17 for(int i; i < 3; ++i)
18 cout<<(int)rgba[i]<<'\t';
19
20 cout<<endl;
21}
22
23int main(int argc, char **argv)
24{
25 Fl_Double_Window* w = new Fl_Double_Window(300, 200, "Transfer Function Test");
26
27 TransferGraph *tg = new TransferGraph(10, 10, 281, 143, "Transfer Function");
28 tg->callback((Fl_Callback*)cb_colorChange);
29 tg->when(FL_WHEN_CHANGED);
30
31 float range[2] = {0, 255.0};
32 tg->setValueRange(range);
33
34 w->end();
35
36 w->show(argc, argv);
37 return Fl::run();
38}

Compile with g++ tfunction.cpp -lfltk -o tfunction