source: GTP/trunk/Lib/Vis/Preprocessing/src/GvsPreprocessor.cpp @ 1981

Revision 1981, 19.0 KB checked in by mattausch, 17 years ago (diff)
  • Property svn:executable set to *
Line 
1#include "Environment.h"
2#include "GvsPreprocessor.h"
3#include "GlRenderer.h"
4#include "VssRay.h"
5#include "ViewCellsManager.h"
6#include "Triangle3.h"
7#include "IntersectableWrapper.h"
8#include "Plane3.h"
9#include "RayCaster.h"
10#include "Exporter.h"
11#include "SamplingStrategy.h"
12
13
14namespace GtpVisibilityPreprocessor
15{
16 
17#define GVS_DEBUG 1
18
19struct VizStruct
20{
21        Polygon3 *enlargedTriangle;
22        Triangle3 originalTriangle;
23        VssRay *ray;
24};
25
26static vector<VizStruct> vizContainer;
27
28GvsPreprocessor::GvsPreprocessor():
29Preprocessor(),
30//mSamplingType(SamplingStrategy::DIRECTION_BASED_DISTRIBUTION),
31mSamplingType(SamplingStrategy::DIRECTION_BOX_BASED_DISTRIBUTION)
32{
33        Environment::GetSingleton()->GetIntValue("GvsPreprocessor.totalSamples", mTotalSamples);
34        Environment::GetSingleton()->GetIntValue("GvsPreprocessor.initialSamples", mInitialSamples);
35        Environment::GetSingleton()->GetIntValue("GvsPreprocessor.samplesPerPass", mSamplesPerPass);
36        Environment::GetSingleton()->GetFloatValue("GvsPreprocessor.epsilon", mEps);
37        Environment::GetSingleton()->GetFloatValue("GvsPreprocessor.threshold", mThreshold);   
38        Environment::GetSingleton()->GetBoolValue("GvsPreprocessor.UsePerViewCellSampling", mPerViewCell);
39
40        char gvsStatsLog[100];
41        Environment::GetSingleton()->GetStringValue("GvsPreprocessor.stats", gvsStatsLog);
42        mGvsStatsStream.open(gvsStatsLog);
43
44        Debug << "Gvs preprocessor options" << endl;
45        Debug << "number of total samples: " << mTotalSamples << endl;
46        Debug << "number of initial samples: " << mInitialSamples << endl;
47        Debug << "number of samples per pass: " << mSamplesPerPass << endl;
48        Debug << "threshold: " << mThreshold << endl;
49        Debug << "epsilon: " << mEps << endl;
50        Debug << "stats: " << gvsStatsLog << endl;
51
52        if (0)
53                mOnlyRandomSampling = true;
54        else
55                mOnlyRandomSampling = false;
56
57        //mGvsStatsStream.open("gvspreprocessor.log");
58        mGvsStats.Reset();
59}
60
61
62bool GvsPreprocessor::CheckDiscontinuity(const VssRay &currentRay,
63                                                                                 const Triangle3 &hitTriangle,
64                                                                                 const VssRay &oldRay)
65{
66        // the predicted hitpoint: we expect to hit the same mesh again
67        const Vector3 predictedHit = CalcPredictedHitPoint(currentRay, hitTriangle, oldRay);
68
69        const float predictedLen = Magnitude(predictedHit - currentRay.mOrigin);
70        const float len = Magnitude(currentRay.mTermination - currentRay.mOrigin);
71       
72        // distance large => this is likely to be a discontinuity
73#if 1
74        if ((predictedLen - len) > mThreshold)
75#else // rather use relative distance
76        if ((predictedLen / len) > mThreshold)
77#endif
78        {
79                //cout << "r";
80                // apply reverse sampling to find the gap
81                VssRay *newRay = ReverseSampling(currentRay, hitTriangle, oldRay);
82
83                if (!newRay)
84                        return false;
85
86                // set flag for visualization
87                newRay->mFlags |= VssRay::ReverseSample;
88               
89                // if ray is not further processed => delete ray
90                if (!HandleRay(newRay))
91                {
92                        delete newRay;
93                }
94                else if (GVS_DEBUG && (mVssRays.size() < 9))
95                {
96                        mVssRays.push_back(new VssRay(oldRay));
97                        mVssRays.push_back(new VssRay(currentRay));
98                        mVssRays.push_back(new VssRay(*newRay));
99                }
100
101                return true;
102        }
103
104        return false;
105}
106
107
108bool GvsPreprocessor::HandleRay(VssRay *vssRay)
109{
110        // compute the contribution to the view cells
111        const bool storeRaysForViz = GVS_DEBUG;
112
113        if (!mPerViewCell)
114        {
115                mViewCellsManager->ComputeSampleContribution(*vssRay,
116                                                                                                         true,
117                                                                                                         storeRaysForViz);
118        }
119        else
120        {
121                mViewCellsManager->ComputeSampleContribution(*vssRay,
122                                                                                                         true,
123                                                                                                         mCurrentViewCell);
124        }
125
126        // some pvs contribution for this ray?
127        if (!vssRay->mPvsContribution)
128                return false;
129
130        // add new ray to ray queue
131        mRayQueue.push(vssRay);
132
133        if (storeRaysForViz)
134        {
135                VssRay *nray = new VssRay(*vssRay);
136                nray->mFlags = vssRay->mFlags;
137
138                // store ray in contributing view cell
139                ViewCellContainer::const_iterator vit, vit_end = vssRay->mViewCells.end();
140                for (vit = vssRay->mViewCells.begin(); vit != vit_end; ++ vit)
141                {                       
142                        (*vit)->GetOrCreateRays()->push_back(nray);                             
143                }
144        }
145
146        ++ mGvsStats.mPassContribution;
147
148        return true;
149}
150
151
152/** Creates 3 new vertices for triangle vertex with specified index.
153*/
154void GvsPreprocessor::CreateDisplacedVertices(VertexContainer &vertices,
155                                                                                          const Triangle3 &hitTriangle,
156                                                                                          const VssRay &ray,
157                                                                                          const int index) const
158{
159        const int indexU = (index + 1) % 3;
160        const int indexL = (index == 0) ? 2 : index - 1;
161
162        const Vector3 a = hitTriangle.mVertices[index] - ray.GetOrigin();
163        const Vector3 b = hitTriangle.mVertices[indexU] - hitTriangle.mVertices[index];
164        const Vector3 c = hitTriangle.mVertices[index] - hitTriangle.mVertices[indexL];
165       
166        const float len = Magnitude(a);
167       
168        const Vector3 dir1 = Normalize(CrossProd(a, b)); //N((pi-xp)×(pi+1- pi));
169        const Vector3 dir2 = Normalize(CrossProd(a, c)); // N((pi-xp)×(pi- pi-1))
170        const Vector3 dir3 = DotProd(dir2, dir1) > 0 ? // N((pi-xp)×di,i-1+di,i+1×(pi-xp))
171                Normalize(dir2 + dir1) : Normalize(CrossProd(a, dir1) + CrossProd(dir2, a));
172
173        // compute the new three hit points
174        // pi, i + 1:  pi+ e·|pi-xp|·di, j
175        const Vector3 pt1 = hitTriangle.mVertices[index] + mEps * len * dir1;
176        // pi, i - 1:  pi+ e·|pi-xp|·di, j
177    const Vector3 pt2 = hitTriangle.mVertices[index] + mEps * len * dir2;
178        // pi, i:  pi+ e·|pi-xp|·di, j
179        const Vector3 pt3 = hitTriangle.mVertices[index] + mEps * len * dir3;
180       
181        vertices.push_back(pt2);
182        vertices.push_back(pt3);
183        vertices.push_back(pt1);
184}
185
186
187void GvsPreprocessor::EnlargeTriangle(VertexContainer &vertices,
188                                                                          const Triangle3 &hitTriangle,
189                                                                          const VssRay &ray) const
190{
191        CreateDisplacedVertices(vertices, hitTriangle, ray, 0);
192        CreateDisplacedVertices(vertices, hitTriangle, ray, 1);
193        CreateDisplacedVertices(vertices, hitTriangle, ray, 2);
194}
195
196
197Vector3 GvsPreprocessor::CalcPredictedHitPoint(const VssRay &newRay,
198                                                                                           const Triangle3 &hitTriangle,
199                                                                                           const VssRay &oldRay) const
200{
201        // find the intersection of the plane induced by the
202        // hit triangle with the new ray
203        Plane3 plane(hitTriangle.GetNormal(), hitTriangle.mVertices[0]);
204
205        const Vector3 hitPt =
206                plane.FindIntersection(newRay.mTermination, newRay.mOrigin);
207       
208        return hitPt;
209}
210
211
212static bool EqualVisibility(const VssRay &a, const VssRay &b)
213{
214        return a.mTerminationObject == b.mTerminationObject;
215}
216
217
218int GvsPreprocessor::SubdivideEdge(const Triangle3 &hitTriangle,
219                                                                   const Vector3 &p1,
220                                                                   const Vector3 &p2,
221                                                                   const VssRay &ray1,
222                                                                   const VssRay &ray2,
223                                                                   const VssRay &oldRay)
224{
225        CheckDiscontinuity(ray1, hitTriangle, oldRay);
226        CheckDiscontinuity(ray2, hitTriangle, oldRay);
227
228        if (EqualVisibility(ray1, ray2) || (Magnitude(p1 - p2) <= mEps))
229        {
230                return 0;
231        }
232        else
233        {
234                // the new subdivision point
235                const Vector3 p = (p1 + p2) * 0.5f;
236       
237                //cout << "tobj " << ray1.mTerminationObject << " " << ray2.mTerminationObject << " " << p1 << " " << p2 << endl;
238                //cout << "term " << ray1.mTermination << " " << ray2.mTermination << endl;
239
240                // cast ray into the new point
241                SimpleRay sray(oldRay.mOrigin, p - oldRay.mOrigin, SamplingStrategy::GVS, 1.0f);
242       
243                VssRay *newRay = mRayCaster->CastRay(sray, mViewCellsManager->GetViewSpaceBox());
244
245                if (!newRay) return 0;
246
247                newRay->mFlags |= VssRay::BorderSample;
248
249                // add new ray to queue
250                const bool enqueued = HandleRay(newRay);
251               
252                // subdivide further
253                const int samples1 = SubdivideEdge(hitTriangle, p1, p, ray1, *newRay, oldRay);
254                const int samples2 = SubdivideEdge(hitTriangle, p, p2, *newRay, ray2, oldRay);
255                       
256                // this ray will not be further processed
257                if (!enqueued)
258                        delete newRay;
259               
260                return samples1 + samples2 + 1;
261        }
262}
263
264
265int GvsPreprocessor::AdaptiveBorderSampling(const VssRay &currentRay)
266{
267        Intersectable *tObj = currentRay.mTerminationObject;
268        Triangle3 hitTriangle;
269
270        // other types not implemented yet
271        if (tObj->Type() == Intersectable::TRIANGLE_INTERSECTABLE)
272        {
273                hitTriangle = dynamic_cast<TriangleIntersectable *>(tObj)->GetItem();
274        }
275        else
276        {
277                cout << "not yet implemented" << endl;
278        }
279
280        VertexContainer enlargedTriangle;
281       
282        /// create 3 new hit points for each vertex
283        EnlargeTriangle(enlargedTriangle, hitTriangle, currentRay);
284       
285        /// create rays from sample points and handle them
286        SimpleRayContainer simpleRays;
287        simpleRays.reserve(9);
288
289        //cout << "currentRay: " << currentRay.mOrigin << " dir: " << currentRay.GetDir() << endl;
290
291        VertexContainer::const_iterator vit, vit_end = enlargedTriangle.end();
292
293        for (vit = enlargedTriangle.begin(); vit != vit_end; ++ vit)
294        {
295                const Vector3 rayDir = (*vit) - currentRay.GetOrigin();
296                SimpleRay sr(currentRay.GetOrigin(), rayDir, SamplingStrategy::GVS, 1.0f);
297                simpleRays.AddRay(sr);
298
299                //cout << "new: " << sr.mOrigin << " dist: " << sr.mDirection << endl;
300        }
301
302        if (0)
303        {
304                // visualize enlarged triangles
305                VizStruct dummy;
306                dummy.enlargedTriangle = new Polygon3(enlargedTriangle);
307                dummy.originalTriangle = hitTriangle;
308                vizContainer.push_back(dummy);
309        }
310
311        // cast rays to triangle vertices and determine visibility
312        VssRayContainer vssRays;
313
314        // don't cast double rays as we need only the forward rays
315        const bool castDoubleRays = false;
316        // cannot prune invalid rays because we have to
317        // compare adjacent  rays.
318        const bool pruneInvalidRays = false;
319
320        CastRays(simpleRays, vssRays, castDoubleRays, pruneInvalidRays);
321
322        // set flags
323        VssRayContainer::const_iterator rit, rit_end = vssRays.end();
324        for (rit = vssRays.begin(); rit != rit_end; ++ rit)
325        {
326                (*rit)->mFlags |= VssRay::BorderSample;
327        }
328
329        // handle rays
330        EnqueueRays(vssRays);
331       
332        const int n = (int)enlargedTriangle.size();
333        int castRays = (int)vssRays.size();
334
335    // recursivly subdivide each edge
336        for (int i = 0; i < n; ++ i)
337        {
338                castRays += SubdivideEdge(hitTriangle,
339                                                                  enlargedTriangle[i],
340                                                                  enlargedTriangle[(i + 1) % n],
341                                                                  *vssRays[i],
342                                                                  *vssRays[(i + 1) % n],
343                                                                  currentRay);
344        }
345
346        mGvsStats.mBorderSamples += castRays;
347
348        return castRays;
349}
350
351
352/*Vector3 GvsPreprocessor::GetPassingPoint(const VssRay &currentRay,
353                                                                                 const Triangle3 &hitTriangle,
354                                                                                 const VssRay &oldRay) const
355{
356        //-- intersect triangle plane with plane spanned by current samples
357        Plane3 plane(currentRay.GetOrigin(), currentRay.GetTermination(), oldRay.GetTermination());
358        Plane3 triPlane(hitTriangle.GetNormal(), hitTriangle.mVertices[0]);
359
360        SimpleRay intersectLine = GetPlaneIntersection(plane, triPlane);
361
362        // Evaluate new hitpoint just outside the triangle
363        const float factor = 0.95f;
364        const float t = triPlane.FindT(intersectLine);
365        const Vector3 newPoint = intersectLine.mOrigin + t * factor * intersectLine.mDirection;
366
367        return newPoint;
368}*/
369
370
371Vector3 GvsPreprocessor::GetPassingPoint(const VssRay &currentRay,
372                                                                                 const Triangle3 &occluder,
373                                                                                 const VssRay &oldRay) const
374{
375        //-- The plane p = (xp, hit(x), hit(xold)) is intersected
376        //-- with the newly found occluder (xold is the previous ray from
377        //-- which x was generated). On the intersecting line, we select a point
378        //-- pnew which lies just outside of the new triangle so the ray
379        //-- just passes through the gap
380
381        const Plane3 plane(currentRay.GetOrigin(),
382                                           currentRay.GetTermination(),
383                                           oldRay.GetTermination());
384       
385        Vector3 pt1, pt2;
386
387        const bool intersects = occluder.GetPlaneIntersection(plane, pt1, pt2);
388
389        if (!intersects)
390                cerr << "big error!! no intersection" << endl;
391
392        // get the intersection point on the old ray
393        const Plane3 triPlane(occluder.GetNormal(), occluder.mVertices[0]);
394
395        const float t = triPlane.FindT(oldRay.mOrigin, oldRay.mTermination);
396        const Vector3 pt3 = oldRay.mOrigin + t * (oldRay.mTermination - oldRay.mOrigin);
397
398        // Evaluate new hitpoint just outside the triangle
399        Vector3 newPoint;
400
401        const float eps = mEps;
402        // the point is chosen to be on the side closer to the original ray
403        if (Distance(pt1, pt3) < Distance(pt2, pt3))
404        {
405                newPoint = pt1 + eps * (pt1 - pt2);
406        }       
407        else
408        {
409                newPoint = pt2 + eps * (pt2 - pt1);
410        }
411
412        //cout << "passing point: " << newPoint << endl << endl;
413        return newPoint;
414}
415
416
417VssRay *GvsPreprocessor::ReverseSampling(const VssRay &currentRay,
418                                                                                 const Triangle3 &hitTriangle,
419                                                                                 const VssRay &oldRay)
420{
421        // get triangle occluding the path to the hit mesh
422        Triangle3 occluder;
423        Intersectable *tObj = currentRay.mTerminationObject;
424
425        // q: why can this happen?
426        if (!tObj)
427                return NULL;
428
429        // other types not implemented yet
430        if (tObj->Type() == Intersectable::TRIANGLE_INTERSECTABLE)
431                occluder = dynamic_cast<TriangleIntersectable *>(tObj)->GetItem();
432        else
433                cout << "not yet implemented" << endl;
434       
435        // get a point which is passing just outside of the occluder
436    const Vector3 newPoint = GetPassingPoint(currentRay, occluder, oldRay);
437        const Vector3 predicted = CalcPredictedHitPoint(currentRay, hitTriangle, oldRay);
438
439        //-- Construct the mutated ray with xnew,
440        //-- dir = predicted(x)- pnew as direction vector
441        const Vector3 newDir = predicted - newPoint;
442
443        // take xnew, p = intersect(viewcell, line(pnew, predicted(x)) as origin ?
444        // difficult to say!!
445        const float offset = 0.5f;
446        const Vector3 newOrigin = newPoint - newDir * offset;
447
448        static Ray ray(newOrigin, newDir, Ray::LOCAL_RAY);
449        ray.Precompute();
450
451        // check if ray intersects view cell
452        if (mPerViewCell && !mCurrentViewCell->CastRay(ray))
453                return NULL;
454
455        const SimpleRay simpleRay(newOrigin, newDir, SamplingStrategy::GVS, 1.0f);
456
457        VssRay *reverseRay =
458                mRayCaster->CastRay(simpleRay, mViewCellsManager->GetViewSpaceBox());
459
460    ++ mGvsStats.mReverseSamples;
461
462        return reverseRay;
463}
464
465
466int GvsPreprocessor::CastInitialSamples(const int numSamples,
467                                                                                const int sampleType)
468{       
469        const long startTime = GetTime();
470
471        // generate simple rays
472        SimpleRayContainer simpleRays;
473        GenerateRays(numSamples, sampleType, simpleRays);
474
475        // generate vss rays
476        VssRayContainer samples;
477        CastRays(simpleRays, samples, true);
478        // add to ray queue
479        EnqueueRays(samples);
480
481        //Debug << "generated " <<  numSamples << " samples in " << TimeDiff(startTime, GetTime()) * 1e-3 << " secs" << endl;
482        return (int)samples.size();
483}
484
485
486void GvsPreprocessor::EnqueueRays(VssRayContainer &samples)
487{
488        // add samples to ray queue
489        VssRayContainer::const_iterator vit, vit_end = samples.end();
490        for (vit = samples.begin(); vit != vit_end; ++ vit)
491        {
492                HandleRay(*vit);
493        }
494}
495
496
497int GvsPreprocessor::Pass()
498{
499        // reset samples
500        int castSamples = 0;
501        mGvsStats.mPassContribution = 0;
502
503        while (castSamples < mSamplesPerPass)
504        {       
505                // Ray queue empty =>
506                // cast a number of uniform samples to fill ray queue
507                castSamples += CastInitialSamples(mInitialSamples, mSamplingType);
508
509                if (!mOnlyRandomSampling)
510                        castSamples += ProcessQueue();
511        }
512
513        mGvsStats.mTotalContribution += mGvsStats.mPassContribution;
514        return castSamples;
515}
516
517
518int GvsPreprocessor::ProcessQueue()
519{
520        int castSamples = 0;
521        ++ mGvsStats.mGvsPass;
522
523        while (!mRayQueue.empty())
524        {
525                // handle next ray
526                VssRay *ray = mRayQueue.top();
527                mRayQueue.pop();
528
529                castSamples += AdaptiveBorderSampling(*ray);
530                delete ray;
531        }
532       
533        return castSamples;
534}
535
536
537bool GvsPreprocessor::ComputeVisibility()
538{
539        cout << "Gvs Preprocessor started\n" << flush;
540        const long startTime = GetTime();
541
542        Randomize(0);
543       
544        mGvsStats.Reset();
545        mGvsStats.Start();
546
547        if (!mLoadViewCells)
548        {       
549                /// construct the view cells from the scratch
550                ConstructViewCells();
551                // reset pvs already gathered during view cells construction
552                mViewCellsManager->ResetPvs();
553                cout << "finished view cell construction" << endl;
554        }
555        else if (0)
556        {       
557                //-- test successful view cells loading by exporting them again
558                VssRayContainer dummies;
559                mViewCellsManager->Visualize(mObjects, dummies);
560                mViewCellsManager->ExportViewCells("test.xml.gz", mViewCellsManager->GetExportPvs(), mObjects);
561        }
562
563        mGvsStats.Stop();
564        mGvsStats.Print(mGvsStatsStream);
565
566        while (mGvsStats.mTotalSamples < mTotalSamples)
567        {
568                ++ mPass;
569
570                mGvsStats.mTotalSamples += Pass();
571                               
572                ////////
573                //-- stats
574
575                cout << "\nPass " << mPass << " #samples: " << mGvsStats.mTotalSamples << " of " << mTotalSamples << endl;
576                mGvsStats.mPass = mPass;
577                mGvsStats.Stop();
578                mGvsStats.Print(mGvsStatsStream);
579                //mViewCellsManager->PrintPvsStatistics(mGvsStats);
580
581                if (GVS_DEBUG)
582                {
583                        char str[64]; sprintf(str, "tmp/pass%04d-", mPass);
584               
585                        // visualization
586                        if (mGvsStats.mPassContribution > 0)
587                        {
588                                const bool exportRays = true;
589                                const bool exportPvs = true;
590
591                                mViewCellsManager->ExportSingleViewCells(mObjects,
592                                                                                                                 10,
593                                                                                                                 false,
594                                                                                                                 exportPvs,
595                                                                                                                 exportRays,
596                                                                                                                 1000,
597                                                                                                                 str);
598                        }
599
600                        // remove pass samples
601                        ViewCellContainer::const_iterator vit, vit_end = mViewCellsManager->GetViewCells().end();
602                        for (vit = mViewCellsManager->GetViewCells().begin(); vit != vit_end; ++ vit)
603                        {
604                                (*vit)->DelRayRefs();
605                        }
606                }
607
608                // ComputeRenderError();
609        }
610
611        cout << "cast " << 2 * mGvsStats.mTotalSamples / (1e3f * TimeDiff(startTime, GetTime())) << "M rays/s" << endl;
612
613        if (GVS_DEBUG)
614        {
615                Visualize();
616                CLEAR_CONTAINER(mVssRays);
617        }
618
619        return true;
620}
621
622
623void GvsPreprocessor::Visualize()
624{
625        Exporter *exporter = Exporter::GetExporter("gvs.wrl");
626
627        if (!exporter)
628                return;
629       
630        vector<VizStruct>::const_iterator vit, vit_end = vizContainer.end();
631        for (vit = vizContainer.begin(); vit != vit_end; ++ vit)
632        {
633                exporter->SetWireframe();
634                exporter->ExportPolygon((*vit).enlargedTriangle);
635                //Material m;
636                exporter->SetFilled();
637                Polygon3 poly = Polygon3((*vit).originalTriangle);
638                exporter->ExportPolygon(&poly);
639        }
640
641        VssRayContainer::const_iterator rit, rit_end = mVssRays.end();
642        for (rit = mVssRays.begin(); rit != rit_end; ++ rit)
643        {
644                Intersectable *obj = (*rit)->mTerminationObject;
645                exporter->ExportIntersectable(obj);
646        }
647
648        VssRayContainer vcRays, vcRays2, vcRays3;
649
650        // prepare some rays for output
651        for (rit = mVssRays.begin(); rit != rit_end; ++ rit)
652        {
653                const float p = RandomValue(0.0f, (float)mVssRays.size());
654                if (1)//(p < raysOut)
655                {
656                        if ((*rit)->mFlags & VssRay::BorderSample)
657                        {
658                                vcRays.push_back(*rit);
659                        }
660                        else if ((*rit)->mFlags & VssRay::ReverseSample)
661                        {
662                                vcRays2.push_back(*rit);
663                        }
664                        else
665                        {
666                                vcRays3.push_back(*rit);
667                        }       
668                }
669        }
670
671        exporter->ExportRays(vcRays, RgbColor(1, 0, 0));
672        exporter->ExportRays(vcRays2, RgbColor(0, 1, 0));
673        exporter->ExportRays(vcRays3, RgbColor(1, 1, 1));
674
675        //exporter->ExportRays(mVssRays);
676        delete exporter;
677}
678
679
680void GvsStatistics::Print(ostream &app) const
681{
682        app << "#Pass\n" << mPass << endl;
683        app << "#Time\n" << Time() << endl;
684        app << "#TotalSamples\n" << mTotalSamples << endl;
685        app << "#ScDiff\n" << mPassContribution << endl;
686        app     << "#SamplesContri\n" << mTotalContribution << endl;
687        app << "#ReverseSamples\n" << mReverseSamples << endl;
688        app << "#BorderSamples\n" << mBorderSamples << endl;           
689        app << "#GvsRuns\n" << mGvsPass << endl;
690}
691
692
693}
Note: See TracBrowser for help on using the repository browser.