Code

Patch from codedread. Prevents rendering of title/desc/metadata elements in text...
[inkscape.git] / src / display / bezier-utils-test.h
index efed5ea4ba9cac0bd2c7ef6660578d622db3e80b..ec980810b66230ebcf51edd071b5b8338b769b81 100644 (file)
-#include <cxxtest/TestSuite.h>\r
-\r
-#include <glib.h>\r
-#include <libnr/nr-macros.h> /* NR_DF_TEST_CLOSE */\r
-#include <sstream>\r
-\r
-/* mental disclaims all responsibility for this evil idea for testing\r
-   static functions.  The main disadvantages are that we retain the\r
-   #define's and `using' directives of the included file. */\r
-#include "bezier-utils.cpp"\r
-\r
-using NR::Point;\r
-\r
-/* (Returns false if NaN encountered.) */\r
-static bool range_approx_equal(double const a[], double const b[], unsigned const len) {\r
-    for (unsigned i = 0; i < len; ++i) {\r
-        if (!( fabs( a[i] - b[i] ) < 1e-4 )) {\r
-            return false;\r
-        }\r
-    }\r
-    return true;\r
-}\r
-\r
-static inline bool point_approx_equal(NR::Point const &a, NR::Point const &b, double const eps)\r
-{\r
-    using NR::X; using NR::Y;\r
-    return ( NR_DF_TEST_CLOSE(a[X], b[X], eps) &&\r
-             NR_DF_TEST_CLOSE(a[Y], b[Y], eps) );\r
-}\r
-\r
-static inline double square(double const x) {\r
-    return x * x;\r
-}\r
-\r
-/** Determine whether the found control points are the same as previously found on some developer's\r
-    machine.  Doesn't call utest__fail, just writes a message to stdout for diagnostic purposes:\r
-    the most important test is that the root-mean-square of errors in the estimation are low rather\r
-    than that the control points found are the same.\r
-**/\r
-static void compare_ctlpts(Point const est_b[], Point const exp_est_b[])\r
-{\r
-    unsigned diff_mask = 0;\r
-    for (unsigned i = 0; i < 4; ++i) {\r
-        for (unsigned d = 0; d < 2; ++d) {\r
-            if ( fabs( est_b[i][d] - exp_est_b[i][d] ) > 1.1e-5 ) {\r
-                diff_mask |= 1 << ( i * 2 + d );\r
-            }\r
-        }\r
-    }\r
-    if ( diff_mask != 0 ) {\r
-        std::stringstream msg;\r
-        msg << "Got different control points from previously-coded (diffs=0x" << std::hex << diff_mask << "\n";\r
-        msg << " Previous:";\r
-        for (unsigned i = 0; i < 4; ++i) {\r
-            msg << " (" << exp_est_b[i][0] << ", " << exp_est_b[i][1] << ")"; // localizing ok\r
-        }\r
-        msg << "\n";\r
-        msg << " Found:   ";\r
-        for (unsigned i = 0; i < 4; ++i) {\r
-            msg << " (" << est_b[i][0] << ", " << est_b[i][1] << ")"; // localizing ok\r
-        }\r
-        msg << "\n";\r
-        TS_WARN(msg.str().c_str());\r
-    }\r
-}\r
-\r
-static void compare_rms(Point const est_b[], double const t[], Point const d[], unsigned const n,\r
-                        double const exp_rms_error)\r
-{\r
-    double sum_errsq = 0.0;\r
-    for (unsigned i = 0; i < n; ++i) {\r
-        Point const fit_pt = bezier_pt(3, est_b, t[i]);\r
-        Point const diff = fit_pt - d[i];\r
-        sum_errsq += dot(diff, diff);\r
-    }\r
-    double const rms_error = sqrt( sum_errsq / n );\r
-    TS_ASSERT_LESS_THAN_EQUALS( rms_error , exp_rms_error + 1.1e-6 );\r
-    if ( rms_error < exp_rms_error - 1.1e-6 ) {\r
-        /* The fitter code appears to have improved [or the floating point calculations differ\r
-           on this machine from the machine where exp_rms_error was calculated]. */\r
-        char msg[200];\r
-        sprintf(msg, "N.B. rms_error regression requirement can be decreased: have rms_error=%g.", rms_error); // localizing ok\r
-        TS_TRACE(msg);\r
-    }\r
-}\r
-\r
-class BezierUtilsTest : public CxxTest::TestSuite {\r
-public:\r
-    static Point const c[4];\r
-    static double const t[24];\r
-    static unsigned const n;\r
-    Point d[24];\r
-    static Point const src_b[4];\r
-    static Point const tHat1;\r
-    static Point const tHat2;\r
-\r
-    BezierUtilsTest()\r
-    {\r
-        /* Feed it some points that can be fit exactly with a single bezier segment, and see how\r
-        well it manages. */\r
-        for (unsigned i = 0; i < n; ++i) {\r
-            d[i] = bezier_pt(3, src_b, t[i]);\r
-        }\r
-    }\r
-    virtual ~BezierUtilsTest() {}\r
-\r
-    void testCopyWithoutNansOrAdjacentDuplicates()\r
-    {\r
-        NR::Point const src[] = {\r
-            Point(2., 3.),\r
-            Point(2., 3.),\r
-            Point(0., 0.),\r
-            Point(2., 3.),\r
-            Point(2., 3.),\r
-            Point(1., 9.),\r
-            Point(1., 9.)\r
-        };\r
-        Point const exp_dest[] = {\r
-            Point(2., 3.),\r
-            Point(0., 0.),\r
-            Point(2., 3.),\r
-            Point(1., 9.)\r
-        };\r
-        g_assert( G_N_ELEMENTS(src) == 7 );\r
-        Point dest[7];\r
-        struct tst {\r
-            unsigned src_ix0;\r
-            unsigned src_len;\r
-            unsigned exp_dest_ix0;\r
-            unsigned exp_dest_len;\r
-        } const test_data[] = {\r
-            /* src start ix, src len, exp_dest start ix, exp dest len */\r
-            {0, 0, 0, 0},\r
-            {2, 1, 1, 1},\r
-            {0, 1, 0, 1},\r
-            {0, 2, 0, 1},\r
-            {0, 3, 0, 2},\r
-            {1, 3, 0, 3},\r
-            {0, 5, 0, 3},\r
-            {0, 6, 0, 4},\r
-            {0, 7, 0, 4}\r
-        };\r
-        for (unsigned i = 0 ; i < G_N_ELEMENTS(test_data) ; ++i) {\r
-            tst const &t = test_data[i];\r
-            TS_ASSERT_EQUALS( t.exp_dest_len,\r
-                              copy_without_nans_or_adjacent_duplicates(src + t.src_ix0,\r
-                                                                       t.src_len,\r
-                                                                       dest) );\r
-            TS_ASSERT_SAME_DATA(dest,\r
-                                exp_dest + t.exp_dest_ix0,\r
-                                t.exp_dest_len);\r
-        }\r
-    }\r
-\r
-    void testBezierPt1()\r
-    {\r
-        Point const a[] = {Point(2.0, 4.0),\r
-                           Point(1.0, 8.0)};\r
-        TS_ASSERT_EQUALS( bezier_pt(1, a, 0.0) , a[0] );\r
-        TS_ASSERT_EQUALS( bezier_pt(1, a, 1.0) , a[1] );\r
-        TS_ASSERT_EQUALS( bezier_pt(1, a, 0.5) , Point(1.5, 6.0) );\r
-        double const t[] = {0.5, 0.25, 0.3, 0.6};\r
-        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {\r
-            double const ti = t[i], si = 1.0 - ti;\r
-            TS_ASSERT_EQUALS( bezier_pt(1, a, ti) , si * a[0] + ti * a[1] );\r
-        }\r
-    }\r
-\r
-    void testBezierPt2()\r
-    {\r
-        Point const b[] = {Point(1.0, 2.0),\r
-                           Point(8.0, 4.0),\r
-                           Point(3.0, 1.0)};\r
-        TS_ASSERT_EQUALS( bezier_pt(2, b, 0.0) , b[0] );\r
-        TS_ASSERT_EQUALS( bezier_pt(2, b, 1.0) , b[2] );\r
-        TS_ASSERT_EQUALS( bezier_pt(2, b, 0.5) , Point(5.0, 2.75) );\r
-        double const t[] = {0.5, 0.25, 0.3, 0.6};\r
-        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {\r
-            double const ti = t[i], si = 1.0 - ti;\r
-            Point const exp_pt( si*si * b[0] + 2*si*ti * b[1] + ti*ti * b[2] );\r
-            Point const pt(bezier_pt(2, b, ti));\r
-            TS_ASSERT(point_approx_equal(pt, exp_pt, 1e-11));\r
-        }\r
-    }\r
-\r
-    void testBezierPt3()\r
-    {\r
-        TS_ASSERT_EQUALS( bezier_pt(3, c, 0.0) , c[0] );\r
-        TS_ASSERT_EQUALS( bezier_pt(3, c, 1.0) , c[3] );\r
-        TS_ASSERT_EQUALS( bezier_pt(3, c, 0.5) , Point(4.0, 13.0/8.0) );\r
-        double const t[] = {0.5, 0.25, 0.3, 0.6};\r
-        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {\r
-            double const ti = t[i], si = 1.0 - ti;\r
-            TS_ASSERT( LInfty( bezier_pt(3, c, ti)\r
-                                  - ( si*si*si * c[0] +\r
-                                      3*si*si*ti * c[1] +\r
-                                      3*si*ti*ti * c[2] +\r
-                                      ti*ti*ti * c[3] ) )\r
-                          < 1e-4 );\r
-        }\r
-    }\r
-\r
-    void testComputeMaxErrorRatio()\r
-    {\r
-        struct Err_tst {\r
-            Point pt;\r
-            double u;\r
-            double err;\r
-        } const err_tst[] = {\r
-            {c[0], 0.0, 0.0},\r
-            {Point(4.0, 13.0/8.0), 0.5, 0.0},\r
-            {Point(4.0, 2.0), 0.5, 9.0/64.0},\r
-            {Point(3.0, 2.0), 0.5, 1.0 + 9.0/64.0},\r
-            {Point(6.0, 2.0), 0.5, 4.0 + 9.0/64.0},\r
-            {c[3], 1.0, 0.0},\r
-        };\r
-        Point d[G_N_ELEMENTS(err_tst)];\r
-        double u[G_N_ELEMENTS(err_tst)];\r
-        for (unsigned i = 0; i < G_N_ELEMENTS(err_tst); ++i) {\r
-            Err_tst const &t = err_tst[i];\r
-            d[i] = t.pt;\r
-            u[i] = t.u;\r
-        }\r
-        g_assert( G_N_ELEMENTS(u) == G_N_ELEMENTS(d) );\r
-        unsigned max_ix = ~0u;\r
-        double const err_ratio = compute_max_error_ratio(d, u, G_N_ELEMENTS(d), c, 1.0, &max_ix);\r
-        TS_ASSERT_LESS_THAN( fabs( sqrt(err_tst[4].err) - err_ratio ) , 1e-12 );\r
-        TS_ASSERT_EQUALS( max_ix , 4 );\r
-    }\r
-\r
-    void testChordLengthParameterize()\r
-    {\r
-        /* n == 2 */\r
-        {\r
-            Point const d[] = {Point(2.9415, -5.8149),\r
-                               Point(23.021, 4.9814)};\r
-            double u[G_N_ELEMENTS(d)];\r
-            double const exp_u[] = {0.0, 1.0};\r
-            g_assert( G_N_ELEMENTS(u) == G_N_ELEMENTS(exp_u) );\r
-            chord_length_parameterize(d, u, G_N_ELEMENTS(d));\r
-            TS_ASSERT_SAME_DATA(u, exp_u, G_N_ELEMENTS(exp_u));\r
-        }\r
-\r
-        /* Straight line. */\r
-        {\r
-            double const exp_u[] = {0.0, 0.1829, 0.2105, 0.2105, 0.619, 0.815, 0.999, 1.0};\r
-            unsigned const n = G_N_ELEMENTS(exp_u);\r
-            Point d[n];\r
-            double u[n];\r
-            Point const a(-23.985, 4.915), b(4.9127, 5.203);\r
-            for (unsigned i = 0; i < n; ++i) {\r
-                double bi = exp_u[i], ai = 1.0 - bi;\r
-                d[i] = ai * a  +  bi * b;\r
-            }\r
-            chord_length_parameterize(d, u, n);\r
-            TS_ASSERT(range_approx_equal(u, exp_u, n));\r
-        }\r
-    }\r
-\r
-    void testGenerateBezier()\r
-    {\r
-        Point est_b[4];\r
-        generate_bezier(est_b, d, t, n, tHat1, tHat2, 1.0);\r
-\r
-        compare_ctlpts(est_b, src_b);\r
-\r
-        /* We're being unfair here in using our t[] rather than best t[] for est_b: we\r
-           may over-estimate RMS of errors. */\r
-        compare_rms(est_b, t, d, n, 1e-8);\r
-    }\r
-\r
-    void testSpBezierFitCubicFull()\r
-    {\r
-        Point est_b[4];\r
-        int splitpoints[2];\r
-        gint const succ = sp_bezier_fit_cubic_full(est_b, splitpoints, d, n, tHat1, tHat2, square(1.2), 1);\r
-        TS_ASSERT_EQUALS( succ , 1 );\r
-\r
-        Point const exp_est_b[4] = {\r
-            Point(5.000000, -3.000000),\r
-            Point(7.5753, -0.4247),\r
-            Point(4.77533, 1.22467),\r
-            Point(3, 3)\r
-        };\r
-        compare_ctlpts(est_b, exp_est_b);\r
-\r
-        /* We're being unfair here in using our t[] rather than best t[] for est_b: we\r
-           may over-estimate RMS of errors. */\r
-        compare_rms(est_b, t, d, n, .307911);\r
-    }\r
-\r
-    void testSpBezierFitCubic()\r
-    {\r
-        Point est_b[4];\r
-        gint const succ = sp_bezier_fit_cubic(est_b, d, n, square(1.2));\r
-        TS_ASSERT_EQUALS( succ , 1 );\r
-\r
-        Point const exp_est_b[4] = {\r
-            Point(5.000000, -3.000000),\r
-            Point(7.57134, -0.423509),\r
-            Point(4.77929, 1.22426),\r
-            Point(3, 3)\r
-        };\r
-        compare_ctlpts(est_b, exp_est_b);\r
-\r
-#if 1 /* A change has been made to right_tangent.  I believe that usually this change\r
-         will result in better fitting, but it won't do as well for this example where\r
-         we happen to be feeding a t=0.999 point to the fitter. */\r
-        TS_WARN("TODO: Update this test case for revised right_tangent implementation.");\r
-        /* In particular, have a test case to show whether the new implementation\r
-           really is likely to be better on average. */\r
-#else\r
-        /* We're being unfair here in using our t[] rather than best t[] for est_b: we\r
-           may over-estimate RMS of errors. */\r
-        compare_rms(est_b, t, d, n, .307983);\r
-#endif\r
-    }\r
-};\r
-\r
-// This is not very neat, but since we know this header is only included by the generated CxxTest file it shouldn't give any problems\r
-Point const BezierUtilsTest::c[4] = {\r
-    Point(1.0, 2.0),\r
-    Point(8.0, 4.0),\r
-    Point(3.0, 1.0),\r
-    Point(-2.0, -4.0)};\r
-double const BezierUtilsTest::t[24] = {\r
-    0.0, .001, .03, .05, .09, .13, .18, .25, .29, .33,  .39, .44,\r
-    .51,  .57, .62, .69, .75, .81, .91, .93, .97, .98, .999, 1.0};\r
-unsigned const BezierUtilsTest::n = G_N_ELEMENTS(BezierUtilsTest::t);\r
-Point const BezierUtilsTest::src_b[4] = {\r
-    Point(5., -3.),\r
-    Point(8., 0.),\r
-    Point(4., 2.),\r
-    Point(3., 3.)};\r
-Point const BezierUtilsTest::tHat1(unit_vector( BezierUtilsTest::src_b[1] - BezierUtilsTest::src_b[0] ));\r
-Point const BezierUtilsTest::tHat2(unit_vector( BezierUtilsTest::src_b[2] - BezierUtilsTest::src_b[3] ));\r
-\r
-/*\r
-  Local Variables:\r
-  mode:c++\r
-  c-file-style:"stroustrup"\r
-  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
-  indent-tabs-mode:nil\r
-  fill-column:99\r
-  End:\r
-*/\r
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
+#include <cxxtest/TestSuite.h>
+
+#include <glib.h>
+#include <libnr/nr-macros.h> /* NR_DF_TEST_CLOSE */
+#include <sstream>
+
+/* mental disclaims all responsibility for this evil idea for testing
+   static functions.  The main disadvantages are that we retain the
+   #define's and `using' directives of the included file. */
+#include "bezier-utils.cpp"
+
+using NR::Point;
+
+/* (Returns false if NaN encountered.) */
+static bool range_approx_equal(double const a[], double const b[], unsigned const len) {
+    for (unsigned i = 0; i < len; ++i) {
+        if (!( fabs( a[i] - b[i] ) < 1e-4 )) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static inline bool point_approx_equal(NR::Point const &a, NR::Point const &b, double const eps)
+{
+    using NR::X; using NR::Y;
+    return ( NR_DF_TEST_CLOSE(a[X], b[X], eps) &&
+             NR_DF_TEST_CLOSE(a[Y], b[Y], eps) );
+}
+
+static inline double square(double const x) {
+    return x * x;
+}
+
+/** Determine whether the found control points are the same as previously found on some developer's
+    machine.  Doesn't call utest__fail, just writes a message to stdout for diagnostic purposes:
+    the most important test is that the root-mean-square of errors in the estimation are low rather
+    than that the control points found are the same.
+**/
+static void compare_ctlpts(Point const est_b[], Point const exp_est_b[])
+{
+    unsigned diff_mask = 0;
+    for (unsigned i = 0; i < 4; ++i) {
+        for (unsigned d = 0; d < 2; ++d) {
+            if ( fabs( est_b[i][d] - exp_est_b[i][d] ) > 1.1e-5 ) {
+                diff_mask |= 1 << ( i * 2 + d );
+            }
+        }
+    }
+    if ( diff_mask != 0 ) {
+        std::stringstream msg;
+        msg << "Got different control points from previously-coded (diffs=0x" << std::hex << diff_mask << "\n";
+        msg << " Previous:";
+        for (unsigned i = 0; i < 4; ++i) {
+            msg << " (" << exp_est_b[i][0] << ", " << exp_est_b[i][1] << ")"; // localizing ok
+        }
+        msg << "\n";
+        msg << " Found:   ";
+        for (unsigned i = 0; i < 4; ++i) {
+            msg << " (" << est_b[i][0] << ", " << est_b[i][1] << ")"; // localizing ok
+        }
+        msg << "\n";
+        TS_WARN(msg.str().c_str());
+    }
+}
+
+static void compare_rms(Point const est_b[], double const t[], Point const d[], unsigned const n,
+                        double const exp_rms_error)
+{
+    double sum_errsq = 0.0;
+    for (unsigned i = 0; i < n; ++i) {
+        Point const fit_pt = bezier_pt(3, est_b, t[i]);
+        Point const diff = fit_pt - d[i];
+        sum_errsq += dot(diff, diff);
+    }
+    double const rms_error = sqrt( sum_errsq / n );
+    TS_ASSERT_LESS_THAN_EQUALS( rms_error , exp_rms_error + 1.1e-6 );
+    if ( rms_error < exp_rms_error - 1.1e-6 ) {
+        /* The fitter code appears to have improved [or the floating point calculations differ
+           on this machine from the machine where exp_rms_error was calculated]. */
+        char msg[200];
+        sprintf(msg, "N.B. rms_error regression requirement can be decreased: have rms_error=%g.", rms_error); // localizing ok
+        TS_TRACE(msg);
+    }
+}
+
+class BezierUtilsTest : public CxxTest::TestSuite {
+public:
+    static Point const c[4];
+    static double const t[24];
+    static unsigned const n;
+    Point d[24];
+    static Point const src_b[4];
+    static Point const tHat1;
+    static Point const tHat2;
+
+    BezierUtilsTest()
+    {
+        /* Feed it some points that can be fit exactly with a single bezier segment, and see how
+        well it manages. */
+        for (unsigned i = 0; i < n; ++i) {
+            d[i] = bezier_pt(3, src_b, t[i]);
+        }
+    }
+    virtual ~BezierUtilsTest() {}
+
+    void testCopyWithoutNansOrAdjacentDuplicates()
+    {
+        NR::Point const src[] = {
+            Point(2., 3.),
+            Point(2., 3.),
+            Point(0., 0.),
+            Point(2., 3.),
+            Point(2., 3.),
+            Point(1., 9.),
+            Point(1., 9.)
+        };
+        Point const exp_dest[] = {
+            Point(2., 3.),
+            Point(0., 0.),
+            Point(2., 3.),
+            Point(1., 9.)
+        };
+        g_assert( G_N_ELEMENTS(src) == 7 );
+        Point dest[7];
+        struct tst {
+            unsigned src_ix0;
+            unsigned src_len;
+            unsigned exp_dest_ix0;
+            unsigned exp_dest_len;
+        } const test_data[] = {
+            /* src start ix, src len, exp_dest start ix, exp dest len */
+            {0, 0, 0, 0},
+            {2, 1, 1, 1},
+            {0, 1, 0, 1},
+            {0, 2, 0, 1},
+            {0, 3, 0, 2},
+            {1, 3, 0, 3},
+            {0, 5, 0, 3},
+            {0, 6, 0, 4},
+            {0, 7, 0, 4}
+        };
+        for (unsigned i = 0 ; i < G_N_ELEMENTS(test_data) ; ++i) {
+            tst const &t = test_data[i];
+            TS_ASSERT_EQUALS( t.exp_dest_len,
+                              copy_without_nans_or_adjacent_duplicates(src + t.src_ix0,
+                                                                       t.src_len,
+                                                                       dest) );
+            TS_ASSERT_SAME_DATA(dest,
+                                exp_dest + t.exp_dest_ix0,
+                                t.exp_dest_len);
+        }
+    }
+
+    void testBezierPt1()
+    {
+        Point const a[] = {Point(2.0, 4.0),
+                           Point(1.0, 8.0)};
+        TS_ASSERT_EQUALS( bezier_pt(1, a, 0.0) , a[0] );
+        TS_ASSERT_EQUALS( bezier_pt(1, a, 1.0) , a[1] );
+        TS_ASSERT_EQUALS( bezier_pt(1, a, 0.5) , Point(1.5, 6.0) );
+        double const t[] = {0.5, 0.25, 0.3, 0.6};
+        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {
+            double const ti = t[i], si = 1.0 - ti;
+            TS_ASSERT_EQUALS( bezier_pt(1, a, ti) , si * a[0] + ti * a[1] );
+        }
+    }
+
+    void testBezierPt2()
+    {
+        Point const b[] = {Point(1.0, 2.0),
+                           Point(8.0, 4.0),
+                           Point(3.0, 1.0)};
+        TS_ASSERT_EQUALS( bezier_pt(2, b, 0.0) , b[0] );
+        TS_ASSERT_EQUALS( bezier_pt(2, b, 1.0) , b[2] );
+        TS_ASSERT_EQUALS( bezier_pt(2, b, 0.5) , Point(5.0, 2.75) );
+        double const t[] = {0.5, 0.25, 0.3, 0.6};
+        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {
+            double const ti = t[i], si = 1.0 - ti;
+            Point const exp_pt( si*si * b[0] + 2*si*ti * b[1] + ti*ti * b[2] );
+            Point const pt(bezier_pt(2, b, ti));
+            TS_ASSERT(point_approx_equal(pt, exp_pt, 1e-11));
+        }
+    }
+
+    void testBezierPt3()
+    {
+        TS_ASSERT_EQUALS( bezier_pt(3, c, 0.0) , c[0] );
+        TS_ASSERT_EQUALS( bezier_pt(3, c, 1.0) , c[3] );
+        TS_ASSERT_EQUALS( bezier_pt(3, c, 0.5) , Point(4.0, 13.0/8.0) );
+        double const t[] = {0.5, 0.25, 0.3, 0.6};
+        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {
+            double const ti = t[i], si = 1.0 - ti;
+            TS_ASSERT( LInfty( bezier_pt(3, c, ti)
+                                  - ( si*si*si * c[0] +
+                                      3*si*si*ti * c[1] +
+                                      3*si*ti*ti * c[2] +
+                                      ti*ti*ti * c[3] ) )
+                          < 1e-4 );
+        }
+    }
+
+    void testComputeMaxErrorRatio()
+    {
+        struct Err_tst {
+            Point pt;
+            double u;
+            double err;
+        } const err_tst[] = {
+            {c[0], 0.0, 0.0},
+            {Point(4.0, 13.0/8.0), 0.5, 0.0},
+            {Point(4.0, 2.0), 0.5, 9.0/64.0},
+            {Point(3.0, 2.0), 0.5, 1.0 + 9.0/64.0},
+            {Point(6.0, 2.0), 0.5, 4.0 + 9.0/64.0},
+            {c[3], 1.0, 0.0},
+        };
+        Point d[G_N_ELEMENTS(err_tst)];
+        double u[G_N_ELEMENTS(err_tst)];
+        for (unsigned i = 0; i < G_N_ELEMENTS(err_tst); ++i) {
+            Err_tst const &t = err_tst[i];
+            d[i] = t.pt;
+            u[i] = t.u;
+        }
+        g_assert( G_N_ELEMENTS(u) == G_N_ELEMENTS(d) );
+        unsigned max_ix = ~0u;
+        double const err_ratio = compute_max_error_ratio(d, u, G_N_ELEMENTS(d), c, 1.0, &max_ix);
+        TS_ASSERT_LESS_THAN( fabs( sqrt(err_tst[4].err) - err_ratio ) , 1e-12 );
+        TS_ASSERT_EQUALS( max_ix , 4 );
+    }
+
+    void testChordLengthParameterize()
+    {
+        /* n == 2 */
+        {
+            Point const d[] = {Point(2.9415, -5.8149),
+                               Point(23.021, 4.9814)};
+            double u[G_N_ELEMENTS(d)];
+            double const exp_u[] = {0.0, 1.0};
+            g_assert( G_N_ELEMENTS(u) == G_N_ELEMENTS(exp_u) );
+            chord_length_parameterize(d, u, G_N_ELEMENTS(d));
+            TS_ASSERT_SAME_DATA(u, exp_u, G_N_ELEMENTS(exp_u));
+        }
+
+        /* Straight line. */
+        {
+            double const exp_u[] = {0.0, 0.1829, 0.2105, 0.2105, 0.619, 0.815, 0.999, 1.0};
+            unsigned const n = G_N_ELEMENTS(exp_u);
+            Point d[n];
+            double u[n];
+            Point const a(-23.985, 4.915), b(4.9127, 5.203);
+            for (unsigned i = 0; i < n; ++i) {
+                double bi = exp_u[i], ai = 1.0 - bi;
+                d[i] = ai * a  +  bi * b;
+            }
+            chord_length_parameterize(d, u, n);
+            TS_ASSERT(range_approx_equal(u, exp_u, n));
+        }
+    }
+
+    void testGenerateBezier()
+    {
+        Point est_b[4];
+        generate_bezier(est_b, d, t, n, tHat1, tHat2, 1.0);
+
+        compare_ctlpts(est_b, src_b);
+
+        /* We're being unfair here in using our t[] rather than best t[] for est_b: we
+           may over-estimate RMS of errors. */
+        compare_rms(est_b, t, d, n, 1e-8);
+    }
+
+    void testSpBezierFitCubicFull()
+    {
+        Point est_b[4];
+        int splitpoints[2];
+        gint const succ = sp_bezier_fit_cubic_full(est_b, splitpoints, d, n, tHat1, tHat2, square(1.2), 1);
+        TS_ASSERT_EQUALS( succ , 1 );
+
+        Point const exp_est_b[4] = {
+            Point(5.000000, -3.000000),
+            Point(7.5753, -0.4247),
+            Point(4.77533, 1.22467),
+            Point(3, 3)
+        };
+        compare_ctlpts(est_b, exp_est_b);
+
+        /* We're being unfair here in using our t[] rather than best t[] for est_b: we
+           may over-estimate RMS of errors. */
+        compare_rms(est_b, t, d, n, .307911);
+    }
+
+    void testSpBezierFitCubic()
+    {
+        Point est_b[4];
+        gint const succ = sp_bezier_fit_cubic(est_b, d, n, square(1.2));
+        TS_ASSERT_EQUALS( succ , 1 );
+
+        Point const exp_est_b[4] = {
+            Point(5.000000, -3.000000),
+            Point(7.57134, -0.423509),
+            Point(4.77929, 1.22426),
+            Point(3, 3)
+        };
+        compare_ctlpts(est_b, exp_est_b);
+
+#if 1 /* A change has been made to right_tangent.  I believe that usually this change
+         will result in better fitting, but it won't do as well for this example where
+         we happen to be feeding a t=0.999 point to the fitter. */
+        TS_WARN("TODO: Update this test case for revised right_tangent implementation.");
+        /* In particular, have a test case to show whether the new implementation
+           really is likely to be better on average. */
+#else
+        /* We're being unfair here in using our t[] rather than best t[] for est_b: we
+           may over-estimate RMS of errors. */
+        compare_rms(est_b, t, d, n, .307983);
+#endif
+    }
+};
+
+// This is not very neat, but since we know this header is only included by the generated CxxTest file it shouldn't give any problems
+Point const BezierUtilsTest::c[4] = {
+    Point(1.0, 2.0),
+    Point(8.0, 4.0),
+    Point(3.0, 1.0),
+    Point(-2.0, -4.0)};
+double const BezierUtilsTest::t[24] = {
+    0.0, .001, .03, .05, .09, .13, .18, .25, .29, .33,  .39, .44,
+    .51,  .57, .62, .69, .75, .81, .91, .93, .97, .98, .999, 1.0};
+unsigned const BezierUtilsTest::n = G_N_ELEMENTS(BezierUtilsTest::t);
+Point const BezierUtilsTest::src_b[4] = {
+    Point(5., -3.),
+    Point(8., 0.),
+    Point(4., 2.),
+    Point(3., 3.)};
+Point const BezierUtilsTest::tHat1(unit_vector( BezierUtilsTest::src_b[1] - BezierUtilsTest::src_b[0] ));
+Point const BezierUtilsTest::tHat2(unit_vector( BezierUtilsTest::src_b[2] - BezierUtilsTest::src_b[3] ));
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :