diff options
| author | Sébastien Dailly <sebastien@chimrod.com> | 2021-01-11 13:51:21 +0100 | 
|---|---|---|
| committer | Sébastien Dailly <sebastien@chimrod.com> | 2021-01-11 13:55:43 +0100 | 
| commit | 1aa90219e3e74bac3afbde0ec120e098b50bd0c5 (patch) | |
| tree | 50613ecc6f1984b9a9824fc347d064df38f33cf0 | |
| parent | 42c3c122c4f53dd68bcdd89411835887c3ae0af9 (diff) | |
Interior curve evaluation
| -rwxr-xr-x | layer/paths.ml | 161 | ||||
| -rwxr-xr-x | path/builder.ml | 18 | ||||
| -rwxr-xr-x | path/builder.mli | 3 | ||||
| -rwxr-xr-x | path/fixed.ml | 7 | ||||
| -rwxr-xr-x | script.it/outline.ml | 2 | ||||
| -rwxr-xr-x | script.it/script.ml | 23 | ||||
| -rwxr-xr-x | script.it/state.ml | 76 | ||||
| -rwxr-xr-x | script.it/worker.ml | 6 | ||||
| -rwxr-xr-x | script.it/worker_messages/worker_messages.ml | 2 | 
9 files changed, 172 insertions, 126 deletions
| diff --git a/layer/paths.ml b/layer/paths.ml index 3a8bfe8..d3baf02 100755 --- a/layer/paths.ml +++ b/layer/paths.ml @@ -84,19 +84,19 @@ module MakePrinter(M:Repr.ENGINE) : P  end  (** Transform the two path, into a single one. *) -module ReprSingle(T:PATH) = struct +module ReprSingle = struct -  type t = T.t * T.t +  type point = Path.Point.t -  module R = struct -    type point = Path.Point.t +  type repr = +    | Move of (point) +    | Line_to of (point * point) +    | Quadratic of (point * Gg.v2 * Gg.v2 * point) -    type repr' = -      | Move of (point) -      | Line_to of (point * point) -      | Quadratic of (point * Gg.v2 * Gg.v2 * point) +  module R = struct +    type t = repr list -    type t = repr' list +    type point = Path.Point.t      let start t actions =        (Move t)::actions @@ -111,92 +111,109 @@ module ReprSingle(T:PATH) = struct      let stop        : t -> t -      = fun v -> List.rev v +      = fun v -> v    end    let repr -    : t -> (module Path.Repr.M with type point = Path.Point.t and type t = 's) -> 's -> 's -    = fun (type s) (path, _) (module Repr:Path.Repr.M with type point = Path.Point.t and type t = s) state -> -      let elems = T.repr path (module R) [] in - -      let state = List.fold_left elems -          ~init:state -          ~f:(fun state -> function -              | R.Move pt -> Repr.start pt state -              | R.Line_to (p0, p1) -> Repr.line_to p0 p1 state -              | R.Quadratic t -> Repr.quadratic_to t state -            ) -      in Repr.stop state +    : (module PATH with type t = 't) -> 't -> 't -> repr list * repr list +    = fun (type t) (module P:PATH with type t = t) path back -> +      let path = P.repr path (module R) [] +      and back = P.repr back (module R) [] in +      path, back  end -module ReprFixed = ReprSingle(Path.Fixed) -module ReprBuild = ReprSingle(Path.Path_Builder) -  (* Canva representation *)  module FillCanva = FillEngine.Make(CanvaPrinter)  module LineCanva = LineEngine.Make(CanvaPrinter) -module DuctusCanva = FillEngine.Make(CanvaPrinter) - -module FillCanvaRepr = MakePrinter(FillCanva) -module DuctusCanvaRepr = MakePrinter(DuctusCanva) -module LineCanvaRepr = MakePrinter(LineCanva) +module DuctusCanva = DuctusEngine.Make(CanvaPrinter)  (* SVG representation *) -module FillSVGRepr = MakePrinter(FillEngine.Make(Svg)) -module DuctusSVGRepr = MakePrinter(DuctusEngine.Make(Svg)) +module FillSVG = FillEngine.Make(Svg) +module DuctusSVG = DuctusEngine.Make(Svg) + +(** Draw a path to a canva.contents -(** Draw a path to a canva *) +    The code may seems scary, but is very repetitive. Firt, all points (from the +    main stroke, and the interior one) are evaluated. Then, they are both rendered +    using the selected engine. +*)  let to_canva -  : (module PATH with type t = 's)  -> 's -> Brr_canvas.C2d.t -> printer -> unit -  = fun (type s) (module R:PATH with type t = s) path ctx -> function +  : (module PATH with type t = 's) -> 's * 's -> Brr_canvas.C2d.t -> printer -> unit +  = fun (type s) (module R:PATH with type t = s) (path, back) ctx engine -> +    let f, b = ReprSingle.repr (module R) path back in +    match engine with      | `Fill -> -      R.repr -        path -        (module FillCanvaRepr) -        (FillCanvaRepr.create_path (fun p -> Brr_canvas.C2d.fill ctx p; p)) -      |> FillCanvaRepr.get +      let t = List.fold_left2 f b +          ~init:(FillCanva.create_path (fun p -> Brr_canvas.C2d.fill ctx p; p)) +          ~f:(fun ctx f b -> +              match (f, b) with +              | ReprSingle.Move p0, ReprSingle.Move p0' -> FillCanva.start p0 p0' ctx +              | ReprSingle.Line_to l, ReprSingle.Line_to l' -> FillCanva.line_to l l' ctx +              | ReprSingle.Quadratic q, ReprSingle.Quadratic q' -> FillCanva.quadratic_to q q' ctx +              | _ -> ctx +            ) in +      FillCanva.get t        |> Brr_canvas.C2d.stroke ctx      | `Line -> -      R.repr -        path -        (module LineCanvaRepr) -        (LineCanvaRepr.create_path (fun p -> Brr_canvas.C2d.fill ctx p; p)) -      |> LineCanvaRepr.get +      let t = List.fold_left2 f b +          ~init:(LineCanva.create_path (fun p -> Brr_canvas.C2d.fill ctx p; p)) +          ~f:(fun ctx f b -> +              match (f, b) with +              | ReprSingle.Move p0, ReprSingle.Move p0' -> LineCanva.start p0 p0' ctx +              | ReprSingle.Line_to l, ReprSingle.Line_to l' -> LineCanva.line_to l l' ctx +              | ReprSingle.Quadratic q, ReprSingle.Quadratic q' -> LineCanva.quadratic_to q q' ctx +              | _ -> ctx +            ) in +      LineCanva.get t        |> Brr_canvas.C2d.stroke ctx      | `Ductus -> -      R.repr -        path -        (module DuctusCanvaRepr) -        (DuctusCanvaRepr.create_path (fun p -> Brr_canvas.C2d.fill ctx p; p)) -      |> DuctusCanvaRepr.get +      let t = List.fold_left2 f b +          ~init:(DuctusCanva.create_path (fun p -> Brr_canvas.C2d.fill ctx p; p)) +          ~f:(fun ctx f b -> +              match (f, b) with +              | ReprSingle.Move p0, ReprSingle.Move p0' -> DuctusCanva.start p0 p0' ctx +              | ReprSingle.Line_to l, ReprSingle.Line_to l' -> DuctusCanva.line_to l l' ctx +              | ReprSingle.Quadratic q, ReprSingle.Quadratic q' -> DuctusCanva.quadratic_to q q' ctx +              | _ -> ctx +            ) in +      DuctusCanva.get t        |> Brr_canvas.C2d.stroke ctx +  (** Draw a path and represent it as SVG *)  let to_svg -  : (module PATH with type t = 's) -> color:Jstr.t -> 's -> printer -> Brr.El.t -  = fun (type s) (module R:PATH with type t = s) ~color path -> function +  : (module PATH with type t = 's) -> color:Jstr.t -> 's * 's -> printer -> Brr.El.t +  = fun (type s) (module R:PATH with type t = s) ~color (path, back) engine -> +    let f, b = ReprSingle.repr (module R) path back in +    match engine with      | `Fill ->        (* In order to deal with over crossing path, I cut the path in as           many segment as there is curve, and fill them all. Then, all of theme           are grouped inside a single element *)        let paths = ref [] in -      let _ = R.repr -          path -          (module FillSVGRepr) -          (FillSVGRepr.create_path -             (fun p -> -                let repr = Svg.path -                    ~at:Brr.At.[ v (Jstr.v "d") p ] -                    [] in - -                paths := repr::!paths; -                Jstr.empty)) in +      let init = (FillSVG.create_path +                    (fun p -> +                       let repr = Svg.path +                           ~at:Brr.At.[ v (Jstr.v "d") p ] +                           [] in + +                       paths := repr::!paths; +                       Jstr.empty)) in +      let _ = List.fold_left2 f b +          ~init +          ~f:(fun ctx f b -> +              match (f, b) with +              | ReprSingle.Move p0, ReprSingle.Move p0' -> FillSVG.start p0 p0' ctx +              | ReprSingle.Line_to l, ReprSingle.Line_to l' -> FillSVG.line_to l l' ctx +              | ReprSingle.Quadratic q, ReprSingle.Quadratic q' -> FillSVG.quadratic_to q q' ctx +              | _ -> ctx +            ) in        Brr.El.v (Jstr.v "g")          ~at:Brr.At.[ @@ -205,11 +222,18 @@ let to_svg          !paths      | `Ductus -> -      let svg_path = R.repr -          path -          (module DuctusSVGRepr) -          (DuctusSVGRepr.create_path (fun _ -> Jstr.empty)) -                     |> DuctusSVGRepr.get in +      let init = DuctusSVG.create_path (fun _ -> Jstr.empty) in +      let svg_path = List.fold_left2 f b +          ~init +          ~f:(fun ctx f b -> +              match (f, b) with +              | ReprSingle.Move p0, ReprSingle.Move p0' -> DuctusSVG.start p0 p0' ctx +              | ReprSingle.Line_to l, ReprSingle.Line_to l' -> DuctusSVG.line_to l l' ctx +              | ReprSingle.Quadratic q, ReprSingle.Quadratic q' -> DuctusSVG.quadratic_to q q' ctx +              | _ -> ctx +            ) +                     |> DuctusSVG.get in +        Svg.path          ~at:Brr.At.[              v (Jstr.v "fill")  color @@ -218,4 +242,3 @@ let to_svg          []      | `Line ->        raise Not_found - diff --git a/path/builder.ml b/path/builder.ml index 166c073..4403599 100755 --- a/path/builder.ml +++ b/path/builder.ml @@ -203,4 +203,22 @@ module Make(Point:P) = struct                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/path/builder.mli b/path/builder.mli index ff66bcb..2afbd4b 100755 --- a/path/builder.mli +++ b/path/builder.mli @@ -37,4 +37,7 @@ module Make(Point:P) : sig    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/path/fixed.ml b/path/fixed.ml index d61bb0a..1362ad3 100755 --- a/path/fixed.ml +++ b/path/fixed.ml @@ -179,7 +179,12 @@ module Make(Point:P) = struct              | Curve bezier ->                let point = f step.point in                { point -              ; move = Curve {bezier with p1 = f bezier.p1} } +              ; 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 diff --git a/script.it/outline.ml b/script.it/outline.ml index 4962d8e..0dbecd0 100755 --- a/script.it/outline.ml +++ b/script.it/outline.ml @@ -6,7 +6,7 @@ type t =    ; back: Path.Fixed.t    } -let get_id = +let get_id () =    let id = !internal_path_id in    incr internal_path_id;    id diff --git a/script.it/script.ml b/script.it/script.ml index 9ef15fe..3859cc9 100755 --- a/script.it/script.ml +++ b/script.it/script.ml @@ -225,7 +225,10 @@ let on_change canva mouse_position timer state =    in -  Layer.Paths.to_canva (module Layer.Paths.ReprBuild) (current, current) context state.rendering; +  let back = Path.Path_Builder.map +      current +      (fun pt -> Path.Point.copy pt @@ Path.Point.get_coord' pt) in +  Layer.Paths.to_canva (module Path.Path_Builder) (current, back) context state.rendering;    List.iter state.paths      ~f:(fun path -> @@ -246,7 +249,7 @@ let on_change canva mouse_position timer state =          in          let p = path.Outline.path in -        Layer.Paths.to_canva (module Layer.Paths.ReprFixed) (p, p) context state.rendering +        Layer.Paths.to_canva (module Path.Fixed) (p, path.Outline.back) context state.rendering        );    let () = match state.mode with @@ -257,7 +260,7 @@ let on_change canva mouse_position timer state =          ~f:(fun path ->              if id = path.Outline.id then                let p = path.Outline.path in -              Layer.Paths.to_canva (module Layer.Paths.ReprFixed) (p, p) context `Line +              Layer.Paths.to_canva (module Path.Fixed) (p, path.Outline.back) context `Line            )      | Selection (Point (id, point)) ->        (* As before, mark the selected path *) @@ -265,22 +268,22 @@ let on_change canva mouse_position timer state =        List.iter          state.paths -        ~f:(fun path -> -            if id = path.Outline.id then +        ~f:(fun outline -> +            if id = outline.Outline.id then                let path = begin match pos with                  | Some pos ->                    let pos_v2 = Gg.V2.of_tuple pos in                    if Elements.Timer.delay timer < 0.3 then -                    path.Outline.path +                    outline.Outline.path                    else                      let point' = Path.Point.copy point pos_v2 in -                    begin match Path.Fixed.replace_point path.Outline.path point' with -                      | None -> path.Outline.path +                    begin match Path.Fixed.replace_point outline.Outline.path point' with +                      | None -> outline.Outline.path                        | Some p -> p                      end -                | None -> path.Outline.path end in -              Layer.Paths.to_canva (module Layer.Paths.ReprFixed) (path, path) context `Line +                | None -> outline.Outline.path end in +              Layer.Paths.to_canva (module Path.Fixed) (path, outline.Outline.back) context `Line            );        (* Now draw the selected point *) diff --git a/script.it/state.ml b/script.it/state.ml index 403efbe..fd35554 100755 --- a/script.it/state.ml +++ b/script.it/state.ml @@ -21,10 +21,7 @@ type render_event =      `Rendering of Layer.Paths.printer    ] -type worker_event = -  [ `Basic of Jv.t -  | `Complete of Outline.t -  ] +type worker_event = Worker_messages.from_worker  type events =    [ canva_events @@ -280,31 +277,31 @@ let do_action          | Some _ ->            let current = insert_or_replace state point stamp state.current in -          let paths = - -            let path = Path.Fixed.to_fixed -                (module Path.Path_Builder) -                current in - -            (* Create a copy from the path with all the interior points *) -            let back = Path.Fixed.map -                path -                (fun pt -> Path.Point.copy pt @@ Path.Point.get_coord' pt) in - -            let last = -              { Outline.path = path -              ; Outline.back = back -              ; Outline.id = Outline.get_id -              } -            in - -            let () = post worker (`Complete last) in -            last::state.paths -          and current = Path.Path_Builder.empty in - -          { state with -            mode = Out -          ; paths; current } +          let path = Path.Fixed.to_fixed +              (module Path.Path_Builder) +              current in + +          (* Create a copy from the path with all the interior points *) +          let back = Path.Fixed.map +              path +              (fun pt -> Path.Point.copy pt @@ Path.Point.get_coord' pt) in + +          let last = +            { Outline.path = path +            ; Outline.back = back +            ; Outline.id = Outline.get_id () +            } +          in + +          (* Send to the worker for a full review *) +          let () = post worker (`Complete last) in + +          let state = +            { state with +              mode = Out +            ; paths = last::state.paths +            ; current = Path.Path_Builder.empty } in +          state          (* Else, check if there is a curve under the cursor, and remove it *)          | None -> @@ -357,8 +354,8 @@ let do_action                     Layer.Paths.to_svg                       ~color:Blog.Nord.nord0 -                     (module Layer.Paths.ReprFixed) -                     (path.Outline.path, path.Outline.path) +                     (module Path.Fixed) +                     (path.Outline.path, path.Outline.back)                       state.rendering                   )) in @@ -388,20 +385,17 @@ let do_action      | `Rendering rendering, _ ->        { state with rendering} - -    | `Basic t, _ -> +    | `Other t, _ ->        Console.(log [t]);        state -    | `Complete path, _ -> -      let id = path.Outline.id in -      let paths = List.map state.paths +    | `Complete newPath, _ -> +      let paths = List.map +          state.paths            ~f:(fun line -> -              let id' = line.Outline.id in -              match id = id' with -              | false -> line -              | true -> path -            ) in +              match newPath.Outline.id = line.Outline.id with +              | true -> newPath +              | false -> line) in        { state with paths } diff --git a/script.it/worker.ml b/script.it/worker.ml index 898df39..51fe49c 100755 --- a/script.it/worker.ml +++ b/script.it/worker.ml @@ -19,7 +19,7 @@ let execute (command: [> Worker_messages.to_worker]) =          path          (fun pt -> Path.Point.copy pt @@ Path.Point.get_coord' pt) in      let=? back = Path.Fixed.rebuild back in -    Worker.post_message (`Complete {outline with path; back}) +    post_message (`Complete {outline with path; back})    (* Remove the point from the main line, and reevaluate the whole path *)    | `DeletePoint (point, outline) -> @@ -28,7 +28,7 @@ let execute (command: [> Worker_messages.to_worker]) =          path          (fun pt -> Path.Point.copy pt @@ Path.Point.get_coord' pt) in      let=? back = Path.Fixed.rebuild back in -    Worker.post_message (`Complete {outline with path; back}) +    post_message (`Complete {outline with path; back})    (* Only evaluate the interior *)    | `Back outline -> @@ -36,7 +36,7 @@ let execute (command: [> Worker_messages.to_worker]) =          outline.Outline.path          (fun pt -> Path.Point.copy pt @@ Path.Point.get_coord' pt) in      let=? back = Path.Fixed.rebuild back in -    Worker.post_message (`Complete {outline with back}) +    post_message (`Complete {outline with back})    | _ ->      post_message (`Other (Js.string "Unknown message received")) diff --git a/script.it/worker_messages/worker_messages.ml b/script.it/worker_messages/worker_messages.ml index a4d05c8..b33bb23 100755 --- a/script.it/worker_messages/worker_messages.ml +++ b/script.it/worker_messages/worker_messages.ml @@ -7,6 +7,6 @@ type to_worker = [  ]  type from_worker = [ -  | `Complete of Path.Fixed.t +  | `Complete of Outline.t    | `Other of Js.js_string Js.t  ] | 
