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
type 'a poly = { corners : (Vec3.t * bool) list; data : 'a }
type plane = Vec3.t * float
let eps = 1e-5
let plane_of corners =
let ps = List.map fst corners in
let n = Vec3.face_normal ps in
if Vec3.length n < 0.5 then None else Some (n, Vec3.dot n (List.hd ps))
let side ((n, d) : plane) p =
let s = Vec3.dot n p -. d in
if s > eps then 1 else if s < -.eps then -1 else 0
let split ((n, d) as plane : plane) corners =
match corners with
| [] -> ([], [])
| first :: _ ->
let rec sides acc = function
| a :: (b :: _ as rest) -> sides ((a, b) :: acc) rest
| [ a ] -> List.rev ((a, first) :: acc)
| [] -> List.rev acc
in
let dist p = Vec3.dot n p -. d in
let front, back =
List.fold_left
(fun (front, back) ((p, drawn), (q, _)) ->
let sp = side plane p and sq = side plane q in
let cut () =
let t = dist p /. (dist p -. dist q) in
Vec3.add p (Vec3.scale t (Vec3.sub q p))
in
match (sp, sq) with
| 1, -1 ->
let i = cut () in
((i, false) :: (p, drawn) :: front, (i, drawn) :: back)
| -1, 1 ->
let i = cut () in
((i, drawn) :: front, (i, false) :: (p, drawn) :: back)
| 1, _ -> ((p, drawn) :: front, back)
| -1, _ -> (front, (p, drawn) :: back)
| _ ->
((p, drawn && sq >= 0) :: front, (p, drawn && sq <= 0) :: back))
([], []) (sides [] corners)
in
let keep l = if List.length l >= 3 then List.rev l else [] in
(keep front, keep back)
type 'a t = Empty | Node of { plane : plane; here : 'a poly list; front : 'a t; back : 'a t }
let area corners =
let ps = List.map fst corners in
match ps with
| [] -> 0.
| first :: _ ->
let rec go acc = function a :: (b :: _ as rest) -> go (Vec3.add acc (Vec3.cross a b)) rest | [ a ] -> Vec3.add acc (Vec3.cross a first) | [] -> acc in
Vec3.length (go (0., 0., 0.) ps) /. 2.
let rec build_in_order polys =
match polys with
| [] -> Empty
| p :: rest -> (
match plane_of p.corners with
| None -> build_in_order rest
| Some plane ->
let here, front, back =
List.fold_left
(fun (here, front, back) q ->
let sides = List.map (fun (c, _) -> side plane c) q.corners in
if List.for_all (( = ) 0) sides then (q :: here, front, back)
else if List.for_all (fun s -> s >= 0) sides then (here, q :: front, back)
else if List.for_all (fun s -> s <= 0) sides then (here, front, q :: back)
else
let f, b = split plane q.corners in
let piece c l = if c = [] then l else { q with corners = c } :: l in
(here, piece f front, piece b back))
([ p ], [], []) rest
in
Node { plane; here = List.rev here; front = build_in_order (List.rev front); back = build_in_order (List.rev back) })
let build polys = build_in_order (List.stable_sort (fun p q -> compare (area q.corners) (area p.corners)) polys)
let back_to_front ~eye t =
let rec walk t acc =
match t with
| Empty -> acc
| Node { plane; here; front; back } ->
if side plane eye >= 0 then walk back (here @ walk front acc) else walk front (here @ walk back acc)
in
walk t []
let rec size = function Empty -> 0 | Node { here; front; back; _ } -> List.length here + size front + size back