source: GTP/trunk/Lib/Vis/Preprocessing/src/OspTree.h @ 1594

Revision 1594, 20.7 KB checked in by bittner, 18 years ago (diff)

support for kd tree based pvs

Line 
1#ifndef _OspTree_H__
2#define _OspTree_H__
3
4#include <stack>
5
6#include "Mesh.h"
7#include "Containers.h"
8#include "Statistics.h"
9#include "VssRay.h"
10#include "RayInfo.h"
11#include "gzstream.h"
12#include "SubdivisionCandidate.h"
13#include "IntersectableWrapper.h"
14
15
16namespace GtpVisibilityPreprocessor {
17
18class ViewCellLeaf;
19class Plane3;
20class AxisAlignedBox3;
21class Ray;
22class ViewCellsStatistics;
23class ViewCellsManager;
24class MergeCandidate;
25class Beam;
26class ViewCellsTree;
27class Environment;
28class VspInterior;
29class VspLeaf;
30class VspNode;
31class KdNode;
32class KdInterior;
33class KdLeaf;
34class OspTree;
35class KdIntersectable;
36class KdTree;
37class VspTree;
38class KdTreeStatistics;
39class SubdivisionCandidate;
40class HierarchyManager;
41
42
43
44/** View space partition statistics.
45*/
46class OspTreeStatistics: public StatisticsBase
47{
48public:
49        // total number of nodes
50        int nodes;
51        // number of splits
52        int splits[3];
53       
54        // maximal reached depth
55        int maxDepth;
56        // minimal depth
57        int minDepth;
58       
59        // max depth nodes
60        int maxDepthNodes;
61        // minimum depth nodes
62        int minDepthNodes;
63        // max depth nodes
64        int minPvsNodes;
65        // minimum area nodes
66        int minProbabilityNodes;
67        /// nodes termination because of max cost ratio;
68        int maxCostNodes;
69        // max number of objects per node
70        int maxObjectRefs;
71        int objectRefs;
72        /// samples contributing to pvs
73        int contributingSamples;
74        /// sample contributions to pvs
75        int sampleContributions;
76        /// largest pvs
77        int maxPvs;
78        /// number of invalid leaves
79        int invalidLeaves;
80        /// accumulated number of rays refs
81        int accumRays;
82        /// overall potentially visible objects
83        int pvs;
84        // accumulated depth (used to compute average)
85        int accumDepth;
86        // global cost ratio violations
87        int mGlobalCostMisses;
88
89
90        // Constructor
91        OspTreeStatistics()
92        {
93                Reset();
94        }
95
96        int Nodes() const {return nodes;}
97        int Interior() const { return nodes / 2; }
98        int Leaves() const { return (nodes / 2) + 1; }
99       
100        // TODO: computation wrong
101        double AvgDepth() const { return accumDepth / (double)Leaves();};
102       
103
104        void Reset()
105        {
106                nodes = 0;
107                for (int i = 0; i < 3; ++ i)
108                        splits[i] = 0;
109               
110                maxDepth = 0;
111                minDepth = 99999;
112                accumDepth = 0;
113        pvs = 0;
114                maxDepthNodes = 0;
115                minPvsNodes = 0;
116                minProbabilityNodes = 0;
117                maxCostNodes = 0;
118                contributingSamples = 0;
119                sampleContributions = 0;
120                maxPvs = 0;
121                invalidLeaves = 0;
122                objectRefs = 0;
123                maxObjectRefs = 0;
124                mGlobalCostMisses = 0;
125        }
126
127
128        void Print(ostream &app) const;
129
130        friend ostream &operator<<(ostream &s, const OspTreeStatistics &stat)
131        {
132                stat.Print(s);
133                return s;
134        }
135};
136
137
138
139
140/** View Space Partitioning tree.
141*/
142class OspTree
143{
144        friend class ViewCellsParseHandlers;
145        friend class HierarchyManager;
146
147public:
148       
149        /** Additional data which is passed down the BSP tree during traversal.
150        */
151        class OspTraversalData
152        { 
153        public:
154                /// the current node
155                KdLeaf *mNode;
156                /// current depth
157                int mDepth;
158                /// rays piercing this node
159                RayInfoContainer *mRays;
160                /// the probability that this node contains view point
161                float mProbability;
162                /// the bounding box of the node
163                AxisAlignedBox3 mBoundingBox;
164                /// pvs size
165                float mRenderCost;
166                /// how often this branch has missed the max-cost ratio
167                int mMaxCostMisses;
168                // current axis
169                int mAxis;
170                // current priority
171                float mPriority;
172
173
174                OspTraversalData():
175                mNode(NULL),
176                mRays(NULL),
177                mDepth(0),
178                mRenderCost(0),
179                mProbability(0.0),
180                mMaxCostMisses(0),
181                mPriority(0),
182                mAxis(0)
183                {}
184               
185                OspTraversalData(KdLeaf *node,
186                                                 const int depth,
187                         RayInfoContainer *rays,
188                                                 const float rc,
189                                                 const float p,
190                                                 const AxisAlignedBox3 &box):
191                mNode(node),
192                mDepth(depth),
193                mRays(rays),
194                mRenderCost(rc),
195                mProbability(p),
196                mBoundingBox(box),
197                mMaxCostMisses(0),
198                mPriority(0),
199                mAxis(0)
200                {}
201
202                OspTraversalData(const int depth,
203                        RayInfoContainer *rays,
204                        const AxisAlignedBox3 &box):
205                mNode(NULL),
206                mDepth(depth),
207                mRays(rays),
208                mRenderCost(0),
209                mProbability(0),
210                mMaxCostMisses(0),
211                mAxis(0),
212                mBoundingBox(box)
213                {}
214
215                /** Returns cost of the traversal data.
216                */
217                float GetCost() const
218                {
219                        //cout << mPriority << endl;
220                        return mPriority;
221                }
222
223                /// deletes contents and sets them to NULL
224                void Clear()
225                {
226                        DEL_PTR(mRays);
227                }
228
229
230                friend bool operator<(const OspTraversalData &a, const OspTraversalData &b)
231                {
232                        return a.GetCost() < b.GetCost();
233                }
234    };
235
236        /** Candidate for a view space split.
237        */
238        class OspSubdivisionCandidate: public SubdivisionCandidate
239        { 
240        public:
241                static OspTree* sOspTree;
242
243                /// the current split plane
244                AxisAlignedPlane mSplitPlane;
245                /// parent data
246                OspTraversalData mParentData;
247               
248                OspSubdivisionCandidate(const OspTraversalData &tData): mParentData(tData)
249                {};
250
251                int Type() const { return OBJECT_SPACE; }
252       
253                void EvalPriority()
254                {
255                        sOspTree->EvalSubdivisionCandidate(*this);     
256                }
257
258                bool GlobalTerminationCriteriaMet() const
259                {
260                        return sOspTree->GlobalTerminationCriteriaMet(mParentData);
261                }
262
263
264                OspSubdivisionCandidate(const AxisAlignedPlane &plane, const OspTraversalData &tData):
265                mSplitPlane(plane), mParentData(tData)
266                {}
267        };
268
269        /** Struct for traversing line segment.
270        */
271        struct LineTraversalData
272        {
273                KdNode *mNode;
274                Vector3 mExitPoint;
275               
276                float mMaxT;
277   
278                LineTraversalData () {}
279                LineTraversalData (KdNode *n, const Vector3 &p, const float maxt):
280                mNode(n), mExitPoint(p), mMaxT(maxt) {}
281        };
282
283        /** Default constructor creating an empty tree.
284        */
285        OspTree();
286
287        /** Copies tree from a kd tree.
288        */
289        OspTree(const KdTree &kdTree);
290
291        /** Default destructor.
292        */
293        ~OspTree();
294
295        /** Returns tree statistics.
296        */
297        const OspTreeStatistics &GetStatistics() const;
298 
299        /** Returns bounding box of the specified node.
300        */
301        AxisAlignedBox3 GetBoundingBox(KdNode *node) const;
302
303        /** Returns list of leaves with pvs smaller than
304                a certain threshold.
305                @param onlyUnmailed if only the unmailed leaves should be considered
306                @param maxPvs the maximal pvs of a leaf to be added (-1 means unlimited)
307        */
308       
309        void CollectLeaves(vector<KdLeaf *> &leaves) const;
310
311        /** Returns bounding box of the whole tree (= bbox of root node)
312        */
313        AxisAlignedBox3 GetBoundingBox()const;
314
315        /** Returns root of the view space partitioning tree.
316        */
317        KdNode *GetRoot() const;
318
319        /** Collects the leaf view cells of the tree
320                @param viewCells returns the view cells
321        */
322        void CollectViewCells(ViewCellContainer &viewCells, bool onlyValid) const;
323
324        /** A ray is cast possible intersecting the tree.
325                @param the ray that is cast.
326                @returns the number of intersections with objects stored in the tree.
327        */
328        int CastRay(Ray &ray);
329
330        /** finds neighbouring leaves of this tree node.
331        */
332        int FindNeighbors(KdLeaf *n,
333                                          vector<VspLeaf *> &neighbors,
334                                          const bool onlyUnmailed) const;
335
336        /** Returns random leaf of BSP tree.
337                @param halfspace defines the halfspace from which the leaf is taken.
338        */
339        KdLeaf *GetRandomLeaf(const Plane3 &halfspace);
340
341        /** Returns random leaf of BSP tree.
342                @param onlyUnmailed if only unmailed leaves should be returned.
343        */
344        KdLeaf *GetRandomLeaf(const bool onlyUnmailed = false);
345
346        /** Returns epsilon of this tree.
347        */
348        float GetEpsilon() const;
349
350        /** Casts line segment into the tree.
351                @param origin the origin of the line segment
352                @param termination the end point of the line segment
353                @returns view cells intersecting the line segment.
354        */
355    int CastLineSegment(const Vector3 &origin,
356                                                const Vector3 &termination,
357                                                ViewCellContainer &viewcells);
358               
359        /** Sets pointer to view cells manager.
360        */
361        void SetViewCellsManager(ViewCellsManager *vcm);
362
363        /** Writes tree to output stream
364        */
365        bool Export(OUT_STREAM &stream);
366
367        /** Returns or creates a new intersectable for use in a kd based pvs.
368                The OspTree is responsible for destruction of the intersectable.
369        */
370        KdIntersectable *GetOrCreateKdIntersectable(KdNode *node);
371
372        /** Collects rays stored in the leaves.
373        */
374        void CollectRays(VssRayContainer &rays);
375
376        /** Intersects box with the tree and returns the number of intersected boxes.
377                @returns number of view cells found
378        */
379        int ComputeBoxIntersections(const AxisAlignedBox3 &box,
380                                                                ViewCellContainer &viewCells) const;
381
382
383        /** Returns kd leaf the point pt lies in, starting from root.
384        */
385        KdLeaf *GetLeaf(const Vector3 &pt, KdNode *root = NULL) const;
386
387
388        ViewCellsTree *GetViewCellsTree() const { return mViewCellsTree; }
389
390        void SetViewCellsTree(ViewCellsTree *vt) { mViewCellsTree = vt; }
391
392        float EvalRenderCost(const VssRayContainer &myrays);
393        float EvalLeafCost(const OspTraversalData &tData);
394
395        /** Adds this objects to the kd leaf objects.
396                @warning: Can corrupt the tree
397        */
398        void InsertObjects(KdNode *node, const ObjectContainer &objects);
399
400        /** Add the leaf to the pvs of the view cell.
401        */
402        bool AddLeafToPvs(
403                KdLeaf *leaf,
404                ViewCell *vc,
405                const float pdf,
406                float &contribution);
407
408protected:
409
410        // --------------------------------------------------------------
411        // For sorting objects
412        // --------------------------------------------------------------
413        struct SortableEntry
414        {
415                /** There is a 3th "event" for rays which intersect a
416                        box in the middle. These "events" don't induce a change in
417                        pvs size, but may induce a change in view cell volume.
418                */
419                enum EType
420                {
421                        BOX_MIN,
422                        BOX_MAX,
423                        BOX_INTERSECT
424                };
425
426                int mType;
427                //int mPvs;
428                float mPos;
429                VssRay *mRay;
430               
431                Intersectable *mObject;
432
433                SortableEntry() {}
434
435                SortableEntry(const int type,
436                        //const float pvs,
437                        const float pos,
438                        Intersectable *obj,
439                        VssRay *ray):
440                mType(type),
441                //mPvs(pvs),
442                mPos(pos),
443                mObject(obj),
444                mRay(ray)
445                {}
446
447                bool operator<(const SortableEntry &b) const
448                {
449                        return mPos < b.mPos;
450                }
451        };
452
453 
454        /** faster evaluation of split plane cost for kd axis aligned cells.
455        */
456        float EvalLocalSplitCost(const OspTraversalData &data,
457                                                         const AxisAlignedBox3 &box,
458                                                         const int axis,
459                                                         const float &position,
460                                                         float &pFront,
461                                                         float &pBack) const;
462
463        /** Evaluates candidate for splitting.
464        */
465        void EvalSubdivisionCandidate(OspSubdivisionCandidate &splitData);
466
467        /** Computes priority of the traversal data and stores it in tData.
468        */
469        void EvalPriority(OspTraversalData &tData) const;
470
471        /** Evaluates render cost decrease of next split.
472        */
473        float EvalRenderCostDecrease(const AxisAlignedPlane &candidatePlane,
474                                                                 const OspTraversalData &data,
475                                                                 float &normalizedOldRenderCost) const;
476
477
478        /** Collects view cells in the subtree under root.
479        */
480        void CollectViewCells(KdNode *root,
481                                                  bool onlyValid,
482                                                  ViewCellContainer &viewCells,
483                                                  bool onlyUnmailed = false) const;
484
485        /** Evaluates tree stats in the BSP tree leafs.
486        */
487        void EvaluateLeafStats(const OspTraversalData &data);
488
489        /** Subdivides node using a best split priority queue.
490            @param tQueue the best split priority queue
491                @param splitCandidate the candidate for the next split
492                @param globalCriteriaMet if the global termination criteria were already met
493                @returns new root of the subtree
494        */
495        KdNode *Subdivide(SplitQueue &tQueue,
496                                          SubdivisionCandidate *splitCandidate,
497                                          const bool globalCriteriaMet);
498       
499        /** Subdivides leaf.
500                @param leaf the leaf to be subdivided
501               
502                @param polys the polygons to be split
503                @param frontPolys returns the polygons in front of the split plane
504                @param backPolys returns the polygons in the back of the split plane
505               
506                @param rays the polygons to be filtered
507                @param frontRays returns the polygons in front of the split plane
508                @param backRays returns the polygons in the back of the split plane
509
510                @returns the root of the subdivision
511        */
512        KdInterior *SubdivideNode(
513                const AxisAlignedPlane &splitPlane,
514                const OspTraversalData &tData,
515                OspTraversalData &frontData,
516                OspTraversalData &backData);
517
518        void SplitObjects(KdLeaf *leaf,
519                                          const AxisAlignedPlane & splitPlane,
520                                          const ObjectContainer &objects,
521                                          ObjectContainer &front,
522                                          ObjectContainer &back);
523
524        /** does some post processing on the objects in the new child leaves.
525        */
526        void ProcessMultipleRefs(KdLeaf *leaf) const;
527
528        /** Sorts split candidates for cost heuristics using axis aligned splits.
529                @param node the current node
530                @param axis the current split axis
531        */
532        void SortSubdivisionCandidates(const OspTraversalData &data,
533                                                         const int axis,
534                                                         float minBand,
535                                                         float maxBand);
536
537        /** Computes best cost for axis aligned planes.
538        */
539        float EvalLocalCostHeuristics(const OspTraversalData &tData,
540                const AxisAlignedBox3 &box,
541                const int axis,
542                float &position,
543                int &objectsFront,
544                int &objectsBack);
545
546        /** Subdivides the rays into front and back rays according to the split plane.
547               
548                @param plane the split plane
549                @param rays contains the rays to be split. The rays are
550                           distributed into front and back rays.
551                @param frontRays returns rays on the front side of the plane
552                @param backRays returns rays on the back side of the plane
553               
554                @returns the number of splits
555        */
556        int SplitRays(const AxisAlignedPlane &plane,
557                RayInfoContainer &rays,
558                RayInfoContainer &frontRays,
559                RayInfoContainer &backRays) const;
560
561        int FilterRays(KdLeaf *leaf, const RayInfoContainer &rays, RayInfoContainer &filteredRays);
562
563        /** Adds the object to the pvs of the front and back leaf with a given classification.
564
565                @param obj the object to be added
566                @param cf the ray classification regarding the split plane
567                @param frontPvs returns the PVS of the front partition
568                @param backPvs returns the PVS of the back partition
569       
570        */
571        void UpdateObjPvsContri(Intersectable *obj,
572                                         const int cf,
573                                         float &frontPvs,
574                                         float &backPvs,
575                                         float &totalPvs) const;
576       
577        /** Returns true if tree can be terminated.
578        */
579        bool LocalTerminationCriteriaMet(const OspTraversalData &data) const;
580
581        /** Returns true if global tree can be terminated.
582        */
583        bool GlobalTerminationCriteriaMet(const OspTraversalData &data) const;
584
585        /** Selects an axis aligned for the next split.
586                @returns cost for this split
587        */
588        float SelectSplitPlane(
589                const OspTraversalData &tData,
590                AxisAlignedPlane &plane);
591       
592        /** Propagates valid flag up the tree.
593        */
594        void PropagateUpValidity(KdNode *node);
595
596        /** Writes the node to disk
597                @note: should be implemented as visitor.
598        */
599        void ExportNode(KdNode *node, OUT_STREAM &stream);
600
601        /** Returns estimated memory usage of tree.
602        */
603        float GetMemUsage() const;
604
605        /** Evaluate the contributions of view cell volume of the left and the right view cell.
606        */
607        void EvalRayContribution(
608                KdLeaf *leaf,
609                const VssRay &ray,
610                float &renderCost);
611       
612        void EvalViewCellContribution(
613                KdLeaf *leaf,
614                ViewCell *viewCell,
615                float &renderCost);
616
617        /** Evaluates the influence on the pvs of the event.
618                @param ve the visibility event
619                @param pvsLeft updates the left pvs
620                @param rightPvs updates the right pvs
621        */
622        void EvalHeuristicsContribution(KdLeaf *leaf,
623                const SortableEntry &ci,
624                float &renderCost,
625                ViewCellContainer &touchedViewCells);
626
627        /** Prepares objects for the cost heuristics.
628                @returns pvs size of the node
629        */
630        float PrepareHeuristics(
631                const OspTraversalData &tData,
632                ViewCellContainer &touchedViewCells);
633
634        /** Prepares heuristics for a particular ray.
635        */
636        void PrepareHeuristics(
637                const VssRay &ray,
638                ViewCellContainer &touchedViewCells);
639
640        /** Prepares construction for vsp and osp trees.
641        */
642        void ComputeBoundingBox(const ObjectContainer &objects);
643
644        void CollectDirtyCandidates(OspSubdivisionCandidate *sc,
645                vector<SubdivisionCandidate *> &dirtyList);
646
647        /** Collect view cells which see this kd leaf.
648        */
649        void CollectViewCells(KdLeaf *leaf,
650                ViewCellContainer &viewCells);
651
652        /** Rays will be clipped to the bounding box.
653        */
654        void PreprocessRays(
655                KdLeaf *root,
656                const VssRayContainer &sampleRays,
657                RayInfoContainer &rays);
658
659        /** Reads parameters from environment singleton.
660        */
661        void ReadEnvironment();
662
663        /** Returns true if the specified ray end points is inside the kd leaf.
664                @param isTermination if origin or termination point should be checked
665        */
666        bool EndPointInsideNode(KdLeaf *leaf, VssRay &ray, bool isTermination) const;
667
668        void AddViewCellVolumeContri(
669                ViewCell *vc,
670                float &frontVol,
671                float &backVol,
672                float &frontAndBackVol) const;
673
674        /** Classifies and mail view cell with respect to the heuristics contribution.
675                Set view cell mail box according to it's influence on
676                front (0), back (1) and front / back node (2).
677        */
678        void  MailViewCell(ViewCell *vc, const int cf) const;
679
680        int ClassifyRay(VssRay *ray, KdLeaf *leaf, const AxisAlignedPlane &plane) const;
681
682        void EvalSubdivisionStats(const SubdivisionCandidate &tData);
683
684        void AddSubdivisionStats(const int viewCells,
685                const float renderCostDecr,
686                const float totalRenderCost);
687
688        void EvalViewCellsForHeuristics(const VssRay &ray,
689                float &volLeft,
690                float &volRight);
691
692        float EvalViewCellsVolume(KdLeaf *leaf, const RayInfoContainer &rays) const;
693       
694        int RemoveParentViewCellsPvs(KdLeaf *leaf,
695                                                                          const RayInfoContainer &rays
696                                                                          ) const;
697
698        int UpdateViewCellsPvs(KdLeaf *leaf, const RayInfoContainer &rays) const;
699int CheckViewCellsPvs(KdLeaf *leaf,
700                                                           const RayInfoContainer &rays) const;
701        bool AddViewCellToObjectPvs(
702                Intersectable *obj,
703                ViewCell *vc,
704                float &contribution,
705                bool onlyMailed) const;
706
707        int ClassifyRays(
708                const RayInfoContainer &rays,
709                KdLeaf *leaf,
710                const AxisAlignedPlane &plane,
711                RayInfoContainer &frontRays,
712                RayInfoContainer &backRays) const;
713
714        void CollectTouchedViewCells(
715                const RayInfoContainer &rays,
716                ViewCellContainer &touchedViewCells) const;
717
718        void AddObjectContribution(
719                KdLeaf *leaf,
720                Intersectable * obj,
721                ViewCellContainer &touchedViewCells,
722                float &renderCost);
723
724        void SubtractObjectContribution(
725                KdLeaf *leaf,
726                Intersectable * obj,
727                ViewCellContainer &touchedViewCells,
728                float &renderCost);
729
730        SubdivisionCandidate *PrepareConstruction(
731                const VssRayContainer &sampleRays,
732                const ObjectContainer &objects,
733                RayInfoContainer &rays);
734
735
736protected:
737       
738        /// pointer to the hierarchy of view cells
739        ViewCellsTree *mViewCellsTree;
740
741        /// pointer to the view space partition
742        /// note: should be handled over the hierarchy manager
743        VspTree *mVspTree;
744
745        /// pointer to the hierarchy manager
746        HierarchyManager *mHierarchyManager;
747
748        /// The view cells manager
749        ViewCellsManager *mViewCellsManager;
750
751        /// candidates for placing split planes during cost heuristics
752        vector<SortableEntry> *mSubdivisionCandidates;
753
754        /// Pointer to the root of the tree
755        KdNode *mRoot;
756               
757        /// Statistics for the object space partition
758        OspTreeStatistics mOspStats;
759       
760        /// box around the whole view domain
761        AxisAlignedBox3 mBoundingBox;
762
763
764        //////////////////////////////
765        //-- local termination
766
767        /// maximal possible depth
768        int mTermMaxDepth;
769        /// mininum probability
770        float mTermMinProbability;
771        /// minimal number of objects
772        int mTermMinObjects;
773        /// maximal contribution per ray
774        float mTermMaxRayContribution;
775        /// maximal acceptable cost ratio
776        float mTermMaxCostRatio;
777        /// tolerance value indicating how often the max cost ratio can be failed
778        int mTermMissTolerance;
779
780
781        ////////////////////////
782        //-- global criteria
783
784        float mTermMinGlobalCostRatio;
785        int mTermGlobalCostMissTolerance;
786        int mGlobalCostMisses;
787
788        /// maximal number of view cells
789        int mTermMaxLeaves;
790        /// maximal tree memory
791        float mMaxMemory;
792        /// the tree is out of memory
793        bool mOutOfMemory;
794
795
796        ///////////////////////////////////////////
797        //-- split heuristics based parameters
798       
799        bool mUseCostHeuristics;
800        /// balancing factor for PVS criterium
801        float mCtDivCi;
802        /// if only driving axis should be used for split
803        bool mOnlyDrivingAxis;
804        /// represents min and max band for sweep
805        float mSplitBorder;
806
807        ////////////////////////////////////////////////
808
809        /// current time stamp (used for keeping split history)
810        int mTimeStamp;
811        // if rays should be stored in leaves
812        bool mStoreRays;
813        /// epsilon for geometric comparisons
814        float mEpsilon;
815        /// subdivision stats output file
816        ofstream  mSubdivisionStats;
817        /// keeps track of cost during subdivision
818        float mTotalCost;
819        /// keeps track of overall pvs size during subdivision
820        int mTotalPvsSize;
821        /// number of currenly generated view cells
822        int mCreatedLeaves;
823       
824        /// weight between  render cost decrease and node render cost
825        float mRenderCostDecreaseWeight;
826
827    /// stores the kd node intersectables used for pvs
828  KdIntersectableMap mKdIntersectables;
829       
830private:
831
832        bool mCopyFromKdTree;
833};
834
835
836}
837
838#endif
Note: See TracBrowser for help on using the repository browser.