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_modeler/Modeler.ml.html
Source file Modeler.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(* 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 Modeler.mli *) type v3 = float * float * float type shape = Cube | Sphere | Cylinder | Cone | Torus of float | Ground type look = Plain | Checker | Marble | Wood type material = { color : int; look : look; mirror : float; glass : bool } type kind = Mesh of shape * material | Point_light of int | Sun_light of int | Camera type boolean = Difference | Union | Intersect type obj = { name : string; kind : kind; location : v3; rotation : v3; scale : v3; hidden : bool; modifier : (boolean * string) option; } type t = obj list let grey = { color = 0xcccccc; look = Plain; mirror = 0.; glass = false } let placed name kind location = { name; kind; location; rotation = (0., 0., 0.); scale = (1., 1., 1.); hidden = false; modifier = None } let default = [ placed "Cube" (Mesh (Cube, grey)) (0., 0., 0.); placed "Light" (Point_light 0xffffff) (4.1, 1., 5.9); placed "Camera" Camera (7.4, -6.9, 5.); ] let find t name = List.find_opt (fun o -> o.name = name) t let fresh_name base t = if find t base = None then base else let rec go i = let n = Printf.sprintf "%s.%03d" base i in if find t n = None then n else go (i + 1) in go 1 let add kind base t = let name = fresh_name base t in (t @ [ placed name kind (0., 0., 0.) ], name) let update name f t = List.map (fun o -> if o.name = name then f o else o) t let remove names t = List.filter_map (fun o -> if List.mem o.name names then None else Some (match o.modifier with Some (_, c) when List.mem c names -> { o with modifier = None } | _ -> o)) t let duplicate names t = List.fold_left (fun (t, fresh) o -> if List.mem o.name names then let base = match String.index_opt o.name '.' with Some i -> String.sub o.name 0 i | None -> o.name in let name = fresh_name base t in (t @ [ { o with name } ], fresh @ [ name ]) else (t, fresh)) (t, []) t let is_cutter t name = List.exists (fun o -> match o.modifier with Some (_, c) -> c = name | None -> false) t (*****************************************************************************) (* Transforms *) (*****************************************************************************) let rad a = a *. Float.pi /. 180. let rot_x a (x, y, z) = let c = Float.cos (rad a) and s = Float.sin (rad a) in (x, (c *. y) -. (s *. z), (s *. y) +. (c *. z)) let rot_y a (x, y, z) = let c = Float.cos (rad a) and s = Float.sin (rad a) in ((c *. x) +. (s *. z), y, (-.s *. x) +. (c *. z)) let rot_z a (x, y, z) = let c = Float.cos (rad a) and s = Float.sin (rad a) in ((c *. x) -. (s *. y), (s *. x) +. (c *. y), z) let transform o (px, py, pz) = let sx, sy, sz = o.scale and rx, ry, rz = o.rotation and lx, ly, lz = o.location in let x, y, z = rot_z rz (rot_y ry (rot_x rx (sx *. px, sy *. py, sz *. pz))) in (lx +. x, ly +. y, lz +. z) let to_world (x, y, z) = (x, z, -.y) let translate (dx, dy, dz) o = let x, y, z = o.location in { o with location = (x +. dx, y +. dy, z +. dz) } let rotate_point axis a p = match axis with 0 -> rot_x a p | 1 -> rot_y a p | _ -> rot_z a p let turn axis a o = let x, y, z = o.rotation in { o with rotation = (match axis with 0 -> (x +. a, y, z) | 1 -> (x, y +. a, z) | _ -> (x, y, z +. a)) } let resize (kx, ky, kz) o = let x, y, z = o.scale in { o with scale = (x *. kx, y *. ky, z *. kz) } (*****************************************************************************) (* Wireframes *) (*****************************************************************************) let add3 (a, b, c) (x, y, z) = (a +. x, b +. y, c +. z) let sub3 (a, b, c) (x, y, z) = (a -. x, b -. y, c -. z) let scale3 k (x, y, z) = (k *. x, k *. y, k *. z) let cross3 (a, b, c) (x, y, z) = ((b *. z) -. (c *. y), (c *. x) -. (a *. z), (a *. y) -. (b *. x)) let norm3 v = let x, y, z = v in let l = Float.sqrt ((x *. x) +. (y *. y) +. (z *. z)) in if l = 0. then v else scale3 (1. /. l) v (* a closed polyline as its segments *) let loop ps = List.mapi (fun i p -> (p, List.nth ps ((i + 1) mod List.length ps))) ps let circle ?(n = 24) f = loop (List.init n (fun i -> f (2. *. Float.pi *. float_of_int i /. float_of_int n))) (* the primitive's edges, in its own space *) let edges = function | Cube -> let c = [ (-1., -1.); (1., -1.); (1., 1.); (-1., 1.) ] in let at z = loop (List.map (fun (x, y) -> (x, y, z)) c) in at (-1.) @ at 1. @ List.map (fun (x, y) -> ((x, y, -1.), (x, y, 1.))) c | Sphere -> let ring lat = circle (fun a -> (Float.cos lat *. Float.cos a, Float.cos lat *. Float.sin a, Float.sin lat)) in let meridian lon = circle (fun a -> (Float.cos a *. Float.cos lon, Float.cos a *. Float.sin lon, Float.sin a)) in List.concat_map ring [ -1.; -0.5; 0.; 0.5; 1. ] @ List.concat_map (fun i -> meridian (float_of_int i *. Float.pi /. 4.)) [ 0; 1; 2; 3 ] | Cylinder -> let at z = circle (fun a -> (Float.cos a, Float.sin a, z)) in at (-1.) @ at 1. @ List.init 8 (fun i -> let a = float_of_int i *. Float.pi /. 4. in ((Float.cos a, Float.sin a, -1.), (Float.cos a, Float.sin a, 1.))) | Cone -> circle (fun a -> (Float.cos a, Float.sin a, -1.)) @ List.init 8 (fun i -> let a = float_of_int i *. Float.pi /. 4. in ((Float.cos a, Float.sin a, -1.), (0., 0., 1.))) | Torus r -> let ring rr z = circle ~n:32 (fun a -> (rr *. Float.cos a, rr *. Float.sin a, z)) in let tube i = let b = float_of_int i *. Float.pi /. 4. in circle ~n:12 (fun a -> ((1. +. (r *. Float.cos a)) *. Float.cos b, (1. +. (r *. Float.cos a)) *. Float.sin b, r *. Float.sin a)) in ring (1. +. r) 0. @ ring (1. -. r) 0. @ ring 1. r @ ring 1. (-.r) @ List.concat_map tube [ 0; 1; 2; 3; 4; 5; 6; 7 ] | Ground -> List.concat_map (fun i -> let v = float_of_int i in [ ((v, -10., 0.), (v, 10., 0.)); ((-10., v, 0.), (10., v, 0.)) ]) [ -10; -8; -6; -4; -2; 0; 2; 4; 6; 8; 10 ] let wires ?(target = (0., 0., 0.)) o = let at = o.location in match o.kind with | Mesh (Ground, _) -> (* the ground only rises and falls: it is the plane z = height *) let _, _, h = at in List.map (fun (a, b) -> (add3 a (0., 0., h), add3 b (0., 0., h))) (edges Ground) | Mesh (s, _) -> List.map (fun (a, b) -> (transform o a, transform o b)) (edges s) | Point_light _ -> let r = 0.25 in circle ~n:12 (fun a -> add3 at (r *. Float.cos a, r *. Float.sin a, 0.)) @ List.map (fun d -> (add3 at (scale3 r d), add3 at (scale3 (2.2 *. r) d))) [ (1., 0., 0.); (-1., 0., 0.); (0., 1., 0.); (0., -1., 0.); (0., 0., 1.); (0., 0., -1.) ] | Sun_light _ -> let d = norm3 (sub3 target at) in circle ~n:12 (fun a -> add3 at (0.3 *. Float.cos a, 0.3 *. Float.sin a, 0.)) @ [ (at, add3 at (scale3 2. d)) ] | Camera -> (* a pyramid from the eye, its base a unit ahead, a triangle above it saying which way is up *) let f = norm3 (sub3 target at) in let r = norm3 (cross3 f (0., 0., 1.)) in let u = cross3 r f in let corner x y = add3 at (add3 f (add3 (scale3 x r) (scale3 y u))) in let c = [ corner (-0.5) (-0.35); corner 0.5 (-0.35); corner 0.5 0.35; corner (-0.5) 0.35 ] in loop c @ List.map (fun p -> (at, p)) c @ loop [ corner (-0.3) 0.45; corner 0.3 0.45; corner 0. 0.7 ]
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>