source: GTP/trunk/Lib/Vis/Preprocessing/src/Preprocessor.cpp @ 1020

Revision 1020, 18.5 KB checked in by mattausch, 18 years ago (diff)

worked on view-object space partition
fixed some loading bugs
fixeds some exporting bugs using line segments
enabling other methods for view space sampling in ViewCellsManager? OBJECT_DIRECTION_BASED_DISTRIBUTION)
added class interface for a sampling strategy

Line 
1#include "SceneGraph.h"
2#include "Exporter.h"
3#include "UnigraphicsParser.h"
4#include "X3dParser.h"
5#include "Preprocessor.h"
6#include "ViewCell.h"
7#include "Environment.h"
8#include "ViewCellsManager.h"
9#include "ViewCellBsp.h"
10#include "VspBspTree.h"
11#include "RenderSimulator.h"
12#include "GlRenderer.h"
13#include "PlyParser.h"
14#include "SamplingStrategy.h"
15
16
17
18namespace GtpVisibilityPreprocessor {
19
20const static bool ADDITIONAL_GEOMETRY_HACK = false;
21
22Preprocessor *preprocessor;
23
24
25// HACK: Artificially modify scene to watch rendercost changes
26static void AddGeometry(SceneGraph *scene)
27{
28        scene->mRoot->UpdateBox();
29
30        AxisAlignedBox3 sceneBox = scene->GetBox();
31
32        int n = 200;
33
34        if (0){
35        // form grid of boxes
36        for (int i = 0; i < n; ++ i)
37        {
38                for (int j = 0; j < n; ++ j)
39                {
40                        const Vector3 scale2((float)j * 0.8 / n + 0.1,  0.05, (float)i * 0.8  / (float)n + 0.1);
41               
42                        const Vector3 pt2 = sceneBox.Min() + scale2 * (sceneBox.Max() - sceneBox.Min());
43               
44                        const Vector3 boxSize = sceneBox.Size() * Vector3(0.0025f, 0.01f, 0.0025f);
45                        AxisAlignedBox3 box(pt2, pt2 + boxSize);
46                        Mesh *mesh = CreateMeshFromBox(box);
47
48                        mesh->Preprocess();
49               
50                        MeshInstance *mi = new MeshInstance(mesh);
51                        scene->mRoot->mGeometry.push_back(mi);
52                }
53        }
54
55        for (int i = 0; i < n; ++ i)
56        {
57                for (int j = 0; j < n; ++ j)
58                {
59                        const Vector3 scale2(0.15, (float)j * 0.8 / n + 0.1, (float)i * 0.8  / (float)n + 0.1);
60               
61                        Vector3 pt2 = sceneBox.Min() + scale2 * (sceneBox.Max() - sceneBox.Min());
62               
63                        Vector3 boxSize = sceneBox.Size() * Vector3(0.0025, 0.01, 0.0025);
64                        AxisAlignedBox3 box(pt2, pt2 + boxSize);
65                        Mesh *mesh = CreateMeshFromBox(box);
66
67                        mesh->Preprocess();
68               
69                        MeshInstance *mi = new MeshInstance(mesh);
70                        scene->mRoot->mGeometry.push_back(mi);
71                }
72        }
73
74        for (int i = 0; i < n; ++ i)
75        {
76                const Vector3 scale2(2, 0.2, (float)i * 0.8  / (float)n + 0.1);
77               
78                Vector3 pt2 = sceneBox.Min() + scale2 * (sceneBox.Max() - sceneBox.Min());
79               
80                //Vector3 boxSize = sceneBox.Size() * Vector3(0.0025, 0.01, 0.0025);
81                Vector3 boxSize = sceneBox.Size() * Vector3(0.005, 0.02, 0.005);
82
83                AxisAlignedBox3 box(pt2 + 0.1, pt2 + boxSize);
84                Mesh *mesh = CreateMeshFromBox(box);
85
86                mesh->Preprocess();
87               
88                MeshInstance *mi = new MeshInstance(mesh);
89                scene->mRoot->mGeometry.push_back(mi);
90        }
91       
92        scene->mRoot->UpdateBox();
93        }
94       
95        // plane separating view space regions
96        if (1)
97        {
98                const Vector3 scale(1.0, 0.0, 0);
99
100                Vector3 pt = sceneBox.Min() + scale * (sceneBox.Max() - sceneBox.Min());
101
102                Plane3 cuttingPlane(Vector3(1, 0, 0), pt);
103                Mesh *planeMesh = new Mesh();
104               
105                Polygon3 *poly = sceneBox.CrossSection(cuttingPlane);
106                IncludePolyInMesh(*poly, *planeMesh);
107               
108                planeMesh->Preprocess();
109               
110                MeshInstance *planeMi = new MeshInstance(planeMesh);
111                scene->mRoot->mGeometry.push_back(planeMi);
112        }       
113}
114
115
116Preprocessor::Preprocessor():
117mKdTree(NULL),
118mBspTree(NULL),
119mVspBspTree(NULL),
120mViewCellsManager(NULL),
121mRenderSimulator(NULL)
122{
123        Environment::GetSingleton()->GetBoolValue("Preprocessor.useGlRenderer", mUseGlRenderer);
124 
125        // renderer will be constructed when the scene graph and viewcell manager will be known
126        renderer = NULL;
127 
128        Environment::GetSingleton()->GetBoolValue("Preprocessor.useGlDebugger", mUseGlDebugger);
129        Environment::GetSingleton()->GetBoolValue("Preprocessor.loadPolygonsAsMeshes", mLoadPolygonsAsMeshes);
130        Environment::GetSingleton()->GetBoolValue("Preprocessor.quitOnFinish", mQuitOnFinish);
131        Environment::GetSingleton()->GetBoolValue("Preprocessor.computeVisibility", mComputeVisibility);
132        Environment::GetSingleton()->GetBoolValue("Preprocessor.detectEmptyViewSpace", mDetectEmptyViewSpace);
133        Environment::GetSingleton()->GetBoolValue("Preprocessor.exportVisibility", mExportVisibility );
134
135        char buffer[256];
136        Environment::GetSingleton()->GetStringValue("Preprocessor.visibilityFile",  buffer);
137        mVisibilityFileName = buffer;
138        Environment::GetSingleton()->GetBoolValue("Preprocessor.applyVisibilityFilter", mApplyVisibilityFilter );
139        Environment::GetSingleton()->GetBoolValue("Preprocessor.applyVisibilitySpatialFilter",
140                                                          mApplyVisibilitySpatialFilter );
141        Environment::GetSingleton()->GetFloatValue("Preprocessor.visibilityFilterWidth", mVisibilityFilterWidth);
142
143        Debug << "detect empty view space=" << mDetectEmptyViewSpace << endl;
144        Debug << "load polygons as meshes: " << mLoadPolygonsAsMeshes << endl;
145}
146
147
148Preprocessor::~Preprocessor()
149{
150  cout << "cleaning up" << endl;
151
152  cout << "Deleting view cells manager ... \n";
153  DEL_PTR(mViewCellsManager);
154  cout << "done.\n";
155
156  cout << "Deleting bsp tree ... \n";
157  DEL_PTR(mBspTree);
158  cout << "done.\n";
159
160  cout << "Deleting kd tree...\n";
161  DEL_PTR(mKdTree);
162  cout << "done.\n";
163 
164#if 0
165  cout << "Deleting vsp osp tree...\n";
166  DEL_PTR(mVspOspTree);
167  cout << "done.\n";
168#endif
169
170  cout << "Deleting vspbsp tree...\n";
171  DEL_PTR(mVspBspTree);
172  cout << "done.\n";
173
174   cout << "Deleting scene graph...\n";
175  DEL_PTR(mSceneGraph);
176  cout << "done.\n";
177
178  DEL_PTR(mRenderSimulator);
179  DEL_PTR(renderer);
180}
181
182int
183SplitFilenames(const string str, vector<string> &filenames)
184{
185        int pos = 0;
186
187        while(1) {
188                int npos = (int)str.find(';', pos);
189               
190                if (npos < 0 || npos - pos < 1)
191                        break;
192                filenames.push_back(string(str, pos, npos - pos));
193                pos = npos + 1;
194        }
195       
196        filenames.push_back(string(str, pos, str.size() - pos));
197        return (int)filenames.size();
198}
199
200
201bool
202Preprocessor::LoadScene(const string filename)
203{
204        // use leaf nodes of the original spatial hierarchy as occludees
205        mSceneGraph = new SceneGraph;
206 
207        Parser *parser;
208        vector<string> filenames;
209        int files = SplitFilenames(filename, filenames);
210        cout << "number of input files: " << files << endl;
211        bool result = false;
212        if (files == 1) {
213               
214                if (strstr(filename.c_str(), ".x3d"))
215                  parser = new X3dParser;
216                else
217                  if (strstr(filename.c_str(), ".ply") || strstr(filename.c_str(), ".plb"))
218                        parser = new PlyParser;
219                  else
220                        parser = new UnigraphicsParser;
221
222                cout<<filename<<endl;
223                result = parser->ParseFile(filename, &mSceneGraph->mRoot, mLoadPolygonsAsMeshes);
224
225                delete parser;
226
227        } else {
228                // root for different files
229                mSceneGraph->mRoot = new SceneGraphNode;
230                for (int i= 0; i < filenames.size(); i++) {
231                        if (strstr(filenames[i].c_str(), ".x3d"))
232                                parser = new X3dParser;
233                        else
234                                parser = new UnigraphicsParser;
235                       
236                        SceneGraphNode *node;
237                        if (parser->ParseFile(filenames[i], &node)) {
238                                mSceneGraph->mRoot->mChildren.push_back(node);
239                                // at least one file parsed
240                                result = true;
241                        }
242                        delete parser;
243                }
244        }
245       
246
247        if (result)
248        {
249                // HACK
250                if (ADDITIONAL_GEOMETRY_HACK)
251                        AddGeometry(mSceneGraph);
252               
253                mSceneGraph->AssignObjectIds();
254       
255                int intersectables, faces;
256                mSceneGraph->GetStatistics(intersectables, faces);
257       
258                cout<<filename<<" parsed successfully."<<endl;
259                cout<<"#NUM_OBJECTS (Total numner of objects)\n"<<intersectables<<endl;
260                cout<<"#NUM_FACES (Total numner of faces)\n"<<faces<<endl;
261                mSceneGraph->CollectObjects(&mObjects);
262                mSceneGraph->mRoot->UpdateBox();
263
264                if (0)
265                {
266                        Exporter *exporter = Exporter::GetExporter("testload.x3d");
267
268                        if (exporter)
269                        {
270                                exporter->ExportGeometry(mObjects);
271                                delete exporter;
272                        }
273                }
274        }
275       
276       
277        return result;
278}
279
280bool
281Preprocessor::ExportPreprocessedData(const string filename)
282{
283 
284  mViewCellsManager->ExportViewCells(filename, true, mObjects);
285 
286  return true;
287}
288
289bool
290Preprocessor::PostProcessVisibility()
291{
292 
293  if (mApplyVisibilityFilter || mApplyVisibilitySpatialFilter) {
294        cout<<"Applying visibility filter ...";
295        cout<<"filter width = " << mVisibilityFilterWidth << endl;
296       
297        if (!mViewCellsManager)
298                return false;
299
300        mViewCellsManager->ApplyFilter(mKdTree,
301                                                                   mApplyVisibilityFilter ? mVisibilityFilterWidth : -1.0f,
302                                                                   mApplyVisibilitySpatialFilter ? mVisibilityFilterWidth : -1.0f);
303        cout << "done." << endl;
304  }
305 
306  // export the preprocessed information to a file
307  if (mExportVisibility)
308        ExportPreprocessedData(mVisibilityFileName);
309 
310  return true;
311}
312
313
314bool
315Preprocessor::BuildKdTree()
316{
317  mKdTree = new KdTree;
318  // add mesh instances of the scene graph to the root of the tree
319  KdLeaf *root = (KdLeaf *)mKdTree->GetRoot();
320  mSceneGraph->CollectObjects(&root->mObjects);
321 
322  mKdTree->Construct();
323  return true;
324}
325
326void
327Preprocessor::KdTreeStatistics(ostream &s)
328{
329  s<<mKdTree->GetStatistics();
330}
331
332void
333Preprocessor::BspTreeStatistics(ostream &s)
334{
335        s << mBspTree->GetStatistics();
336}
337
338bool
339Preprocessor::Export( const string filename,
340                                          const bool scene,
341                                          const bool kdtree,
342                                          const bool bsptree
343                                          )
344{
345  Exporter *exporter = Exporter::GetExporter(filename);
346       
347  if (exporter) {
348    if (scene)
349      exporter->ExportScene(mSceneGraph->mRoot);
350
351    if (kdtree) {
352      exporter->SetWireframe();
353      exporter->ExportKdTree(*mKdTree);
354    }
355
356        if (bsptree) {
357                //exporter->SetWireframe();
358                exporter->ExportBspTree(*mBspTree);
359        }
360
361    delete exporter;
362    return true;
363  }
364
365  return false;
366}
367
368
369bool Preprocessor::PrepareViewCells()
370{
371        //-- parse view cells construction method
372        Environment::GetSingleton()->GetBoolValue("ViewCells.loadFromFile", mLoadViewCells);
373        char buf[100];
374       
375        if (mLoadViewCells)
376        {       
377                Environment::GetSingleton()->GetStringValue("ViewCells.filename", buf);
378                mViewCellsManager = ViewCellsManager::LoadViewCells(buf, &mObjects, true);
379        }
380        else
381        {
382                //-- parse type of view cell container
383                Environment::GetSingleton()->GetStringValue("ViewCells.type", buf);             
384            mViewCellsManager = CreateViewCellsManager(buf);
385        }
386
387        //-- parameters for render heuristics evaluation
388        float objRenderCost = 0, vcOverhead = 0, moveSpeed = 0;
389
390        Environment::GetSingleton()->GetFloatValue("Simulation.objRenderCost",objRenderCost);
391        Environment::GetSingleton()->GetFloatValue("Simulation.vcOverhead", vcOverhead);
392        Environment::GetSingleton()->GetFloatValue("Simulation.moveSpeed", moveSpeed);
393       
394        mRenderSimulator =
395                new RenderSimulator(mViewCellsManager, objRenderCost, vcOverhead, moveSpeed);
396
397        mViewCellsManager->SetRenderer(mRenderSimulator);
398
399        // default view space is the extent of the scene
400        mViewCellsManager->SetViewSpaceBox(mSceneGraph->GetBox());
401
402        if (mUseGlRenderer || mUseGlDebugger)
403        {
404                // NOTE: render texture should be power of 2 and square
405                // renderer must be initialised
406                renderer = new GlRendererBuffer(1024, 768, mSceneGraph, mViewCellsManager, mKdTree);
407                //              renderer->makeCurrent();
408               
409        }
410       
411        return true;
412}
413
414
415ViewCellsManager *Preprocessor::CreateViewCellsManager(const char *name)
416{
417        if (strcmp(name, "kdTree") == 0)
418        {
419                mViewCellsManager = new KdViewCellsManager(mKdTree);
420        }
421        else if (strcmp(name, "bspTree") == 0)
422        {
423                Debug << "view cell type: Bsp" << endl;
424
425                mBspTree = new BspTree();
426                mViewCellsManager = new BspViewCellsManager(mBspTree);
427        }
428        else if (strcmp(name, "vspBspTree") == 0)
429        {
430                Debug << "view cell type: VspBsp" << endl;
431
432                mVspBspTree = new VspBspTree();
433                mViewCellsManager = new VspBspViewCellsManager(mVspBspTree);
434        }
435#if 0
436        else if (strcmp(name, "vspOspTree") == 0)
437        {
438                mVspOspTree = new VspOspTree();         
439                mViewCellsManager = new VspOspViewCellsManager(mVspOspTree);
440        }
441#endif
442        else if (strcmp(name, "sceneDependent") == 0)
443        {
444                //TODO
445                mBspTree = new BspTree();
446
447                Debug << "view cell type: Bsp" << endl;
448               
449                mViewCellsManager = new BspViewCellsManager(mBspTree);
450        }
451        else
452        {
453                cerr << "Wrong view cells type " << name << "!!!" << endl;
454                exit(1);
455        }
456
457        return mViewCellsManager;
458}
459
460
461// use ascii format to store rays
462#define USE_ASCII 0
463
464
465inline bool ilt(Intersectable *obj1, Intersectable *obj2)
466{
467        return obj1->mId < obj2->mId;
468}
469
470
471bool Preprocessor::LoadSamples(VssRayContainer &samples,
472                                                           ObjectContainer &objects) const
473{
474        std::stable_sort(objects.begin(), objects.end(), ilt);
475        char fileName[100];
476        Environment::GetSingleton()->GetStringValue("Preprocessor.samplesFilename", fileName);
477       
478    Vector3 origin, termination;
479        // HACK: needed only for lower_bound algorithm to find the
480        // intersected objects
481        MeshInstance sObj(NULL);
482        MeshInstance tObj(NULL);
483
484#if USE_ASCII
485        ifstream samplesIn(fileName);
486        if (!samplesIn.is_open())
487                return false;
488
489        string buf;
490        while (!(getline(samplesIn, buf)).eof())
491        {
492                sscanf(buf.c_str(), "%f %f %f %f %f %f %d %d",
493                           &origin.x, &origin.y, &origin.z,
494                           &termination.x, &termination.y, &termination.z,
495                           &(sObj.mId), &(tObj.mId));
496               
497                Intersectable *sourceObj = NULL;
498                Intersectable *termObj = NULL;
499               
500                if (sObj.mId >= 0)
501                {
502                        ObjectContainer::iterator oit =
503                                lower_bound(objects.begin(), objects.end(), &sObj, ilt);
504                        sourceObj = *oit;
505                }
506               
507                if (tObj.mId >= 0)
508                {
509                        ObjectContainer::iterator oit =
510                                lower_bound(objects.begin(), objects.end(), &tObj, ilt);
511                        termObj = *oit;
512                }
513
514                samples.push_back(new VssRay(origin, termination, sourceObj, termObj));
515        }
516#else
517        ifstream samplesIn(fileName, ios::binary);
518        if (!samplesIn.is_open())
519                return false;
520
521        while (1)
522        {
523                 samplesIn.read(reinterpret_cast<char *>(&origin), sizeof(Vector3));
524                 samplesIn.read(reinterpret_cast<char *>(&termination), sizeof(Vector3));
525                 samplesIn.read(reinterpret_cast<char *>(&(sObj.mId)), sizeof(int));
526                 samplesIn.read(reinterpret_cast<char *>(&(tObj.mId)), sizeof(int));
527               
528                 if (samplesIn.eof())
529                        break;
530
531                Intersectable *sourceObj = NULL;
532                Intersectable *termObj = NULL;
533               
534                if (sObj.mId >= 0)
535                {
536                        ObjectContainer::iterator oit =
537                                lower_bound(objects.begin(), objects.end(), &sObj, ilt);
538                        sourceObj = *oit;
539                }
540               
541                if (tObj.mId >= 0)
542                {
543                        ObjectContainer::iterator oit =
544                                lower_bound(objects.begin(), objects.end(), &tObj, ilt);
545                        termObj = *oit;
546                }
547
548                samples.push_back(new VssRay(origin, termination, sourceObj, termObj));
549        }
550
551#endif
552        samplesIn.close();
553
554        return true;
555}
556
557
558bool Preprocessor::ExportSamples(const VssRayContainer &samples) const
559{
560        char fileName[100];
561        Environment::GetSingleton()->GetStringValue("Preprocessor.samplesFilename", fileName);
562       
563
564        VssRayContainer::const_iterator it, it_end = samples.end();
565       
566#if USE_ASCII
567        ofstream samplesOut(fileName);
568        if (!samplesOut.is_open())
569                return false;
570
571        for (it = samples.begin(); it != it_end; ++ it)
572        {
573                VssRay *ray = *it;
574                int sourceid = ray->mOriginObject ? ray->mOriginObject->mId : -1;               
575                int termid = ray->mTerminationObject ? ray->mTerminationObject->mId : -1;       
576
577                samplesOut << ray->GetOrigin().x << " " << ray->GetOrigin().y << " " << ray->GetOrigin().z << " "
578                                   << ray->GetTermination().x << " " << ray->GetTermination().y << " " << ray->GetTermination().z << " "
579                                   << sourceid << " " << termid << "\n";
580        }
581#else
582        ofstream samplesOut(fileName, ios::binary);
583        if (!samplesOut.is_open())
584                return false;
585
586        for (it = samples.begin(); it != it_end; ++ it)
587        {       
588                VssRay *ray = *it;
589                Vector3 origin(ray->GetOrigin());
590                Vector3 termination(ray->GetTermination());
591               
592                int sourceid = ray->mOriginObject ? ray->mOriginObject->mId : -1;               
593                int termid = ray->mTerminationObject ? ray->mTerminationObject->mId : -1;               
594
595                samplesOut.write(reinterpret_cast<char *>(&origin), sizeof(Vector3));
596                samplesOut.write(reinterpret_cast<char *>(&termination), sizeof(Vector3));
597                samplesOut.write(reinterpret_cast<char *>(&sourceid), sizeof(int));
598                samplesOut.write(reinterpret_cast<char *>(&termid), sizeof(int));
599    }
600#endif
601        samplesOut.close();
602        return true;
603}
604
605#if 0 // matt: implemented interface samplestrategy
606bool
607Preprocessor::GenerateRays(
608                                                   const int number,
609                                                   const int sampleType,
610                                                   SimpleRayContainer &rays
611                                                   )
612{
613  Vector3 origin, direction;
614  int startSize = (int)rays.size();
615  for (int i=0; (int)rays.size() - startSize  < number; i ++) {
616        // now get the direction
617        switch (sampleType) {
618        case OBJECT_BASED_DISTRIBUTION: {
619          mViewCellsManager->GetViewPoint(origin);
620          Vector3 point;
621          Vector3 normal;
622          int i = RandomValue(0, mObjects.size() - 1);
623          Intersectable *object = mObjects[i];
624          object->GetRandomSurfacePoint(point, normal);
625          direction = point - origin;
626        }
627          break;
628        case OBJECT_DIRECTION_BASED_DISTRIBUTION: {
629          int i = RandomValue(0, mObjects.size() - 1);
630          Intersectable *object = mObjects[i];
631          Vector3 normal;
632          object->GetRandomSurfacePoint(origin, normal);
633          direction = UniformRandomVector(normal);
634          origin += 0.1f*direction;
635        }
636          break;
637        case DIRECTION_BASED_DISTRIBUTION:
638          mViewCellsManager->GetViewPoint(origin);
639          direction = UniformRandomVector();
640          break;
641        case DIRECTION_BOX_BASED_DISTRIBUTION: {
642          mViewCellsManager->GetViewPoint(origin);
643          float alpha = RandomValue(0.0f, 2*M_PI);
644          float beta = RandomValue(-M_PI/2, M_PI/2);
645          direction = VssRay::GetDirection(alpha, beta);
646          break;
647        }
648        case SPATIAL_BOX_BASED_DISTRIBUTION:
649          mViewCellsManager->GetViewPoint(origin);
650          direction = mKdTree->GetBox().GetRandomPoint() - origin;
651          break;
652        default:
653          // unsuported distribution type
654          return false;
655        }
656        // $$ jb the pdf is yet not correct for all sampling methods!
657        float pdf = 1.0f;
658        float c = Magnitude(direction);
659        if (c > Limits::Small) {
660          direction*=1.0f/c;
661          rays.AddRay(SimpleRay(origin, direction, pdf));
662        }
663  }
664  return true;
665}
666#endif
667bool Preprocessor::GenerateRays(const int number,
668                                                                const int sampleType,
669                                                                SimpleRayContainer &rays)
670{
671        Vector3 origin, direction;
672       
673        const int startSize = (int)rays.size();
674        SamplingStrategy *strategy = GenerateSamplingStrategy(sampleType);
675
676        if (!strategy)
677                return false;
678
679        for (int i=0; (int)rays.size() - startSize < number; ++ i)
680        {
681                SimpleRay newRay;
682                bool success = strategy->GenerateSample(newRay);
683
684                if (success)
685                        rays.AddRay(newRay);
686        }
687
688        delete strategy;
689
690    return true;
691}
692
693
694SamplingStrategy *Preprocessor::GenerateSamplingStrategy(const int strategyId) const
695{
696        switch (strategyId)
697        {
698        case OBJECT_BASED_DISTRIBUTION:
699                return new ObjectBasedDistribution(*this);
700        case OBJECT_DIRECTION_BASED_DISTRIBUTION:
701                return new ObjectDirectionBasedDistribution(*this);
702        case DIRECTION_BASED_DISTRIBUTION:
703                return new DirectionBasedDistribution(*this);
704        case DIRECTION_BOX_BASED_DISTRIBUTION:
705                return new DirectionBoxBasedDistribution(*this);
706        case SPATIAL_BOX_BASED_DISTRIBUTION:
707                return new SpatialBoxBasedDistribution(*this);
708        case OBJECTS_INTERIOR_DISTRIBUTION:
709                return new ObjectsInteriorDistribution(*this);
710        default: // no valid strategy
711                return NULL;
712        }
713        // should never come here
714        return NULL;
715}
716
717
718}
Note: See TracBrowser for help on using the repository browser.