1 /* OpenSceneGraph example, osggeometry.
2 *
3 *  Permission is hereby granted, free of charge, to any person obtaining a copy
4 *  of this software and associated documentation files (the "Software"), to deal
5 *  in the Software without restriction, including without limitation the rights
6 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 *  copies of the Software, and to permit persons to whom the Software is
8 *  furnished to do so, subject to the following conditions:
9 *
10 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16 *  THE SOFTWARE.
17 */
18 
19 #include <osg/Geode>
20 #include <osg/Geometry>
21 #include <osg/ImageUtils>
22 #include <osg/MatrixTransform>
23 #include <osg/PositionAttitudeTransform>
24 #include <osg/ComputeBoundsVisitor>
25 #include <osg/UserDataContainer>
26 #include <osg/ValueObject>
27 #include <osg/io_utils>
28 
29 #include <osgDB/ReadFile>
30 #include <osgDB/WriteFile>
31 #include <osgDB/FileUtils>
32 
33 #include <osgVolume/Volume>
34 #include <osgVolume/VolumeTile>
35 #include <osgVolume/RayTracedTechnique>
36 #include <osgVolume/FixedFunctionTechnique>
37 
38 #include <osgViewer/Viewer>
39 #include <osgViewer/ViewerEventHandlers>
40 
41 
42 #include "TransferFunctionWidget.h"
43 
44 class Histogram
45 {
46 public:
Histogram()47     Histogram() {}
48 
49     void analyse(const osg::Image* image, double interval=0.0);
50 
51     void insertZeroBoundaryValues(float xMin=FLT_MAX, float xMax=-FLT_MAX);
52 
53     osg::Node* createGraphicalRepresentation();
54 
55     typedef std::map<float, float> ValueMap;
56 
getValueMap()57     ValueMap& getValueMap() { return _valueMap; }
58 
59 protected:
60 
61     ValueMap _valueMap;
62 };
63 
64 struct PopulateHistogram
65 {
66 
PopulateHistogramPopulateHistogram67     PopulateHistogram(Histogram::ValueMap& valueMap):
68         _histogram(valueMap) {}
69 
castPopulateHistogram70     float cast(char v) { return v; }
castPopulateHistogram71     float cast(unsigned char v) { return v; }
castPopulateHistogram72     float cast(short v) { return v; }
castPopulateHistogram73     float cast(unsigned short v) { return v; }
castPopulateHistogram74     float cast(int v) { return v; }
castPopulateHistogram75     float cast(unsigned int v) { return v; }
castPopulateHistogram76     float cast(float v) { return v; }
castPopulateHistogram77     float cast(double v) { return v; }
78 
79     Histogram::ValueMap& _histogram;
80 
updatePopulateHistogram81     void update(int v)
82     {
83         _histogram[v]+=1.0;
84     }
85 
normalizePopulateHistogram86     void normalize()
87     {
88         double maxValue = 0;
89         for(Histogram::ValueMap::iterator itr = _histogram.begin();
90             itr != _histogram.end();
91             ++itr)
92         {
93             if (itr->second>maxValue) maxValue = itr->second;
94         }
95 
96         for(Histogram::ValueMap::iterator itr = _histogram.begin();
97             itr != _histogram.end();
98             ++itr)
99         {
100             itr->second /= maxValue;
101         }
102     }
103 
luminancePopulateHistogram104     void luminance(float l) { update(l); }
alphaPopulateHistogram105     void alpha(float a) { update(a); }
luminance_alphaPopulateHistogram106     void luminance_alpha(float l, float a) { update(l); }
rgbPopulateHistogram107     void rgb(float r, float g, float b) { update(r); }
rgbaPopulateHistogram108     void rgba(float r, float g, float b, float a) { update(a); }
109 };
110 
analyse(const osg::Image * image,double interval)111 void Histogram::analyse(const osg::Image* image, double interval)
112 {
113     PopulateHistogram populateHistogram(_valueMap);
114     readImage(image, populateHistogram);
115     populateHistogram.normalize();
116 
117     for(Histogram::ValueMap::iterator itr = populateHistogram._histogram.begin();
118         itr != populateHistogram._histogram.end();
119         ++itr)
120     {
121         OSG_NOTICE<<"  "<<itr->first<<", "<<itr->second<<std::endl;
122     }
123 }
124 
insertZeroBoundaryValues(float xMin,float xMax)125 void Histogram::insertZeroBoundaryValues(float xMin, float xMax)
126 {
127     if (_valueMap.empty())
128     {
129         if (xMin<xMax)
130         {
131             _valueMap[xMin] = 0.0;
132             _valueMap[xMax] = 0.0;
133         }
134         return;
135     }
136 
137     float interval = 1.0f;
138     float min_gap_for_single_insertion = interval*1.5;
139     float min_gap_for_double_insertion = interval*2.5;
140 
141     if (xMin<_valueMap.begin()->first)
142     {
143         _valueMap[xMin] = 0.0;
144     }
145 
146     if (xMax>_valueMap.rbegin()->first)
147     {
148         _valueMap[xMax] = 0.0;
149     }
150 
151     ValueMap::iterator itr = _valueMap.begin();
152     float previous_x = itr->first;
153     for(;
154         itr != _valueMap.end();
155         ++itr)
156     {
157         float current_x = itr->first;
158         float gap = current_x-previous_x;
159         if (gap>min_gap_for_double_insertion)
160         {
161             _valueMap[previous_x+interval] = 0.0f;
162             _valueMap[current_x-interval] = 0.0f;
163         }
164         else if  (gap>min_gap_for_single_insertion)
165         {
166             _valueMap[(previous_x+current_x)*0.5]=0.0f;
167         }
168         previous_x = current_x;
169     }
170 }
171 
createGraphicalRepresentation()172 osg::Node* Histogram::createGraphicalRepresentation()
173 {
174     if (_valueMap.empty()) return 0;
175 
176     osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform;
177 
178     float xMin = _valueMap.begin()->first;
179     float xMax = _valueMap.rbegin()->first;
180 
181     float depth = 0.0f;
182     float yMax = 0.0f;
183 
184     // find yMax
185     for(ValueMap::iterator itr = _valueMap.begin();
186         itr != _valueMap.end();
187         ++itr)
188     {
189         float y = itr->second;
190         if (y>yMax) yMax = y;
191     }
192 
193     float xScale = 1.0f/(xMax-xMin);
194     float yScale = 1.0f/yMax;
195 
196     {
197         osg::ref_ptr<osg::Geode> geode = new osg::Geode;
198         transform->addChild(geode.get());
199 
200         osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
201         geode->addDrawable(geometry.get());
202         geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
203         geode->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
204 
205         osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
206         geometry->setVertexArray(vertices.get());
207 
208         osg::ref_ptr<osg::Vec4Array> colours = new osg::Vec4Array;
209         geometry->setColorArray(colours.get(), osg::Array::BIND_PER_PRIMITIVE_SET);
210         colours->push_back(osg::Vec4(1.0,1.0,1.0,1.0));
211         colours->push_back(osg::Vec4(1.0,1.0,1.0,1.0));
212         colours->push_back(osg::Vec4(1.0,1.0,1.0,0.1));
213 
214 
215         unsigned numColumnsRequired = _valueMap.size();
216         vertices->reserve(numColumnsRequired*3);
217         for(ValueMap::iterator itr = _valueMap.begin();
218             itr != _valueMap.end();
219             ++itr)
220         {
221             float x = itr->first;
222             float y = itr->second;
223 
224             vertices->push_back(osg::Vec3(x*xScale, 0.0f, depth));
225             vertices->push_back(osg::Vec3(x*xScale, y*yScale, depth));
226             vertices->push_back(osg::Vec3(x*xScale, yMax*yScale, depth));
227         }
228 
229         osg::ref_ptr<osg::DrawElementsUShort> background_primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
230         osg::ref_ptr<osg::DrawElementsUShort> historgram_primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
231         osg::ref_ptr<osg::DrawElementsUShort> outline_primitives = new osg::DrawElementsUShort(GL_LINE_STRIP);
232         for(unsigned int i=0; i<numColumnsRequired; ++i)
233         {
234             int iv = i*3;
235 
236             background_primitives->push_back(iv+2);
237             background_primitives->push_back(iv+1);
238 
239             historgram_primitives->push_back(iv+1);
240             historgram_primitives->push_back(iv+0);
241 
242             outline_primitives->push_back(iv+1);
243 
244         }
245 
246         geometry->addPrimitiveSet(outline_primitives.get());
247         geometry->addPrimitiveSet(historgram_primitives.get());
248         geometry->addPrimitiveSet(background_primitives.get());
249     }
250 
251     //transform->setMatrix(osg::Matrix::scale(xScale/(maxX-minY), yScale/(yMax), 1.0f));
252 
253     transform->setMatrix(osg::Matrix::scale(2.0,1.0,1.0)*osg::Matrix::rotate(osg::DegreesToRadians(90.0), osg::Vec3d(1.0,0.0,0.0)));
254 
255     return transform.release();
256 }
257 
readTransferFunctionFile(const std::string & filename,float colorScale=1.0f)258 osg::TransferFunction1D* readTransferFunctionFile(const std::string& filename, float colorScale=1.0f)
259 {
260     std::string foundFile = osgDB::findDataFile(filename);
261     if (foundFile.empty())
262     {
263         std::cout<<"Error: could not find transfer function file : "<<filename<<std::endl;
264         return 0;
265     }
266 
267     std::cout<<"Reading transfer function "<<filename<<std::endl;
268 
269     osg::TransferFunction1D::ColorMap colorMap;
270     osgDB::ifstream fin(foundFile.c_str());
271     while(fin)
272     {
273         float value, red, green, blue, alpha;
274         fin >> value >> red >> green >> blue >> alpha;
275         if (fin)
276         {
277             std::cout<<"value = "<<value<<" ("<<red<<", "<<green<<", "<<blue<<", "<<alpha<<")"<<std::endl;
278             colorMap[value] = osg::Vec4(red*colorScale,green*colorScale,blue*colorScale,alpha*colorScale);
279         }
280     }
281 
282     if (colorMap.empty())
283     {
284         std::cout<<"Error: No values read from transfer function file: "<<filename<<std::endl;
285         return 0;
286     }
287 
288     osg::TransferFunction1D* tf = new osg::TransferFunction1D;
289     tf->assign(colorMap);
290 
291     return tf;
292 }
293 
294 class FindTransferFunctionPropertyVisitor : public osgVolume::PropertyVisitor
295 {
296 public:
297 
298     osg::ref_ptr<osgVolume::TransferFunctionProperty> _tfp;
299 
300 #if 0
301     virtual void apply(osgVolume::SwitchProperty& sp)
302     {
303         OSG_NOTICE<<"Found SwitchProperty"<<std::endl;
304         apply(static_cast<osgVolume::CompositeProperty&>(sp));
305     }
306 
307     virtual void apply(osgVolume::CompositeProperty& cp)
308     {
309         OSG_NOTICE<<"Found CompositeProperty"<<std::endl;
310         for(unsigned int i=0; i<cp.getNumProperties(); ++i)
311         {
312             cp.getProperty(i)->accept(*this);
313         }
314     }
315 #endif
apply(osgVolume::TransferFunctionProperty & tfp)316     virtual void apply(osgVolume::TransferFunctionProperty& tfp)
317     {
318         OSG_NOTICE<<"Found TransferFunctionProperty "<<&tfp<<std::endl;
319         _tfp = &tfp;
320     }
321 };
322 
323 
324 class InsertTransferFunctionPropertyVisitor : public osgVolume::PropertyVisitor
325 {
326 public:
327 
InsertTransferFunctionPropertyVisitor(osg::TransferFunction1D * tf)328     InsertTransferFunctionPropertyVisitor(osg::TransferFunction1D* tf)
329     {
330         _tfp = new osgVolume::TransferFunctionProperty(tf);
331     }
332 
333     osg::ref_ptr<osgVolume::TransferFunctionProperty> _tfp;
334 
apply(osgVolume::SwitchProperty & sp)335     virtual void apply(osgVolume::SwitchProperty& sp)
336     {
337         OSG_NOTICE<<"Found SwitchProperty"<<std::endl;
338         for(unsigned int i=0; i<sp.getNumProperties(); ++i)
339         {
340             sp.getProperty(i)->accept(*this);
341         }
342     }
343 
apply(osgVolume::CompositeProperty & cp)344     virtual void apply(osgVolume::CompositeProperty& cp)
345     {
346         OSG_NOTICE<<"Found CompositeProperty, inserting transfer function"<<std::endl;
347         if (_tfp.valid()) cp.addProperty(_tfp.get());
348     }
349 };
350 
351 class FindVolumeTiles : public osg::NodeVisitor
352 {
353 public:
FindVolumeTiles()354     FindVolumeTiles(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
355 
356     typedef std::vector< osg::ref_ptr<osgVolume::VolumeTile> > Tiles;
357     Tiles _tiles;
358 
apply(osg::Group & group)359     void apply(osg::Group& group)
360     {
361         osgVolume::VolumeTile* tile = dynamic_cast<osgVolume::VolumeTile*>(&group);
362         if (tile) _tiles.push_back(tile);
363         else traverse(group);
364     }
365 };
366 
367 
368 
369 class MyScriptCallback : public osg::CallbackObject
370 {
371 public:
MyScriptCallback(osg::ScriptEngine * se,osg::Script * script,const std::string & entryPoint)372     MyScriptCallback(osg::ScriptEngine* se, osg::Script* script, const std::string& entryPoint) : _scriptEngine(se), _script(script) { setName(entryPoint); }
373 
run(osg::Object * object,osg::Parameters & inputParameters,osg::Parameters & outputParameters) const374     virtual bool run(osg::Object* object, osg::Parameters& inputParameters, osg::Parameters& outputParameters) const
375     {
376         inputParameters.insert(inputParameters.begin(), object);
377         return _scriptEngine->run(_script.get(), getName(), inputParameters, outputParameters);
378     }
379 
380     osg::ref_ptr<osg::ScriptEngine> _scriptEngine;
381     osg::ref_ptr<osg::Script> _script;
382 };
383 
384 class MyClass : public osg::Object
385 {
386 public:
MyClass()387     MyClass() {}
MyClass(const MyClass & rhs,const osg::CopyOp copyop=osg::CopyOp::SHALLOW_COPY)388     MyClass(const MyClass& rhs, const osg::CopyOp copyop=osg::CopyOp::SHALLOW_COPY):osg::Object(rhs,copyop) {}
META_Object(local,MyClass)389     META_Object(local,MyClass)
390 
391     void myMethod()
392     {
393         osg::CallbackObject* co = osg::getCallbackObject(this, "myMethod");
394         if (co) co->run(this);
395         else myMethodImplementation();
396     }
397 
myMethodImplementation()398     virtual void myMethodImplementation()
399     {
400         OSG_NOTICE<<"MyClass::myMethodImplementation()"<<std::endl;
401     }
402 };
403 
404 
main(int argc,char ** argv)405 int main(int argc, char ** argv)
406 {
407     osg::ArgumentParser arguments(&argc, argv);
408 
409 #if 0
410 
411     osg::ref_ptr<MyClass> myobject = new MyClass;
412     myobject->getOrCreateUserDataContainer()->addUserObject(new osg::CallbackObject("myMethod"));
413     myobject->myMethod();
414 
415     osg::ref_ptr<osg::ScriptEngine> se = osgDB::readFile<osg::ScriptEngine>("ScriptEngine.lua");
416     osg::ref_ptr<osg::Script> script = osgDB::readFile<osg::Script>("script.lua");
417 
418     osg::ref_ptr<MyClass> copyobject = new MyClass;
419     copyobject->getOrCreateUserDataContainer()->addUserObject(new MyScriptCallback(se.get(), script.get(), "myMethod"));
420     copyobject->myMethod();
421 #endif
422 
423 #if 0
424     osg::ref_ptr<osg::Object> object = osgDB::readNodeFile("load.lua");
425     if (object.valid())
426     {
427         osg::CallbackObject* co = osg::getCallbackObject(object.get(), "method");
428         OSG_NOTICE<<"Have object = "<<object->className()<<std::endl;
429         OSG_NOTICE<<"Have co = "<<co<<std::endl;
430         if (co)
431         {
432             osg::Parameters inputParameters, outputParameters;
433             inputParameters.push_back(new osg::StringValueObject("string",std::string("all there is to celeberate is here.")));
434             co->run(object.get(), inputParameters, outputParameters);
435             OSG_NOTICE<<"outputParameters.size()="<<outputParameters.size()<<std::endl;
436             for(osg::Parameters::iterator itr = outputParameters.begin();
437                 itr != outputParameters.end();
438                 ++itr)
439             {
440                 OSG_NOTICE<<"   returned "<<(*itr)->className()<<std::endl;
441             }
442         }
443     }
444     return 0;
445 #endif
446 
447 
448     #if 1
449     osgViewer::Viewer viewer(arguments);
450 
451     viewer.addEventHandler(new osgViewer::StatsHandler());
452 
453     osg::ref_ptr<osg::TransferFunction1D> tf;
454     std::string filename;
455     if (arguments.read("--tf",filename))
456     {
457         tf = readTransferFunctionFile(filename, 1.0f);
458     }
459     if (arguments.read("--tf-255",filename))
460     {
461         tf = readTransferFunctionFile(filename,1.0f/255.0f);
462     }
463 
464     bool createHistorgram = arguments.read("--histogram");
465 
466 #if 0
467     for(int i=1; i<arguments.argc(); ++i)
468     {
469         if (!arguments.isOption(i))
470         {
471             osg::ref_ptr<osg::Image> image;
472             osg::ref_ptr<osgVolume::Volume> volume;
473             osg::ref_ptr<osgVolume::VolumeTile> volumeTile;
474 
475             std::string filename = arguments[i];
476             osgDB::FileType fileType = osgDB::fileType(foundFile);
477 
478             if (fileType == osgDB::DIRECTORY)
479             {
480                 osg::ref_ptr<osg::Image> image = osgDB::readImageFile(foundFile+".dicom", options.get());
481             }
482             else if (fileType == osgDB::REGULAR_FILE)
483             {
484                 std::string ext = osgDB::getFileExtension(foundFile);
485                 if (ext=="osg" || ext=="ive" || ext=="osgx" || ext=="osgb" || ext=="osgt")
486                 {
487                     osg::ref_ptr<osg::Object> obj = osgDB::readObjectFile(foundFile);
488                     image = dynamic_cast<osg::Image*>(obj.get());
489                     volume = dynamic_cast<osgVolume::Volume*>(obj.get());
490                     volumeTile = dynamic_cast<osgVolume::VolumeTile*>(obj.get());
491                 }
492                 else
493                 {
494                     image = osgDB::readImageFile( foundFile );
495                 }
496             }
497             else
498             {
499                 // not found image, so fallback to plugins/callbacks to find the model.
500                 image = osgDB::readImageFile( filename);
501             }
502 
503             if (image.valid())
504             {
505                 volumeTile = new osgVolume::VolumeTile;
506             }
507 
508         }
509         OSG_NOTICE<<"Argument "<<i<<" "<<arguments[i]<<std::endl;
510     }
511 
512     return 1;
513 #else
514 
515     osg::ref_ptr<osg::Node> model = osgDB::readRefNodeFiles(arguments);
516 #endif
517     typedef std::vector< osg::ref_ptr<osg::Node> > Nodes;
518     Nodes nodes;
519 
520     if (!model && !tf)
521     {
522         OSG_NOTICE<<"Please specify dataset on command line."<<std::endl;
523         return 1;
524     }
525 
526     osgVolume::ImageLayer* imageLayer = 0;
527 
528 
529     if (model.valid())
530     {
531         osg::ref_ptr<osgVolume::VolumeTile> volumeTile = dynamic_cast<osgVolume::VolumeTile*>(model.get());
532         if (volumeTile.valid())
533         {
534             OSG_NOTICE<<"Inserting Volume above VolumeTile."<<std::endl;
535             osg::ref_ptr<osgVolume::Volume> volume = new osgVolume::Volume;
536             volume->addChild(model.get());
537             model = volume.get();
538 
539 //            volumeTile->setVolumeTechnique(new osgVolume::RayTracedTechnique);
540 //            volumeTile->setVolumeTechnique(new osgVolume::FixedFunctionTechnique);
541         }
542 
543         nodes.push_back(model.get());
544 
545         FindVolumeTiles fvt;
546         model->accept(fvt);
547 
548         if (!fvt._tiles.empty())
549         {
550             osgVolume::VolumeTile* tile = fvt._tiles[0].get();
551             imageLayer = dynamic_cast<osgVolume::ImageLayer*>(tile->getLayer());
552             tile->addEventCallback(new osgVolume::PropertyAdjustmentCallback());
553         }
554     }
555 
556 
557     if (createHistorgram && imageLayer)
558     {
559         Histogram histogram;
560         histogram.analyse(imageLayer->getImage());
561         nodes.push_back(histogram.createGraphicalRepresentation());
562     }
563 
564     if (imageLayer)
565     {
566         osgVolume::Property* property = imageLayer->getProperty();
567         if (property)
568         {
569             FindTransferFunctionPropertyVisitor ftfpv;
570             property->accept(ftfpv);
571 
572             if (ftfpv._tfp.valid())
573             {
574                 if (tf.valid())
575                 {
576                     OSG_NOTICE<<"Need to replace volumes transfer function"<<std::endl;
577                     ftfpv._tfp->setTransferFunction(tf.get());
578                 }
579                 else
580                 {
581                     OSG_NOTICE<<"Using volumes transfer function"<<std::endl;
582                     tf = dynamic_cast<osg::TransferFunction1D*>(ftfpv._tfp->getTransferFunction());
583                 }
584             }
585             else if (tf.valid())
586             {
587                 // No existing transfer function but need to assign one
588                 OSG_NOTICE<<"Need to assign transfer function to CompositeProperty"<<std::endl;
589                 InsertTransferFunctionPropertyVisitor itfpv(tf.get());
590                 property->accept(itfpv);
591             }
592         }
593         else if (tf.valid())
594         {
595             OSG_NOTICE<<"Assign transfer function directly"<<std::endl;
596             imageLayer->setProperty(new osgVolume::TransferFunctionProperty(tf.get()));
597         }
598     }
599 
600     if (tf.valid())
601     {
602         osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform;
603         transform->setMatrix(osg::Matrix::scale(2.0,1.0,1.0)*osg::Matrix::rotate(osg::DegreesToRadians(90.0), osg::Vec3d(1.0,0.0,0.0)));
604         transform->addChild(new osgUI::TransferFunctionWidget(tf.get()));
605         nodes.push_back(transform.get());
606     }
607 
608     if (nodes.empty())
609     {
610         OSG_NOTICE<<"Please specify dataset on command line."<<std::endl;
611         return 1;
612     }
613 
614     if (nodes.size()==1)
615     {
616         viewer.setSceneData(nodes[0].get());
617     }
618     else
619     {
620         osg::Vec3d position(0.0,0.0,0.0);
621 
622         osg::ref_ptr<osg::Group> group = new osg::Group;
623 
624         for(Nodes::iterator itr = nodes.begin();
625             itr != nodes.end();
626             ++itr)
627         {
628             osg::ref_ptr<osg::Node> child = *itr;
629             if (!child) continue;
630 #if 0
631             osg::ComputeBoundsVisitor cbv;
632             child->accept(cbv);
633 
634             osg::BoundingBox bb = cbv.getBoundingBox();
635             double scale = 1.0/(bb.xMax()-bb.xMin());
636 #endif
637             osg::BoundingSphere bb = child->getBound();
638             double scale = 0.7/bb.radius();
639 
640             osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform;
641             pat->addChild(child.get());
642             pat->setPosition(position);
643             pat->setPivotPoint(bb.center());
644             pat->setScale(osg::Vec3d(scale, scale, scale));
645             position.x() += 1.1;
646 
647             group->addChild(pat.get());
648         }
649 
650         viewer.setSceneData(group.get());
651     }
652 
653     OSG_NOTICE<<"Reading to run viewer"<<std::endl;
654 
655     osgDB::writeNodeFile(*viewer.getSceneData(),"graph.osgt");
656 
657     return viewer.run();
658 #endif
659 
660 }
661