1 /** @file
2  * @brief Unit tests for Affine.
3  * Uses the Google Testing Framework
4  *//*
5  * Authors:
6  *   Nathan Hurst <njh@njhurst.com>
7  *   Krzysztof Kosiński <tweenk.pl@gmail.com>
8  *   Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
9  *
10  * Copyright 2010 Authors
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it either under the terms of the GNU Lesser General Public
14  * License version 2.1 as published by the Free Software Foundation
15  * (the "LGPL") or, at your option, under the terms of the Mozilla
16  * Public License Version 1.1 (the "MPL"). If you do not alter this
17  * notice, a recipient may use your version of this file under either
18  * the MPL or the LGPL.
19  *
20  * You should have received a copy of the LGPL along with this library
21  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  * You should have received a copy of the MPL along with this library
24  * in the file COPYING-MPL-1.1
25  *
26  * The contents of this file are subject to the Mozilla Public License
27  * Version 1.1 (the "License"); you may not use this file except in
28  * compliance with the License. You may obtain a copy of the License at
29  * http://www.mozilla.org/MPL/
30  *
31  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
32  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
33  * the specific language governing rights and limitations.
34  */
35 
36 #include "testing.h"
37 #include <iostream>
38 
39 #include <2geom/bezier.h>
40 #include <2geom/polynomial.h>
41 #include <2geom/basic-intersection.h>
42 #include <2geom/bezier-curve.h>
43 #include <vector>
44 #include <iterator>
45 #include <glib.h>
46 
47 using namespace std;
48 using namespace Geom;
49 
lin_poly(double a,double b)50 Poly lin_poly(double a, double b) { // ax + b
51     Poly p;
52     p.push_back(b);
53     p.push_back(a);
54     return p;
55 }
56 
are_equal(Bezier A,Bezier B)57 bool are_equal(Bezier A, Bezier B) {
58     int maxSize = max(A.size(), B.size());
59     double t = 0., dt = 1./maxSize;
60 
61     for(int i = 0; i <= maxSize; i++) {
62         EXPECT_FLOAT_EQ(A.valueAt(t), B.valueAt(t));// return false;
63         t += dt;
64     }
65     return true;
66 }
67 
68 class BezierTest : public ::testing::Test {
69 protected:
70 
BezierTest()71     BezierTest()
72         : zero(fragments[0])
73         , unit(fragments[1])
74         , hump(fragments[2])
75         , wiggle(fragments[3])
76     {
77         zero = Bezier(0.0,0.0);
78         unit = Bezier(0.0,1.0);
79         hump = Bezier(0,1,0);
80         wiggle = Bezier(0,1,-2,3);
81     }
82 
83     Bezier fragments[4];
84     Bezier &zero, &unit, &hump, &wiggle;
85 };
86 
TEST_F(BezierTest,Basics)87 TEST_F(BezierTest, Basics) {
88 
89     //std::cout << unit <<std::endl;
90     //std::cout << hump <<std::endl;
91 
92     EXPECT_TRUE(Bezier(0,0,0,0).isZero());
93     EXPECT_TRUE(Bezier(0,1,2,3).isFinite());
94 
95     EXPECT_EQ(3u, Bezier(0,2,4,5).order());
96 
97     ///cout << " Bezier::Bezier(const Bezier& b);\n";
98     //cout << Bezier(wiggle) << " == " << wiggle << endl;
99 
100     //cout << "explicit Bezier(unsigned ord);\n";
101     //cout << Bezier(10) << endl;
102 
103     //cout << "Bezier(Coord c0, Coord c1);\n";
104     //cout << Bezier(0.0,1.0) << endl;
105 
106     //cout << "Bezier(Coord c0, Coord c1, Coord c2);\n";
107     //cout << Bezier(0,1, 2) << endl;
108 
109     //cout << "Bezier(Coord c0, Coord c1, Coord c2, Coord c3);\n";
110     //cout << Bezier(0,1,2,3) << endl;
111 
112     //cout << "unsigned degree();\n";
113     EXPECT_EQ(2u, hump.degree());
114 
115     //cout << "unsigned size();\n";
116     EXPECT_EQ(3u, hump.size());
117 }
118 
TEST_F(BezierTest,ValueAt)119 TEST_F(BezierTest, ValueAt) {
120     EXPECT_EQ(0.0, wiggle.at0());
121     EXPECT_EQ(3.0, wiggle.at1());
122 
123     EXPECT_EQ(0.0, wiggle.valueAt(0.5));
124 
125     EXPECT_EQ(0.0, wiggle(0.5));
126 
127     //cout << "SBasis toSBasis();\n";
128     //cout << unit.toSBasis() << endl;
129     //cout << hump.toSBasis() << endl;
130     //cout << wiggle.toSBasis() << endl;
131 }
132 
TEST_F(BezierTest,Casteljau)133 TEST_F(BezierTest, Casteljau) {
134     unsigned N = wiggle.order() + 1;
135     std::vector<Coord> left(N), right(N);
136     std::vector<Coord> left2(N), right2(N);
137 
138     for (unsigned i = 0; i < 10000; ++i) {
139         double t = g_random_double_range(0, 1);
140         double vok = bernstein_value_at(t, &wiggle[0], wiggle.order());
141         double v = casteljau_subdivision<double>(t, &wiggle[0], &left[0], &right[0], wiggle.order());
142         EXPECT_EQ(v, vok);
143         EXPECT_EQ(left[0], wiggle.at0());
144         EXPECT_EQ(left[wiggle.order()], right[0]);
145         EXPECT_EQ(right[wiggle.order()], wiggle.at1());
146 
147         double vl = casteljau_subdivision<double>(t, &wiggle[0], &left2[0], NULL, wiggle.order());
148         double vr = casteljau_subdivision<double>(t, &wiggle[0], NULL, &right2[0], wiggle.order());
149         EXPECT_EQ(vl, vok);
150         EXPECT_EQ(vr, vok);
151         EXPECT_vector_equal(left2, left);
152         EXPECT_vector_equal(right2, right);
153 
154         double vnone = casteljau_subdivision<double>(t, &wiggle[0], NULL, NULL, wiggle.order());
155         EXPECT_near(vnone, vok, 1e-12);
156     }
157 }
158 
TEST_F(BezierTest,Portion)159 TEST_F(BezierTest, Portion) {
160     constexpr Coord eps{1e-12};
161 
162     for (unsigned i = 0; i < 10000; ++i) {
163         double from = g_random_double_range(0, 1);
164         double to = g_random_double_range(0, 1);
165         for (auto & input : fragments) {
166             Bezier result = portion(input, from, to);
167 
168             // the endpoints must correspond exactly
169             EXPECT_near(result.at0(), input.valueAt(from), eps);
170             EXPECT_near(result.at1(), input.valueAt(to), eps);
171         }
172     }
173 }
174 
TEST_F(BezierTest,Subdivide)175 TEST_F(BezierTest, Subdivide) {
176     std::vector<std::pair<Bezier, double> > errors;
177     for (unsigned i = 0; i < 10000; ++i) {
178         double t = g_random_double_range(0, 1e-6);
179         for (auto & input : fragments) {
180             std::pair<Bezier, Bezier> result = input.subdivide(t);
181 
182             // the endpoints must correspond exactly
183             // moreover, the subdivision point must be exactly equal to valueAt(t)
184             EXPECT_DOUBLE_EQ(result.first.at0(), input.at0());
185             EXPECT_DOUBLE_EQ(result.first.at1(), result.second.at0());
186             EXPECT_DOUBLE_EQ(result.second.at0(), input.valueAt(t));
187             EXPECT_DOUBLE_EQ(result.second.at1(), input.at1());
188 
189             // ditto for valueAt
190             EXPECT_DOUBLE_EQ(result.first.valueAt(0), input.valueAt(0));
191             EXPECT_DOUBLE_EQ(result.first.valueAt(1), result.second.valueAt(0));
192             EXPECT_DOUBLE_EQ(result.second.valueAt(0), input.valueAt(t));
193             EXPECT_DOUBLE_EQ(result.second.valueAt(1), input.valueAt(1));
194 
195             if (result.first.at1() != result.second.at0()) {
196                 errors.emplace_back(input, t);
197             }
198         }
199     }
200     if (!errors.empty()) {
201         std::cout << "Found " << errors.size() << " subdivision errors" << std::endl;
202         for (unsigned i = 0; i < errors.size(); ++i) {
203             std::cout << "Error #" << i << ":\n"
204                           << errors[i].first << "\n"
205                           << "t: " << format_coord_nice(errors[i].second) << std::endl;
206         }
207     }
208 }
209 
TEST_F(BezierTest,Mutation)210 TEST_F(BezierTest, Mutation) {
211 //Coord &operator[](unsigned ix);
212 //Coord const &operator[](unsigned ix);
213 //void setCoeff(unsigned ix double val);
214     //cout << "bigun\n";
215     Bezier bigun(Bezier::Order(30));
216     bigun.setCoeff(5,10.0);
217     for(unsigned i = 0; i < bigun.size(); i++) {
218         EXPECT_EQ((i == 5) ? 10 : 0, bigun[i]);
219     }
220 
221     bigun[5] = -3;
222     for(unsigned i = 0; i < bigun.size(); i++) {
223         EXPECT_EQ((i == 5) ? -3 : 0, bigun[i]);
224     }
225 }
226 
TEST_F(BezierTest,MultiDerivative)227 TEST_F(BezierTest, MultiDerivative) {
228     vector<double> vnd = wiggle.valueAndDerivatives(0.5, 5);
229     expect_array((const double[]){0,0,12,72,0,0}, vnd);
230 }
231 
TEST_F(BezierTest,DegreeElevation)232 TEST_F(BezierTest, DegreeElevation) {
233     EXPECT_TRUE(are_equal(wiggle, wiggle));
234     Bezier Q = wiggle;
235     Bezier P = Q.elevate_degree();
236     EXPECT_EQ(P.size(), Q.size()+1);
237     //EXPECT_EQ(0, P.forward_difference(1)[0]);
238     EXPECT_TRUE(are_equal(Q, P));
239     Q = wiggle;
240     P = Q.elevate_to_degree(10);
241     EXPECT_EQ(10u, P.order());
242     EXPECT_TRUE(are_equal(Q, P));
243     //EXPECT_EQ(0, P.forward_difference(10)[0]);
244     /*Q = wiggle.elevate_degree();
245     P = Q.reduce_degree();
246     EXPECT_EQ(P.size()+1, Q.size());
247     EXPECT_TRUE(are_equal(Q, P));*/
248 }
249 //std::pair<Bezier, Bezier > subdivide(Coord t);
250 
251 // Constructs a linear Bezier with root at t
linear_root(double t)252 Bezier linear_root(double t) {
253     return Bezier(0-t, 1-t);
254 }
255 
256 // Constructs a Bezier with roots at the locations in x
array_roots(vector<double> x)257 Bezier array_roots(vector<double> x) {
258     Bezier b(1);
259     for(double i : x) {
260         b = multiply(b, linear_root(i));
261     }
262     return b;
263 }
264 
TEST_F(BezierTest,Deflate)265 TEST_F(BezierTest, Deflate) {
266     Bezier b = array_roots(vector_from_array((const double[]){0,0.25,0.5}));
267     EXPECT_FLOAT_EQ(0, b.at0());
268     b = b.deflate();
269     EXPECT_FLOAT_EQ(0, b.valueAt(0.25));
270     b = b.subdivide(0.25).second;
271     EXPECT_FLOAT_EQ(0, b.at0());
272     b = b.deflate();
273     const double rootposition = (0.5-0.25) / (1-0.25);
274     constexpr Coord eps{1e-12};
275     EXPECT_near(0.0, b.valueAt(rootposition), eps);
276     b = b.subdivide(rootposition).second;
277     EXPECT_near(0.0, b.at0(), eps);
278 }
279 
TEST_F(BezierTest,Roots)280 TEST_F(BezierTest, Roots) {
281     expect_array((const double[]){0, 0.5, 0.5}, wiggle.roots());
282 
283     /*Bezier bigun(Bezier::Order(30));
284     for(unsigned i = 0; i < bigun.size(); i++) {
285         bigun.setCoeff(i,rand()-0.5);
286     }
287     cout << bigun.roots() << endl;*/
288 
289     // The results of our rootfinding are at the moment fairly inaccurate.
290     double eps = 5e-4;
291 
292     vector<vector<double> > tests;
293     tests.push_back(vector_from_array((const double[]){0}));
294     tests.push_back(vector_from_array((const double[]){1}));
295     tests.push_back(vector_from_array((const double[]){0, 0}));
296     tests.push_back(vector_from_array((const double[]){0.5}));
297     tests.push_back(vector_from_array((const double[]){0.5, 0.5}));
298     tests.push_back(vector_from_array((const double[]){0.1, 0.1}));
299     tests.push_back(vector_from_array((const double[]){0.1, 0.1, 0.1}));
300     tests.push_back(vector_from_array((const double[]){0.25,0.75}));
301     tests.push_back(vector_from_array((const double[]){0.5,0.5}));
302     tests.push_back(vector_from_array((const double[]){0, 0.2, 0.6, 0.6, 1}));
303     tests.push_back(vector_from_array((const double[]){.1,.2,.3,.4,.5,.6}));
304     tests.push_back(vector_from_array((const double[]){0.25,0.25,0.25,0.75,0.75,0.75}));
305 
306     for(auto & test : tests) {
307         Bezier b = array_roots(test);
308         //std::cout << tests[test_i] << ": " << b << std::endl;
309         //std::cout << b.roots() << std::endl;
310         EXPECT_vector_near(test, b.roots(), eps);
311     }
312 }
313 
TEST_F(BezierTest,BoundsExact)314 TEST_F(BezierTest, BoundsExact) {
315     OptInterval unit_bounds = bounds_exact(unit);
316     EXPECT_EQ(unit_bounds->min(), 0);
317     EXPECT_EQ(unit_bounds->max(), 1);
318 
319     OptInterval hump_bounds = bounds_exact(hump);
320     EXPECT_EQ(hump_bounds->min(), 0);
321     EXPECT_FLOAT_EQ(hump_bounds->max(), hump.valueAt(0.5));
322 
323     OptInterval wiggle_bounds = bounds_exact(wiggle);
324     EXPECT_EQ(wiggle_bounds->min(), 0);
325     EXPECT_EQ(wiggle_bounds->max(), 3);
326 }
327 
TEST_F(BezierTest,Operators)328 TEST_F(BezierTest, Operators) {
329     /*cout << "scalar operators\n";
330     cout << hump + 3 << endl;
331     cout << hump - 3 << endl;
332     cout << hump*3 << endl;
333     cout << hump/3 << endl;*/
334 
335     Bezier reverse_wiggle = reverse(wiggle);
336     EXPECT_EQ(reverse_wiggle.at0(), wiggle.at1());
337     EXPECT_EQ(reverse_wiggle.at1(), wiggle.at0());
338     EXPECT_TRUE(are_equal(reverse(reverse_wiggle), wiggle));
339 
340     //cout << "Bezier portion(const Bezier & a, double from, double to);\n";
341     //cout << portion(Bezier(0.0,2.0), 0.5, 1) << endl;
342 
343 // std::vector<Point> bezier_points(const D2<Bezier > & a) {
344 
345     /*cout << "Bezier derivative(const Bezier & a);\n";
346     std::cout << derivative(hump) <<std::endl;
347     std::cout << integral(hump) <<std::endl;*/
348 
349     EXPECT_TRUE(are_equal(derivative(integral(wiggle)), wiggle));
350     //std::cout << derivative(integral(hump)) <<std::endl;
351     expect_array((const double []){0.5}, derivative(hump).roots());
352 
353     EXPECT_TRUE(bounds_fast(hump)->contains(Interval(0,hump.valueAt(0.5))));
354 
355     EXPECT_EQ(Interval(0,hump.valueAt(0.5)), *bounds_exact(hump));
356 
357     Interval tight_local_bounds(min(hump.valueAt(0.3),hump.valueAt(0.6)),
358              hump.valueAt(0.5));
359     EXPECT_TRUE(bounds_local(hump, Interval(0.3, 0.6))->contains(tight_local_bounds));
360 
361     Bezier Bs[] = {unit, hump, wiggle};
362     for(auto B : Bs) {
363         Bezier product = multiply(B, B);
364         for(int i = 0; i <= 16; i++) {
365             double t = i/16.0;
366             double b = B.valueAt(t);
367             EXPECT_near(b*b, product.valueAt(t), 1e-12);
368         }
369     }
370 }
371 
372 struct XPt {
XPtXPt373     XPt(Coord x, Coord y, Coord ta, Coord tb)
374         : p(x, y), ta(ta), tb(tb)
375     {}
XPtXPt376     XPt() {}
377     Point p;
378     Coord ta, tb;
379 };
380 
381 struct XTest {
382     D2<Bezier> a;
383     D2<Bezier> b;
384     std::vector<XPt> s;
385 };
386 
387 struct CILess {
operator ()CILess388     bool operator()(CurveIntersection const &a, CurveIntersection const &b) const {
389         if (a.first < b.first) return true;
390         if (a.first == b.first && a.second < b.second) return true;
391         return false;
392     }
393 };
394 
TEST_F(BezierTest,Intersection)395 TEST_F(BezierTest, Intersection) {
396     /* Intersection test cases taken from:
397      * Dieter Lasser (1988), Calculating the Self-Intersections of Bezier Curves
398      * https://archive.org/stream/calculatingselfi00lass
399      *
400      * The intersection points are not actually calculated to a high precision
401      * in the paper. The most relevant tests are whether the curves actually
402      * intersect at the returned time values (i.e. whether a(ta) = b(tb))
403      * and whether the number of intersections is correct.
404      */
405     typedef D2<Bezier> D2Bez;
406     std::vector<XTest> tests;
407 
408     // Example 1
409     tests.emplace_back();
410     tests.back().a = D2Bez(Bezier(-3.3, -3.3, 0, 3.3, 3.3), Bezier(1.3, -0.7, 2.3, -0.7, 1.3));
411     tests.back().b = D2Bez(Bezier(-4.0, -4.0, 0, 4.0, 4.0), Bezier(-0.35, 3.0, -2.6, 3.0, -0.35));
412     tests.back().s.resize(4);
413     tests.back().s[0] = XPt(-3.12109, 0.76362, 0.09834, 0.20604);
414     tests.back().s[1] = XPt(-1.67341, 0.60298, 0.32366, 0.35662);
415     tests.back().s[2] = XPt(1.67341, 0.60298, 0.67634, 0.64338);
416     tests.back().s[3] = XPt(3.12109, 0.76362, 0.90166, 0.79396);
417 
418     // Example 2
419     tests.emplace_back();
420     tests.back().a = D2Bez(Bezier(0, 0, 3, 3), Bezier(0, 14, -9, 5));
421     tests.back().b = D2Bez(Bezier(-1, 13, -10, 4), Bezier(4, 4, 1, 1));
422     tests.back().s.resize(9);
423     tests.back().s[0] = XPt(0.00809, 1.17249, 0.03029, 0.85430);
424     tests.back().s[1] = XPt(0.02596, 1.97778, 0.05471, 0.61825);
425     tests.back().s[2] = XPt(0.17250, 3.99191, 0.14570, 0.03029);
426     tests.back().s[3] = XPt(0.97778, 3.97404, 0.38175, 0.05471);
427     tests.back().s[4] = XPt(1.5, 2.5, 0.5, 0.5);
428     tests.back().s[5] = XPt(2.02221, 1.02596, 0.61825, 0.94529);
429     tests.back().s[6] = XPt(2.82750, 1.00809, 0.85430, 0.96971);
430     tests.back().s[7] = XPt(2.97404, 3.02221, 0.94529, 0.38175);
431     tests.back().s[8] = XPt(2.99191, 3.82750, 0.96971, 0.14570);
432 
433     // Example 3
434     tests.emplace_back();
435     tests.back().a = D2Bez(Bezier(-5, -5, -3, 0, 3, 5, 5), Bezier(0, 3.555, -1, 4.17, -1, 3.555, 0));
436     tests.back().b = D2Bez(Bezier(-6, -6, -3, 0, 3, 6, 6), Bezier(3, -0.555, 4, -1.17, 4, -0.555, 3));
437     tests.back().s.resize(6);
438     tests.back().s[0] = XPt(-3.64353, 1.49822, 0.23120, 0.27305);
439     tests.back().s[1] = XPt(-2.92393, 1.50086, 0.29330, 0.32148);
440     tests.back().s[2] = XPt(-0.77325, 1.49989, 0.44827, 0.45409);
441     tests.back().s[3] = XPt(0.77325, 1.49989, 0.55173, 0.54591);
442     tests.back().s[4] = XPt(2.92393, 1.50086, 0.70670, 0.67852);
443     tests.back().s[5] = XPt(3.64353, 1.49822, 0.76880, 0.72695);
444 
445     // Example 4
446     tests.emplace_back();
447     tests.back().a = D2Bez(Bezier(-4, -10, -2, -2, 2, 2, 10, 4), Bezier(0, 6, 6, 0, 0, 6, 6, 0));
448     tests.back().b = D2Bez(Bezier(-8, 0, 8), Bezier(1, 6, 1));
449     tests.back().s.resize(4);
450     tests.back().s[0] = XPt(-5.69310, 2.23393, 0.06613, 0.14418);
451     tests.back().s[1] = XPt(-2.68113, 3.21920, 0.35152, 0.33243);
452     tests.back().s[2] = XPt(2.68113, 3.21920, 0.64848, 0.66757);
453     tests.back().s[3] = XPt(5.69310, 2.23393, 0.93387, 0.85582);
454 
455     //std::cout << std::setprecision(5);
456 
457     for (unsigned i = 0; i < tests.size(); ++i) {
458         BezierCurve a(tests[i].a), b(tests[i].b);
459         std::vector<CurveIntersection> xs;
460         xs = a.intersect(b, 1e-8);
461         std::sort(xs.begin(), xs.end(), CILess());
462         //xs.erase(std::unique(xs.begin(), xs.end(), XEqual()), xs.end());
463 
464         std::cout << "\n\n"
465                   << "===============================\n"
466                   << "=== Intersection Testcase " << i+1 << " ===\n"
467                   << "===============================\n" << std::endl;
468 
469         EXPECT_EQ(xs.size(), tests[i].s.size());
470         //if (xs.size() != tests[i].s.size()) continue;
471 
472         for (unsigned j = 0; j < std::min(xs.size(), tests[i].s.size()); ++j) {
473             std::cout << xs[j].first << " = " << a.pointAt(xs[j].first) << "   "
474                       << xs[j].second << " = " << b.pointAt(xs[j].second) << "\n"
475                       << tests[i].s[j].ta << " = " << tests[i].a.valueAt(tests[i].s[j].ta) << "   "
476                       << tests[i].s[j].tb << " = " << tests[i].b.valueAt(tests[i].s[j].tb) << std::endl;
477         }
478 
479         EXPECT_intersections_valid(a, b, xs, 1e-6);
480     }
481 
482     #if 0
483     // these contain second-order intersections
484     Coord a5x[] = {-1.5, -1.5, -10, -10, 0, 10, 10, 1.5, 1.5};
485     Coord a5y[] = {0, -8, -8, 9, 9, 9, -8, -8, 0};
486     Coord b5x[] = {-3, -12, 0, 12, 3};
487     Coord b5y[] = {-5, 8, 2.062507, 8, -5};
488     Coord p5x[] = {-3.60359, -5.44653, 0, 5.44653, 3.60359};
489     Coord p5y[] = {-4.10631, -0.76332, 4.14844, -0.76332, -4.10631};
490     Coord p5ta[] = {0.01787, 0.10171, 0.5, 0.89829, 0.98213};
491     Coord p5tb[] = {0.12443, 0.28110, 0.5, 0.71890, 0.87557};
492 
493     Coord a6x[] = {5, 14, 10, -12, -12, -2};
494     Coord a6y[] = {1, 6, -6, -6, 2, 2};
495     Coord b6x[] = {0, 2, -10.5, -10.5, 3.5, 3, 8, 6};
496     Coord b6y[] = {0, -8, -8, 9, 9, -4.129807, -4.129807, 3};
497     Coord p6x[] = {6.29966, 5.87601, 0.04246, -4.67397, -3.57214};
498     Coord p6y[] = {1.63288, -0.86192, -2.38219, -2.17973, 1.91463};
499     Coord p6ta[] = {0.03184, 0.33990, 0.49353, 0.62148, 0.96618};
500     Coord p6tb[] = {0.96977, 0.85797, 0.05087, 0.28232, 0.46102};
501     #endif
502 }
503 
504 /*
505   Local Variables:
506   mode:c++
507   c-file-style:"stroustrup"
508   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
509   indent-tabs-mode:nil
510   fill-column:99
511   End:
512 */
513 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
514