@bitefoundry/retrieviewer 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/ASSET-PROVENANCE.md +6 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +65 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/PLUGIN.md +13 -10
  7. package/PREREQUISITES.md +95 -0
  8. package/PUPPY-MEMORY.md +102 -1
  9. package/README.ko.md +110 -0
  10. package/README.md +117 -59
  11. package/RELEASING.md +23 -12
  12. package/THIRD-PARTY-NOTICES.md +5 -3
  13. package/braille.mjs +35 -9
  14. package/examples/live.mjs +2 -2
  15. package/integrations/linux-overlay/overlay.py +2 -2
  16. package/integrations/macos-overlay/overlay.jxa +8 -6
  17. package/integrations/opencode/runtime.mjs +4 -4
  18. package/integrations/opencode/tui.tsx +1 -1
  19. package/integrations/windows-overlay/Overlay.cs +4 -4
  20. package/integrations/windows-overlay/protocol.mjs +11 -7
  21. package/package.json +34 -13
  22. package/puppy-comfort-sprites.mjs +25 -0
  23. package/puppy-companion-comfort-q5.bin.gz +0 -0
  24. package/puppy-companion-comfort.json +77 -0
  25. package/puppy-companion-life.mjs +49 -16
  26. package/puppy-companion-objects.mjs +12 -0
  27. package/puppy-frame-predictive.mjs +15 -9
  28. package/puppy-frame-store.mjs +2 -2
  29. package/puppy-paper-world.mjs +14 -0
  30. package/puppy-paper.mjs +65 -14
  31. package/retrieviewer-plugin.d.mts +1 -1
  32. package/retrieviewer-plugin.mjs +1 -1
  33. package/retrieviewer.d.mts +10 -4
  34. package/retrieviewer.mjs +20 -14
  35. package/locomotion-core.mjs +0 -21
  36. package/locomotion-puppy.animal +0 -0
  37. package/vendor/procedural-animals/package.json +0 -40
  38. package/vendor/procedural-animals/src/animal.js +0 -146
  39. package/vendor/procedural-animals/src/bake.js +0 -51
  40. package/vendor/procedural-animals/src/build.js +0 -81
  41. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  42. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  43. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  44. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  45. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  46. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  47. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  48. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  49. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  50. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  51. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  52. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  53. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  54. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  55. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  56. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  57. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  58. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  59. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  60. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  61. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  62. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  63. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  64. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  65. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  66. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  67. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  68. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  69. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  70. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  71. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  72. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  73. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  74. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  75. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  76. package/vendor/procedural-animals/src/index.d.ts +0 -98
  77. package/vendor/procedural-animals/src/index.js +0 -29
  78. package/vendor/procedural-animals/src/registry.js +0 -48
  79. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  80. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  81. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  82. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  83. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  84. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  85. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  86. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  87. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  88. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  89. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  90. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  91. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  92. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  93. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  94. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  95. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  96. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  97. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  98. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  99. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  100. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  101. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  102. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  103. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  104. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  105. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  106. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  107. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  108. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  109. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  110. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  111. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  112. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  113. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  114. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  115. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  116. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  117. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  118. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  119. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  120. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  121. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  122. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  123. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  124. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  125. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  126. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  127. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  128. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  129. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  130. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  131. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  132. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  133. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  134. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  135. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  136. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  137. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  138. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  139. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  140. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  141. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  142. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  143. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  144. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  145. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  146. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  147. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  148. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  149. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  150. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  151. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  152. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  153. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  154. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  155. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  156. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  157. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  158. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  159. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  160. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  161. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  162. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  163. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  164. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  165. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  166. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  167. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  168. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  169. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  170. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  171. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  172. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  173. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  174. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  175. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  176. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  177. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  178. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  179. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  180. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  181. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  182. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  183. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  184. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  185. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  186. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  187. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  188. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  189. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  190. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  191. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  192. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  193. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  194. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  195. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  196. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  197. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  198. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  199. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  200. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  201. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  202. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  203. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  204. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  205. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  206. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  207. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  208. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  209. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  210. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  211. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  212. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  213. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  214. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  215. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  216. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  217. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  218. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  219. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  220. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  221. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  222. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  223. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  224. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  225. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  226. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  227. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  228. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  229. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  230. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  231. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  232. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  233. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  234. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  235. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  236. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  237. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  238. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  239. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  240. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  241. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  242. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  243. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  244. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  245. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  246. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  247. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  248. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  249. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  250. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  251. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  252. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  253. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  254. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,76 +0,0 @@
1
- // Standard snake skeleton (the names the snake motion engine, core/motion/snake.js, expects).
2
- //
3
- // Axial chain, head to tail tip: nose <- v0 -> v1 -> ... -> vN
4
- // head v0 -> nose (v0 = occiput / atlas, the head's pivot on the neck)
5
- // spine0.. v(i) -> v(i+1) (N bones, each pointing tail-ward; spine0 is the root bone)
6
- // Rigid parts riding on the head (each meshed as its own rigid region):
7
- // jaw jawHinge -> jawTip (lower jaw, rotates about the quadrate hinge)
8
- // tongue tongueBase -> tongueFork (tongue shaft; slides forward out of its sheath in the jaw)
9
- // tongueTip tongueFork -> tongueTip (the forked tips; flick up and down)
10
- // fang fangBase -> fangTip (hinged maxillary fangs of vipers; folded along the palate at rest)
11
- // A rattle (or any tail-tip ornament) is meshed as a rigid region on the last spine bone.
12
- //
13
- // Bind pose: the snake lies straight along -Z with its belly on y = 0; joint vi sits on the body axis
14
- // at height = the distance from the axis to the belly (the engine reads it back as the axis height).
15
- // Joints are on the midline (no left/right pairs).
16
-
17
- /**
18
- * Spine joints v0..vN from a list of segment lengths and axis heights.
19
- * @param J joint table to fill
20
- * @param z0 z of v0 (the occiput)
21
- * @param lens N segment lengths (m), head -> tail
22
- * @param height (sigma, t) -> axis height above the belly at arc distance sigma from v0 (t = sigma / total)
23
- */
24
- export function snakeSpine(J, z0, lens, height) {
25
- const total = lens.reduce((a, b) => a + b, 0);
26
- let s = 0;
27
- J.v0 = [0, height(0, 0), z0];
28
- // place joints by arc length along the (slightly tilted) axis so bone lengths equal `lens`
29
- let z = z0;
30
- for (let i = 0; i < lens.length; i++) {
31
- s += lens[i];
32
- const y = height(s, s / total);
33
- const dy = y - J['v' + i][1];
34
- z -= Math.sqrt(Math.max(1e-10, lens[i] * lens[i] - dy * dy));
35
- J['v' + (i + 1)] = [0, y, z];
36
- }
37
- return J;
38
- }
39
-
40
- /** Bone list: head, jaw, tongue, tongueTip, fang, spine0..spine(n-1). */
41
- export function snakeBones(n, { neck = 4, tail = 0 } = {}) {
42
- const b = [];
43
- for (let i = 0; i < n; i++) {
44
- const region = i < neck ? 'neck' : i >= n - tail ? 'tail' : 'torso';
45
- b.push(['spine' + i, 'v' + i, 'v' + (i + 1), i === 0 ? null : 'spine' + (i - 1), { region }]);
46
- }
47
- b.push(
48
- ['head', 'v0', 'nose', 'spine0', { region: 'head' }],
49
- ['jaw', 'jawHinge', 'jawTip', 'head', { region: 'jaw', group: 'jaw' }],
50
- ['tongue', 'tongueBase', 'tongueFork', 'jaw', { region: 'tongue', group: 'tongue' }],
51
- ['tongueTip', 'tongueFork', 'tongueTip', 'tongue', { region: 'tongue', group: 'tongue' }],
52
- ['fang', 'fangBase', 'fangTip', 'head', { region: 'fang', group: 'fang' }],
53
- );
54
- return b;
55
- }
56
-
57
- /** Axial chain description for buildRig (skin weights along nose -> tail tip). */
58
- export function snakeAxial(n) {
59
- return {
60
- points: ['nose', ...Array.from({ length: n + 1 }, (_, i) => 'v' + i)],
61
- bones: ['head', ...Array.from({ length: n }, (_, i) => 'spine' + i)],
62
- };
63
- }
64
-
65
- /** Segment lengths for n bones over `total` metres: uniform, shortened toward the tail tip so the
66
- * thin tail can curl tightly (tipShrink = length of the last bone relative to the others). */
67
- export function snakeSegments(n, total, { tipShrink = 0.55, tipBones = 8, neckShrink = 0.85, neckBones = 3 } = {}) {
68
- const w = Array.from({ length: n }, (_, i) => {
69
- let k = 1;
70
- if (i >= n - tipBones) k = 1 - (1 - tipShrink) * ((i - (n - tipBones) + 1) / tipBones);
71
- if (i < neckBones) k = neckShrink + (1 - neckShrink) * (i / neckBones);
72
- return k;
73
- });
74
- const sw = w.reduce((a, b) => a + b, 0);
75
- return w.map((k) => (k * total) / sw);
76
- }
@@ -1,110 +0,0 @@
1
- // Standard spider skeleton (the names the spider motion engine expects) and helpers to build it.
2
- //
3
- // Axial (nose -> tail): front (clypeus) - ceph (cephalic / thoracic boundary) - pedA (rear of the
4
- // carapace, pedicel start) - pedB (pedicel end, abdomen front) - abdEnd (above the spinnerets) -
5
- // spinTip. Bones: head (ceph -> front, rigid on the prosoma, carries the eyes), prosoma (root,
6
- // pedA -> ceph), pedicel, abdomen, spinnerets.
7
- // Chelicerae: cheBase{S} -> cheTip{S} (bone chelicera{S}), fang cheTip{S} -> fangTip{S} (fang{S}).
8
- // Legs i = 1..4 (I front .. IV hind), 7 segments each: coxa, troch, femur, patella, tibia, meta,
9
- // tarsus. The joint named after a segment is where that segment starts; `claw{i}{S}` is the tip.
10
- // Bones carry the same names as their start joint (coxa1L = coxa1L -> troch1L ...); groups 'L1' ..
11
- // 'R4' (the metrics and state.legs keys); the tarsus is the 'foot' region.
12
- // Pedipalps: palpCoxa, palpTroch, palpFemur, palpPatella, palpTibia, palpTarsus (+ palpTip), group
13
- // 'P{S}', no foot region (palps are not walking legs).
14
- import { add, mul } from '../math/vec.js';
15
-
16
- export const LEG_SEGS = ['coxa', 'troch', 'femur', 'patella', 'tibia', 'meta', 'tarsus'];
17
- export const PALP_SEGS = ['palpCoxa', 'palpTroch', 'palpFemur', 'palpPatella', 'palpTibia', 'palpTarsus'];
18
- export const LEG_KEYS = ['L1', 'L2', 'L3', 'L4', 'R1', 'R2', 'R3', 'R4'];
19
- export const AXIAL_POINTS = ['front', 'ceph', 'pedA', 'pedB', 'abdEnd', 'spinTip'];
20
- export const AXIAL_BONES = ['head', 'prosoma', 'pedicel', 'abdomen', 'spinnerets'];
21
-
22
- /** joint names of leg i (1..4) on side S ('L' | 'R'), base -> claw tip */
23
- export const legJoints = (i, S) => [...LEG_SEGS.map((g) => g + i + S), 'claw' + i + S];
24
- export const legBones = (i, S) => LEG_SEGS.map((g) => g + i + S);
25
- export const palpJoints = (S) => [...PALP_SEGS.map((g) => g + S), 'palpTip' + S];
26
- export const palpBones = (S) => PALP_SEGS.map((g) => g + S);
27
-
28
- // Direction of a segment in a vertical leg plane: azimuth phi from forward (+Z) toward the leg's
29
- // side (side = +1 left, -1 right), elevation e above horizontal.
30
- export function planeDir(phi, e, side) {
31
- const c = Math.cos(e);
32
- return [Math.sin(phi) * c * side, Math.sin(e), Math.cos(phi) * c];
33
- }
34
-
35
- /**
36
- * Writes a planar chain into J: joints names[0..n] starting at base, segment k along azimuth
37
- * az[k] (or one number) and elevation el[k] (radians), length lens[k].
38
- */
39
- export function planarChain(J, names, base, az, el, lens, side) {
40
- let p = base.slice();
41
- J[names[0]] = p.slice();
42
- for (let k = 0; k < lens.length; k++) {
43
- const a = Array.isArray(az) ? az[k] : az;
44
- p = add(p, mul(planeDir(a, el[k], side), lens[k]));
45
- J[names[k + 1]] = p;
46
- }
47
- return J;
48
- }
49
-
50
- /**
51
- * Bind-pose leg: finds the elevation of segment `free` so that the claw tip ends at height yTip.
52
- * Returns the elevations used (radians). If the target cannot be met the free segment is clamped
53
- * to straight down / up.
54
- */
55
- export function solveBindElevations(yBase, lens, el, free, yTip) {
56
- let drop = 0;
57
- for (let k = 0; k < lens.length; k++) if (k !== free) drop += lens[k] * Math.sin(el[k]);
58
- const s = (yTip - yBase - drop) / lens[free];
59
- const out = el.slice();
60
- out[free] = Math.asin(Math.max(-1, Math.min(1, s)));
61
- return out;
62
- }
63
-
64
- export function spiderBones({ chelicerae = true, palps = true, spinnerets = true, extra = [] } = {}) {
65
- const b = [
66
- ['prosoma', 'pedA', 'ceph', null, { region: 'torso' }],
67
- ['head', 'ceph', 'front', 'prosoma', { region: 'head' }],
68
- ['pedicel', 'pedA', 'pedB', 'prosoma', { region: 'torso' }],
69
- ['abdomen', 'pedB', 'abdEnd', 'pedicel', { region: 'torso' }],
70
- ];
71
- if (spinnerets) b.push(['spinnerets', 'abdEnd', 'spinTip', 'abdomen', { region: 'tail' }]);
72
- if (chelicerae) {
73
- b.push(['chelicera{S}', 'cheBase{S}', 'cheTip{S}', 'head', { region: 'jaw', group: 'chel{S}' }]);
74
- b.push(['fang{S}', 'cheTip{S}', 'fangTip{S}', 'chelicera{S}', { region: 'jaw', group: 'fang{S}' }]);
75
- }
76
- if (palps) {
77
- const pj = palpJoints('{S}'), pb = palpBones('{S}');
78
- pb.forEach((name, k) => b.push([name, pj[k], pj[k + 1], k === 0 ? 'prosoma' : pb[k - 1], { region: 'limb', group: 'P{S}' }]));
79
- }
80
- for (let i = 1; i <= 4; i++) {
81
- const lj = legJoints(i, '{S}'), lb = legBones(i, '{S}');
82
- lb.forEach((name, k) => b.push([name, lj[k], lj[k + 1], k === 0 ? 'prosoma' : lb[k - 1], { region: k === 6 ? 'foot' : 'limb', group: '{S}' + i }]));
83
- }
84
- return b.concat(extra);
85
- }
86
-
87
- // Limb descriptions for the harmonic limb weights (see core/build/weights.js). The limb core starts
88
- // part way along the coxa (tCore of the way from the coxa base to the femur base), so the coxa's
89
- // arthrodial membrane blends into the prosoma.
90
- export function spiderLimbs({ leg = {}, palp = {} } = {}) {
91
- const L = {};
92
- for (const S of ['L', 'R']) {
93
- const side = S === 'L' ? 1 : -1;
94
- for (let i = 1; i <= 4; i++) {
95
- const bones = legBones(i, S);
96
- L[S + i] = {
97
- side, S, front: i <= 2, leg: i,
98
- bones, proximal: [bones[0]], distal: bones.slice(5),
99
- field: { a: 'coxa' + i + S, b: 'femur' + i + S, top: bones[0], tCore: 0.45, aCore: 0.03, aBody: -0.05, midX: 0.0, ...leg },
100
- };
101
- }
102
- const pb = palpBones(S);
103
- L['P' + S] = {
104
- side, S, front: true, palp: true,
105
- bones: pb, proximal: [pb[0]], distal: pb.slice(4),
106
- field: { a: 'palpCoxa' + S, b: 'palpFemur' + S, top: pb[0], tCore: 0.4, aCore: 0.03, aBody: -0.05, midX: 0.0, ...palp },
107
- };
108
- }
109
- return L;
110
- }
@@ -1,181 +0,0 @@
1
- // Standard swimmer skeleton (fish, sharks: the names the swimmer motion engine expects) and helpers
2
- // for sculpting fins as thin membranes.
3
- //
4
- // Axial chain (snout -> caudal fin tip; drives the body wave, and the median fins ride on it):
5
- // joints snout, spine0 (head-trunk joint, behind the skull), spine1 .. spine{N} (spine{N} = caudal
6
- // base / hypural plate = caudal0), caudal1 .. caudal{M} (along the caudal fin; for a shark's
7
- // heterocercal tail they climb into the upper lobe)
8
- // bones head (spine0 -> snout), spine0 .. spine{N-1}, caudal0 .. caudal{M-1}
9
- // Head: jaw (jawHinge -> jawTip, lower jaw, rigid part), upperJaw (premaxBase -> premaxTip, optional:
10
- // protrusion for suction feeding / shark bites), operculum{S} (opHinge -> opEdge, optional;
11
- // auxiliary joint opLow{S} gives the hinge axis with opHinge)
12
- // Paired fins (appendages): pectoral{S} (pecBase -> pecTip, auxiliary pecUp{S}: a second point on the
13
- // fin base line = the fold axis), pelvic{S} (pelBase -> pelTip, auxiliary pelFront{S})
14
- // Dorsal, anal and adipose fins are skinned to the spine (they follow the body wave); the caudal fin
15
- // to the caudal bones.
16
- //
17
- // Everything is measured from the joints, so any fish (or shark) shape works: the engine reads the
18
- // rest geometry of the chain (segment lengths and pitch, e.g. an up-turned heterocercal tail).
19
- import { add, sub, mul, norm, cross, dot, lerp, len } from '../math/vec.js';
20
-
21
- export const spineName = (i) => 'spine' + i;
22
- export const caudalName = (i) => 'caudal' + i;
23
-
24
- /** Joint list of the axial chain, snout -> caudal tip. */
25
- export function axialJoints(spineSegs, caudalSegs) {
26
- const pts = ['snout'];
27
- for (let i = 0; i <= spineSegs; i++) pts.push(spineName(i));
28
- for (let i = 1; i <= caudalSegs; i++) pts.push(caudalName(i));
29
- return pts;
30
- }
31
- /** Bone list of the axial chain, matching axialJoints (head first). */
32
- export function axialBones(spineSegs, caudalSegs) {
33
- const b = ['head'];
34
- for (let i = 0; i < spineSegs; i++) b.push(spineName(i));
35
- for (let i = 0; i < caudalSegs; i++) b.push(caudalName(i));
36
- return b;
37
- }
38
-
39
- /**
40
- * Standard swimmer bones for buildRig.
41
- * @param o { spineSegs, caudalSegs, jaw, upperJaw, opercula, pectoral, pelvic,
42
- * pectoralParent, pelvicParent (spine bone names), extra }
43
- * The caudal chain's joint 0 is spine{N}: J.caudal0 must equal J['spine' + N] (swimmerJointsAlias does it).
44
- */
45
- export function swimmerBones({ spineSegs = 12, caudalSegs = 3, jaw = true, upperJaw = false, opercula = true, pectoral = true, pelvic = true, pectoralParent = 'spine0', pelvicParent = 'spine4', extra = [] } = {}) {
46
- const b = [];
47
- b.push([spineName(0), spineName(0), spineName(1), null, { region: 'torso' }]);
48
- b.push(['head', spineName(0), 'snout', spineName(0), { region: 'head' }]);
49
- for (let i = 1; i < spineSegs; i++) b.push([spineName(i), spineName(i), spineName(i + 1), spineName(i - 1), { region: 'torso' }]);
50
- for (let i = 0; i < caudalSegs; i++) b.push([caudalName(i), caudalName(i), caudalName(i + 1), i === 0 ? spineName(spineSegs - 1) : caudalName(i - 1), { region: 'tail' }]);
51
- if (jaw) b.push(['jaw', 'jawHinge', 'jawTip', 'head', { region: 'jaw', group: 'jaw' }]);
52
- if (upperJaw) b.push(['upperJaw', 'premaxBase', 'premaxTip', 'head', { region: 'head', group: 'upperJaw' }]);
53
- if (opercula) b.push(['operculum{S}', 'opHinge{S}', 'opEdge{S}', 'head', { region: 'head', group: 'operculum{S}' }]);
54
- if (pectoral) b.push(['pectoral{S}', 'pecBase{S}', 'pecTip{S}', pectoralParent, { region: 'fin', group: 'pectoral{S}' }]);
55
- if (pelvic) b.push(['pelvic{S}', 'pelBase{S}', 'pelTip{S}', pelvicParent, { region: 'fin', group: 'pelvic{S}' }]);
56
- return b.concat(extra);
57
- }
58
-
59
- /** Appendage list (skin blended by SDF distance) for the optional head and paired-fin bones. */
60
- export function swimmerAppendages({ upperJaw = false, opercula = true, pectoral = true, pelvic = true, blend = [-0.003, 0.005] } = {}) {
61
- const a = [];
62
- const add2 = (g) => { for (const S of ['L', 'R']) a.push({ group: g + S, bone: g + S, blend }); };
63
- if (opercula) add2('operculum');
64
- if (pectoral) add2('pectoral');
65
- if (pelvic) add2('pelvic');
66
- if (upperJaw) a.push({ group: 'upperJaw', bone: 'upperJaw', blend });
67
- return a;
68
- }
69
-
70
- /** The caudal chain starts at the last spine joint. */
71
- export function swimmerJointsAlias(J, spineSegs) {
72
- J.caudal0 = J[spineName(spineSegs)].slice();
73
- return J;
74
- }
75
-
76
- // ------------------------------------------------------------------------------------------------
77
- // Fins as membranes: an outline polygon in the fin's plane, extruded to a thin slab (SDF T_FIN).
78
- // A fin is described by its rays: each ray runs from a point on the base line to a tip. The outline
79
- // is base0 (sunk into the body) -> ray tips (with optional notches between spiny rays) -> baseN (sunk).
80
- // The prim keeps the ray layout so the coat can paint the rays (phase between rays, fraction along).
81
- // ------------------------------------------------------------------------------------------------
82
-
83
- /**
84
- * @param m SDFModel
85
- * @param o { origin [x,y,z], u, v (in-plane axes, 3D), rays: [[bu, bv], [tu, tv]] (2D, u/v coords),
86
- * sink: how far the base is pushed into the body (along -outward2D), notch: 0..1 depth of
87
- * the membrane notch between ray tips (spiny fins), t: thickness, bone, group, tag, k,
88
- * thin, round }
89
- * @returns the primitive (with prim.fin = { rays2D, origin, u, v })
90
- */
91
- export function sculptFin(m, o) {
92
- const rays = o.rays;
93
- const n = rays.length;
94
- const sink = o.sink || 0;
95
- const poly = [];
96
- // base line direction (first base -> last base) and the side the tips are on
97
- const b0 = rays[0][0], bN = rays[n - 1][0];
98
- const bd = [bN[0] - b0[0], bN[1] - b0[1]];
99
- const bl = Math.hypot(bd[0], bd[1]) || 1;
100
- let nx = -bd[1] / bl, ny = bd[0] / bl; // normal to the base line
101
- const tipMid = rays[Math.floor(n / 2)][1];
102
- if ((tipMid[0] - b0[0]) * nx + (tipMid[1] - b0[1]) * ny < 0) { nx = -nx; ny = -ny; }
103
- const sunk = (p) => [p[0] - nx * sink, p[1] - ny * sink];
104
- poly.push(sunk(b0));
105
- for (let i = 0; i < n; i++) {
106
- poly.push(rays[i][1].slice());
107
- if (o.notch && i < n - 1) {
108
- const a = rays[i][1], b = rays[i + 1][1], ba = rays[i][0], bb = rays[i + 1][0];
109
- const tm = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2], bm = [(ba[0] + bb[0]) / 2, (ba[1] + bb[1]) / 2];
110
- const nt = typeof o.notch === 'function' ? o.notch(i) : o.notch;
111
- poly.push([tm[0] + (bm[0] - tm[0]) * nt, tm[1] + (bm[1] - tm[1]) * nt]);
112
- }
113
- }
114
- poly.push(sunk(bN));
115
- const prim = m.fin({ o: o.origin, u: o.u, v: o.v, t: o.t, poly, bone: o.bone, group: o.group, tag: o.tag, k: o.k ?? o.t, round: o.round, part: o.part });
116
- prim.thin = o.thin !== false;
117
- const u = norm(o.u), nn = norm(cross(o.u, o.v)), v = cross(nn, u);
118
- prim.fin = { rays: rays.map((r) => [r[0].slice(), r[1].slice()]), origin: o.origin.slice(), u, v, n: nn, name: o.name || o.tag };
119
- return prim;
120
- }
121
-
122
- /** 2D coordinates of a 3D point in a fin's plane. */
123
- export function finUV(fin, p) {
124
- const d = sub(p, fin.origin);
125
- return [dot(d, fin.u), dot(d, fin.v)];
126
- }
127
-
128
- /**
129
- * Ray coordinates of a point in a fin: { phase: continuous ray index (0 .. n-1), along: 0 at the base,
130
- * 1 at the tip, dir: 3D ray direction there }. Points outside the fan are clamped to the nearest ray.
131
- */
132
- export function finRayCoords(fin, p) {
133
- const q = finUV(fin, p);
134
- const R = fin.rays, n = R.length;
135
- // side of q relative to ray i (positive toward ray i+1)
136
- const side = (i) => {
137
- const [b, t] = R[i];
138
- const dx = t[0] - b[0], dy = t[1] - b[1];
139
- const l = Math.hypot(dx, dy) || 1;
140
- return ((q[0] - b[0]) * -dy + (q[1] - b[1]) * dx) / l;
141
- };
142
- // orientation: does side increase from ray 0 to ray n-1?
143
- const s0 = side(0), sN = side(n - 1);
144
- const [b0, t0] = R[0], [bN] = R[n - 1];
145
- const sgn = ((bN[0] - b0[0]) * -(t0[1] - b0[1]) + (bN[1] - b0[1]) * (t0[0] - b0[0])) >= 0 ? 1 : -1;
146
- let phase;
147
- if (s0 * sgn <= 0) phase = 0;
148
- else if (sN * sgn >= 0) phase = n - 1;
149
- else {
150
- phase = 0;
151
- let prev = s0 * sgn;
152
- for (let i = 1; i < n; i++) {
153
- const si = side(i) * sgn;
154
- if (si <= 0) { phase = i - 1 + prev / (prev - si); break; }
155
- prev = si;
156
- }
157
- }
158
- const i0 = Math.min(n - 2, Math.floor(phase)), f = phase - i0;
159
- const lp = (a, b) => [a[0] + (b[0] - a[0]) * f, a[1] + (b[1] - a[1]) * f];
160
- const b = lp(R[i0][0], R[i0 + 1][0]), t = lp(R[i0][1], R[i0 + 1][1]);
161
- const dx = t[0] - b[0], dy = t[1] - b[1];
162
- const L2 = dx * dx + dy * dy || 1e-12;
163
- const along = Math.max(0, Math.min(1.2, ((q[0] - b[0]) * dx + (q[1] - b[1]) * dy) / L2));
164
- const dir3 = norm(add(mul(fin.u, dx), mul(fin.v, dy)));
165
- return { phase, along, dir: dir3 };
166
- }
167
-
168
- /** Fan of rays between two base points toward tips given by angle (deg, in the u/v plane) and length. */
169
- export function fanRays({ base0, base1, n, angle0, angle1, length }) {
170
- const rays = [];
171
- for (let i = 0; i < n; i++) {
172
- const t = n === 1 ? 0 : i / (n - 1);
173
- const b = [base0[0] + (base1[0] - base0[0]) * t, base0[1] + (base1[1] - base0[1]) * t];
174
- const a = ((angle0 + (angle1 - angle0) * t) * Math.PI) / 180;
175
- const L = typeof length === 'function' ? length(t, i) : length;
176
- rays.push([b, [b[0] + Math.cos(a) * L, b[1] + Math.sin(a) * L]]);
177
- }
178
- return rays;
179
- }
180
-
181
- export { lerp, len };
@@ -1,53 +0,0 @@
1
- // Eye geometry helpers shared by the sculpt (orbit, lids, almond aperture) and the eyeball shader.
2
- //
3
- // An eye spec (per species, head-local, left eye):
4
- // c: centre [x,y,z] relative to the head origin; r: eyeball radius; back: how far the ball sits
5
- // behind c; yaw: outward divergence (rad); pitch: upward tilt of the view axis (rad, optional);
6
- // lid: lid skin thickness; R, d: the almond aperture is the intersection of two circles of radius R
7
- // whose centres are offset -/+d (width = 2*sqrt(R^2-d^2), height = 2*(R-d)); off: aperture offset
8
- // along the eye's up axis; tilt: roll of the aperture (inner corner lower for +);
9
- // irisZ / irisR: iris plane depth and radius (eyeball-local).
10
- import { add, mul, frameZY } from '../math/vec.js';
11
-
12
- export function eyeFrameOf(E, headO, s) {
13
- const c = [headO[0] + E.c[0] * s, headO[1] + E.c[1], headO[2] + E.c[2] - E.back];
14
- const p = E.pitch || 0;
15
- const z = [Math.sin(E.yaw) * Math.cos(p) * s, Math.sin(p), Math.cos(E.yaw) * Math.cos(p)];
16
- const [x0, y0] = frameZY(z, [0, 1, 0]);
17
- const ph = s * E.tilt;
18
- const x = add(mul(x0, Math.cos(ph)), mul(y0, Math.sin(ph)));
19
- const y = add(mul(x0, -Math.sin(ph)), mul(y0, Math.cos(ph)));
20
- return { c, x, y, z };
21
- }
22
-
23
- // Adds an orbit hollow, a lid shell hugging the eyeball and the almond aperture cut into it.
24
- // `bone` is usually 'head'. `orbit` (optional) = { r:[rx,ry,rz], at:[u,v,w], k } in aperture frame.
25
- export function sculptEyeSocket(m, E, headO, s, { bone = 'head', orbit = null, zMax = null } = {}) {
26
- const ef = eyeFrameOf(E, headO, s);
27
- const at = (u, v, w) => add(add(add(ef.c, mul(ef.x, u)), mul(ef.y, v)), mul(ef.z, w));
28
- if (orbit) m.ell({ bone, tag: 'orbit', c: at(orbit.at[0] * s, orbit.at[1], orbit.at[2]), axis: ef.z, up: ef.y, r: orbit.r, k: orbit.k ?? E.r * 0.65, carve: true });
29
- m.sphere({ bone, tag: 'eyelid', c: ef.c, rad: E.r + E.lid, k: E.r * 0.4 });
30
- m.lens({ bone, tag: 'eyesocket', c: add(ef.c, mul(ef.y, E.off)), x: ef.x, y: ef.y, z: ef.z, R: E.R, d: E.d, zMin: -E.r * 0.16, zMax: zMax ?? E.r * 2, k: E.r * 0.18, carve: true });
31
- return ef;
32
- }
33
-
34
- // The aperture roll (E.tilt) that lays the almond's long axis along `axis` (e.g. the head axis: the eye
35
- // slit follows the face line), with the upper lid kept up. The long axis has no direction, so the roll
36
- // is the one within +-90 degrees of upright (the frame's y, the upper lid, stays on the upper side).
37
- // (A search for the largest dot(x, axis) over tilt in [-1.6, 1.6] does not do this: the left eye's
38
- // untilted x points backward, so such a search ends at the edge of its range and stands the almond on
39
- // end, the upper lid facing the ear.) Add the species' own roll to the result: + lowers the inner corner.
40
- export function apertureTiltAlong(E, headO, axis) {
41
- const f = eyeFrameOf({ ...E, tilt: 0 }, headO, 1);
42
- const k = axis[0] * f.z[0] + axis[1] * f.z[1] + axis[2] * f.z[2];
43
- const a = [axis[0] - f.z[0] * k, axis[1] - f.z[1] * k, axis[2] - f.z[2] * k];
44
- let ph = Math.atan2(a[0] * f.y[0] + a[1] * f.y[1] + a[2] * f.y[2], a[0] * f.x[0] + a[1] * f.x[1] + a[2] * f.x[2]);
45
- if (ph > Math.PI / 2) ph -= Math.PI;
46
- else if (ph < -Math.PI / 2) ph += Math.PI;
47
- return ph;
48
- }
49
-
50
- // Aperture size in metres (for docs and checks).
51
- export function apertureSize(E) {
52
- return { width: 2 * Math.sqrt(Math.max(0, E.R * E.R - E.d * E.d)), height: 2 * (E.R - E.d) };
53
- }
@@ -1,238 +0,0 @@
1
- // Narrow-band Surface Nets polygoniser for the SDF model.
2
- // Coarse pass finds cells near the zero set; only those are refined to the fine grid.
3
- // Vertices are projected onto the true surface (Newton steps along the SDF gradient)
4
- // and normals come from the analytic field gradient, so the mesh is smooth and accurate.
5
- import { SDFModel, gradient } from './sdf.js';
6
-
7
- export function meshSDF(prims, bmin, bmax, h, F = 6, onProgress, clip = null) {
8
- const kmax = prims.reduce((m, p) => Math.max(m, p.k), 0);
9
- // absolute steps tuned for cheetah-sized cells (>= 1 mm), scaled down for finer grids
10
- const eP = Math.min(0.0006, 0.6 * h), eN = Math.min(0.0008, 0.8 * h), tolP = Math.min(2e-5, 0.02 * h);
11
- const nx = Math.ceil((bmax[0] - bmin[0]) / h) + 3;
12
- const ny = Math.ceil((bmax[1] - bmin[1]) / h) + 3;
13
- const nz = Math.ceil((bmax[2] - bmin[2]) / h) + 3;
14
- const ox = bmin[0] - h, oy = bmin[1] - h, oz = bmin[2] - h;
15
- const cnx = Math.ceil((nx - 1) / F), cny = Math.ceil((ny - 1) / F), cnz = Math.ceil((nz - 1) / F);
16
- const H = h * F;
17
-
18
- // --- coarse samples, evaluated per super-cell with culled primitive lists
19
- const T0 = performance.now();
20
- const cv = new Float32Array((cnx + 1) * (cny + 1) * (cnz + 1)).fill(NaN);
21
- const cidx = (i, j, k) => i + (cnx + 1) * (j + (cny + 1) * k);
22
- const F2 = 4;
23
- const snx = Math.ceil(cnx / F2), sny = Math.ceil(cny / F2), snz = Math.ceil(cnz / F2);
24
- const H2 = H * F2, rho2 = H2 * Math.sqrt(3) * 0.5;
25
- const superLists = new Map();
26
- for (let sk = 0; sk < snz; sk++)
27
- for (let sj = 0; sj < sny; sj++)
28
- for (let si = 0; si < snx; si++) {
29
- const list2 = SDFModel.cull(prims, ox + (si + 0.5) * H2, oy + (sj + 0.5) * H2, oz + (sk + 0.5) * H2, rho2, kmax);
30
- superLists.set(si + snx * (sj + sny * sk), list2);
31
- for (let k = sk * F2; k <= Math.min(sk * F2 + F2, cnz); k++)
32
- for (let j = sj * F2; j <= Math.min(sj * F2 + F2, cny); j++)
33
- for (let i = si * F2; i <= Math.min(si * F2 + F2, cnx); i++) {
34
- const id = cidx(i, j, k);
35
- if (cv[id] === cv[id]) continue;
36
- cv[id] = list2.length ? SDFModel.evalList(list2, ox + i * H, oy + j * H, oz + k * H) : 1;
37
- }
38
- }
39
- const superListFor = (i, j, k) => superLists.get(Math.min(snx - 1, Math.floor(i / F2)) + snx * (Math.min(sny - 1, Math.floor(j / F2)) + sny * Math.min(snz - 1, Math.floor(k / F2))));
40
- const T1 = performance.now();
41
-
42
- // --- active coarse cells (surface may pass through), dilated by one
43
- const cellIdx = (i, j, k) => i + cnx * (j + cny * k);
44
- const active0 = new Uint8Array(cnx * cny * cnz);
45
- const reach = H * Math.sqrt(3) * 1.15;
46
- for (let k = 0; k < cnz; k++)
47
- for (let j = 0; j < cny; j++)
48
- for (let i = 0; i < cnx; i++) {
49
- let mn = 1e9, neg = false, pos_ = false;
50
- for (let c = 0; c < 8; c++) {
51
- const v = cv[cidx(i + (c & 1), j + ((c >> 1) & 1), k + ((c >> 2) & 1))];
52
- const a = Math.abs(v);
53
- if (a < mn) mn = a;
54
- if (v < 0) neg = true; else pos_ = true;
55
- }
56
- // a surface point inside the cube is within half a diagonal of some corner
57
- if ((neg && pos_) || mn < reach * 0.5) active0[cellIdx(i, j, k)] = 1;
58
- }
59
- const active = active0; // every cell crossed by the surface is already active (1-Lipschitz bound)
60
-
61
- // --- fine samples inside active coarse cells
62
- const N = nx * ny * nz;
63
- const fv = new Float32Array(N).fill(NaN);
64
- const fidx = (x, y, z) => x + nx * (y + ny * z);
65
- const cellLists = new Map();
66
- const rho = H * Math.sqrt(3) * 0.5;
67
- let cellCount = 0;
68
- for (let k = 0; k < cnz; k++)
69
- for (let j = 0; j < cny; j++)
70
- for (let i = 0; i < cnx; i++) {
71
- const ci = cellIdx(i, j, k);
72
- if (!active[ci]) continue;
73
- cellCount++;
74
- const cx = ox + (i + 0.5) * H, cy = oy + (j + 0.5) * H, cz = oz + (k + 0.5) * H;
75
- const list = SDFModel.cull(superListFor(i, j, k), cx, cy, cz, rho, kmax);
76
- cellLists.set(ci, list);
77
- const x0 = i * F, y0 = j * F, z0 = k * F;
78
- for (let z = z0; z <= Math.min(z0 + F, nz - 1); z++)
79
- for (let y = y0; y <= Math.min(y0 + F, ny - 1); y++)
80
- for (let x = x0; x <= Math.min(x0 + F, nx - 1); x++) {
81
- const id = fidx(x, y, z);
82
- if (fv[id] === fv[id]) continue; // already evaluated
83
- fv[id] = SDFModel.evalList(list, ox + x * h, oy + y * h, oz + z * h);
84
- }
85
- }
86
- const T2 = performance.now();
87
- onProgress && onProgress('field', cellCount);
88
-
89
- const listFor = (x, y, z) => {
90
- const i = Math.min(cnx - 1, Math.floor(x / F)), j = Math.min(cny - 1, Math.floor(y / F)), k = Math.min(cnz - 1, Math.floor(z / F));
91
- return cellLists.get(cellIdx(i, j, k)) || prims;
92
- };
93
-
94
- // --- surface nets vertices
95
- const vmap = new Map();
96
- const pos = [];
97
- const cellOf = [];
98
- const edges = [
99
- [0, 1], [2, 3], [4, 5], [6, 7],
100
- [0, 2], [1, 3], [4, 6], [5, 7],
101
- [0, 4], [1, 5], [2, 6], [3, 7],
102
- ];
103
- const corner = new Float64Array(8);
104
- for (let k = 0; k < cnz; k++)
105
- for (let j = 0; j < cny; j++)
106
- for (let i = 0; i < cnx; i++) {
107
- if (!active[cellIdx(i, j, k)]) continue;
108
- const list = cellLists.get(cellIdx(i, j, k));
109
- const x0 = i * F, y0 = j * F, z0 = k * F;
110
- for (let z = z0; z < Math.min(z0 + F, nz - 1); z++)
111
- for (let y = y0; y < Math.min(y0 + F, ny - 1); y++)
112
- for (let x = x0; x < Math.min(x0 + F, nx - 1); x++) {
113
- let mask = 0, bad = false;
114
- for (let c = 0; c < 8; c++) {
115
- const v = fv[fidx(x + (c & 1), y + ((c >> 1) & 1), z + ((c >> 2) & 1))];
116
- if (v !== v) { bad = true; break; }
117
- corner[c] = v;
118
- if (v < 0) mask |= 1 << c;
119
- }
120
- if (bad || mask === 0 || mask === 255) continue;
121
- if (clip && !clip(ox + (x + 0.5) * h, oy + (y + 0.5) * h, oz + (z + 0.5) * h)) continue;
122
- // mass point of edge crossings
123
- let sx = 0, sy = 0, sz = 0, cnt = 0;
124
- for (const [a, b] of edges) {
125
- const va = corner[a], vb = corner[b];
126
- if ((va < 0) === (vb < 0)) continue;
127
- const t = va / (va - vb);
128
- const ax = a & 1, ay = (a >> 1) & 1, az = (a >> 2) & 1;
129
- const bx = b & 1, by = (b >> 1) & 1, bz = (b >> 2) & 1;
130
- sx += ax + (bx - ax) * t;
131
- sy += ay + (by - ay) * t;
132
- sz += az + (bz - az) * t;
133
- cnt++;
134
- }
135
- let px = ox + (x + sx / cnt) * h, py = oy + (y + sy / cnt) * h, pz = oz + (z + sz / cnt) * h;
136
- // project onto the surface (tetrahedral samples give value + gradient)
137
- for (let it = 0; it < 2; it++) {
138
- const e = eP; // finite-difference step: 0.6 mm, smaller on very fine grids (tiny animals)
139
- const a = SDFModel.evalList(list, px + e, py - e, pz - e);
140
- const b = SDFModel.evalList(list, px - e, py - e, pz + e);
141
- const c2 = SDFModel.evalList(list, px - e, py + e, pz - e);
142
- const d2 = SDFModel.evalList(list, px + e, py + e, pz + e);
143
- const d = (a + b + c2 + d2) * 0.25;
144
- const gx = (a - b - c2 + d2) / (4 * e), gy = (-a - b + c2 + d2) / (4 * e), gz = (-a + b - c2 + d2) / (4 * e);
145
- const g2 = gx * gx + gy * gy + gz * gz + 1e-12;
146
- let stx = (-d * gx) / g2, sty = (-d * gy) / g2, stz = (-d * gz) / g2;
147
- const sl = Math.hypot(stx, sty, stz);
148
- if (sl > h) { stx *= h / sl; sty *= h / sl; stz *= h / sl; }
149
- px += stx; py += sty; pz += stz;
150
- if (Math.abs(d) < tolP) break;
151
- }
152
- // keep inside an expanded cell to avoid folding
153
- const lo = 0.35 * h;
154
- px = Math.min(Math.max(px, ox + x * h - lo), ox + (x + 1) * h + lo);
155
- py = Math.min(Math.max(py, oy + y * h - lo), oy + (y + 1) * h + lo);
156
- pz = Math.min(Math.max(pz, oz + z * h - lo), oz + (z + 1) * h + lo);
157
- vmap.set(fidx(x, y, z), pos.length / 3);
158
- pos.push(px, py, pz);
159
- cellOf.push(fidx(x, y, z));
160
- }
161
- }
162
- const T3 = performance.now();
163
- onProgress && onProgress('verts', pos.length / 3);
164
-
165
- // --- faces
166
- const idx = [];
167
- const nV = pos.length / 3;
168
- const P = pos;
169
- const dist2 = (a, b) => {
170
- const dx = P[a * 3] - P[b * 3], dy = P[a * 3 + 1] - P[b * 3 + 1], dz = P[a * 3 + 2] - P[b * 3 + 2];
171
- return dx * dx + dy * dy + dz * dz;
172
- };
173
- const strides = [1, nx, nx * ny];
174
- for (let v = 0; v < nV; v++) {
175
- const c = cellOf[v];
176
- const z = Math.floor(c / (nx * ny));
177
- const y = Math.floor((c - z * nx * ny) / nx);
178
- const x = c - z * nx * ny - y * nx;
179
- const xyz = [x, y, z];
180
- for (let a = 0; a < 3; a++) {
181
- const b = (a + 1) % 3, cc = (a + 2) % 3;
182
- // edge from grid point (cell + e_b + e_c) along axis a
183
- const p0 = [x, y, z];
184
- p0[b] += 1;
185
- p0[cc] += 1;
186
- if (p0[0] >= nx || p0[1] >= ny || p0[2] >= nz) continue;
187
- const p1 = p0.slice();
188
- p1[a] += 1;
189
- if (p1[a] >= [nx, ny, nz][a]) continue;
190
- const v0 = fv[fidx(p0[0], p0[1], p0[2])], v1 = fv[fidx(p1[0], p1[1], p1[2])];
191
- if (v0 !== v0 || v1 !== v1) continue;
192
- if ((v0 < 0) === (v1 < 0)) continue;
193
- const cA = c, cB = c + strides[b], cC = c + strides[b] + strides[cc], cD = c + strides[cc];
194
- const A = vmap.get(cA), B = vmap.get(cB), C = vmap.get(cC), D = vmap.get(cD);
195
- if (A === undefined || B === undefined || C === undefined || D === undefined) continue;
196
- // orientation: inside->outside along +a means normal points +a
197
- const flip = v0 < 0;
198
- let q = flip ? [A, B, C, D] : [A, D, C, B];
199
- if (dist2(q[0], q[2]) <= dist2(q[1], q[3])) idx.push(q[0], q[1], q[2], q[0], q[2], q[3]);
200
- else idx.push(q[0], q[1], q[3], q[1], q[2], q[3]);
201
- void xyz;
202
- }
203
- }
204
-
205
- // --- normals from field gradient
206
- const nrm = new Float32Array(nV * 3);
207
- for (let v = 0; v < nV; v++) {
208
- const c = cellOf[v];
209
- const z = Math.floor(c / (nx * ny));
210
- const y = Math.floor((c - z * nx * ny) / nx);
211
- const x = c - z * nx * ny - y * nx;
212
- const list = listFor(x, y, z);
213
- const g = gradient(list, P[v * 3], P[v * 3 + 1], P[v * 3 + 2], eN);
214
- const l = Math.hypot(g[0], g[1], g[2]) || 1;
215
- nrm[v * 3] = g[0] / l;
216
- nrm[v * 3 + 1] = g[1] / l;
217
- nrm[v * 3 + 2] = g[2] / l;
218
- }
219
-
220
- const lists = new Array(nV);
221
- for (let v = 0; v < nV; v++) {
222
- const c = cellOf[v];
223
- const z = Math.floor(c / (nx * ny));
224
- const y = Math.floor((c - z * nx * ny) / nx);
225
- const x = c - z * nx * ny - y * nx;
226
- lists[v] = listFor(x, y, z);
227
- }
228
-
229
- const T4 = performance.now();
230
- if (typeof process !== 'undefined' && process.env && process.env.MESH_TIMING) console.log('coarse', (T1-T0)|0, 'fine', (T2-T1)|0, 'verts', (T3-T2)|0, 'faces+normals', (T4-T3)|0, 'cells', cellCount);
231
- return {
232
- positions: new Float32Array(pos),
233
- normals: nrm,
234
- indices: nV > 65535 ? new Uint32Array(idx) : new Uint16Array(idx),
235
- lists,
236
- count: nV,
237
- };
238
- }