package tiny_appkits
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Application engines from scratch: a spreadsheet, rich text, paint, draw, CAD, editors and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_appkits.appkit_pushpull/Bsp.ml.html
Source file Bsp.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(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Bsp.mli *) type 'a poly = { corners : (Vec3.t * bool) list; data : 'a } type plane = Vec3.t * float (* a hundredth of a millimetre, the model being in metres *) let eps = 1e-5 (* Newell's normal (Vec3.face_normal): right for a concave polygon or one with a keyhole, where the cross product of two sides is not *) 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 (* Sutherland-Hodgman, both halves at once. Going round the corners, a side from p to q (drawn or not): p goes to its own half (to both if on the plane); if the side crosses the plane, the crossing point goes to both, and the side's two pieces keep its flag, while the side the cut makes along the plane, from where the polygon leaves a half to where it comes back, is not drawn *) 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 on the plane: in both, its side drawn only in the half the side goes into (in both if it runs along) *) ((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 } (* half the length of the sum of the cross products of the corners taken in turn: the area of a flat polygon *) 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) }) (* the largest first: a wall seldom cuts the small faces around it (the sides of a window's recess are all behind it), while a small face often cuts a large one -- fewer cuts, fewer pieces, and fewer seams where the pieces meet *) 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 } -> (* acc is built from the nearest backwards: the far half last *) 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
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>