Source file spline_lib.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
open Format
exception Not_implemented of string
let not_implemented s = raise (Not_implemented s)
module Error = struct
let max_absc t f =
invalid_arg
( f ^ ": the abscissa given is greater than max_abscissa : "
^ string_of_float t )
let min_absc ?value f =
let value =
match value with None -> "" | Some f -> ": " ^ string_of_float f
in
invalid_arg (f ^ ": the abscissa given is smaller than min_abscissa" ^ value)
let absc_point f = invalid_arg (f ^ ": a point has only the abscissa 0.")
let dir_point f = invalid_arg (f ^ ": a point has no direction.")
end
module P = Point_lib
type point = P.t
open Point_lib
open Point_lib.Infix
let one_to_one2 f acc a b =
List.fold_left
(fun acc ea -> List.fold_left (fun acc eb -> f acc ea eb) acc b)
acc a
let debug = Spline.debug
type spline = Spline.t
type abscissa = Spline.abscissa
type path_ = { pl : spline list; cycle : bool }
type path = Point of point | Path of path_
let is_closed = function Point _ -> false | Path p -> p.cycle
let is_a_point = function Point p -> Some p | Path _ -> None
let rec print_list sep prf fmt = function
| [] -> ()
| [ x ] -> prf fmt x
| x :: xs ->
prf fmt x;
sep fmt ();
print_list sep prf fmt xs
let semicolon fmt () = Format.fprintf fmt ";@ "
let print_splines = print_list semicolon Spline.print
let print fmt = function
| Point p -> fprintf fmt "@[Point %a@]" P.print p
| Path p -> fprintf fmt "@[cycle : %b; %a@]" p.cycle print_splines p.pl
let create_point p = Point p
let create a b c d = Path { pl = [ Spline.create a b c d ]; cycle = false }
let create_line a d = create a a d d
let create_lines = function
| [] -> assert false
| [ a ] -> Point a
| l ->
let rec aux = function
| [] | [ _ ] -> []
| a :: (d :: _ as l) -> Spline.create a a d d :: aux l
in
Path { pl = aux l; cycle = false }
let min_abscissa = function Path _ -> 0. | Point _ -> 0.
let length = function Point _ -> 0 | Path p -> List.length p.pl
let max_abscissa p = float (length p)
let with_last f p acc =
let rec aux = function
| [] -> assert false
| [ e ] ->
let sd = Spline.right_point e and sc = Spline.right_control_point e in
e :: f sc sd :: acc
| a :: l -> a :: aux l
in
{ p with pl = aux p.pl }
let add_end p c d =
match p with
| Point p -> create p c c d
| Path p ->
Path (with_last (fun mb a -> Spline.create a ((2. */ a) -/ mb) c d) p [])
let add_end_line p d =
match p with
| Point p -> create_line p d
| Path p -> Path (with_last (fun _ a -> Spline.create a a d d) p [])
let add_end_spline p sb sc d =
match p with
| Point p -> create p sb sc d
| Path p -> Path (with_last (fun _ a -> Spline.create a sb sc d) p [])
let abscissa_to f pl t =
let tn, tf = (truncate t, t -. floor t) in
let rec aux tn l =
match (tn, l) with
| _, [] -> Error.max_absc t "abscissa_to"
| 1, [ a ] when tf = 0. -> f a 1.
| 0, a :: _ -> f a tf
| _, _ :: l -> aux (pred tn) l
in
if 0. > t then Error.min_absc "abscissa_to" else aux tn pl
let abscissa_to_point p0 t =
match p0 with
| Path p -> abscissa_to Spline.point_of_s p.pl t
| Point p when t = 0. -> p
| Point _ -> Error.absc_point "abscissa_to_point"
let direction_of_abscissa p0 t =
match p0 with
| Point _ -> Error.dir_point "direction_of_abscissa"
| Path p -> abscissa_to Spline.direction p.pl t
let unprecise_bounding_box = function
| Path s ->
let x_min, y_min, x_max, y_max =
P.list_min_max_float Spline.bounding_box s.pl
in
({ x = x_min; y = y_min }, { x = x_max; y = y_max })
| Point s -> (s, s)
let bounding_box = function
| Path s ->
let x_min, y_min, x_max, y_max =
P.list_min_max_float Spline.precise_bounding_box s.pl
in
({ x = x_min; y = y_min }, { x = x_max; y = y_max })
| Point s -> (s, s)
exception Found of (float * float)
let one_intersection a b =
match (a, b) with
| Path a, Path b -> (
try
one_to_one2
(fun () a b ->
try raise (Found (Spline.one_intersection a b))
with Not_found -> ())
() a.pl b.pl;
if debug then Format.printf "one_intersection : Not_found@.";
raise Not_found
with Found a -> a )
| _ ->
if debug then Format.printf "one_intersection : Not_found not two paths@.";
raise Not_found
let intersection a b =
match (a, b) with
| Path a, Path b ->
one_to_one2 (fun acc a b -> acc @ Spline.intersection a b) [] a.pl b.pl
| _ -> []
let fold_left f acc = function
| Path p -> List.fold_left (fun acc s -> Spline.apply4 (f acc) s) acc p.pl
| Point _ -> acc
let iter f = function
| Path p -> List.iter (Spline.apply4 f) p.pl
| Point _ -> ()
let ext_list = function [] -> assert false | a :: _ as l -> (a, l)
let append ap0 sb sc bp0 =
match bp0 with
| Path bp -> (
let fbpconv, bpconv = ext_list bp.pl in
match ap0 with
| Path ap ->
let spl =
with_last
(fun _ sa -> Spline.create sa sb sc (Spline.left_point fbpconv))
ap bpconv
in
Path { spl with cycle = false }
| Point p1 ->
Path
{
bp with
pl = Spline.create p1 sb sc (Spline.left_point fbpconv) :: bp.pl;
} )
| Point p2 -> (
match ap0 with
| Point p1 -> create p1 sb sc p2
| Path _ -> add_end_spline ap0 sb sc p2 )
let reverse x =
match x with
| Path p as p0 ->
let conv =
let max = max_abscissa p0 in
let min = min_abscissa p0 in
let sum = max +. min in
fun x -> sum -. x
in
let rec aux acc = function
| [] -> acc
| a :: l -> aux (Spline.reverse conv a :: acc) l
in
Path { p with pl = aux [] p.pl }
| Point _ as p -> p
let cast_path_to_point p = function Path { pl = []; _ } -> Point p | x -> x
let split_aux s t l =
match Spline.split s t with
| Spline.Min -> ([], Path { pl = s :: l; cycle = false })
| Spline.Max ->
let p =
cast_path_to_point (Spline.right_point s)
(Path { pl = l; cycle = false })
in
([ s ], p)
| Spline.InBetween (s1, s2) -> ([ s1 ], Path { pl = s2 :: l; cycle = false })
let split p0 t =
match p0 with
| Path p ->
let tn, tf = (truncate t, t -. floor t) in
let rec aux tn l =
match (tn, l) with
| _, [] -> Error.max_absc t "split"
| 1, [ a ] when tf = 0. -> split_aux a 1. l
| 0, a :: l -> split_aux a tf l
| _, a :: l ->
let p1, p2 = aux (pred tn) l in
(a :: p1, p2)
in
if 0. > t then Error.min_absc "split"
else
let p1, p2 = aux tn p.pl in
( cast_path_to_point
(Spline.left_point (List.hd p.pl))
(Path { pl = p1; cycle = false }),
p2 )
| Point _ when t = 0. -> (p0, p0)
| Point _ -> Error.absc_point "split"
let subpath p t1 t2 =
assert (t1 <= t2);
let t2 =
if ceil t1 = ceil t2 then (t2 -. t1) /. (ceil t1 -. t1) else t2 -. floor t1
in
fst (split (snd (split p t1)) t2)
let cut_before a b =
try
let t = fst (one_intersection b a) in
let res = snd (split b t) in
res
with Not_found -> b
let cut_after a b =
try
let b = reverse b in
reverse (snd (split b (fst (one_intersection b a))))
with Not_found -> b
let dist_min_point p point =
match p with
| Path p -> (
match p.pl with
| [] -> assert false
| x :: xs ->
let m = Spline.dist_min_point point x in
List.fold_left
(fun ((d1, _) as m1) x ->
let ((d2, _) as m2) = Spline.dist_min_point point x in
if d1 < d2 then m1 else m2)
m xs )
| Point p -> (P.dist2 p point, 0.)
let dist_min_path p1 p2 =
match (p1, p2) with
| Path p1, Path p2 -> (
match (p1.pl, p2.pl) with
| [], _ | _, [] -> assert false
| x :: xs, y :: ys ->
let acc = Spline.dist_min_spline x y in
one_to_one2
(fun ((d1, _) as m1) a b ->
let ((d2, _) as m2) = Spline.dist_min_spline a b in
if d1 < d2 then m1 else m2)
acc xs ys )
| (Path _ as p1), Point p2 ->
let d, a = dist_min_point p1 p2 in
(d, (a, 0.))
| Point p1, (Path _ as p2) ->
let d, a = dist_min_point p2 p1 in
(d, (0., a))
| Point p1, Point p2 -> (P.dist2 p1 p2, (0., 0.))
let translate t p =
match p with
| Path p -> Path { p with pl = List.map (Spline.translate t) p.pl }
| Point p -> Point (p +/ t)
let transform t = function
| Path p -> Path { p with pl = List.map (Spline.transform t) p.pl }
| Point p -> Point (P.transform t p)
let buildcycle _ _ = not_implemented "buildcycle"
let close = function
| Path p1 -> Path { p1 with cycle = true }
| Point _ -> invalid_arg "This path cannot be closed"
let of_bounding_box ({ x = x_min; y = y_min }, { x = x_max; y = y_max }) =
let dl = { x = x_min; y = y_min } in
let dr = { x = x_max; y = y_min } in
let ul = { x = x_min; y = y_max } in
let ur = { x = x_max; y = y_max } in
close (create_lines [ ul; ur; dr; dl; ul ])