Commits

Barry Schwartz committed 056da50

Mainly checking in some big macros that show how I am approaching "inheritance" in the wrappers/stubs code. But actually one could do some bezier curves work with what's already there, I think.

  • Participants
  • Parent commits 772c000

Comments (0)

Files changed (4)

File caml2geom.ml

   external div_by : t -> Coord.t -> unit = "point_div_by_wrapper"
 end
 
+module Interval =
+struct
+  type t
+  external default : unit -> t = "new_default_interval_wrapper"
+end
+
+module Rect =
+struct
+  type t
+  external default : unit -> t = "new_default_rect_wrapper"
+end
+
 module Bezier =
 struct
   type t
   val point_at : t -> Coord.t -> Point.t
   val value_at : t -> Coord.t -> Coord.dim2 -> Coord.t
   val point_and_derivatives : t -> Coord.t -> int -> Point.t array
+  val set_initial : t -> Point.t -> unit
+  val set_final : t -> Point.t -> unit
+  val portion : t -> Coord.t -> Coord.t -> t
+  val reverse : t -> t
+  val derivative : t -> t
+  val length : t -> Coord.t -> Coord.t
+  val degrees_of_freedom : t -> int
 end
 
 module Bezier_curve =
   external of_four_points : Point.t -> Point.t -> Point.t -> Point.t -> t = "bezier_curve_of_four_points_wrapper"
   external initial_point : t -> Point.t = "bezier_curve_initial_point_wrapper"
   external final_point : t -> Point.t = "bezier_curve_final_point_wrapper"
+  external is_degenerate : t -> bool = "bezier_curve_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "bezier_curve_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "bezier_curve_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "bezier_curve_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "bezier_curve_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "bezier_curve_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "bezier_curve_portion_wrapper"
+  external reverse : t -> t = "bezier_curve_reverse_wrapper"
+  external derivative : t -> t = "bezier_curve_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "bezier_curve_length_wrapper"
+  external degrees_of_freedom : t -> int = "bezier_curve_degrees_of_freedom_wrapper"
 end
+
+module Bezier_curve_as_curve : Curve = Bezier_curve

File caml2geom.mli

   external div_by : t -> Coord.t -> unit = "point_div_by_wrapper"
 end
 
+module Interval :
+sig
+  type t
+  external default : unit -> t = "new_default_interval_wrapper"
+end
+
+module Rect :
+sig
+  type t
+  external default : unit -> t = "new_default_rect_wrapper"
+end
+
 module Bezier :
 sig
   type t
   val point_at : t -> Coord.t -> Point.t
   val value_at : t -> Coord.t -> Coord.dim2 -> Coord.t
   val point_and_derivatives : t -> Coord.t -> int -> Point.t array
+  val set_initial : t -> Point.t -> unit
+  val set_final : t -> Point.t -> unit
+  val portion : t -> Coord.t -> Coord.t -> t
+  val reverse : t -> t
+  val derivative : t -> t
+  val length : t -> Coord.t -> Coord.t
+  val degrees_of_freedom : t -> int
 end
 
 module Bezier_curve :
   external of_four_points : Point.t -> Point.t -> Point.t -> Point.t -> t = "bezier_curve_of_four_points_wrapper"
   external initial_point : t -> Point.t = "bezier_curve_initial_point_wrapper"
   external final_point : t -> Point.t = "bezier_curve_final_point_wrapper"
+  external is_degenerate : t -> bool = "bezier_curve_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "bezier_curve_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "bezier_curve_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "bezier_curve_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "bezier_curve_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "bezier_curve_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "bezier_curve_portion_wrapper"
+  external reverse : t -> t = "bezier_curve_reverse_wrapper"
+  external derivative : t -> t = "bezier_curve_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "bezier_curve_length_wrapper"
+  external degrees_of_freedom : t -> int = "bezier_curve_degrees_of_freedom_wrapper"
 end
+
+module Bezier_curve_as_curve : Curve

File caml2geom_stubs.c

 #include <2geom/bezier.h>
 #include <2geom/bezier-curve.h>
 #include <2geom/coord.h>
+#include <2geom/interval.h>
 #include <2geom/point.h>
+#include <2geom/rect.h>
 #include <2geom/transforms.h>
 
 extern "C" {
-#include <string.h>
+#include <stdio.h>
 #include <caml/alloc.h>
 #include <caml/custom.h>
 #include <caml/fail.h>
 #define OPAQUE_P(t, name) (*(t**)Data_custom_val((name)))
 #define OPAQUE_P_ALLOC(t, ops) caml_alloc_custom(&(ops), sizeof(t*), 0, 1);
 
+#define CUSTOM_OPS(t, name)                             \
+    void finalize_##t(value _v)                         \
+    {                                                   \
+        delete OPAQUE_P(Geom::t, _v);                   \
+    }                                                   \
+    static struct custom_operations name = {            \
+        (char *) "Lib2geom." #t,                        \
+        finalize_##t,                                   \
+        custom_compare_default,                         \
+        custom_hash_default,                            \
+        custom_serialize_default,                       \
+        custom_deserialize_default                      \
+    };
+
+// We know that Geom::X == 0 and Geom::Y == 1, but let's ignore that
+// fact and write our code this safer way that avoids casting int to
+// Geom::Dim2.
+Geom::Dim2 dim2[2] = { Geom::X, Geom::Y };
+
 inline Geom::Translate *translate_from_ml_floats(value _x, value _y)
 {
     return new Geom::Translate(Double_val(_x), Double_val(_y));
 }
 
-void finalize_translate(value _v)
-{
-    delete OPAQUE_P(Geom::Translate, _v);
-}
-
-static struct custom_operations translate_ops = {
-    (char *) "lib2geom.Translate",
-    finalize_translate,
-    custom_compare_default,
-    custom_hash_default,
-    custom_serialize_default,
-    custom_deserialize_default
-};
-
 inline Geom::Point *point_from_ml_floats(value _x, value _y)
 {
     return new Geom::Point(Double_val(_x), Double_val(_y));
 }
 
-void finalize_point(value _v)
-{
-    delete OPAQUE_P(Geom::Point, _v);
-}
+CUSTOM_OPS(Translate, translate_ops);
+CUSTOM_OPS(Point, point_ops);
+CUSTOM_OPS(Interval, interval_ops);
+CUSTOM_OPS(Rect, rect_ops);
+CUSTOM_OPS(Bezier, bezier_ops);
+CUSTOM_OPS(BezierCurve, bezier_curve_ops);
 
-static struct custom_operations point_ops = {
-    (char *) "lib2geom.Point",
-    finalize_point,
-    custom_compare_default,
-    custom_hash_default,
-    custom_serialize_default,
-    custom_deserialize_default
-};
+//-------------------------------------------------------------------------
 
-void finalize_bezier(value _v)
-{
-    delete OPAQUE_P(Geom::Bezier, _v);
-}
+#define fragment_getval_wrapper(t, name, func, typeconv)                \
+    extern "C" CAMLprim value name(value _v)                            \
+    {                                                                   \
+        CAMLparam1(_v);                                                 \
+        CAMLreturn(typeconv(OPAQUE_P(t, _v)->func()));                  \
+    }
 
-static struct custom_operations bezier_ops = {
-    (char *) "lib2geom.Bezier",
-    finalize_bezier,
-    custom_compare_default,
-    custom_hash_default,
-    custom_serialize_default,
-    custom_deserialize_default
-};
+#define fragment_wrappers(t, name)                                      \
+                                                                        \
+    extern "C" CAMLprim value new_default_##name##_wrapper(void)        \
+    {                                                                   \
+        CAMLparam0();                                                   \
+        CAMLlocal1(_v);                                                 \
+        _v = OPAQUE_P_ALLOC(t, name##_ops);                             \
+        OPAQUE_P(t, _v) = new t();                                      \
+        CAMLreturn(_v);                                                 \
+    }                                                                   \
+                                                                        \
+    fragment_getval_wrapper(t, name##_is_zero_wrapper, isZero, Val_bool); \
+    fragment_getval_wrapper(t, name##_is_constant_wrapper, isConstant, Val_bool); \
+    fragment_getval_wrapper(t, name##_is_finite_wrapper, isFinite, Val_bool); \
+    fragment_getval_wrapper(t, name##_at0_wrapper, at0, caml_copy_double); \
+    fragment_getval_wrapper(t, name##_at1_wrapper, at1, caml_copy_double); \
+                                                                        \
+    extern "C" CAMLprim value name##_value_at_wrapper(value _v, value _s) \
+    {                                                                   \
+        CAMLparam2(_v, _s);                                             \
+        CAMLreturn(caml_copy_double(OPAQUE_P(t, _v)->valueAt(Double_val(_s)))); \
+    }
 
-void finalize_bezier_curve(value _v)
-{
-    delete OPAQUE_P(Geom::BezierCurve, _v);
-}
+//-------------------------------------------------------------------------
 
-static struct custom_operations bezier_curve_ops = {
-    (char *) "lib2geom.Bezier_curve",
-    finalize_bezier_curve,
-    custom_compare_default,
-    custom_hash_default,
-    custom_serialize_default,
-    custom_deserialize_default
-};
+#define curve_wrappers(t, name)                                         \
+                                                                        \
+    extern "C" CAMLprim value name##_initial_point_wrapper(value _curve) \
+    {                                                                   \
+        CAMLparam1(_curve);                                             \
+        CAMLlocal1(_p);                                                 \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        Geom::Point pt = curve->initialPoint();                         \
+        _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);                    \
+        OPAQUE_P(Geom::Point, _p) = new Geom::Point(pt);                \
+        CAMLreturn(_p);                                                 \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_final_point_wrapper(value _curve)  \
+    {                                                                   \
+        CAMLparam1(_curve);                                             \
+        CAMLlocal1(_p);                                                 \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        Geom::Point pt = curve->finalPoint();                           \
+        _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);                    \
+        OPAQUE_P(Geom::Point, _p) = new Geom::Point(pt);                \
+        CAMLreturn(_p);                                                 \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_is_degenerate_wrapper(value _curve) \
+    {                                                                   \
+        CAMLparam1(_curve);                                             \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        CAMLreturn(Val_bool(curve->isDegenerate()));                    \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_point_at_wrapper(value _curve, value _t) \
+    {                                                                   \
+        CAMLparam2(_curve, _t);                                         \
+        CAMLlocal1(_p);                                                 \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        Geom::Point pt = curve->pointAt(Double_val(_t));                \
+        _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);                    \
+        OPAQUE_P(Geom::Point, _p) = new Geom::Point(pt);                \
+        CAMLreturn(_p);                                                 \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_value_at_wrapper(value _curve, value _t, value _dim2) \
+    {                                                                   \
+        CAMLparam3(_curve, _dim2, _t);                                  \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        CAMLreturn(caml_copy_double(curve->valueAt(Double_val(_t), dim2[Int_val(_dim2)]))); \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_point_and_derivatives_wrapper(value _curve, value _t, value _n) \
+    {                                                                   \
+        CAMLparam3(_curve, _t, _n);                                     \
+        CAMLlocal2(_p_array, _p);                                       \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        double s = Double_val(_t);                                      \
+        int n = Int_val(_n);                                            \
+        std::vector<Geom::Point> p = curve->pointAndDerivatives(s, n);  \
+        _p_array = caml_alloc(p.size(), 0);                             \
+        for (unsigned int i = 0; i < p.size(); i++) {                   \
+            _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);                \
+            OPAQUE_P(Geom::Point, _p) = new Geom::Point(p[i]);          \
+            Store_field(_p_array, i, _p);                               \
+        }                                                               \
+        CAMLreturn(_p_array);                                           \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_set_initial_wrapper(value _curve, value _p) \
+    {                                                                   \
+        CAMLparam2(_curve, _p);                                         \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        curve->setInitial(*OPAQUE_P(Geom::Point, _p));                  \
+        CAMLreturn(Val_unit);                                           \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_set_final_wrapper(value _curve, value _p) \
+    {                                                                   \
+        CAMLparam2(_curve, _p);                                         \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        curve->setFinal(*OPAQUE_P(Geom::Point, _p));                    \
+        CAMLreturn(Val_unit);                                           \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_portion_wrapper(value _curve, value _a, value _b) \
+    {                                                                   \
+        CAMLparam3(_curve, _a, _b);                                     \
+        CAMLlocal1(_reverse);                                           \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        _reverse = OPAQUE_P_ALLOC(t, name##_ops);                       \
+        OPAQUE_P(t, _reverse) =                                         \
+            (t*)curve->portion(Double_val(_a), Double_val(_b));         \
+        CAMLreturn(_reverse);                                           \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_reverse_wrapper(value _curve)      \
+    {                                                                   \
+        CAMLparam1(_curve);                                             \
+        CAMLlocal1(_reverse);                                           \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        _reverse = OPAQUE_P_ALLOC(t, name##_ops);                       \
+        OPAQUE_P(t, _reverse) = (t*)curve->reverse();                   \
+        CAMLreturn(_reverse);                                           \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_derivative_wrapper(value _curve)   \
+    {                                                                   \
+        CAMLparam1(_curve);                                             \
+        CAMLlocal1(_derivative);                                        \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        _derivative = OPAQUE_P_ALLOC(t, name##_ops);                    \
+        OPAQUE_P(t, _derivative) = (t*)curve->derivative();             \
+        CAMLreturn(_derivative);                                        \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_length_wrapper(value _curve, value _tolerance) \
+    {                                                                   \
+        CAMLparam2(_curve, _tolerance);                                 \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        CAMLreturn(caml_copy_double(curve->length(Double_val(_tolerance)))); \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_degrees_of_freedom_wrapper(value _curve) \
+    {                                                                   \
+        CAMLparam1(_curve);                                             \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        CAMLreturn(Val_int(curve->degreesOfFreedom()));                 \
+    }
 
 //-------------------------------------------------------------------------
 
     extern "C" CAMLprim value name(value _p, value _other)              \
     {                                                                   \
         CAMLparam2(_p, _other);                                         \
-        OPAQUE_P(Geom::Point, _p)->op(*OPAQUE_P(Geom::Point, _other)); \
+        OPAQUE_P(Geom::Point, _p)->op(*OPAQUE_P(Geom::Point, _other));  \
         CAMLreturn(Val_unit);                                           \
     }
 
     extern "C" CAMLprim value name(value _p, value _other)              \
     {                                                                   \
         CAMLparam2(_p, _other);                                         \
-        OPAQUE_P(Geom::Point, _p)->op(Double_val(_other));       \
+        OPAQUE_P(Geom::Point, _p)->op(Double_val(_other));              \
         CAMLreturn(Val_unit);                                           \
     }
 
 
 //-------------------------------------------------------------------------
 
-extern "C" CAMLprim value new_default_bezier_wrapper(void)
+extern "C" CAMLprim value new_default_interval_wrapper(void)
 {
     CAMLparam0();
-    CAMLlocal1(_bez);
-    _bez = OPAQUE_P_ALLOC(Geom::Bezier, bezier_ops);
-    OPAQUE_P(Geom::Bezier, _bez) = new Geom::Bezier();
-    CAMLreturn(_bez);
+    CAMLlocal1(_interval);
+    _interval = OPAQUE_P_ALLOC(Geom::Interval, interval_ops);
+    OPAQUE_P(Geom::Interval, _interval) = new Geom::Interval();
+    CAMLreturn(_interval);
 }
 
+//-------------------------------------------------------------------------
+
+extern "C" CAMLprim value new_default_rect_wrapper(void)
+{
+    CAMLparam0();
+    CAMLlocal1(_rect);
+    _rect = OPAQUE_P_ALLOC(Geom::Rect, rect_ops);
+    OPAQUE_P(Geom::Rect, _rect) = new Geom::Rect();
+    CAMLreturn(_rect);
+}
+
+//-------------------------------------------------------------------------
+
+fragment_wrappers(Geom::Bezier, bezier);
+
 extern "C" CAMLprim value new_bezier_order0_wrapper(value _c0)
 {
     CAMLparam1(_c0);
     CAMLreturn(_bez);
 }
 
-#define bezier_getval_wrapper(name, func, typeconv)                     \
-    extern "C" CAMLprim value name(value _bez)                          \
-    {                                                                   \
-        CAMLparam1(_bez);                                               \
-        CAMLreturn(typeconv(OPAQUE_P(Geom::Bezier, _bez)->func())); \
-    }
+//-------------------------------------------------------------------------
 
-bezier_getval_wrapper(bezier_is_zero_wrapper, isZero, Val_bool);
-bezier_getval_wrapper(bezier_is_constant_wrapper, isConstant, Val_bool);
-bezier_getval_wrapper(bezier_is_finite_wrapper, isFinite, Val_bool);
-bezier_getval_wrapper(bezier_at0_wrapper, at0, caml_copy_double);
-bezier_getval_wrapper(bezier_at1_wrapper, at1, caml_copy_double);
-
-extern "C" CAMLprim value bezier_value_at_wrapper(value _bez, value _t)
-{
-    CAMLparam2(_bez, _t);
-    CAMLreturn(caml_copy_double(OPAQUE_P(Geom::Bezier, _bez)->valueAt(Double_val(_t))));
-}
-
-//-------------------------------------------------------------------------
+curve_wrappers(Geom::BezierCurve, bezier_curve);
 
 extern "C" CAMLprim value bezier_curve_of_two_beziers_wrapper(value _bez0, value _bez1)
 {
     CAMLreturn(_bezcurve);
 }
 
-#include <stdio.h>
 extern "C" CAMLprim value bezier_curve_of_four_points_wrapper(value _p0, value _p1, value _p2, value _p3)
 {
     CAMLparam4(_p0, _p1, _p2, _p3);
     CAMLreturn(_bezcurve);
 }
 
-extern "C" CAMLprim value bezier_curve_initial_point_wrapper(value _bezcurve)
-{
-    CAMLparam1(_bezcurve);
-    CAMLlocal1(_p);
-    Geom::BezierCurve *bc = OPAQUE_P(Geom::BezierCurve, _bezcurve);
-    Geom::Point pt = bc->initialPoint();
-    _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);
-    OPAQUE_P(Geom::Point, _p) = new Geom::Point(pt);
-    CAMLreturn(_p);
-}
-
-#include <stdio.h>
-extern "C" CAMLprim value bezier_curve_final_point_wrapper(value _bezcurve)
-{
-    CAMLparam1(_bezcurve);
-    CAMLlocal1(_p);
-    Geom::BezierCurve *bc = OPAQUE_P(Geom::BezierCurve, _bezcurve);
-    Geom::Point pt = bc->finalPoint();
-    _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);
-    OPAQUE_P(Geom::Point, _p) = new Geom::Point(pt);
-    CAMLreturn(_p);
-}
-
 //-------------------------------------------------------------------------
 
 (*-----------------------------------------------------------------------*)
 
-let bezcurve =
+let bc1 =
   Bezier_curve.of_four_points (Point.make (-1.) (-3.)) (Point.make 10. 10.) (Point.make 15. 5.) (Point.make 20. 1.) ;;
 let _ =
-  printf "initial_point = (%f,%f)\n" (Point.coord (Bezier_curve.initial_point bezcurve) 0)
-    (Point.coord (Bezier_curve.initial_point bezcurve) 1) ;;
+  printf "initial_point = (%f,%f)\n" (Point.coord (Bezier_curve.initial_point bc1) 0)
+    (Point.coord (Bezier_curve.initial_point bc1) 1) ;;
 let _ =
-  printf "final_point = (%f,%f)\n" (Point.coord (Bezier_curve.final_point bezcurve) 0)
-    (Point.coord (Bezier_curve.final_point bezcurve) 1) ;;
+  printf "final_point = (%f,%f)\n" (Point.coord (Bezier_curve.final_point bc1) 0)
+    (Point.coord (Bezier_curve.final_point bc1) 1) ;;
+let _ =
+  printf "point_at 0.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc1 0.0) 0)
+    (Point.coord (Bezier_curve.point_at bc1 0.0) 1) ;;
+let _ =
+  printf "point_at 0.4 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc1 0.4) 0)
+    (Point.coord (Bezier_curve.point_at bc1 0.4) 1) ;;
+let _ =
+  printf "point_at 1.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc1 1.0) 0)
+    (Point.coord (Bezier_curve.point_at bc1 1.0) 1) ;;
+let _ =
+  printf "value_at 0.0 = (%f,%f)\n" (Bezier_curve.value_at bc1 0.0 Coord.X)
+    (Bezier_curve.value_at bc1 0.0 Coord.Y) ;;
+let _ =
+  printf "value_at 0.4 = (%f,%f)\n" (Bezier_curve.value_at bc1 0.4 Coord.X)
+    (Bezier_curve.value_at bc1 0.4 Coord.Y) ;;
+let _ =
+  printf "value_at 1.0 = (%f,%f)\n" (Bezier_curve.value_at bc1 1.0 Coord.X)
+    (Bezier_curve.value_at bc1 1.0 Coord.Y) ;;
+let _ =
+  printf "value_at 0.0 = (%f,%f)\n" (Bezier_curve.value_at (Bezier_curve.reverse bc1) 0.0 Coord.X)
+    (Bezier_curve.value_at (Bezier_curve.reverse bc1) 0.0 Coord.Y) ;;
+let _ =
+  printf "value_at 0.4 = (%f,%f)\n" (Bezier_curve.value_at (Bezier_curve.reverse bc1) 0.4 Coord.X)
+    (Bezier_curve.value_at (Bezier_curve.reverse bc1) 0.4 Coord.Y) ;;
+let _ =
+  printf "value_at 1.0 = (%f,%f)\n" (Bezier_curve.value_at (Bezier_curve.reverse bc1) 1.0 Coord.X)
+    (Bezier_curve.value_at (Bezier_curve.reverse bc1) 1.0 Coord.Y) ;;
+let _ =
+  printf "point_at 0.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at (Bezier_curve.portion bc1 0. 0.4) 0.0) 0)
+    (Point.coord (Bezier_curve.point_at (Bezier_curve.portion bc1 0. 0.4) 0.0) 1) ;;
+let _ =
+  printf "point_at 0.4 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at (Bezier_curve.portion bc1 0. 0.4) 0.4) 0)
+    (Point.coord (Bezier_curve.point_at (Bezier_curve.portion bc1 0. 0.4) 0.4) 1) ;;
+let _ =
+  printf "point_at 1.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at (Bezier_curve.portion bc1 0. 0.4) 1.0) 0)
+    (Point.coord (Bezier_curve.point_at (Bezier_curve.portion bc1 0. 0.4) 1.0) 1) ;;
+let _ =
+  printf "value_at 0.0 = (%f,%f)\n" (Bezier_curve.value_at (Bezier_curve.derivative bc1) 0.0 Coord.X)
+    (Bezier_curve.value_at (Bezier_curve.derivative bc1) 0.0 Coord.Y) ;;
+let _ =
+  printf "value_at 0.4 = (%f,%f)\n" (Bezier_curve.value_at (Bezier_curve.derivative bc1) 0.4 Coord.X)
+    (Bezier_curve.value_at (Bezier_curve.derivative bc1) 0.4 Coord.Y) ;;
+let _ =
+  printf "value_at 1.0 = (%f,%f)\n" (Bezier_curve.value_at (Bezier_curve.derivative bc1) 1.0 Coord.X)
+    (Bezier_curve.value_at (Bezier_curve.derivative bc1) 1.0 Coord.Y) ;;
+let bc2 =
+  Bezier_curve.of_four_points (Point.make (1.) (1.)) (Point.make 10. 10.) (Point.make 15. 15.) (Point.make 20. 20.) ;;
+let bc3 = Bezier_curve.of_two_points (Point.make (1.) (1.)) (Point.make 10. 10.) ;;
+let bc4 =
+  Bezier_curve.of_four_points (Point.make (1.) (1.)) (Point.make 1. 1.) (Point.make 1. 1.) (Point.make 1. 1.) ;;
+let _ = printf "bc1 is_degenerate = %B\n" (Bezier_curve.is_degenerate bc1) ;;
+let _ = printf "bc2 is_degenerate = %B\n" (Bezier_curve.is_degenerate bc2) ;;
+let _ = printf "bc3 is_degenerate = %B\n" (Bezier_curve.is_degenerate bc3) ;;
+let _ = printf "bc4 is_degenerate = %B\n" (Bezier_curve.is_degenerate bc4) ;;
+let p_array = Bezier_curve.point_and_derivatives bc1 1.0 3 ;;
+Array.iteri (fun i p -> printf "deriv %d (%f,%f)\n" i (Point.coord p 0) (Point.coord p 1)) p_array ;;
+let p_array = Bezier_curve.point_and_derivatives bc2 1.0 3 ;;
+Array.iteri (fun i p -> printf "deriv %d (%f,%f)\n" i (Point.coord p 0) (Point.coord p 1)) p_array ;;
+let p_array = Bezier_curve.point_and_derivatives bc3 1.0 3 ;;
+Array.iteri (fun i p -> printf "deriv %d (%f,%f)\n" i (Point.coord p 0) (Point.coord p 1)) p_array ;;
+let p_array = Bezier_curve.point_and_derivatives bc4 1.0 3 ;;
+Array.iteri (fun i p -> printf "deriv %d (%f,%f)\n" i (Point.coord p 0) (Point.coord p 1)) p_array ;;
+let _ = printf "bc1 length = %f\n" (Bezier_curve.length bc1 0.01) ;;
+let _ = printf "bc2 length = %f\n" (Bezier_curve.length bc2 0.01) ;;
+let _ = printf "bc3 length = %f\n" (Bezier_curve.length bc3 0.01) ;;
+let _ = printf "bc4 length = %f\n" (Bezier_curve.length bc4 0.01) ;;
+let p1 = Point.make (-400.) (-200.) ;;
+Bezier_curve.set_initial bc4 p1 ;;
+let _ = printf "point_at 0.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc4 0.0) 0)
+  (Point.coord (Bezier_curve.point_at bc4 0.0) 1) ;;
+Point.set_coord p1 1 200. ;;
+let _ = printf "point_at 0.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc4 0.0) 0)
+  (Point.coord (Bezier_curve.point_at bc4 0.0) 1) ;;
+Bezier_curve.set_initial bc4 p1 ;;
+let _ = printf "point_at 0.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc4 0.0) 0)
+  (Point.coord (Bezier_curve.point_at bc4 0.0) 1) ;;
+let p1 = Point.make (500.) (300.) ;;
+Bezier_curve.set_final bc4 p1 ;;
+let _ = printf "point_at 1.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc4 1.0) 0)
+  (Point.coord (Bezier_curve.point_at bc4 1.0) 1) ;;
+Point.set_coord p1 1 (-300.) ;;
+let _ = printf "point_at 1.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc4 1.0) 0)
+  (Point.coord (Bezier_curve.point_at bc4 1.0) 1) ;;
+Bezier_curve.set_final bc4 p1 ;;
+let _ = printf "point_at 1.0 = (%f,%f)\n" (Point.coord (Bezier_curve.point_at bc4 1.0) 0)
+  (Point.coord (Bezier_curve.point_at bc4 1.0) 1) ;;
+
+let _ = printf "bc1 degrees_of_freedom = %d\n" (Bezier_curve.degrees_of_freedom bc1) ;;
+let _ = printf "bc2 degrees_of_freedom = %d\n" (Bezier_curve.degrees_of_freedom bc2) ;;
+let _ = printf "bc3 degrees_of_freedom = %d\n" (Bezier_curve.degrees_of_freedom bc3) ;;
+let _ = printf "bc4 degrees_of_freedom = %d\n" (Bezier_curve.degrees_of_freedom bc4) ;;
 
 (*-----------------------------------------------------------------------*)