Source file Ps_graphics.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
type matrix = { a : float; b : float; c : float; d : float; tx : float; ty : float }
let identity = { a = 1.; b = 0.; c = 0.; d = 1.; tx = 0.; ty = 0. }
let translation tx ty = { identity with tx; ty }
let scaling sx sy = { identity with a = sx; d = sy }
let rotation deg =
let r = deg *. Float.pi /. 180. in
{ identity with a = Float.cos r; b = Float.sin r; c = -.Float.sin r; d = Float.cos r }
let transform m (x, y) = ((m.a *. x) +. (m.c *. y) +. m.tx, (m.b *. x) +. (m.d *. y) +. m.ty)
let dtransform m (x, y) = ((m.a *. x) +. (m.c *. y), (m.b *. x) +. (m.d *. y))
let concat m ctm =
let tx, ty = transform ctm (m.tx, m.ty) in
{
a = (ctm.a *. m.a) +. (ctm.c *. m.b);
b = (ctm.b *. m.a) +. (ctm.d *. m.b);
c = (ctm.a *. m.c) +. (ctm.c *. m.d);
d = (ctm.b *. m.c) +. (ctm.d *. m.d);
tx;
ty;
}
let invert m =
let det = (m.a *. m.d) -. (m.b *. m.c) in
if det = 0. then identity
else
let a = m.d /. det and b = -.m.b /. det and c = -.m.c /. det and d = m.a /. det in
{ a; b; c; d; tx = -.((a *. m.tx) +. (c *. m.ty)); ty = -.((b *. m.tx) +. (d *. m.ty)) }
let scale_of m = Float.sqrt (Float.abs ((m.a *. m.d) -. (m.b *. m.c)))
type point = float * float
type segment = Move of point | Line of point | Curve of point * point * point | Close
let arc (cx, cy) r a1 a2 ~clockwise =
let rad d = d *. Float.pi /. 180. in
let sweep =
if clockwise then -.Float.rem (Float.rem (a1 -. a2) 360. +. 360.) 360. else Float.rem (Float.rem (a2 -. a1) 360. +. 360.) 360.
in
let sweep = if sweep = 0. && a1 <> a2 then if clockwise then -360. else 360. else sweep in
let pieces = max 1 (int_of_float (Float.ceil (Float.abs sweep /. 90.))) in
let step = sweep /. float_of_int pieces in
let at deg = (cx +. (r *. Float.cos (rad deg)), cy +. (r *. Float.sin (rad deg))) in
let k = 4. /. 3. *. Float.tan (rad step /. 4.) *. r in
let piece i =
let t0 = a1 +. (step *. float_of_int i) and t1 = a1 +. (step *. float_of_int (i + 1)) in
let (x0, y0), (x3, y3) = (at t0, at t1) in
let c1 = (x0 -. (k *. Float.sin (rad t0)), y0 +. (k *. Float.cos (rad t0))) in
let c2 = (x3 +. (k *. Float.sin (rad t1)), y3 -. (k *. Float.cos (rad t1))) in
(c1, c2, (x3, y3))
in
(at a1, List.init pieces piece)
let mid (x0, y0) (x1, y1) = ((x0 +. x1) /. 2., (y0 +. y1) /. 2.)
let distance (px, py) (x0, y0) (x1, y1) =
let dx = x1 -. x0 and dy = y1 -. y0 in
let len = Float.sqrt ((dx *. dx) +. (dy *. dy)) in
if len = 0. then Float.sqrt (((px -. x0) ** 2.) +. ((py -. y0) ** 2.)) else Float.abs ((dx *. (y0 -. py)) -. ((x0 -. px) *. dy)) /. len
let rec bezier tolerance depth p0 p1 p2 p3 : point list =
if depth > 12 || (distance p1 p0 p3 <= tolerance && distance p2 p0 p3 <= tolerance) then [ p3 ]
else
let p01 = mid p0 p1 and p12 = mid p1 p2 and p23 = mid p2 p3 in
let p012 = mid p01 p12 and p123 = mid p12 p23 in
let m = mid p012 p123 in
bezier tolerance (depth + 1) p0 p01 p012 m @ bezier tolerance (depth + 1) m p123 p23 p3
let flatten ?(tolerance = 0.25) (segments : segment list) : (point list * bool) list =
let finish acc current closed = match current with [] | [ _ ] -> acc | pts -> (List.rev pts, closed) :: acc in
let rec go acc current segments =
match segments with
| [] -> List.rev (finish acc current false)
| Move p :: rest -> go (finish acc current false) [ p ] rest
| Line p :: rest -> go acc (p :: current) rest
| Curve (p1, p2, p3) :: rest ->
let p0 = match current with p :: _ -> p | [] -> p1 in
go acc (List.rev_append (bezier tolerance 0 p0 p1 p2 p3) current) rest
| Close :: rest ->
let start = match List.rev current with p :: _ -> [ p ] | [] -> [] in
go (finish acc current true) start rest
in
go [] [] segments