@bitefoundry/retrieviewer 0.0.3 → 0.0.5

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 (263) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +95 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/INSTALLING.md +4 -0
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +116 -0
  11. package/README.md +121 -59
  12. package/RELEASING.md +33 -13
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/linux-overlay/overlay.py +8 -2
  17. package/integrations/macos-overlay/overlay.jxa +9 -7
  18. package/integrations/opencode/runtime.mjs +4 -4
  19. package/integrations/opencode/tui.tsx +1 -1
  20. package/integrations/windows-overlay/Overlay.cs +7 -6
  21. package/integrations/windows-overlay/desktop.mjs +2 -1
  22. package/integrations/windows-overlay/protocol.mjs +13 -7
  23. package/integrations/windows-overlay/worker.mjs +1 -1
  24. package/package.json +34 -13
  25. package/puppy-comfort-sprites.mjs +33 -0
  26. package/puppy-companion-comfort-q5.bin.gz +0 -0
  27. package/puppy-companion-comfort.json +140 -0
  28. package/puppy-companion-life.mjs +58 -18
  29. package/puppy-companion-objects.mjs +12 -0
  30. package/puppy-companion-sprites.mjs +4 -2
  31. package/puppy-frame-predictive.mjs +15 -9
  32. package/puppy-frame-store.mjs +2 -2
  33. package/puppy-paper-actions.mjs +4 -2
  34. package/puppy-paper-behaviors.mjs +1 -1
  35. package/puppy-paper-contact.mjs +4 -2
  36. package/puppy-paper-seat.mjs +4 -2
  37. package/puppy-paper-world.mjs +14 -0
  38. package/puppy-paper.mjs +79 -21
  39. package/retrieviewer-cli.mjs +8 -6
  40. package/retrieviewer-plugin.d.mts +1 -1
  41. package/retrieviewer-plugin.mjs +1 -1
  42. package/retrieviewer.d.mts +12 -4
  43. package/retrieviewer.mjs +20 -14
  44. package/locomotion-core.mjs +0 -21
  45. package/locomotion-puppy.animal +0 -0
  46. package/vendor/procedural-animals/package.json +0 -40
  47. package/vendor/procedural-animals/src/animal.js +0 -146
  48. package/vendor/procedural-animals/src/bake.js +0 -51
  49. package/vendor/procedural-animals/src/build.js +0 -81
  50. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  51. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  52. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  53. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  54. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  55. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  56. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  57. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  58. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  59. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  60. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  61. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  62. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  63. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  64. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  65. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  66. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  67. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  68. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  69. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  70. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  71. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  72. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  73. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  74. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  75. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  76. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  77. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  79. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  80. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  81. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  82. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  83. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  84. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  85. package/vendor/procedural-animals/src/index.d.ts +0 -98
  86. package/vendor/procedural-animals/src/index.js +0 -29
  87. package/vendor/procedural-animals/src/registry.js +0 -48
  88. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  89. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  90. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  91. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  92. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  94. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  95. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  96. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  97. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  98. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  99. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  100. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  101. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  102. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  103. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  104. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  105. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  106. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  107. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  108. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  109. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  110. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  111. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  112. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  113. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  114. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  115. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  116. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  117. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  118. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  119. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  120. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  121. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  122. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  123. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  124. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  125. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  126. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  127. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  128. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  129. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  130. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  131. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  132. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  133. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  134. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  135. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  136. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  137. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  138. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  139. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  140. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  141. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  142. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  143. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  144. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  145. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  146. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  147. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  148. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  149. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  150. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  151. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  152. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  153. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  154. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  155. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  156. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  157. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  158. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  159. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  160. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  161. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  162. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  163. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  164. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  165. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  166. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  167. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  168. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  169. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  170. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  171. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  172. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  173. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  174. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  175. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  176. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  177. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  178. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  179. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  180. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  181. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  182. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  183. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  184. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  185. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  186. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  187. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  188. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  189. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  190. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  191. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  192. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  193. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  194. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  195. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  196. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  197. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  198. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  199. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  200. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  201. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  202. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  203. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  204. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  205. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  206. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  207. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  208. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  209. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  210. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  211. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  212. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  213. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  214. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  215. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  216. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  217. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  218. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  219. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  220. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  221. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  222. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  223. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  224. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  225. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  226. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  227. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  228. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  229. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  230. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  231. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  232. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  233. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  234. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  235. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  236. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  237. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  238. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  239. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  240. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  241. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  242. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  243. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  244. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  245. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  246. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  247. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  248. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  249. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  250. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  251. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  252. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  253. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  254. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  255. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  256. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  257. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  258. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  259. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  260. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  261. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  262. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  263. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,156 +0,0 @@
1
- // True frog (Ranidae) skeleton in bind pose (metres). Forward = +Z, up = +Y, the animal's left = +X.
2
- //
3
- // Reference individual: adult female American bullfrog (Lithobates catesbeianus), snout-vent length
4
- // (SVL) 140 mm. The bind pose IS the classic sitting frog (the resting stance of the
5
- // quadruped engine): forelegs upright on flat hands, hind legs folded in a Z beside the body (femur
6
- // forward and out, tibiofibula back alongside it, tarsus and the long webbed foot flat on the ground
7
- // pointing forward and out), the trunk rising from the vent to the sacral hump and on to the flat
8
- // head. No neck: the neck joints sit packed behind the skull. No tail (the urostyle is the pelvis
9
- // bone, lumbosacral -> tailBase).
10
- //
11
- // Frog bones mapped onto the standard quadruped set (core/rig/quadruped.js):
12
- // pelvis (lumbosacral -> tailBase) ilia + urostyle: the ilio-sacral hinge is the sacral hump
13
- // femur / tibia femur / tibiofibula
14
- // metatarsus (hock -> mtp) the elongated tarsus (astragalus + calcaneum)
15
- // hpaw (mtp -> htoe) metatarsals + toes (toe IV), webbed
16
- // humerus / radius humerus / radio-ulna
17
- // metacarpus / fpaw palm / fingers (4)
18
- // Extra bones: eyeL / eyeR (the eyes retract into the head to blink and swallow), throat (buccal
19
- // floor and vocal sac: pumping and croaking), tongue (attached at the front of the lower jaw, flips
20
- // out forward).
21
- import { buildRig } from '../../core/rig/rig.js';
22
- import { quadrupedBones, quadrupedLimbs } from '../../core/rig/quadruped.js';
23
- import { sub, add, mul, norm, cross, dot, len } from '../../core/math/vec.js';
24
-
25
- // head-local origin: on the midline, level with the eye centres
26
- export const HEAD_O = [0, 0.06, 0.049];
27
- export const hl = (v) => [v[0] + HEAD_O[0], v[1] + HEAD_O[1], v[2] + HEAD_O[2]];
28
-
29
- // limb segment lengths at SVL 140 mm (ranid ratios)
30
- export const SEG = { hum: 0.026, rad: 0.022, fem: 0.064, tib: 0.071 };
31
- // stance.sprawl of motion.js: the engine places the feet sprawl x leg length x 0.35 further out than
32
- // the bind, and bends knees / elbows toward poles turned out by sprawl x 1.6; the bind is built with
33
- // the same poles and its feet that much further in, so the resting stance reproduces it
34
- export const SPRAWL = 0.25;
35
-
36
- // Knee / elbow of a two-bone chain a -> c (lengths l1, l2) bent toward the pole direction (the
37
- // engine's IK solve, so the bind limb lies in the plane the engine bends it in)
38
- export function solve2(a, c, l1, l2, pole) {
39
- const d = sub(c, a), D = len(d), e1 = norm(d);
40
- let e2 = sub(pole, mul(e1, dot(pole, e1)));
41
- e2 = norm(e2);
42
- const cosA = Math.max(-1, Math.min(1, (l1 * l1 + D * D - l2 * l2) / (2 * l1 * D)));
43
- return add(a, add(mul(e1, cosA * l1), mul(e2, Math.sqrt(1 - cosA * cosA) * l1)));
44
- }
45
-
46
- // the sitting foot (left): heel beside the vent, tarsus and foot forward (tarsus pitched ~4.4 deg)
47
- const FOOT = { x: 0.035, hockBack: 0.0175, tilt: 4.4 };
48
-
49
- // the eye centre (the eye bone's head) of the left eye, see sculpt.js EYE
50
- export const EYE_C = [0.0152, 0.0012, -0.0002];
51
-
52
- export function frogJoints(params = {}) {
53
- const J = {
54
- // axial: vent -> sacral hump -> back -> skull
55
- tailBase: [0, 0.021, -0.052],
56
- lumbosacral: [0, 0.037, -0.03],
57
- lumbarMid: [0, 0.042, -0.017],
58
- thoraxRear: [0, 0.045, -0.004],
59
- chestMid: [0, 0.047, 0.009],
60
- neckBase: [0, 0.049, 0.018],
61
- neckMid: [0, 0.0505, 0.023],
62
- occiput: [0, 0.052, 0.028],
63
- nose: [0, 0.0505, 0.088],
64
- jawHinge: [0, 0.0395, 0.03],
65
- jawTip: [0, 0.0405, 0.086],
66
- // forelimb: shoulder behind the jaw angle, humerus back and down along the flank, radio-ulna
67
- // nearly upright, palm flat, fingers pointing forward and in
68
- scapTopL: [0.013, 0.054, 0.012],
69
- shoulderL: [0.0175, 0.034, 0.016],
70
- wristL: [0.025, 0.0055, 0.011],
71
- mcpL: [0.025, 0.0036, 0.0225],
72
- ftoeL: [0.025, 0.0011, 0.0395],
73
- // hind limb: the Z-fold of the sit: femur forward and out, tibiofibula back alongside it, the heel
74
- // beside the vent, tarsus and the long foot forward on the ground
75
- hipL: [0.0105, 0.028, -0.044],
76
- };
77
- // (the engine keeps a foot in the plane of its forward axis and places it by its joints' y / z only:
78
- // the bind hands and feet run straight ahead, the fingers and toes fan out from them in the sculpt:
79
- // fingers turned in, the long toes turned out)
80
- const tt = (FOOT.tilt * Math.PI) / 180;
81
- J.hockL = [FOOT.x, 0.0056 + 0.0377 * Math.sin(tt), J.hipL[2] - FOOT.hockBack];
82
- J.mtpL = [FOOT.x, 0.0056, J.hockL[2] + 0.0377 * Math.cos(tt)];
83
- J.htoeL = [FOOT.x, 0.0012, J.mtpL[2] + 0.0606];
84
- // knee and elbow from the engine's poles (sprawl)
85
- J.kneeL = solve2(J.hipL, J.hockL, SEG.fem, SEG.tib, [0.12 + SPRAWL * 1.6, 0, 1]);
86
- J.elbowL = solve2(J.shoulderL, J.wristL, SEG.hum, SEG.rad, [0.08 + SPRAWL * 1.6, 0, -1]);
87
- // extra bones
88
- const ec = hl(EYE_C);
89
- J.eyeBaseL = ec;
90
- J.eyeTopL = [ec[0], ec[1] + 0.008, ec[2]];
91
- J.throatBase = [0, 0.044, 0.036];
92
- J.throatTip = [0, 0.03, 0.046];
93
- J.tongueBase = [0, 0.0425, 0.079];
94
- J.tongueTip = [0, 0.0435, 0.046];
95
- void params;
96
- return J;
97
- }
98
-
99
- // rig.unit: the frog is short at the shoulder (0.054 m / 0.76 m = 0.07 of the cheetah) but its limbs are
100
- // long (hind limb 0.17 m, ~0.3 of the cheetah's). The core scales its skin-blend widths by the unit, and
101
- // the knee and ankle of the Z-folded hind leg swing ~150 deg between the sit and a leap: they need
102
- // blends as wide as the limb's scale, so the limb scale is used. The species' own blend distances
103
- // below are given in metres (u()).
104
- export const UNIT = 0.3;
105
- const u = (m) => m / UNIT;
106
-
107
- export function frogRig(params = {}) {
108
- const J = frogJoints(params);
109
- const rig = buildRig({
110
- joints: J,
111
- // characteristic length relative to the cheetah: shoulder (scapula top) height 0.054 m / 0.76 m
112
- unit: UNIT,
113
- headOrigin: HEAD_O,
114
- bones: quadrupedBones({
115
- tailSegs: 0, ears: false, jaw: true,
116
- extra: [
117
- ['eye{S}', 'eyeBase{S}', 'eyeTop{S}', 'head', { region: 'head', group: 'eye{S}' }],
118
- ['throat', 'throatBase', 'throatTip', 'head', { region: 'head', group: 'throat' }],
119
- ['tongue', 'tongueBase', 'tongueTip', 'jaw', { region: 'jaw', group: 'tongue' }],
120
- ],
121
- }),
122
- axial: {
123
- points: ['nose', 'occiput', 'neckMid', 'neckBase', 'chestMid', 'thoraxRear', 'lumbarMid', 'lumbosacral', 'tailBase'],
124
- bones: ['head', 'neck2', 'neck1', 'chest', 'spine3', 'spine2', 'spine1', 'pelvis'],
125
- // no neck: the skull's skin blends into the shoulders over a wide band
126
- blend: { 1: [u(-0.0084), u(0.0084), 0.5], 2: [u(-0.0042), u(0.0042)], 3: [u(-0.0042), u(0.0042)] },
127
- },
128
- limbs: frogLimbs(params),
129
- appendages: [
130
- { group: 'eyeL', bone: 'eyeL', blend: [u(-0.0014), u(0.0021)] },
131
- { group: 'eyeR', bone: 'eyeR', blend: [u(-0.0014), u(0.0021)] },
132
- { group: 'throat', bone: 'throat', blend: [u(-0.00105), u(0.0021)] },
133
- ],
134
- webTags: ['groin', 'thighweb', 'armpit'],
135
- });
136
- return rig;
137
- }
138
-
139
- // Skin-weight limbs: the standard ones; in the hind limb only the rigid webbed foot is 'distal' (the
140
- // ankle of the frog's Z-folded leg swings as far as its knee and blends as widely).
141
- export function frogLimbs(params = {}) {
142
- const L = quadrupedLimbs({ front: { tCore: 0.75, aCore: u(0.0035), aBody: u(0.0021), midX: u(0.00028) }, hind: {} });
143
- const hf = params.hindField || { tCore: 0.15, aCore: u(0.0035), aBody: u(0.0007), midX: u(0.00028) };
144
- const seg = params.segField || { tCore: 0, aCore: u(0.003), aBody: u(0.0005), midX: u(0.00028) };
145
- for (const S of ['L', 'R']) {
146
- const side = S === 'L' ? 1 : -1;
147
- if (params.splitHind !== true) {
148
- L['H' + S] = { side, S, front: false, bones: ['femur' + S, 'tibia' + S, 'metatarsus' + S, 'hpaw' + S], proximal: ['femur' + S], distal: ['hpaw' + S], field: { a: 'hip' + S, b: 'knee' + S, top: 'femur' + S, ...hf } };
149
- continue;
150
- }
151
- L['H' + S] = { side, S, front: false, bones: ['femur' + S], proximal: ['femur' + S], distal: [], field: { a: 'hip' + S, b: 'knee' + S, top: 'femur' + S, ...hf } };
152
- L['H' + S + 's'] = { side, S, front: false, bones: ['tibia' + S], proximal: ['tibia' + S], distal: [], field: { a: 'knee' + S, b: 'hock' + S, top: 'tibia' + S, ...seg } };
153
- L['H' + S + 'f'] = { side, S, front: false, bones: ['metatarsus' + S, 'hpaw' + S], proximal: ['metatarsus' + S], distal: ['hpaw' + S], field: { a: 'hock' + S, b: 'mtp' + S, top: 'metatarsus' + S, ...seg } };
154
- }
155
- return L;
156
- }
@@ -1,240 +0,0 @@
1
- // The frog, sculpted as a signed distance field around the sitting bind pose (rig.js).
2
- // Every primitive is attached to a bone (skinning) and a group (axial / limb / eye / throat / jaw /
3
- // tongue). Head primitives are placed in head-local coordinates (HEAD_O: midline, level with the eye
4
- // centres), measured from the profile, front and dorsal reference photos.
5
- //
6
- // Anatomy: no neck; a broad flat head wider than long with a huge mouth line running back to below
7
- // the tympanum; large bulging eyes on top of the head; a big round tympanum behind each eye (larger
8
- // than the eye in males) under the supratympanic fold; a plump trunk rising from the vent to the
9
- // sacral hump; short forelimbs with four slender fingers; very long hind limbs (heavy thigh,
10
- // tibiofibula, elongated tarsus, long foot with five webbed toes). Common frog variant: dorsolateral
11
- // folds along the back.
12
- import { HEAD_O, hl, EYE_C } from './rig.js';
13
- import { add, sub, mul, lerp, norm, cross, dot } from '../../core/math/vec.js';
14
- import { sculptEyeSocket } from '../../core/sdf/eyeSocket.js';
15
- import { SDFModel } from '../../core/sdf/sdf.js';
16
-
17
- // Large bulging eye: globe radius 7.2 mm (visible eye ~14-15 mm at SVL 140 mm), looking out ~57 deg
18
- // and up ~24 deg; a wide, nearly round aperture (no sclera), thick upper lid.
19
- export const EYE = {
20
- c: EYE_C, r: 0.0079, back: 0, yaw: 1.0, pitch: 0.42, lid: 0.0013,
21
- R: 0.0081, d: 0.0009, off: 0.0006, tilt: (6 * Math.PI) / 180, irisZ: 0.0052, irisR: 0.0071,
22
- };
23
-
24
- const X = [1, 0, 0];
25
-
26
- // A carved slot in the fold of a joint J between the segments toward A and toward B: a thin slab in
27
- // the plane of the joint's hinge axis and the bisector of the two segments, from `start` (m from the
28
- // joint) out to `len`.
29
- function foldSlot(m, Jt, A, Bp, start, len, tag, bone, group) {
30
- const a = norm(sub(A, Jt)), b = norm(sub(Bp, Jt));
31
- const bis = norm(add(a, b)), axis = norm(cross(a, b));
32
- m.fin({ tag, bone, group, o: add(Jt, mul(bis, start)), u: bis, v: axis, poly: [[0, -0.02], [len, -0.02], [len, 0.02], [0, 0.02]], t: 0.0026, round: 0.0012, k: 0.0006, carve: true });
33
- }
34
-
35
- // point where the ray p + t dir (t in -2..2 cm) crosses the body surface sculpted so far
36
- function surfaceAlong(m, p, dir) {
37
- const prims = m.prims.filter((q) => q.part === 'body');
38
- const f = (t) => SDFModel.evalList(prims, p[0] + dir[0] * t, p[1] + dir[1] * t, p[2] + dir[2] * t);
39
- let lo = -0.02, hi = 0.02;
40
- for (let i = 0; i < 40; i++) { const mid = 0.5 * (lo + hi); if (f(mid) < 0) lo = mid; else hi = mid; }
41
- return add(p, mul(dir, 0.5 * (lo + hi)));
42
- }
43
-
44
- // tympanum (head-local, left): centre, outward normal, radius by sex
45
- export function tympanum(params = {}) {
46
- const male = params.sex === 'male';
47
- const r = (params.tympanum || (male ? 0.0105 : 0.0074)) * (params.variant === 'common-frog' ? 0.78 : 1);
48
- return { c: [0.0262, -0.0085, -0.0235], n: norm([1, 0.12, -0.25]), r };
49
- }
50
-
51
- export function sculptFrog(m, rig, params = {}) {
52
- const J = rig.J;
53
- const male = params.sex === 'male';
54
- const temporaria = params.variant === 'common-frog';
55
- const plump = params.plump || 1;
56
-
57
- // Ellipsoid whose local Y follows `ydir`; radii = [lateral, along ydir, third axis]
58
- const ellY = (o) => {
59
- const y = norm(o.ydir);
60
- const lat = o.lateral || X;
61
- const z = norm(cross(lat, y));
62
- return m.ell({ ...o, axis: z, up: y, r: [o.r[0], o.r[1], o.r[2]] });
63
- };
64
-
65
- // ---------------------------------------------------------------- TRUNK (vent low, sacral hump, plump belly)
66
- const bodyAx = norm([0, 0.22, 1]);
67
- m.ell({ tag: 'trunk', bone: 'spine2', c: ([0, 0.0375, -0.012]), r: [0.032 * plump, 0.0205, 0.037], axis: bodyAx, k: 0 });
68
- m.ell({ tag: 'chest', bone: 'chest', c: [0, 0.041, 0.012], r: [0.026 * plump, 0.0185, 0.02], axis: bodyAx, k: 0.012 });
69
- m.ell({ tag: 'shoulders', bone: 'chest', c: [0, 0.049, 0.015], r: [0.024, 0.012, 0.016], k: 0.012 });
70
- m.ell({ tag: 'belly', bone: 'spine2', c: ([0, 0.027, -0.016]), r: [0.031 * plump, 0.0135, 0.03], axis: norm([0, 0.3, 1]), k: 0.014 });
71
- m.ell({ tag: 'hump', bone: 'pelvis', c: ([0, 0.041, -0.033]), r: [0.022, 0.0165, 0.017], k: 0.012 });
72
- m.ell({ tag: 'rump', bone: 'pelvis', c: ([0, 0.027, -0.046]), r: [0.0175, 0.0155, 0.0135], axis: norm([0, 0.5, 1]), k: 0.012 });
73
- // the vent: a slight crease at the back of the rump
74
- m.ell({ tag: 'vent', bone: 'pelvis', c: ([0, 0.016, -0.059]), r: [0.004, 0.0022, 0.004], k: 0.002, carve: true });
75
-
76
- // ---------------------------------------------------------------- HEAD (head-local, see HEAD_O)
77
- const H = { bone: 'head' };
78
- const hw = params.headWidth || 1;
79
- m.ell({ ...H, tag: 'cranium', c: hl([0, -0.01, -0.008]), r: [0.0268 * hw, 0.0105, 0.03], k: 0.012 });
80
- m.ell({ ...H, tag: 'snout', c: hl([0, -0.0115, 0.013]), r: [0.019 * hw, 0.0088, 0.027], k: 0.01 });
81
- // the canthus: a soft ridge from the eye to the nostril, the flat loreal region below it
82
- for (const s of [1, -1]) {
83
- m.cone({ ...H, tag: 'canthus', a: hl([0.0105 * s, -0.0028, 0.006]), b: hl([0.0052 * s, -0.0045, 0.0305]), ra: 0.0028, rb: 0.0018, k: 0.005 });
84
- // cheeks and the corners of the jaw (the head is widest here)
85
- m.ell({ ...H, tag: 'cheek', c: hl([0.0205 * s * hw, -0.0145, -0.017]), r: [0.0095, 0.0092, 0.0145], k: 0.009 });
86
- m.ell({ ...H, tag: 'lipwall', c: hl([0.0165 * s * hw, -0.0142, 0.002]), r: [0.0075, 0.0048, 0.028], axis: norm([-0.32 * s, 0, 1]), k: 0.008 });
87
- m.ell({ ...H, tag: 'temple', c: hl([0.02 * s * hw, -0.0065, -0.028]), r: [0.009, 0.008, 0.011], k: 0.008 });
88
- // nostrils: small raised rims with the opening carved
89
- m.sphere({ ...H, tag: 'naris', c: hl([0.0056 * s, -0.0048, 0.0318]), rad: 0.0019, k: 0.0025 });
90
- m.sphere({ ...H, tag: 'nostril', c: hl([0.0058 * s, -0.0035, 0.0322]), rad: 0.0009, k: 0.0006, carve: true });
91
- // tympanum: a flat disc set a little into the side of the head, with a low rim
92
- const T = tympanum(params);
93
- const tc = [T.c[0] * s, T.c[1], T.c[2]], tn = [T.n[0] * s, T.n[1], T.n[2]];
94
- // (placed on the surface sculpted so far: a flat disc 0.35 mm below it at its centre)
95
- const ts = surfaceAlong(m, hl(tc), tn);
96
- m.ell({ ...H, tag: 'tympanum', c: add(ts, mul(tn, 0.0019 - 0.00035)), r: [T.r, T.r, 0.0019], axis: tn, k: 0.0012, carve: true });
97
- // supratympanic fold: from behind the eye over the tympanum and down behind it to the shoulder
98
- const f0 = hl([0.0142 * s, 0.0012, -0.0095]), f1 = hl([0.0228 * s, -0.001, -0.0245]);
99
- const f2 = hl([0.0268 * s, -0.0075, -0.037]), f3 = [0.024 * s, 0.037, 0.018];
100
- m.cone({ ...H, tag: 'stfold', a: f0, b: f1, ra: 0.0017, rb: 0.0021, k: 0.004 });
101
- m.cone({ ...H, tag: 'stfold', a: f1, b: f2, ra: 0.0021, rb: 0.002, k: 0.004 });
102
- m.cone({ tag: 'stfold', bone: 'chest', a: f2, b: f3, ra: 0.002, rb: 0.0015, k: 0.005 });
103
- // eyes: bulging lids on their own bone (they retract into the head to blink)
104
- const n0 = m.prims.length;
105
- sculptEyeSocket(m, EYE, HEAD_O, s, { bone: 'eye' + (s > 0 ? 'L' : 'R'), orbit: { r: [0.0085, 0.006, 0.004], at: [0, -0.001, 0.007], k: 0.003 } });
106
- for (const p of m.prims.slice(n0)) p.group = 'eye' + (s > 0 ? 'L' : 'R');
107
- // the fleshy base of the eye bulge (a turret the globe sits in), on the eye bone
108
- const ec = hl([EYE_C[0] * s, EYE_C[1], EYE_C[2]]);
109
- m.ell({ tag: 'eyebase', bone: 'eye' + (s > 0 ? 'L' : 'R'), group: 'eye' + (s > 0 ? 'L' : 'R'), c: add(ec, [0.0005 * s, -0.004, -0.0005]), r: [0.0075, 0.005, 0.0082], k: 0.006 });
110
- }
111
- // the mouth: the upper jaw's underside is cut flat along the lip line (the lower jaw is its own part)
112
- m.ell({ ...H, tag: 'palate', c: hl([0, -0.028, 0.005]), r: [0.05, 0.0115, 0.06], axis: norm([0, 0.04, 1]), k: 0.0008, carve: true });
113
-
114
- // ---------------------------------------------------------------- LOWER JAW (rigid, meshed separately)
115
- const JW = { bone: 'jaw', group: 'jaw', part: 'jaw' };
116
- m.ell({ ...JW, tag: 'mandible', c: hl([0, -0.0185, 0.004]), r: [0.0232 * hw, 0.0066, 0.028], k: 0 });
117
- m.ell({ ...JW, tag: 'chin', c: hl([0, -0.0188, 0.0255]), r: [0.0105, 0.0055, 0.0068], k: 0.006 });
118
- for (const s of [1, -1]) m.ell({ ...JW, tag: 'jawcorner', c: hl([0.0195 * s * hw, -0.0188, -0.017]), r: [0.0062, 0.0062, 0.0105], k: 0.006 });
119
-
120
- // ---------------------------------------------------------------- THROAT (buccal floor and vocal sac: the throat bone)
121
- const TH = { bone: 'throat', group: 'throat' };
122
- m.ell({ ...TH, tag: 'throat', c: [0, 0.0355, 0.036], r: [0.0205, 0.0082, 0.019], k: 0.012 });
123
- m.ell({ ...TH, tag: 'gular', c: [0, 0.0335, 0.022], r: [0.0195, 0.0095, 0.012], k: 0.012 });
124
-
125
- // ---------------------------------------------------------------- TONGUE (rigid, attached at the front of the jaw)
126
- m.cone({ bone: 'tongue', group: 'tongue', part: 'tongue', tag: 'tongue', a: J.tongueBase, b: lerp(J.tongueBase, J.tongueTip, 0.82), ra: 0.0028, rb: 0.0048, k: 0 });
127
- m.ell({ bone: 'tongue', group: 'tongue', part: 'tongue', tag: 'tonguetip', c: lerp(J.tongueBase, J.tongueTip, 0.88), r: [0.0068, 0.0032, 0.0055], k: 0.003 });
128
-
129
- // ---------------------------------------------------------------- DORSOLATERAL FOLDS (common frog)
130
- if (temporaria) {
131
- for (const s of [1, -1]) {
132
- const pts = [hl([0.0125 * s, 0.0005, -0.012]), [0.0165 * s, 0.059, 0.013], [0.0175 * s, 0.0565, -0.008], [0.0165 * s, 0.054, -0.026], [0.0135 * s, 0.047, -0.042]];
133
- const bones = ['head', 'chest', 'spine3', 'spine2'];
134
- for (let i = 0; i < 4; i++) m.cone({ tag: 'dlfold', bone: bones[i], a: pts[i], b: pts[i + 1], ra: 0.0016, rb: 0.0016, k: 0.004 });
135
- }
136
- }
137
-
138
- // ---------------------------------------------------------------- LEGS
139
- const arm = (male ? 1.18 : 1) * (params.armK || 1);
140
- for (const S of ['L', 'R']) {
141
- const s = S === 'L' ? 1 : -1;
142
- const lat = [s, 0, 0];
143
- const sx = (v) => [v[0] * s, v[1], v[2]];
144
- // ---- front: short, upright, four slender fingers
145
- const g = 'F' + S;
146
- const Sh = J['shoulder' + S], E = J['elbow' + S], W = J['wrist' + S], M = J['mcp' + S], T = J['ftoe' + S];
147
- ellY({ tag: 'armpit', bone: 'humerus' + S, group: g, c: add(lerp(Sh, E, 0.3), sx([-0.003, 0.002, 0.002])), ydir: sub(E, Sh), lateral: lat, r: [0.0075, 0.012, 0.0075], k: 0.01 });
148
- m.cone({ tag: 'upperarm', bone: 'humerus' + S, group: g, a: Sh, b: E, ra: 0.0062 * arm, rb: 0.0048 * arm, k: 0.008 });
149
- m.cone({ tag: 'forearm', bone: 'radius' + S, group: g, a: E, b: W, ra: 0.0048 * arm, rb: 0.0031, k: 0.003 });
150
- ellY({ tag: 'forearmmuscle', bone: 'radius' + S, group: g, c: lerp(E, W, 0.3), ydir: sub(W, E), lateral: lat, r: [0.0046 * arm, 0.0085, 0.0048 * arm], k: 0.003 });
151
- const dp = norm(sub(M, W)), up = [0, 1, 0];
152
- ellY({ tag: 'palm', bone: 'metacarpus' + S, group: g, c: lerp(W, M, 0.55), ydir: dp, lateral: norm(cross(dp, up)), r: [0.0045, 0.0068, 0.0022], k: 0.003 });
153
- // fingers fan out from the palm: III longest, pointing forward and in; I (thumb) inside
154
- const fdir = norm(sub(T, M));
155
- // [angle from straight ahead (rad, + outward), length]
156
- const FING = [[-0.95, 0.0105, 1], [-0.62, 0.0135, 1], [-0.3, 0.0184, 1], [0.08, 0.0128, 1]];
157
- for (let i = 0; i < 4; i++) {
158
- const [a, L] = FING[i];
159
- const d = norm([s * Math.sin(a), fdir[1], Math.cos(a)]);
160
- const base = add(M, mul(d, -0.001));
161
- const tip = add(base, mul(d, L));
162
- tip[1] = 0.0014;
163
- m.cone({ tag: 'finger', bone: 'fpaw' + S, group: g, a: base, b: tip, ra: 0.0017, rb: 0.0011, k: 0.0015 });
164
- m.sphere({ tag: 'fingertip', bone: 'fpaw' + S, group: g, c: tip, rad: 0.00135, k: 0.001 });
165
- // male: dark nuptial pad on the thumb
166
- if (i === 0 && male) m.ell({ tag: 'nuptial', bone: 'fpaw' + S, group: g, c: lerp(base, tip, 0.3), r: [0.0022, 0.0022, 0.003], axis: d, k: 0.002 });
167
- }
168
-
169
- // ---- hind: heavy thigh, tibiofibula, long tarsus, long webbed foot
170
- const h = 'H' + S;
171
- const hs = params.splitHind !== true ? h : h + 's', hf = params.splitHind !== true ? h : h + 'f';
172
- const Hp = J['hip' + S], K = J['knee' + S], Hk = J['hock' + S], Mt = J['mtp' + S], Tt = J['htoe' + S];
173
- // web between thigh and body (spans limb and body)
174
- ellY({ tag: 'groin', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.22), sx([-0.002, 0.004, 0])), ydir: sub(K, Hp), lateral: lat, r: [0.0095, 0.016, 0.0095], k: 0.012, bias: 0.001 });
175
- // thigh: a thick muscle mass (the widest part of the hind limb), tapering to the knee
176
- m.cone({ tag: 'thigh', bone: 'femur' + S, group: h, a: Hp, b: K, ra: 0.0105, rb: 0.0058, k: 0.004 });
177
- ellY({ tag: 'thighmuscle', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.42), sx([0.0015, 0.0022, 0.0])), ydir: sub(K, Hp), lateral: lat, r: [0.0128, 0.026, 0.0118], k: 0.0025 });
178
- // shank: the calf bulges near the knee, slim toward the ankle (small blend with the thigh so the
179
- // two stay separate when the leg extends)
180
- m.sphere({ tag: 'kneecap', bone: 'tibia' + S, group: hs, c: K, rad: 0.0046, k: 0.005 });
181
- m.cone({ tag: 'shank', bone: 'tibia' + S, group: hs, a: K, b: Hk, ra: 0.0056, rb: 0.0036, k: 0.006 });
182
- ellY({ tag: 'calf', bone: 'tibia' + S, group: hs, c: add(lerp(K, Hk, 0.36), sx([0.0012, 0.0006, 0])), ydir: sub(Hk, K), lateral: lat, r: [0.0066, 0.0185, 0.0064], k: 0.006 });
183
- // the folds between thigh and shank and between shank and tarsus: in the Z-folded sit the segments
184
- // lie pressed together; a narrow slot down to near each joint keeps their skins apart (each wall
185
- // rides its own bone), so the legs straighten in a leap without stretching a web of skin across
186
- // the fold
187
- if (params.foldSlots !== false) foldSlot(m, K, Hp, Hk, 0.0045, 0.038, 'kneefold', 'tibia' + S, h);
188
- if (params.foldSlots !== false) foldSlot(m, Hk, K, Mt, 0.0035, 0.024, 'anklefold', 'metatarsus' + S, h);
189
- // tarsus (the frog's extra long segment) and the heel
190
- m.sphere({ tag: 'heel', bone: 'metatarsus' + S, group: hf, c: Hk, rad: 0.0038, k: 0.004 });
191
- m.cone({ tag: 'tarsus', bone: 'metatarsus' + S, group: hf, a: Hk, b: Mt, ra: 0.0036, rb: 0.0031, k: 0.004 });
192
- // foot: five long toes fanning out, IV longest; webbing to near the tips (not IV's last phalanges)
193
- const fd = norm(sub(Tt, Mt));
194
- const fl = norm(cross(fd, [0, 1, 0])); // toward the midline for the left foot? (fd x up)
195
- const inward = mul(fl, dot(fl, [s, 0, 0]) > 0 ? -1 : 1); // unit vector toward the midline
196
- // [angle from straight ahead (rad, + outward), length from the tarsus (m)]: I-V, IV longest
197
- const TOES = [[-0.08, 0.021], [0.1, 0.031], [0.28, 0.043], [0.52, 0.07], [0.74, 0.042]];
198
- const rot = (a) => { // rotate fd about y, + turns outward
199
- const c = Math.cos(a), sn = Math.sin(a);
200
- return norm(add(mul(fd, c), mul(inward, -sn)));
201
- };
202
- const base0 = add(Mt, mul(fd, 0.004));
203
- const tips = [];
204
- m.ell({ tag: 'sole', bone: 'hpaw' + S, group: hf, c: add(Mt, mul(fd, 0.009)), r: [0.0052, 0.0028, 0.0105], axis: fd, k: 0.003 });
205
- for (let i = 0; i < 5; i++) {
206
- const [a, lk] = TOES[i];
207
- const d = rot(a);
208
- const b = add(base0, mul(d, 0.004));
209
- const tip = add(b, mul(d, lk - 0.008));
210
- tip[1] = 0.0017;
211
- tips.push({ b, tip, d });
212
- m.cone({ tag: 'toe', bone: 'hpaw' + S, group: hf, a: add(Mt, mul(d, 0.002)), b: tip, ra: 0.0024, rb: 0.0013, k: 0.002 });
213
- m.sphere({ tag: 'toetip', bone: 'hpaw' + S, group: hf, c: tip, rad: 0.0015, k: 0.0008 });
214
- }
215
- // web: a thin membrane in the plane of the foot, its free margin notched between the toes
216
- {
217
- const o = [Mt[0], Mt[1] - 0.0024, Mt[2]];
218
- const un = norm(sub([Tt[0], 0.0019, Tt[2]], o));
219
- const v = norm(cross([0, 1, 0], un)); // lateral in-plane axis (points to the animal's left for fd forward)
220
- const to2 = (p) => { const q = sub(p, o); return [dot(q, un), dot(q, v)]; };
221
- const poly = [to2(add(Mt, mul(fd, -0.002)))];
222
- const webTo = [0.92, 0.9, 0.86, 0.7, 0.92];
223
- const P2 = tips.map((t, i) => to2(lerp(t.b, t.tip, webTo[i])));
224
- // order the toes by their in-plane angle so the outline does not cross itself
225
- const order = [0, 1, 2, 3, 4].sort((a, b) => Math.atan2(P2[a][1], P2[a][0]) - Math.atan2(P2[b][1], P2[b][0]));
226
- for (let k = 0; k < order.length; k++) {
227
- const i = order[k];
228
- poly.push(P2[i]);
229
- if (k < order.length - 1) {
230
- // concave margin between two toes: back toward the base
231
- const j = order[k + 1];
232
- const mid = [(P2[i][0] + P2[j][0]) * 0.5, (P2[i][1] + P2[j][1]) * 0.5];
233
- poly.push([mid[0] * 0.8, mid[1] * 0.8]);
234
- }
235
- }
236
- m.fin({ tag: 'web', bone: 'hpaw' + S, group: hf, o, u: un, v, poly, t: 0.0014, k: 0.0018, thin: true });
237
- }
238
- }
239
- return m;
240
- }