Commits

Barry Schwartz committed 769b1de

We are up to basic support for Path.

Comments (0)

Files changed (4)

 
 module Bezier_as_fragment : Fragment = Bezier
 
-module type Curve =
+let general_unit_tangent_at
+    default_tangent_func tangent_func reverse_func
+    ?num_derivs curve time =
+  let rec inner curve1 time1 =
+    if time1 = 1.0 then
+      (* The documented workaround for a bug in current 2geom code,
+         04-April-2011.  See 2geom/curve.h. *)
+      let curve_reversed = reverse_func curve1 in
+      Point.neg (inner curve_reversed 0.0)
+    else
+      match num_derivs with
+        | None -> default_tangent_func curve1 time1
+        | Some n -> tangent_func curve1 time1 n
+  in
+  inner curve time
+
+module type Curve_type =
 sig
   type t
   val initial_point : t -> Point.t
   val reverse : t -> t
   val derivative : t -> t
   val length : t -> Coord.t -> Coord.t
+  val winding : t -> Point.t -> int
+  val unit_tangent_at : ?num_derivs:int -> t -> Coord.t -> Point.t
   val degrees_of_freedom : t -> int
 end
 
+module Curve : Curve_type =
+struct
+  type t
+  external initial_point : t -> Point.t = "curve_initial_point_wrapper"
+  external final_point : t -> Point.t = "curve_final_point_wrapper"
+  external is_degenerate : t -> bool = "curve_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "curve_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "curve_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "curve_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "curve_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "curve_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "curve_portion_wrapper"
+  external reverse : t -> t = "curve_reverse_wrapper"
+  external derivative : t -> t = "curve_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "curve_length_wrapper"
+  external winding : t -> Point.t -> int = "curve_winding_wrapper"
+  external degrees_of_freedom : t -> int = "curve_degrees_of_freedom_wrapper"
+
+  external _unit_tangent_at : t -> Coord.t -> int -> Point.t = "curve_unit_tangent_at_wrapper"
+  external _default_unit_tangent_at : t -> Coord.t -> Point.t = "curve_default_unit_tangent_at_wrapper"
+  let unit_tangent_at = general_unit_tangent_at _default_unit_tangent_at _unit_tangent_at reverse
+end
+
 module Bezier_curve =
 struct
-  type t
+  type t = Curve.t
+  external to_curve : t -> Curve.t = "%identity"
   external of_two_beziers : Bezier.t -> Bezier.t -> t = "bezier_curve_of_two_beziers_wrapper"
   external of_two_points : Point.t -> Point.t -> t = "bezier_curve_of_two_points_wrapper"
   external of_three_points : Point.t -> Point.t -> Point.t -> t = "bezier_curve_of_three_points_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 winding : t -> Point.t -> int = "bezier_curve_winding_wrapper"
   external degrees_of_freedom : t -> int = "bezier_curve_degrees_of_freedom_wrapper"
+
+  external _unit_tangent_at : t -> Coord.t -> int -> Point.t = "bezier_curve_unit_tangent_at_wrapper"
+  external _default_unit_tangent_at : t -> Coord.t -> Point.t = "bezier_curve_default_unit_tangent_at_wrapper"
+  let unit_tangent_at = general_unit_tangent_at _default_unit_tangent_at _unit_tangent_at reverse
 end
 
-module Bezier_curve_as_curve : Curve = Bezier_curve
+module Line_segment =
+struct
+  type t
+  external to_curve : t -> Curve.t = "%identity"
+  external initial_point : t -> Point.t = "line_segment_initial_point_wrapper"
+  external final_point : t -> Point.t = "line_segment_final_point_wrapper"
+  external is_degenerate : t -> bool = "line_segment_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "line_segment_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "line_segment_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "line_segment_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "line_segment_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "line_segment_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "line_segment_portion_wrapper"
+  external reverse : t -> t = "line_segment_reverse_wrapper"
+  external derivative : t -> t = "line_segment_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "line_segment_length_wrapper"
+  external winding : t -> Point.t -> int = "line_segment_winding_wrapper"
+  external degrees_of_freedom : t -> int = "line_segment_degrees_of_freedom_wrapper"
+
+  external _unit_tangent_at : t -> Coord.t -> int -> Point.t = "line_segment_unit_tangent_at_wrapper"
+  external _default_unit_tangent_at : t -> Coord.t -> Point.t = "line_segment_default_unit_tangent_at_wrapper"
+  let unit_tangent_at = general_unit_tangent_at _default_unit_tangent_at _unit_tangent_at reverse
+end
+
+module Quadratic_bezier =
+struct
+  type t
+  external to_curve : t -> Curve.t = "%identity"
+  external initial_point : t -> Point.t = "quadratic_bezier_initial_point_wrapper"
+  external final_point : t -> Point.t = "quadratic_bezier_final_point_wrapper"
+  external is_degenerate : t -> bool = "quadratic_bezier_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "quadratic_bezier_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "quadratic_bezier_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "quadratic_bezier_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "quadratic_bezier_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "quadratic_bezier_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "quadratic_bezier_portion_wrapper"
+  external reverse : t -> t = "quadratic_bezier_reverse_wrapper"
+  external derivative : t -> t = "quadratic_bezier_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "quadratic_bezier_length_wrapper"
+  external winding : t -> Point.t -> int = "quadratic_bezier_winding_wrapper"
+  external degrees_of_freedom : t -> int = "quadratic_bezier_degrees_of_freedom_wrapper"
+
+  external _unit_tangent_at : t -> Coord.t -> int -> Point.t = "line_segment_unit_tangent_at_wrapper"
+  external _default_unit_tangent_at : t -> Coord.t -> Point.t = "line_segment_default_unit_tangent_at_wrapper"
+  let unit_tangent_at = general_unit_tangent_at _default_unit_tangent_at _unit_tangent_at reverse
+end
+
+module Cubic_bezier =
+struct
+  type t
+  external to_curve : t -> Curve.t = "%identity"
+  external initial_point : t -> Point.t = "cubic_bezier_initial_point_wrapper"
+  external final_point : t -> Point.t = "cubic_bezier_final_point_wrapper"
+  external is_degenerate : t -> bool = "cubic_bezier_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "cubic_bezier_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "cubic_bezier_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "cubic_bezier_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "cubic_bezier_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "cubic_bezier_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "cubic_bezier_portion_wrapper"
+  external reverse : t -> t = "cubic_bezier_reverse_wrapper"
+  external derivative : t -> t = "cubic_bezier_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "cubic_bezier_length_wrapper"
+  external winding : t -> Point.t -> int = "cubic_bezier_winding_wrapper"
+  external degrees_of_freedom : t -> int = "cubic_bezier_degrees_of_freedom_wrapper"
+
+  external _unit_tangent_at : t -> Coord.t -> int -> Point.t = "line_segment_unit_tangent_at_wrapper"
+  external _default_unit_tangent_at : t -> Coord.t -> Point.t = "line_segment_default_unit_tangent_at_wrapper"
+  let unit_tangent_at = general_unit_tangent_at _default_unit_tangent_at _unit_tangent_at reverse
+end
+
+module type Path_type =
+sig
+  type t
+
+  module type ClosingSegment =
+  sig
+  end
+
+  type stitching = NO_STITCHING | STITCH_DISCONTINUOUS
+  module type StitchSegment =
+  sig
+  end
+
+  external default : unit -> t = "new_default_path_wrapper"
+  external make : Point.t -> t = "new_path_wrapper"
+  external point_at : t -> float -> Point.t = "path_point_at_wrapper"
+  external value_at : t -> float -> Coord.dim2 -> float = "path_value_at_wrapper"
+  external portion : t -> float -> float -> t = "path_portion_wrapper"
+  external reverse : t -> t = "path_reverse_wrapper"
+  external append_curve : ?stitch:stitching -> t -> Curve.t -> unit = "path_append_curve_wrapper"
+  external append : ?stitch:stitching -> t -> t -> unit = "path_append_wrapper"
+end
+
+module Path : Path_type =
+struct
+  type t
+
+  module type ClosingSegment =
+  sig
+  end
+
+  type stitching = NO_STITCHING | STITCH_DISCONTINUOUS
+  module type StitchSegment =
+  sig
+  end
+
+  external default : unit -> t = "new_default_path_wrapper"
+  external make : Point.t -> t = "new_path_wrapper"
+  external point_at : t -> float -> Point.t = "path_point_at_wrapper"
+  external value_at : t -> float -> Coord.dim2 -> float = "path_value_at_wrapper"
+  external portion : t -> float -> float -> t = "path_portion_wrapper"
+  external reverse : t -> t = "path_reverse_wrapper"
+  external append_curve : ?stitch:stitching -> t -> Curve.t -> unit = "path_append_curve_wrapper"
+  external append : ?stitch:stitching -> t -> t -> unit = "path_append_wrapper"
+end
 
 module Bezier_as_fragment : Fragment
 
-module type Curve =
+module type Curve_type =
 sig
   type t
   val initial_point : t -> Point.t
   val reverse : t -> t
   val derivative : t -> t
   val length : t -> Coord.t -> Coord.t
+  val winding : t -> Point.t -> int
+  val unit_tangent_at : ?num_derivs:int -> t -> Coord.t -> Point.t
   val degrees_of_freedom : t -> int
 end
 
+module Curve : Curve_type
+
 module Bezier_curve :
 sig
   type t
+  external to_curve : t -> Curve.t = "%identity"
   external of_two_beziers : Bezier.t -> Bezier.t -> t = "bezier_curve_of_two_beziers_wrapper"
   external of_two_points : Point.t -> Point.t -> t = "bezier_curve_of_two_points_wrapper"
   external of_three_points : Point.t -> Point.t -> Point.t -> t = "bezier_curve_of_three_points_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 winding : t -> Point.t -> int = "bezier_curve_winding_wrapper"
+  val unit_tangent_at : ?num_derivs:int -> t -> Coord.t -> Point.t
   external degrees_of_freedom : t -> int = "bezier_curve_degrees_of_freedom_wrapper"
 end
 
-module Bezier_curve_as_curve : Curve
+module Line_segment :
+sig
+  type t
+  external to_curve : t -> Curve.t = "%identity"
+  external initial_point : t -> Point.t = "line_segment_initial_point_wrapper"
+  external final_point : t -> Point.t = "line_segment_final_point_wrapper"
+  external is_degenerate : t -> bool = "line_segment_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "line_segment_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "line_segment_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "line_segment_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "line_segment_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "line_segment_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "line_segment_portion_wrapper"
+  external reverse : t -> t = "line_segment_reverse_wrapper"
+  external derivative : t -> t = "line_segment_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "line_segment_length_wrapper"
+  external winding : t -> Point.t -> int = "line_segment_winding_wrapper"
+  val unit_tangent_at : ?num_derivs:int -> t -> Coord.t -> Point.t
+  external degrees_of_freedom : t -> int = "line_segment_degrees_of_freedom_wrapper"
+end
+
+module Quadratic_bezier :
+sig
+  type t
+  external to_curve : t -> Curve.t = "%identity"
+  external initial_point : t -> Point.t = "quadratic_bezier_initial_point_wrapper"
+  external final_point : t -> Point.t = "quadratic_bezier_final_point_wrapper"
+  external is_degenerate : t -> bool = "quadratic_bezier_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "quadratic_bezier_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "quadratic_bezier_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "quadratic_bezier_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "quadratic_bezier_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "quadratic_bezier_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "quadratic_bezier_portion_wrapper"
+  external reverse : t -> t = "quadratic_bezier_reverse_wrapper"
+  external derivative : t -> t = "quadratic_bezier_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "quadratic_bezier_length_wrapper"
+  external winding : t -> Point.t -> int = "quadratic_bezier_winding_wrapper"
+  val unit_tangent_at : ?num_derivs:int -> t -> Coord.t -> Point.t
+  external degrees_of_freedom : t -> int = "quadratic_bezier_degrees_of_freedom_wrapper"
+end
+
+module Cubic_bezier :
+sig
+  type t
+  external to_curve : t -> Curve.t = "%identity"
+  external initial_point : t -> Point.t = "cubic_bezier_initial_point_wrapper"
+  external final_point : t -> Point.t = "cubic_bezier_final_point_wrapper"
+  external is_degenerate : t -> bool = "cubic_bezier_is_degenerate_wrapper"
+  external point_at : t -> Coord.t -> Point.t = "cubic_bezier_point_at_wrapper"
+  external value_at : t -> Coord.t -> Coord.dim2 -> Coord.t = "cubic_bezier_value_at_wrapper"
+  external point_and_derivatives : t -> Coord.t -> int -> Point.t array = "cubic_bezier_point_and_derivatives_wrapper"
+  external set_initial : t -> Point.t -> unit = "cubic_bezier_set_initial_wrapper"
+  external set_final : t -> Point.t -> unit = "cubic_bezier_set_final_wrapper"
+  external portion : t -> Coord.t -> Coord.t -> t = "cubic_bezier_portion_wrapper"
+  external reverse : t -> t = "cubic_bezier_reverse_wrapper"
+  external derivative : t -> t = "cubic_bezier_derivative_wrapper"
+  external length : t -> Coord.t -> Coord.t = "cubic_bezier_length_wrapper"
+  external winding : t -> Point.t -> int = "cubic_bezier_winding_wrapper"
+  val unit_tangent_at : ?num_derivs:int -> t -> Coord.t -> Point.t
+  external degrees_of_freedom : t -> int = "cubic_bezier_degrees_of_freedom_wrapper"
+end
+
+module type Path_type =
+sig
+  type t
+
+  module type ClosingSegment =
+  sig
+  end
+
+  type stitching = NO_STITCHING | STITCH_DISCONTINUOUS
+  module type StitchSegment =
+  sig
+  end
+
+  external default : unit -> t = "new_default_path_wrapper"
+  external make : Point.t -> t = "new_path_wrapper"
+  external point_at : t -> float -> Point.t = "path_point_at_wrapper"
+  external value_at : t -> float -> Coord.dim2 -> float = "path_value_at_wrapper"
+  external portion : t -> float -> float -> t = "path_portion_wrapper"
+  external reverse : t -> t = "path_reverse_wrapper"
+  external append_curve : ?stitch:stitching -> t -> Curve.t -> unit = "path_append_curve_wrapper"
+  external append : ?stitch:stitching -> t -> t -> unit = "path_append_wrapper"
+end
+
+module Path : Path_type

caml2geom_stubs.c

 #include <2geom/bezier.h>
 #include <2geom/bezier-curve.h>
 #include <2geom/coord.h>
+#include <2geom/exception.h>
 #include <2geom/interval.h>
+#include <2geom/path.h>
 #include <2geom/point.h>
 #include <2geom/rect.h>
 #include <2geom/transforms.h>
 // Geom::Dim2.
 Geom::Dim2 dim2[2] = { Geom::X, Geom::Y };
 
+// Similarly for enum Geom::Path::Stitching.
+enum Geom::Path::Stitching stitching[2] = { Geom::Path::NO_STITCHING,
+                                            Geom::Path::STITCH_DISCONTINUOUS };
+
 inline Geom::Translate *translate_from_ml_floats(value _x, value _y)
 {
     return new Geom::Translate(Double_val(_x), Double_val(_y));
 CUSTOM_OPS(Interval, interval_ops);
 CUSTOM_OPS(Rect, rect_ops);
 CUSTOM_OPS(Bezier, bezier_ops);
+CUSTOM_OPS(Curve, curve_ops);
 CUSTOM_OPS(BezierCurve, bezier_curve_ops);
+CUSTOM_OPS(LineSegment, line_segment_ops);
+CUSTOM_OPS(QuadraticBezier, quadratic_bezier_ops);
+CUSTOM_OPS(CubicBezier, cubic_bezier_ops);
+CUSTOM_OPS(Path, path_ops);
 
 //-------------------------------------------------------------------------
 
         CAMLreturn(caml_copy_double(curve->length(Double_val(_tolerance)))); \
     }                                                                   \
                                                                         \
+    extern "C" CAMLprim value name##_winding_wrapper(value _curve, value _point) \
+    {                                                                   \
+        CAMLparam2(_curve, _point);                                     \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        CAMLreturn(Int_val(curve->winding(*OPAQUE_P(Geom::Point, _point)))); \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_unit_tangent_at_wrapper(value _curve, value _t, value _n) \
+    {                                                                   \
+        CAMLparam3(_curve, _t, _n);                                     \
+        CAMLlocal1(_p);                                                 \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);                    \
+        OPAQUE_P(Geom::Point, _p) = new Geom::Point(curve->unitTangentAt(Double_val(_t), Int_val(_n))); \
+        CAMLreturn(_p);                                                 \
+    }                                                                   \
+                                                                        \
+    extern "C" CAMLprim value name##_default_unit_tangent_at_wrapper(value _curve, value _t) \
+    {                                                                   \
+        CAMLparam2(_curve, _t);                                         \
+        CAMLlocal1(_p);                                                 \
+        t *curve = OPAQUE_P(t, _curve);                                 \
+        _p = OPAQUE_P_ALLOC(Geom::Point, point_ops);                    \
+        OPAQUE_P(Geom::Point, _p) = new Geom::Point(curve->unitTangentAt(Double_val(_t))); \
+        CAMLreturn(_p);                                                 \
+    }                                                                   \
+                                                                        \
     extern "C" CAMLprim value name##_degrees_of_freedom_wrapper(value _curve) \
     {                                                                   \
         CAMLparam1(_curve);                                             \
 
 //-------------------------------------------------------------------------
 
+curve_wrappers(Geom::Curve, curve);
+
+//-------------------------------------------------------------------------
+
 curve_wrappers(Geom::BezierCurve, bezier_curve);
 
 extern "C" CAMLprim value bezier_curve_of_two_beziers_wrapper(value _bez0, value _bez1)
 }
 
 //-------------------------------------------------------------------------
+
+curve_wrappers(Geom::LineSegment, line_segment);
+curve_wrappers(Geom::QuadraticBezier, quadratic_bezier);
+curve_wrappers(Geom::CubicBezier, cubic_bezier);
+
+//-------------------------------------------------------------------------
+
+extern "C" CAMLprim value new_default_path_wrapper(void)
+{
+    CAMLparam0();
+    CAMLlocal1(_path);
+    _path = OPAQUE_P_ALLOC(Geom::Path, path_ops);
+    OPAQUE_P(Geom::Path, _path) = new Geom::Path();
+    CAMLreturn(_path);
+}
+
+extern "C" CAMLprim value new_path_wrapper(value _point)
+{
+    CAMLparam1(_point);
+    CAMLlocal1(_path);
+    _path = OPAQUE_P_ALLOC(Geom::Path, path_ops);
+    OPAQUE_P(Geom::Path, _path) = new Geom::Path(*OPAQUE_P(Geom::Point, _point));
+    CAMLreturn(_path);
+}
+
+extern "C" CAMLprim value path_point_at_wrapper(value _path, value _t)
+{
+    CAMLparam2(_path, _t);
+    CAMLlocal1(_point);
+    Geom::Path *path = OPAQUE_P(Geom::Path, _path);
+    double time = Double_val(_t);
+    Geom::Point point;
+    try {
+        point = path->pointAt(time);
+    }
+    catch (Geom::RangeError&) {
+        caml_invalid_argument("Parameter t out of bounds");
+    }
+    _point = OPAQUE_P_ALLOC(Geom::Point, point_ops);
+    OPAQUE_P(Geom::Point, _point) = new Geom::Point(point);
+    CAMLreturn(_point);
+}
+
+extern "C" CAMLprim value path_value_at_wrapper(value _path, value _t, value _dim2)
+{
+    CAMLparam3(_path, _t, _dim2);
+    Geom::Path *path = OPAQUE_P(Geom::Path, _path);
+    double time = Double_val(_t);
+    double val;
+    try {
+        val = path->valueAt(time, dim2[Int_val(_dim2)]);
+    }
+    catch (Geom::RangeError&) {
+        caml_invalid_argument("Parameter t out of bounds");
+    }
+    CAMLreturn(caml_copy_double(val));
+}
+
+extern "C" CAMLprim value path_portion_wrapper(value _path, value _a, value _b)
+{
+    CAMLparam3(_path, _a, _b);
+    CAMLlocal1(_portion);
+    Geom::Path *path = OPAQUE_P(Geom::Path, _path);
+    _portion = OPAQUE_P_ALLOC(Geom::Path, path_ops);
+    OPAQUE_P(Geom::Path, _portion) = new Geom::Path(path->portion(Double_val(_a), Double_val(_b)));
+    CAMLreturn(_portion);
+}
+
+extern "C" CAMLprim value path_reverse_wrapper(value _path)
+{
+    CAMLparam1(_path);
+    CAMLlocal1(_reversed);
+    Geom::Path *path = OPAQUE_P(Geom::Path, _path);
+    _reversed = OPAQUE_P_ALLOC(Geom::Path, path_ops);
+    OPAQUE_P(Geom::Path, _reversed) = new Geom::Path(path->reverse());
+    CAMLreturn(_reversed);
+}
+
+extern "C" CAMLprim value path_append_curve_wrapper(value _stitch, value _path, value _curve)
+{
+    CAMLparam3(_stitch, _path, _curve);
+    Geom::Path *path = OPAQUE_P(Geom::Path, _path);
+    Geom::Curve *curve = OPAQUE_P(Geom::Curve, _curve);
+    try {
+        if (_stitch == Val_int(0))
+            path->append(*curve);
+        else
+            path->append(*curve, stitching[Int_val(Field(_stitch, 0))]);
+    }
+    catch (Geom::ContinuityError&) {
+        caml_invalid_argument("Non-contiguous path");
+    }
+    CAMLreturn(Val_unit);
+}
+
+extern "C" CAMLprim value path_append_wrapper(value _stitch, value _path, value _other)
+{
+    CAMLparam3(_stitch, _path, _other);
+    Geom::Path *path = OPAQUE_P(Geom::Path, _path);
+    Geom::Path *other = OPAQUE_P(Geom::Path, _other);
+    try {
+        if (_stitch == Val_int(0))
+            path->append(*other);
+        else
+            path->append(*other, stitching[Int_val(Field(_stitch, 0))]);
+    }
+    catch (Geom::ContinuityError&) {
+        caml_invalid_argument("Non-contiguous path");
+    }
+    CAMLreturn(Val_unit);
+}
+
+//-------------------------------------------------------------------------
 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) ;;
 
+let _ = printf "bc1 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc1 0.4) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc1 0.4) 1) ;;
+let _ = printf "bc2 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc2 0.4) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc2 0.4) 1) ;;
+let _ = printf "bc3 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc3 0.4) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc3 0.4) 1) ;;
+let _ = printf "bc4 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc4 0.4) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc4 0.4) 1) ;;
+
+let _ = printf "bc1 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc1 1.0) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc1 1.0) 1) ;;
+let _ = printf "bc2 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc2 1.0) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc2 1.0) 1) ;;
+let _ = printf "bc3 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc3 1.0) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc3 1.0) 1) ;;
+let _ = printf "bc4 unit_tangent_at = (%f,%f)\n"
+  (Point.coord (Bezier_curve.unit_tangent_at bc4 1.0) 0)
+  (Point.coord (Bezier_curve.unit_tangent_at bc4 1.0) 1) ;;
+
+let _ = printf "winding: %d\n" (Bezier_curve.winding bc1 (Point.make 4. 36.)) ;;
+
 (*-----------------------------------------------------------------------*)
+
+let path1 = Path.default () ;;
+let path2 = Path.make (Point.make 10. 10.) ;;
+let _ = printf "path2 point_at 0.0 = (%f,%f)\n"
+  (Point.coord (Path.point_at path2 0.0) 0)
+  (Point.coord (Path.point_at path2 0.0) 1) ;;
+let _ = printf "path2 point_at 0.0 = (%f,%f)\n"
+  (Path.value_at path2 0.0 Coord.X)
+  (Path.value_at path2 0.0 Coord.Y) ;;
+let path3 = Path.portion path2 0.0 0.0 ;;
+let _ = printf "path3 point_at 0.0 = (%f,%f)\n"
+  (Point.coord (Path.point_at path3 0.0) 0)
+  (Point.coord (Path.point_at path3 0.0) 1) ;;
+let path4 = Path.portion path2 0.0 1.0 ;;
+let _ = printf "path4 point_at 0.0 = (%f,%f)\n"
+  (Point.coord (Path.point_at path4 0.0) 0)
+  (Point.coord (Path.point_at path4 0.0) 1) ;;
+let path5 = Path.make (Bezier_curve.initial_point bc1) ;;
+Path.append_curve ~stitch:Path.STITCH_DISCONTINUOUS path5 (Bezier_curve.to_curve bc1) ;;
+Path.append_curve ~stitch:Path.STITCH_DISCONTINUOUS path5 (Bezier_curve.to_curve bc2) ;;
+let _ = printf "path5 point_at 0.5 = (%f,%f)\n"
+  (Point.coord (Path.point_at path5 0.5) 0)
+  (Point.coord (Path.point_at path5 0.5) 1) ;;
+Path.append ~stitch:Path.STITCH_DISCONTINUOUS path5 path5 ;;
+let _ = printf "path5 point_at 4.5 = (%f,%f)\n"
+  (Point.coord (Path.point_at path5 4.5) 0)
+  (Point.coord (Path.point_at path5 4.5) 1) ;;
+
+(*-----------------------------------------------------------------------*)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.