From 561d0f0155f4906d90eb7e73a3ff9cb28909126f Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Fri, 5 Feb 2021 09:08:39 +0100 Subject: Update project structure --- script.it/path/builder.ml | 224 +++++++++++++++++++++ script.it/path/builder.mli | 43 ++++ script.it/path/dune | 7 + script.it/path/fixed.ml | 487 +++++++++++++++++++++++++++++++++++++++++++++ script.it/path/fixed.mli | 81 ++++++++ script.it/path/path.ml | 7 + script.it/path/point.ml | 77 +++++++ script.it/path/point.mli | 40 ++++ script.it/path/repr.ml | 19 ++ 9 files changed, 985 insertions(+) create mode 100755 script.it/path/builder.ml create mode 100755 script.it/path/builder.mli create mode 100755 script.it/path/dune create mode 100755 script.it/path/fixed.ml create mode 100755 script.it/path/fixed.mli create mode 100755 script.it/path/path.ml create mode 100755 script.it/path/point.ml create mode 100755 script.it/path/point.mli create mode 100755 script.it/path/repr.ml (limited to 'script.it/path') diff --git a/script.it/path/builder.ml b/script.it/path/builder.ml new file mode 100755 index 0000000..4403599 --- /dev/null +++ b/script.it/path/builder.ml @@ -0,0 +1,224 @@ +open StdLabels + +(** Signature for points *) +module type P = sig + type t + + val empty : t + + val get_coord : t -> Gg.v2 + + val copy : t -> Gg.v2 -> t + +end + +module Make(Point:P) = struct + + (** Point creation **) + + type bezier = + { p0:Point.t (* The starting point *) + ; p1:Point.t (* The end point *) + ; ctrl0:Gg.v2 (* The control point *) + ; ctrl1:Gg.v2 } (* The control point *) + + type t = Point.t list * bezier list + + let get_new_segment connexion0 p5 p4 p3 p2 p1 = + let p5' = Point.get_coord p5 + and p4' = Point.get_coord p4 + and p3' = Point.get_coord p3 + and p2' = Point.get_coord p2 + and p1' = Point.get_coord p1 in + + let points_to_link = + [ p1' + ; p2' + ; p3' + ; p4' + ; p5' ] in + Shapes.Bspline.to_bezier ?connexion0 points_to_link + + let empty = ([], []) + + let add_point + : Point.t -> t -> t + = fun lastPoint (path, beziers) -> + let (let*) v f = + match v with + | Ok bezier -> + if Array.length bezier > 0 then + f (Array.get bezier 0) + else + (lastPoint::path, beziers) + | _ -> + (lastPoint::path, beziers) + in + + let connexion0 = match beziers with + | hd::_ -> Some (Point.get_coord hd.p1) + | _ -> None in + + match path with + | p4::p3::p2::p1::_ -> + let* bezier = get_new_segment connexion0 + lastPoint p4 p3 p2 p1 in + + let bezier_point = + { p0 = p2 + ; p1 = p1 + ; ctrl0 = bezier.Shapes.Bezier.ctrl1 + ; ctrl1 = bezier.Shapes.Bezier.ctrl0 + } in + + (* We remove the last point and add the bezier curve in the list*) + let firsts = lastPoint::p4::p3::p2::[] in + (firsts, bezier_point::beziers) + | _ -> + ( lastPoint::path + , beziers) + + let replace_last + : Point.t -> t -> t + = fun lastPoint ((path, beziers) as t) -> + match path, beziers with + | _::(tl), beziers -> + + ( lastPoint::tl + , beziers ) + | _ -> + add_point lastPoint t + + let peek2 + : t -> (Point.t * Point.t) option + = fun (path, _) -> + match path with + | h1::h2::_ -> Some (h1, h2) + | _ -> None + + let peek + : t -> Point.t option + = fun (path, _) -> + match path with + | [] -> None + | hd::_ -> Some hd + + let repr + : t -> (module Repr.M with type point = Point.t and type t = 's) -> 's -> 's + = fun (type s) (points, beziers) (module Repr : Repr.M with type point = Point.t and type t = s) path -> + + (* Represent the last points *) + let path = match points with + | [] -> + ( path ) + | p1::[] -> + ( Repr.start p1 path ) + | p1::p2::[] -> + let path = + Repr.start p1 path + |> Repr.line_to p1 p2 in + ( path ) + | p0::p1::p2::[] -> + + let b0, b1 = Shapes.Bezier.quadratic_to_cubic + @@ Shapes.Bezier.three_points_quadratic + (Point.get_coord p0) + (Point.get_coord p1) + (Point.get_coord p2) + |> Shapes.Bezier.slice 0.5 + in + let p0' = Point.copy p0 b0.Shapes.Bezier.p0 + and p1' = Point.copy p1 b0.Shapes.Bezier.p1 + and p2' = Point.copy p2 b1.Shapes.Bezier.p1 in + + Repr.start p0 path + |> Repr.quadratic_to + ( p0' + , b0.Shapes.Bezier.ctrl0 + , b0.Shapes.Bezier.ctrl1 + , p1' ) + |> Repr.quadratic_to + ( p1' + , b1.Shapes.Bezier.ctrl0 + , b1.Shapes.Bezier.ctrl1 + , p2' ) + | (p0::_ as points) -> + + let (let*) v f = + match v with + | Ok beziers -> f beziers + | _ -> path in + + let points' = List.map ~f:Point.get_coord points in + let connexion = match beziers with + | [] -> None + | hd ::_ -> Some (Point.get_coord hd.p1) in + + let* beziers = Shapes.Bspline.to_bezier ?connexion1:connexion points' in + + (* Stdlib does not provide fold_left_i function and we need to map + each bezier point with the associated point in the curve. + + So I use references here for keeping each result element + + *) + let path = ref path in + let point = ref p0 in + + List.iteri + points + ~f:(fun i pt -> + + (* The first iteration is ignored, as we need both previous and + current point for the two point in the curve. + + Do not forget that there is always n-1 bezier curve for n + points *) + if i > 0 then ( + + let bezier = Array.get beziers (i - 1) in + + path := Repr.quadratic_to + ( !point + , bezier.Shapes.Bezier.ctrl0 + , bezier.Shapes.Bezier.ctrl1 + , pt ) + (!path); + point := pt; + ) + ); + ( !path ) + in + + (* Now represent the already evaluated points. Much easer to do, just + iterate on them *) + Repr.stop @@ List.fold_left beziers + ~init:path + ~f:(fun path bezier -> + Repr.quadratic_to + ( bezier.p0 + , bezier.ctrl0 + , bezier.ctrl1 + , bezier.p1 ) + path + ) + + let map + : t -> (Point.t -> Point.t) -> t + = fun (points, beziers) f -> + let points = List.map + points + ~f + and beziers = List.map + beziers + ~f:(fun bezier -> + + { p0 = f bezier.p0 + ; p1 = f bezier.p1 + ; ctrl0 = Point.(get_coord (f ( copy bezier.p0 bezier.ctrl0))) + ; ctrl1 = Point.(get_coord (f ( copy bezier.p1 bezier.ctrl1))) + } + ) in + points, beziers + +end diff --git a/script.it/path/builder.mli b/script.it/path/builder.mli new file mode 100755 index 0000000..2afbd4b --- /dev/null +++ b/script.it/path/builder.mli @@ -0,0 +1,43 @@ +(** Signature for points *) +module type P = sig + type t + + val empty : t + + val get_coord : t -> Gg.v2 + + (** Copy a point and all thoses properties to the given location *) + val copy : t -> Gg.v2 -> t + +end + +module Make(Point:P) : sig + + type t + + (** Create an empty path *) + val empty: t + + val add_point + : Point.t -> t -> t + + (** Replace the last alement in the path by the one given in parameter *) + val replace_last + : Point.t -> t -> t + + (** Retrieve the last element, if any *) + val peek + : t -> Point.t option + + (** Retrieve the last element, if any *) + val peek2 + : t -> (Point.t * Point.t) option + + (** Represent the path *) + val repr + : t -> (module Repr.M with type point = Point.t and type t = 's) -> 's -> 's + + val map + : t -> (Point.t -> Point.t) -> t + +end diff --git a/script.it/path/dune b/script.it/path/dune new file mode 100755 index 0000000..863c768 --- /dev/null +++ b/script.it/path/dune @@ -0,0 +1,7 @@ +(library + (name path) + (libraries + gg + shapes + ) + ) diff --git a/script.it/path/fixed.ml b/script.it/path/fixed.ml new file mode 100755 index 0000000..1362ad3 --- /dev/null +++ b/script.it/path/fixed.ml @@ -0,0 +1,487 @@ +open StdLabels + +(** Signature for points *) +module type P = sig + type t + + val get_coord : t -> Gg.v2 + + val id : t -> int + + val copy : t -> Gg.v2 -> t + +end + +module Make(Point:P) = struct + + type bezier = + { ctrl0:Gg.v2 (* The control point *) + ; ctrl1:Gg.v2 (* The control point *) + ; p1:Point.t (* The end point *) + } + + module type BUILDER = sig + type t + + val repr + : t -> (module Repr.M with type point = Point.t and type t = 's) -> 's -> 's + end + + type path = + | Line of Point.t + | Curve of bezier + + + type step = + { point : Point.t + ; move : path + } + + type t = step array + + module ToFixed = struct + type point = Point.t + + type t = int * step list + + let create_path () = 0, [] + + (* Start a new path. *) + let start point t = + let _ = point in + t + + let line_to + : point -> point -> t -> t + = fun p1 p2 (i, t) -> + ( i + 1 + , { point = p1 + ; move = Line p2 + }:: t ) + + let quadratic_to + : (point * Gg.v2 * Gg.v2 * point) -> t -> t + = fun (p0, ctrl0, ctrl1, p1) (i, t) -> + let curve = Curve + { ctrl0 + ; ctrl1 + ; p1} in + ( i + 1 + , { point = p0 + ; move = curve + } ::t) + + let stop t = t + + let get + : int * step list -> step array + = fun (n, t) -> + + (* The array is initialized with a magic number, and just after + filled with the values from the list in reverse. All the elements are set. + *) + let res = Obj.magic (Array.make n 0) in + List.iteri t + ~f:(fun i elem -> Array.set res (n - i - 1) elem ); + res + end + + let to_fixed + : (module BUILDER with type t = 'a) -> 'a -> t + = fun (type s) (module Builder: BUILDER with type t = s) t -> + Builder.repr t (module ToFixed) (ToFixed.create_path ()) + |> ToFixed.get + + let repr + : t -> (module Repr.M with type point = Point.t and type t = 's) -> 's -> 's + = fun (type s) t (module Repr : Repr.M with type point = Point.t and type t = s) repr -> + let repr_bezier p p0 bezier = + Repr.quadratic_to + ( p0 + , bezier.ctrl0 + , bezier.ctrl1 + , bezier.p1 ) + p in + + let _, repr = Array.fold_left t + ~init:(true, repr) + ~f:(fun (first, path) element -> + let path = if first then + Repr.start element.point path + else path in + match element.move with + | Line p1 -> + ( false + , Repr.line_to element.point p1 path ) + | Curve bezier -> + ( false + , repr_bezier path element.point bezier ) + ) in + Repr.stop repr + + + type approx = + { distance : float + ; closest_point : Gg.v2 + ; ratio : float + ; p0 : Point.t + ; p1 : Point.t } + + (** Return the distance between a given point and the curve. May return + None if the point is out of the curve *) + let distance + : Gg.v2 -> t -> approx option + = fun point t -> + + Array.fold_left t + ~init:None + ~f:(fun res step -> + match step.move with + | Line p1 -> + let box = Gg.Box2.of_pts (Point.get_coord step.point) (Point.get_coord p1) in + begin match Gg.Box2.mem point box with + | false -> res + | true -> + (* TODO Evaluate the normal *) + res + end + | Curve bezier -> + + let bezier' = Shapes.Bezier.( + + { p0 = Point.get_coord step.point + ; p1 = Point.get_coord bezier.p1 + ; ctrl0 = bezier.ctrl0 + ; ctrl1 = bezier.ctrl1 } + ) in + let ratio, point' = Shapes.Bezier.get_closest_point point bezier' in + let distance' = Gg.V2.( norm (point - point') ) in + match res with + | Some {distance; _} when distance < distance' -> res + | _ -> Some + { closest_point = point' + ; distance = distance' + ; p0 = step.point + ; p1 = bezier.p1 + ; ratio } + ) + + let map + : t -> (Point.t -> Point.t) -> t + = fun t f -> + Array.map t + ~f:(fun step -> + match step.move with + | Line p2 -> + { point = f step.point + ; move = Line (f p2) + } + | Curve bezier -> + let point = f step.point in + { point + ; move = Curve + { p1 = f bezier.p1 + ; ctrl0 = Point.get_coord (f (Point.copy step.point bezier.ctrl0)) + ; ctrl1 = Point.get_coord (f (Point.copy bezier.p1 bezier.ctrl1)) + } + } + ) + + let iter + : t -> f:(Point.t -> unit) -> unit + = fun t ~f -> + Array.iter t + ~f:(fun step -> + match step.move with + | Line p2 -> f step.point; f p2 + | Curve bezier -> f step.point ; f bezier.p1 + ) + + let get_point' + : step -> Point.t + = fun { move ; _} -> + match move with + | Line p1 -> p1 + | Curve bezier -> bezier.p1 + + (** Associate the return from the bezier point to an existing path *) + let assoc_point + : Shapes.Bezier.t -> step -> step + = fun bezier step -> + match step.move with + | Line p1 + | Curve {p1; _} -> + let p0' = Point.copy step.point bezier.Shapes.Bezier.p0 + and p1' = Point.copy p1 bezier.Shapes.Bezier.p1 in + { point = p0' + ; move = Curve + { p1 = p1' + ; ctrl0 = bezier.Shapes.Bezier.ctrl0 + ; ctrl1 = bezier.Shapes.Bezier.ctrl1 + } + } + + + let build_from_three_points p0 p1 p2 = + let bezier = + Shapes.Bezier.quadratic_to_cubic + @@ Shapes.Bezier.three_points_quadratic + (Point.get_coord p0) + (Point.get_coord p1) + (Point.get_coord p2) in + + (* The middle point is not exactly at the middle anymore (it can have been + moved), we have the reevaluate it's position *) + let ratio, _ = Shapes.Bezier.get_closest_point + (Point.get_coord p1) + bezier in + + let b0, b1 = Shapes.Bezier.slice ratio bezier in + let p0' = Point.copy p0 b0.Shapes.Bezier.p0 + and p1' = Point.copy p1 b0.Shapes.Bezier.p1 + and p2' = Point.copy p2 b1.Shapes.Bezier.p1 in + + [| { point = p0' + ; move = + Curve { ctrl0 = b0.Shapes.Bezier.ctrl0 + ; ctrl1 = b0.Shapes.Bezier.ctrl1 + ; p1 = p1' + } } + ; { point = p1' + ; move = Curve { ctrl0 = b1.Shapes.Bezier.ctrl0 + ; ctrl1 = b1.Shapes.Bezier.ctrl1 + ; p1 = p2' } + } |] + + (** Rebuild the whole curve by evaluating all the points *) + let rebuild + : t -> t option + = fun t -> + + match Array.length t with + | 0 -> None + | 1 -> + let step = Array.get t 0 in + begin match step.move with + | Curve {p1; _} + | Line p1 -> + Some + [| + { point = step.point + ; move = Line p1 } |] + end + | 2 -> + let p0 = (Array.get t 0).point + and p1 = (Array.get t 1).point + and p2 = get_point' @@ Array.get t 1 in + Some (build_from_three_points p0 p1 p2) + + | _ -> + + (* Convert all the points in list *) + let points = List.init + ~len:((Array.length t) ) + ~f:(fun i -> Point.get_coord @@ get_point' (Array.get t i)) in + let p0 = Point.get_coord @@ (Array.get t 0).point in + + let points = p0::points in + + (* We process the whole curve in a single block *) + begin match Shapes.Bspline.to_bezier points with + | Error `InvalidPath -> None + | Ok beziers -> + + (* Now for each point, reassociate the same point information, + We should have as many points as before *) + let rebuilded = Array.map2 beziers t ~f:assoc_point in + Some rebuilded + end + + let find_pt_index + : Point.t -> step array -> int option + = fun point path -> + (* First search the element to remove. The counter mark the position of + the point to remove, not the segment itself. *) + let idx = ref None + and counter = ref 0 in + + let _ = Array.exists + path + ~f:(fun element -> + let res = + if (Point.id element.point) = (Point.id point) then ( + idx := Some (!counter) ; + true + ) else match element.move with + | Line p1 + | Curve {p1;_} when (Point.id p1) = (Point.id point) -> + idx := Some (!counter+1) ; + true + | _ -> + false + in + incr counter; + res) in + !idx + + let remove_point + : t -> Point.t -> t option + = fun t point -> + + match Array.length t with + | 0 + | 1 -> None + | 2 -> + (* Two segment, we get the points and transform this into a single line *) + let p0 = (Array.get t 0).point + and p1 = (Array.get t 1).point + and p2 = get_point' @@ Array.get t 1 in + let elms = List.filter [p0; p1; p2] + ~f:(fun pt -> Point.id pt != Point.id point) in + begin match elms with + | p0::p1::[] -> + Some + [| { point = p0 + ; move = Line p1 }|] + | _ -> None + end + | l -> + match find_pt_index point t with + | None -> Some t + | Some 0 -> + (* Remove the first point *) + let path = Array.init (l-1) + ~f:( fun i -> Array.get t (i+1)) in + Some path + | Some n when n = (Array.length t) -> + (* Remove the last point *) + let path = Array.init (l-1) + ~f:( fun i -> Array.get t i) in + Some path + | Some n -> + let path' = Array.init (l-1) + ~f:(fun i -> + if i < (n-1) then + Array.get t (i) + else if i = (n-1) then + (* We know that the point is not the first nor the last one. + So it is safe to call n-1 or n + 1 point + + We have to rebuild the point and set that + point_(-1).id = point_(+1).id + *) + let p0 = (Array.get t i).point in + + match (Array.get t (i+1)).move with + | Line p1 -> + { point = p0 + ; move = Line p1 } + | Curve c -> + { point = p0 + ; move = Curve c } + + else + Array.get t (i+1) + ) in + rebuild path' + + let first_point + : step -> Point.t + = fun {point; _} -> point + + let replace_point + : t -> Point.t -> t option + = fun t p -> + + let add_path paths idx f points = + if 0 <= idx && idx < Array.length paths then + let path = Array.get t idx in + Point.get_coord (f path) + :: points + else points in + + match Array.length t with + | 0 -> None + | 1 -> (* Only one point, easy ? *) + let step = Array.get t 0 in + begin match step.move with + | Curve {p1; _} + | Line p1 -> + let p0 = if (Point.id step.point = Point.id p) then p else step.point + and p1 = if (Point.id p1 = Point.id p) then p else p1 in + Some [| + { point = p0 + ; move = Line p1 } + |] + end + + | 2 -> + let p0 = (Array.get t 0).point + and p1 = (Array.get t 1).point + and p2 = get_point' @@ Array.get t 1 in + + let p0 = if (Point.id p0 = Point.id p) then p else p0 + and p1 = if (Point.id p1 = Point.id p) then p else p1 + and p2 = if (Point.id p2 = Point.id p) then p else p2 in + Some (build_from_three_points p0 p1 p2) + + (* More than two segmend, it is ok for a partial reevaluation *) + | _ -> + match find_pt_index p t with + | None -> None + | Some n -> + let path = Array.copy t in + + let p0, p1 = + + if n < Array.length path then + p, get_point' (Array.get path n) + else + (Array.get path (n -1)).point, p + in + + let min_idx = max (n-3) 0 in + + let points = + add_path path (n-3) first_point + @@ add_path path (n-2) first_point + @@ add_path path (n-1) first_point + @@ (fun tl -> (Point.get_coord p)::tl) + @@ add_path path n get_point' + @@ add_path path (n+1) get_point' + @@ add_path path (n+2) get_point' + @@ [] in + + (* It is impressive how fast it is to evaluate the curve ! Maybe is the + worker not required at all… + *) + let bezier_opt = Shapes.Bspline.to_bezier points in + begin match bezier_opt with + | Ok paths -> + Array.iteri paths + ~f:(fun i bezier -> + (* Only take two points before, and two after *) + let idx = min_idx + i in + if (n-2 < idx) && (idx < n +2) && idx < Array.length path then + Array.set path idx (assoc_point bezier (Array.get path idx)) + ); + Some path + | Error _ -> + let bezier', _ = Shapes.Bezier.three_points_quadratic + (Point.get_coord p) + (Point.get_coord @@ get_point' (Array.get path 0)) + (Point.get_coord @@ get_point' (Array.get path 1)) + |> Shapes.Bezier.quadratic_to_cubic + |> Shapes.Bezier.slice 0.5 + in + Array.set path 0 + { point = p0 + ; move = (Curve + { ctrl0 = bezier'.Shapes.Bezier.ctrl0 + ; ctrl1 = bezier'.Shapes.Bezier.ctrl1 + ; p1 + }) + }; + Some path + end +end diff --git a/script.it/path/fixed.mli b/script.it/path/fixed.mli new file mode 100755 index 0000000..111187c --- /dev/null +++ b/script.it/path/fixed.mli @@ -0,0 +1,81 @@ +(** Signature for points *) +module type P = sig + type t + + val get_coord : t -> Gg.v2 + + val id : t -> int + + val copy : t -> Gg.v2 -> t + +end + +module Make(Point:P) : sig + + module type BUILDER = sig + type t + + val repr + : t -> (module Repr.M with type point = Point.t and type t = 's) -> 's -> 's + + end + + type t + + (** Create a path from a builder *) + val to_fixed + : (module BUILDER with type t = 'a) -> 'a -> t + + (** Represent the path *) + val repr + : t -> (module Repr.M with type point = Point.t and type t = 's) -> 's -> 's + + (** Structure to represent all the required information for evaluating the + distance between a point and a path *) + type approx = + { distance : float + ; closest_point : Gg.v2 + ; ratio : float + ; p0 : Point.t + ; p1 : Point.t } + + (** Return the distance between a given point and the curve. May return + None if the point is out of the curve *) + val distance + : Gg.v2 -> t -> approx option + + (** Iterate over a path *) + val iter + : t -> f:(Point.t -> unit) -> unit + + (** Map all the points in the path *) + val map + : t -> (Point.t -> Point.t) -> t + + (** Reevaluate all the control points on the path in order to get a smooth + curve *) + val rebuild + : t -> t option + + (** Delete a point in the path. + + Reconnect the path without the point removed, and reevaluate all the + control points from the nodes + + return None if the point is not present in the curve + *) + val remove_point + : t -> Point.t -> t option + + (** Replace a point by the given one. + + An existing point with the same id shall be present in the path. + + The path is not fully evaluated, and rebuild shall be runned in order to + get the path completely smooth. + + *) + val replace_point + : t -> Point.t -> t option + +end diff --git a/script.it/path/path.ml b/script.it/path/path.ml new file mode 100755 index 0000000..ea90de4 --- /dev/null +++ b/script.it/path/path.ml @@ -0,0 +1,7 @@ +(** Common module for ensuring that the function is evaluated only once *) + +module Point = Point +module Repr = Repr +module Path_Builder = Builder.Make(Point) +module Fixed = Fixed.Make(Point) + diff --git a/script.it/path/point.ml b/script.it/path/point.ml new file mode 100755 index 0000000..ec6f8ad --- /dev/null +++ b/script.it/path/point.ml @@ -0,0 +1,77 @@ +let internal_id = ref 0 + +type t = + { p: Gg.v2 + ; size : float + ; angle: float + ; stamp : float + ; id : int + } + +let empty = + { p = Gg.V2.of_tuple (0., 0.) + ; size = 0. + ; angle = 0. + ; stamp = 0. + ; id = 0 + } + +let create ~angle ~width ~stamp ~x ~y = + + incr internal_id; + { p = Gg.V2.v x y + ; size = width + ; angle = Gg.Float.rad_of_deg (180. -. angle ) + ; stamp + ; id = !internal_id + } + +let copy point p = + { point with + p + } + +let set_angle p angle = + { p with angle = Gg.Float.rad_of_deg (180. -. angle) } + +let get_angle { angle; _} = 180. -. (Gg.Float.deg_of_rad angle) + +let set_width p size = + { p with size } + +let get_width { size; _} = size + +let (+) p1 p2 = + { p1 with p = Gg.V2.(+) p1.p p2 } + +let get_coord { p; _ } = p + +let get_stamp { stamp; _} = stamp + +let get_coord' + : t -> Gg.v2 + = fun t -> + let open Gg.V2 in + let trans = of_polar @@ v t.size t.angle in + t.p + trans + +let mix + : float -> Gg.v2 -> t -> t -> t + = fun f point p0 p1 -> + let angle0 = p0.angle + and angle1 = p1.angle + and width0 = get_width p0 + and width1 = get_width p1 + and stamp0 = get_stamp p0 + and stamp1 = get_stamp p1 in + let angle = angle0 +. f *. ( angle1 -. angle0 ) in + let width = width0 +. f *. ( width1 -. width0 ) in + let stamp = stamp0 +. f *. ( stamp1 -. stamp0 ) in + { p = point + ; size = width + ; angle + ; stamp + ; id = !internal_id + } + +let id { id; _} = id diff --git a/script.it/path/point.mli b/script.it/path/point.mli new file mode 100755 index 0000000..fe4cb45 --- /dev/null +++ b/script.it/path/point.mli @@ -0,0 +1,40 @@ +type t + +(** Return the point id. Each id is unique *) +val id + : t -> int + +val empty : t + +val (+): t -> Gg.v2 -> t + +val get_coord : t -> Gg.v2 + +val get_stamp : t -> float + +val create: angle:float -> width:float -> stamp:float -> x:float -> y:float -> t + +(** Return a copy of the point at given posistion + + This is a true copy, the id will be the same for the two points + TODO : Should this be renamed set_position ? + +*) +val copy : t -> Gg.v2 -> t + +val set_angle : t -> float -> t + +val get_angle : t -> float + +val set_width: t -> float -> t + +val get_width: t -> float + +val get_coord' + : t -> Gg.v2 + +(** [mix f point p0 p1] create a new point at the position point, with the + characteristics from p0 and p1 *) +val mix + : float -> Gg.v2 -> t -> t -> t + diff --git a/script.it/path/repr.ml b/script.it/path/repr.ml new file mode 100755 index 0000000..17fd914 --- /dev/null +++ b/script.it/path/repr.ml @@ -0,0 +1,19 @@ +module type M = sig + + type point + + type t + + (* Start a new path. *) + val start + : point -> t -> t + + val line_to + : point -> point -> t -> t + + val quadratic_to + : (point * Gg.v2 * Gg.v2 * point) -> t -> t + + val stop + : t -> t +end -- cgit v1.2.3