@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,339 +0,0 @@
1
- // The grey wolf, sculpted as a signed distance field.
2
- // Every primitive is attached to a bone (skinning) and a group (axial / limb / ear / jaw). The dense
3
- // winter coat is partly sculpted as volume (neck ruff, cheek ruff, cape, breeches, the tail brush),
4
- // with the shells adding the hair on top of it, as a wolf's silhouette is mostly fur.
5
- import { HEAD_O, TAIL_SEGS, HS, hlOf } from './rig.js';
6
- import { add, sub, mul, lerp, norm, cross, smoothstep } from '../../core/math/vec.js';
7
- import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
8
-
9
- // Eye geometry shared by the sculpt (lids + aperture) and the eyeball shader (head-local, left eye).
10
- // Axial eyeball ~23 mm (r 11.8 mm); almond fissure 22 x 10.5 mm, set obliquely (outer corner
11
- // higher, 18 deg); eyes ~68 mm apart, diverging ~13 deg each; the iris fills the fissure.
12
- export const EYE_BASE = { c: [0.034, 0.021, 0.036], r: 0.0118, back: 0.0025, yaw: 0.23, pitch: 0.04, lid: 0.0015, R: 0.01415, d: 0.0089, off: -0.0008, tilt: (18 * Math.PI) / 180, irisZ: 0.0072, irisR: 0.0096 };
13
- // per individual: the eye moves with the head width (the muzzle stretch starts in front of it)
14
- export const eyeOf = (params = {}) => ({ ...EYE_BASE, c: [EYE_BASE.c[0] * (params.headW || 1) * HS, EYE_BASE.c[1] * HS, EYE_BASE.c[2] * HS] });
15
-
16
- const X = [1, 0, 0];
17
-
18
- // The nose leather (head-local), two ellipsoids painted as one (coat.js): in profile a rounded wedge whose
19
- // most forward point lies high, near the top, with the front face sloping back and down into the upper
20
- // lip (lying1, threequarter2); from the front a broad pad with a flat top (face1, face2).
21
- // - NOSE: the front lobe, flattened along its axis, which looks forward and down (~14 deg), so its lower
22
- // front recedes under the tip; the nostrils open on its face. (Tilted further, 24 deg, the face and the
23
- // nostrils on it turned away from a viewer above the head: face2 shows them from 20 deg above.)
24
- // - NOSE_TOP: the dorsal plate, running back along the bridge line; its front end is the tip.
25
- // (one ellipsoid of 27 x 31 mm in profile made a ball, and a ball focuses the key light into one dot)
26
- export const NOSE = { c: [0, -0.0015, 0.1485], r: [0.0182, 0.0122, 0.0095], ax: norm([0, -0.25, 1]) };
27
- export const NOSE_TOP = { c: [0, 0.0075, 0.142], r: [0.0184, 0.006, 0.017], ax: norm([0, -0.12, 1]) };
28
- // The upper lip's margin (head-local, left side; x, y, z, radius): from the front of the lip ellipsoid round the
29
- // corner of the muzzle and across under the nose to the midline
30
- export const LIP_MARGIN = [[0.018, -0.031, 0.095, 0.0072], [0.0158, -0.0218, 0.1235, 0.0062], [0.0112, -0.0198, 0.1362, 0.0048], [0, -0.0198, 0.1418, 0.0043]];
31
- // head-local point on the front lobe's surface at lateral lx and local height ly (along its tilted up axis),
32
- // pushed `out` along its axis
33
- export function noseAt(lx, ly, out = 0) {
34
- const Z = NOSE.ax, Y = [0, Z[2], -Z[1]], [rx, ry, rz] = NOSE.r;
35
- const lz = rz * Math.sqrt(Math.max(0, 1 - (lx / rx) ** 2 - (ly / ry) ** 2)) + out;
36
- return [NOSE.c[0] + lx, NOSE.c[1] + Y[1] * ly + Z[1] * lz, NOSE.c[2] + Y[2] * ly + Z[2] * lz];
37
- }
38
- // The nostril (left side, mirrored) in the front lobe's frame (lx lateral, ly along its tilted up axis):
39
- // a rounded opening in the lower half of the pad (face1: ~0.26 of the pad's width, centred a quarter of the
40
- // width off the midline, two thirds of the way down, a thin rim below), and its tail, the alar groove, curving up and out
41
- // round the side of the pad (lying1, threequarter2). The carve (below) gives it depth; the coat paints the
42
- // same shape dark (render hint noseMarks), so it reads at every tier, whatever the mesh resolves.
43
- export const NOSTRIL = { c: [0.0088, -0.0036], r: [0.0048, 0.0051], tail: [[0.0124, -0.0012], [0.0148, 0.0004], [0.0172, 0.001]], ta: 0.0015, tb: 0.0007 };
44
- export function nostrilSDF(lx, ly) {
45
- const x = Math.abs(lx), [cx, cy] = NOSTRIL.c, [rx, ry] = NOSTRIL.r;
46
- let d = (Math.hypot((x - cx) / rx, (ly - cy) / ry) - 1) * Math.min(rx, ry);
47
- const T = NOSTRIL.tail;
48
- let s0 = 0, L = 0;
49
- for (let i = 1; i < T.length; i++) L += Math.hypot(T[i][0] - T[i - 1][0], T[i][1] - T[i - 1][1]);
50
- for (let i = 1; i < T.length; i++) {
51
- const [ax, ay] = T[i - 1], ex = T[i][0] - ax, ey = T[i][1] - ay, l = Math.hypot(ex, ey);
52
- const t = Math.max(0, Math.min(1, ((x - ax) * ex + (ly - ay) * ey) / (l * l)));
53
- const r = NOSTRIL.ta + (NOSTRIL.tb - NOSTRIL.ta) * ((s0 + t * l) / L);
54
- d = Math.min(d, Math.hypot(x - ax - t * ex, ly - ay - t * ey) - r);
55
- s0 += l;
56
- }
57
- return d;
58
- }
59
- // the front lobe's frame coordinates (lx, ly) of a head-local point
60
- export function noseFrame(h) {
61
- const Z = NOSE.ax, d = [h[0] - NOSE.c[0], h[1] - NOSE.c[1], h[2] - NOSE.c[2]];
62
- return [d[0], d[1] * Z[2] - d[2] * Z[1]];
63
- }
64
- // outward normal of the front lobe there (head-local)
65
- export function noseNormal(lx, ly) {
66
- const Z = NOSE.ax, Y = [0, Z[2], -Z[1]], [rx, ry, rz] = NOSE.r;
67
- const lz = rz * Math.sqrt(Math.max(1e-4, 1 - (lx / rx) ** 2 - (ly / ry) ** 2));
68
- const g = [lx / rx ** 2, ly / ry ** 2, lz / rz ** 2];
69
- return norm([g[0], Y[1] * g[1] + Z[1] * g[2], Y[2] * g[1] + Z[2] * g[2]]);
70
- }
71
-
72
- export function sculptWolf(m, rig, params = {}) {
73
- const J = rig.J;
74
- const EYE = eyeOf(params);
75
- const mz = params.muzzle || 1, hw = params.headW || 1, ruff = params.ruff ?? 1, juv = params.juv || 0;
76
- // head-local -> reference space: width factor, and the muzzle stretched ahead of MUZZLE_Z0
77
- const hl = hlOf(mz, hw);
78
- const hr = (r) => [r[0] * hw * HS, r[1] * HS, r[2] * HS]; // radii in head space
79
- const eyeFrame = (s) => eyeFrameOf(EYE, HEAD_O, s);
80
-
81
- // Ellipsoid whose local Y follows `ydir`; radii = [lateral, along ydir, third axis]
82
- const ellY = (o) => {
83
- const y = norm(o.ydir);
84
- const lat = o.lateral || X;
85
- const z = norm(cross(lat, y));
86
- return m.ell({ ...o, axis: z, up: y, r: [o.r[0], o.r[1], o.r[2]] });
87
- };
88
-
89
- // A tube along a Catmull-Rom curve through control points [[point, radius], ...] (`after`: a point beyond the last,
90
- // setting its end tangent), n round cones per span after the first. The first span blends into the face with o.k;
91
- // the rest are a hard union: round cones sharing their end spheres and tangents meet smoothly on the outside, while
92
- // a smooth union of overlapping segments swells the tube by up to k/4 at every joint (a string of beads)
93
- const tube = (pts, after, n, o) => {
94
- const P = pts.map((q) => q[0]), Rr = pts.map((q) => q[1]);
95
- const at = (i, t) => {
96
- const p0 = P[Math.max(0, i - 1)], p1 = P[i], p2 = P[i + 1], p3 = i + 2 < P.length ? P[i + 2] : after || P[i + 1];
97
- const t2 = t * t, t3 = t2 * t;
98
- return [0, 1, 2].map((k) => 0.5 * (2 * p1[k] + (-p0[k] + p2[k]) * t + (2 * p0[k] - 5 * p1[k] + 4 * p2[k] - p3[k]) * t2 + (-p0[k] + 3 * p1[k] - 3 * p2[k] + p3[k]) * t3));
99
- };
100
- for (let i = 0; i < P.length - 1; i++) {
101
- const ns = i === 0 ? 1 : n;
102
- for (let j = 0; j < ns; j++) {
103
- const t0 = j / ns, t1 = (j + 1) / ns;
104
- m.cone({ ...o, k: i === 0 ? o.k : 0, a: at(i, t0), b: at(i, t1), ra: Rr[i] + (Rr[i + 1] - Rr[i]) * t0, rb: Rr[i] + (Rr[i + 1] - Rr[i]) * t1 });
105
- }
106
- }
107
- };
108
-
109
- // ---------------------------------------------------------------- TORSO
110
- // deep, narrow "keeled" chest; level back; belly tucked up behind the ribs
111
- m.ell({ tag: 'ribcage', bone: 'spine3', c: [0, 0.55, 0.14], r: [0.101, 0.142, 0.2], axis: norm([0, 0.12, -1]), k: 0 });
112
- m.ell({ tag: 'brisket', bone: 'chest', c: [0, 0.455, 0.29], r: [0.056, 0.07, 0.1], axis: norm([0, -0.35, 1]), k: 0.06 });
113
- m.ell({ tag: 'pectoral', bone: 'chest', c: [0, 0.5, 0.405], r: [0.066, 0.078, 0.06], k: 0.05 });
114
- m.ell({ tag: 'withers', bone: 'chest', c: [0, 0.69, 0.26], r: [0.052, 0.055, 0.13], axis: norm([0, -0.1, 1]), k: 0.06 });
115
- m.ell({ tag: 'back', bone: 'spine3', c: [0, 0.692, 0.03], r: [0.066, 0.05, 0.16], k: 0.06 });
116
- m.ell({ tag: 'abdomen', bone: 'spine2', c: [0, 0.585, -0.08], r: [0.079, 0.087, 0.16], axis: norm([0, 0.33, -1]), k: 0.07 });
117
- m.ell({ tag: 'loin', bone: 'spine1', c: [0, 0.68, -0.14], r: [0.06, 0.05, 0.14], k: 0.05 });
118
- m.ell({ tag: 'flank', bone: 'spine1', c: [0, 0.59, -0.19], r: [0.064, 0.064, 0.09], k: 0.05 });
119
- m.ell({ tag: 'pelvis', bone: 'pelvis', c: [0, 0.625, -0.28], r: [0.07, 0.085, 0.11], k: 0.06 });
120
- m.ell({ tag: 'croup', bone: 'pelvis', c: [0, 0.675, -0.27], r: [0.055, 0.04, 0.1], k: 0.04 });
121
- for (const s of [1, -1]) {
122
- m.ell({ tag: 'rump', bone: 'pelvis', c: [0.042 * s, 0.585, -0.355], r: [0.042, 0.07, 0.05], k: 0.04 });
123
- }
124
- // cape: the long guard hair over the shoulders and hackles (sculpted coat volume)
125
- m.ell({ tag: 'cape', bone: 'chest', c: [0, 0.675, 0.31], r: [0.09 * (0.8 + 0.2 * ruff), 0.075, 0.13], axis: norm([0, 0.25, 1]), k: 0.07 });
126
-
127
- // ---------------------------------------------------------------- NECK (thick, wrapped in the ruff)
128
- m.cone({ tag: 'neck', bone: 'neck1', a: [0, 0.58, 0.37], b: [0, 0.685, 0.455], ra: 0.088, rb: 0.07, k: 0.06 });
129
- m.cone({ tag: 'neck', bone: 'neck2', a: [0, 0.685, 0.455], b: [0, 0.765, 0.51], ra: 0.07, rb: 0.058, k: 0.04 });
130
- m.ell({ tag: 'nape', bone: 'neck1', c: [0, 0.745, 0.42], r: [0.046, 0.034, 0.1], axis: norm([0, 0.5, 1]), k: 0.04 });
131
- m.ell({ tag: 'throat', bone: 'neck2', c: [0, 0.625, 0.5], r: [0.048, 0.06, 0.07], axis: norm([0, 0.8, 0.6]), k: 0.05 });
132
- // the ruff ("mane"): a collar of long hair framing the face, fullest at the throat and the sides
133
- const rf = (0.75 + 0.25 * ruff) * 1.2; // (+20 %: even a slight female keeps a ruff that frames the face)
134
- m.ell({ tag: 'ruff', bone: 'neck1', c: [0, 0.64, 0.43], r: [0.082 * rf, 0.088 * rf, 0.095], axis: norm([0, 0.6, 1]), k: 0.06 });
135
- m.ell({ tag: 'ruff', bone: 'neck2', c: [0, 0.67, 0.505], r: [0.062 * rf, 0.055 * rf, 0.05], axis: norm([0, 0.8, 1]), k: 0.05 });
136
-
137
- // ---------------------------------------------------------------- HEAD (head-local coords, see HEAD_O)
138
- // landmarks from profile / frontal photos (adult ~0.25 m occiput -> nose): nose tip (0,-0.012,0.155),
139
- // eyes (+-0.034, 0.021, 0.036), stop ~z 0.05, chin (0,-0.058,0.125), skull top ~0.077, occiput -0.1,
140
- // zygomatic width ~0.13 (+ cheek ruff); a pup has a domed skull and a short muzzle (params).
141
- // Profile (lying1 / threequarter2 overlaid on the eye and the nose tip): a moderate stop in front of
142
- // the eyes, the forehead rising ~0.8 mm per mm from the bridge (z 0.065 -> 0.035) to a broad, domed crown that stands
143
- // ~10 mm above the eye line's old straight wedge; the midline top at z 100 / 65 / 35 / 0 is 32 / 44 / 69 / 79 mm
144
- // (was 34 / 50 / 60 / 71: the bridge ran level into the crown, a small flat skull behind a long snout)
145
- const H = { bone: 'head' };
146
- m.ell({ ...H, tag: 'cranium', c: hl([0, 0.024 + 0.002 * juv, -0.035]), r: hr([0.053 + 0.002 * juv, 0.053 + 0.005 * juv, 0.066]), k: 0.04 });
147
- // temporal muscles: a broad, domed skull between the ears (the ears sit on its corners)
148
- for (const s of [1, -1]) m.ell({ ...H, tag: 'temporal', c: hl([0.03 * s, 0.041, -0.045]), r: hr([0.03, 0.026, 0.045]), k: 0.03 });
149
- m.ell({ ...H, tag: 'forehead', c: hl([0, 0.043 + 0.002 * juv, 0.014]), r: hr([0.037, 0.032, 0.038]), axis: norm([0, -0.35, 1]), k: 0.025 });
150
- m.ell({ ...H, tag: 'crest', c: hl([0, 0.052, -0.055]), r: hr([0.018, 0.014, 0.05]), k: 0.03 });
151
- for (const s of [1, -1]) {
152
- m.ell({ ...H, tag: 'brow', c: hl([0.029 * s, 0.037, 0.032]), r: hr([0.016, 0.008, 0.014]), k: 0.012 });
153
- m.ell({ ...H, tag: 'zygomatic', c: hl([0.056 * s, 0.002, -0.004]), r: hr([0.016, 0.022, 0.045]), axis: norm([-0.3 * s, 0, 1]), k: 0.035 });
154
- m.ell({ ...H, tag: 'cheek', c: hl([0.038 * s, -0.028, -0.006]), r: hr([0.018, 0.026, 0.038]), k: 0.035 });
155
- // maxilla: the side of the face under the eye tapers into the muzzle in one plane (a wolf has no
156
- // step between cheek and muzzle); converges toward the nose, deepest at the lip line
157
- m.ell({ ...H, tag: 'maxilla', c: hl([0.022 * s, -0.013, 0.052]), r: hr([0.02, 0.027, 0.062]), axis: norm([-0.2 * s, -0.1, 1]), k: 0.024 });
158
- // mouth corner: the upper lip runs back to the commissure under the eye and meets the cheek
159
- m.ell({ ...H, tag: 'commissure', c: hl([0.031 * s, -0.036, 0.028]), r: hr([0.013, 0.012, 0.026]), k: 0.016 });
160
- // cheek ruff: the hair flares sideways and back below the ears (the wide wolf face)
161
- m.ell({ ...H, tag: 'cheekruff', c: hl([0.056 * s, -0.036, -0.07]), r: hr([0.03 * rf, 0.045, 0.034]), axis: norm([0.35 * s, 0, 1]), k: 0.03 });
162
- m.ell({ ...H, tag: 'mastoid', c: hl([0.036 * s, -0.018, -0.062]), r: hr([0.024, 0.03, 0.03]), k: 0.03 });
163
- // upper lip: from the mouth corner under the eye (the lips meet the commissure ~0.6 eye spacings off
164
- // the midline, face1) forward to under the nose. Its margin, the lip line, keeps its height behind and
165
- // rises toward the front to the leather's lower back: only ~6-15 mm of lip under the leather (lying1,
166
- // threequarter2, face1). (Lips running level to 2 cm behind the leather hung a 3 cm white
167
- // block under it, with the chin as a second lobe below: a puppy's muzzle.)
168
- m.ell({ ...H, tag: 'lip', c: hl([0.0195 * s, -0.0395, 0.062]), r: hr([0.0095, 0.0108, 0.04]), axis: norm([-0.2 * s, 0.11, 1]), k: 0.014 });
169
- }
170
- // the front of the lip: one tube along the margin, from the side of the muzzle round its corner under the side of
171
- // the nose and across under the nose to the midline, where it meets its mirror image with the same tangent; from
172
- // the front its margin runs nearly level across the width of the leather before it turns back (face1, face2: a
173
- // flat "w"). (Three separately blended rolls stood as two knobs either side of a crease under the nose: a pursed
174
- // mouth from the front and below. Both sides come before the whisker pads, which blend into them.)
175
- for (const s of [1, -1]) {
176
- const C = LIP_MARGIN.map(([x, y, z, r]) => [hl([x * s, y, z]), r * HS]);
177
- const [mx, my, mz2] = LIP_MARGIN[LIP_MARGIN.length - 2];
178
- tube(C, hl([-mx * s, my, mz2]), 3, { ...H, tag: 'lipfront', k: 0.01 });
179
- }
180
- for (const s of [1, -1]) {
181
- m.ell({ ...H, tag: 'whisker', c: hl([0.0115 * s, -0.0195, 0.116]), r: hr([0.009, 0.0088, 0.018]), k: 0.01 });
182
- }
183
- // long, fairly narrow muzzle with a straight nasal bridge and a moderate stop (the cone starts low in front of
184
- // the forehead, which rises above it). The bridge ends behind
185
- // the nose pad and its top line runs straight into the top of the leather (threequarter2, side3): a
186
- // cone ending level with the pad capped it with a round fur dome (a bear's or pig's snout from the front)
187
- m.cone({ ...H, tag: 'nasal', a: hl([0, 0.021, 0.058]), b: hl([0, 0.0084, 0.126]), ra: 0.024 * hw, rb: 0.013 * hw, k: 0.02 });
188
- // the bridge: a broad, flat-topped muzzle (square in section, not a wedge), ending behind the leather
189
- m.ell({ ...H, tag: 'bridge', c: hl([0, 0.009, 0.088]), r: hr([0.02, 0.012, 0.045]), axis: norm([0, -0.25, 1]), k: 0.02 });
190
- m.ell({ ...H, tag: 'muzzle', c: hl([0, -0.016, 0.082]), r: hr([0.025, 0.0245, 0.05]), axis: norm([0, 0.1, 1]), k: 0.02 });
191
- // the rostrum keeps the nose pad's width up to the leather (no waist behind the pad), its top under the
192
- // bridge line
193
- m.ell({ ...H, tag: 'rostrum', c: hl([0, -0.004, 0.13]), r: hr([0.0165, 0.012, 0.019]), k: 0.012 });
194
- // the nose leather (face1: 0.55 x 0.38 eye spacings; threequarter2: it caps the whole end of the muzzle,
195
- // its top running on from the bridge line and its sides reaching ~2.5 cm back)
196
- m.ell({ ...H, tag: 'nose', c: hl(NOSE.c), r: hr(NOSE.r), axis: NOSE.ax, k: 0.008 });
197
- m.ell({ ...H, tag: 'nosetop', c: hl(NOSE_TOP.c), r: hr(NOSE_TOP.r), axis: NOSE_TOP.ax, k: 0.006 });
198
- // (no philtrum primitive: under the nose the two rostral lip rolls meet on the midline, and the groove that
199
- // splits the bottom of the pad and runs down to the mouth line is painted. A small plate there, along the
200
- // receding lip, caught the key light on the 5 mm cells of medium as a pale tab under the nose.)
201
- // (the groove splitting the bottom of the pad is painted (coat.js): carved, it folded into a sliver on
202
- // the 5 mm cells of the medium tier)
203
- for (const s of [1, -1]) {
204
- const ef = eyeFrame(s);
205
- const at = (u, v, w) => add(add(add(ef.c, mul(ef.x, u)), mul(ef.y, v)), mul(ef.z, w));
206
- // orbit: a soft hollow so brow, cheek and bridge fall away to the lids
207
- ellY({ ...H, tag: 'orbit', c: at(-0.002 * s, -0.0005, 0.019), ydir: ef.y, lateral: ef.x, r: [0.018, 0.0125, 0.009], k: 0.008, carve: true });
208
- m.sphere({ ...H, tag: 'eyelid', c: ef.c, rad: EYE.r + EYE.lid, k: 0.005 });
209
- m.lens({ ...H, tag: 'eyesocket', c: add(ef.c, mul(ef.y, EYE.off)), x: ef.x, y: ef.y, z: ef.z, R: EYE.R, d: EYE.d, zMin: -0.002, zMax: 0.024, k: 0.0022, carve: true });
210
- // nostrils: comma-shaped openings in the lower half of the pad's face (face1, face2: the darkest marks in
211
- // the face): a rounded opening that looks forward, down and out, and its tail, the alar slit, running
212
- // back round the side of the pad (lying1, threequarter2)
213
- const [ncx, ncy] = NOSTRIL.c, nn = noseNormal(ncx * s, ncy);
214
- m.ell({ ...H, tag: 'nostril', c: hl(noseAt(ncx * s, ncy, 0.0009)), r: hr([NOSTRIL.r[0], NOSTRIL.r[1], 0.005]), axis: nn, up: [0, 1, 0], k: 0.0012, carve: true });
215
- const [t0, t1] = NOSTRIL.tail;
216
- m.cone({ ...H, tag: 'alar', a: hl(noseAt(t0[0] * s, t0[1], 0.0003)), b: hl(noseAt(t1[0] * s, t1[1], 0.0002)), ra: 0.0014, rb: 0.001, k: 0.0008, carve: true });
217
- // upper canine, hidden behind the lip and inside the closed jaw; shows when the mouth opens
218
- // (4 mm further back with the front of the mouth: the lips' front ends and the lower jaw)
219
- m.cone({ ...H, tag: 'canine', a: hl([0.014 * s, -0.0295, 0.1095]), b: hl([0.013 * s, -0.0362, 0.1067]), ra: 0.004, rb: 0.0012, k: 0.002 });
220
- // upper incisors: three a side in an arc along the front of the upper jaw, just behind the rostral lip, their
221
- // tips under the head's underside (inside the closed jaw at rest, in front of the lower incisors: a scissor
222
- // bite); the open mouth shows them under the nose (the third, outer one larger)
223
- for (const [x, z, ra] of [[0.0024, 0.1215, 0.0014], [0.0066, 0.1202, 0.0014], [0.0106, 0.1175, 0.0017]]) {
224
- m.cone({ ...H, tag: 'tooth', a: hl([x * s, -0.027, z + 0.001]), b: hl([x * s * 1.03, -0.034, z - 0.0004]), ra, rb: 0.0008, k: 0.001 });
225
- }
226
- }
227
-
228
- // ---------------------------------------------------------------- JAW (separate surface so the mouth can open)
229
- const JW = { bone: 'jaw', group: 'jaw', part: 'jaw' };
230
- // The lower jaw runs forward under the rising lip line to below the back of the leather, its ventral line
231
- // straight from the throat to the chin (a scissor bite: the lower incisors just behind the upper lip's
232
- // front, the lower canines in front of the upper ones). (With the jaw ending 2 cm further back, a chin
233
- // ellipsoid sagged below the lip line as a round lobe: a pouch from the side, a second white lobe from
234
- // the front.) The front is narrower than the upper lip, so from the front little chin shows under it.
235
- m.cone({ ...JW, tag: 'mandible', a: hl([0, -0.05, -0.015]), b: hl([0, -0.049, 0.095]), ra: 0.016, rb: 0.0105, k: 0 });
236
- m.cone({ ...JW, tag: 'mandible', a: hl([0, -0.049, 0.095]), b: hl([0, -0.0328, 0.1142]), ra: 0.0105, rb: 0.0062, k: 0.006 });
237
- for (const s of [1, -1]) {
238
- m.cone({ ...JW, tag: 'mandible', a: hl([0.036 * s, -0.044, -0.03]), b: hl([0.0105 * s, -0.0315, 0.1135]), ra: 0.013, rb: 0.007, k: 0.02 });
239
- }
240
- // the chin: the rounded front of the jaw under the lower lip
241
- m.ell({ ...JW, tag: 'chin', c: hl([0, -0.038, 0.1005]), r: hr([0.0105, 0.006, 0.008]), k: 0.01 });
242
- // lower lip: the front of the jaw rises to meet the upper lip under the nose (no slit at rest)
243
- m.ell({ ...JW, tag: 'lowerlip', c: hl([0, -0.0262, 0.118]), r: hr([0.0125, 0.004, 0.0075]), k: 0.01 });
244
- // lower canines: at rest their tips sit inside the upper lip; they come clear as soon as the jaw drops
245
- // (the snarl and the bite show fangs; the core cannot retract the lips)
246
- for (const s of [1, -1]) {
247
- m.cone({ ...JW, tag: 'canine', a: hl([0.0085 * s, -0.0355, 0.1112]), b: hl([0.0096 * s, -0.0212, 0.1142]), ra: 0.0036, rb: 0.0012, k: 0.002 });
248
- // the rest of the lower teeth, hidden inside the upper lips at rest (the jaw's crest lies 2-13 mm inside
249
- // the head there): three incisors between the canines, and the cheek teeth along the crest (P2-P4 and
250
- // the carnassial), so the open mouth shows a tooth row instead of bare gum
251
- for (const [x, z] of [[0.0026, 0.1197], [0.0062, 0.1182], [0.0094, 0.1158]]) m.cone({ ...JW, tag: 'tooth', a: hl([x * s, -0.0295, z]), b: hl([x * s * 1.05, -0.0225, z + 0.0012]), ra: 0.0017, rb: 0.0009, k: 0.001 });
252
- for (const [x, z, y0, hgt, ra] of [[0.0128, 0.086, -0.041, 0.0045, 0.0028], [0.0137, 0.073, -0.041, 0.0055, 0.0032], [0.0146, 0.059, -0.0405, 0.006, 0.0036], [0.0149, 0.043, -0.0395, 0.0065, 0.0044]]) {
253
- m.cone({ ...JW, tag: 'tooth', a: hl([x * s, y0 - 0.002, z]), b: hl([(x - 0.0006) * s, y0 + hgt, z - 0.001]), ra, rb: 0.0009, k: 0.0015 });
254
- }
255
- }
256
-
257
- // ---------------------------------------------------------------- EARS
258
- // erect, triangular, set wide; broad at the base, pointed tip, cupped front (shorter than a fox's)
259
- for (const S of ['L', 'R']) {
260
- const s = S === 'L' ? 1 : -1;
261
- const base = J['earBase' + S], tip = J['earTip' + S];
262
- const up = norm(sub(tip, base));
263
- const facing = norm([0.32 * s, 0.05, 1]); // (the cups face forward, a little outward: face1, face2)
264
- const lat = norm(cross(up, facing));
265
- const L = Math.hypot(...sub(tip, base));
266
- const ear = { bone: 'ear' + S, group: 'ear' + S, thin: true };
267
- const at = (t) => lerp(base, tip, t);
268
- ellY({ ...ear, tag: 'ear', c: at(0.22), ydir: up, lateral: lat, r: [0.036, 0.036, 0.0095], k: 0.012 });
269
- ellY({ ...ear, tag: 'ear', c: at(0.55), ydir: up, lateral: lat, r: [0.023, 0.034, 0.0068], k: 0.012 });
270
- ellY({ ...ear, tag: 'ear', c: at(0.84), ydir: up, lateral: lat, r: [0.0085, 0.022, 0.0048], k: 0.01 });
271
- ellY({ ...ear, tag: 'earinner', c: add(at(0.36), mul(facing, 0.0085)), ydir: up, lateral: lat, r: [0.024, 0.034, 0.0062], k: 0.004, carve: true });
272
- ellY({ ...ear, tag: 'earinner', c: add(at(0.68), mul(facing, 0.0062)), ydir: up, lateral: lat, r: [0.011, 0.026, 0.0045], k: 0.003, carve: true });
273
- void L;
274
- }
275
-
276
- // ---------------------------------------------------------------- LEGS
277
- // long, lean legs; big feet; elbows tucked in under the narrow chest
278
- for (const S of ['L', 'R']) {
279
- const s = S === 'L' ? 1 : -1;
280
- const lat = [s, 0, 0];
281
- const sx = (v) => [v[0] * s, v[1], v[2]];
282
- const g = 'F' + S;
283
- const Sc = J['scapTop' + S], Sh = J['shoulder' + S], E = J['elbow' + S], W = J['wrist' + S], M = J['mcp' + S], T = J['ftoe' + S];
284
- ellY({ tag: 'scapmuscle', bone: 'scapula' + S, group: g, c: add(lerp(Sc, Sh, 0.5), sx([0.008, 0, 0])), ydir: sub(Sh, Sc), lateral: lat, r: [0.026, 0.1, 0.058], k: 0.06, bias: 0.004 });
285
- m.cone({ tag: 'upperarm', bone: 'humerus' + S, group: g, a: Sh, b: E, ra: 0.043, rb: 0.032, k: 0.06 });
286
- ellY({ tag: 'triceps', bone: 'humerus' + S, group: g, c: add(lerp(Sh, E, 0.55), [0, 0, -0.03]), ydir: sub(E, Sh), lateral: lat, r: [0.03, 0.075, 0.036], k: 0.05 });
287
- m.sphere({ tag: 'olecranon', bone: 'radius' + S, group: g, c: add(E, [0, 0.008, -0.024]), rad: 0.018, k: 0.02 });
288
- m.cone({ tag: 'forearm', bone: 'radius' + S, group: g, a: E, b: W, ra: 0.032, rb: 0.019, k: 0.025 });
289
- ellY({ tag: 'forearmmuscle', bone: 'radius' + S, group: g, c: add(lerp(E, W, 0.25), sx([0.003, 0, 0.004])), ydir: sub(W, E), lateral: lat, r: [0.028, 0.068, 0.032], k: 0.03 });
290
- // feathering on the back of the forearm
291
- ellY({ tag: 'feather', bone: 'radius' + S, group: g, c: add(lerp(E, W, 0.45), [0, 0, -0.018]), ydir: sub(W, E), lateral: lat, r: [0.016, 0.06, 0.018], k: 0.02 });
292
- m.sphere({ tag: 'wrist', bone: 'metacarpus' + S, group: g, c: add(W, [0, 0, -0.003]), rad: 0.019, k: 0.012 });
293
- m.sphere({ tag: 'carpalpad', bone: 'metacarpus' + S, group: g, c: add(W, [0, -0.008, -0.019]), rad: 0.009, k: 0.01 });
294
- m.cone({ tag: 'pastern', bone: 'metacarpus' + S, group: g, a: W, b: M, ra: 0.018, rb: 0.018, k: 0.012 });
295
- m.sphere({ tag: 'dewclaw', bone: 'metacarpus' + S, group: g, c: add(lerp(W, M, 0.3), sx([-0.016, 0, 0.002])), rad: 0.006, k: 0.005 });
296
- const dp = norm(sub(T, M));
297
- // big oval forefoot (print ~11 x 9.5 cm incl. claws)
298
- ellY({ tag: 'paw', bone: 'fpaw' + S, group: g, c: add(add(M, mul(dp, 0.022)), [0, -0.006, 0]), ydir: dp, lateral: lat, r: [0.03, 0.04, 0.019], k: 0.014 });
299
- m.sphere({ tag: 'pad', bone: 'fpaw' + S, group: g, c: add(M, [0, -0.025, 0.012]), rad: 0.013, k: 0.01 });
300
- const toeX = [-0.021, -0.0072, 0.0072, 0.021], toeZ = [-0.011, 0, 0, -0.011];
301
- for (let i = 0; i < 4; i++) m.sphere({ tag: 'toe', bone: 'fpaw' + S, group: g, c: [T[0] + toeX[i] * s, 0.0122, T[2] - 0.009 + toeZ[i]], rad: 0.0115, k: 0.007 });
302
- for (let i = 0; i < 4; i++) m.cone({ tag: 'claw', bone: 'fpaw' + S, group: g, a: [T[0] + toeX[i] * s * 0.95, 0.011, T[2] + 0.0 + toeZ[i]], b: [T[0] + toeX[i] * s * 0.95, 0.004, T[2] + 0.011 + toeZ[i]], ra: 0.0032, rb: 0.0012, k: 0.002 });
303
-
304
- const h = 'H' + S;
305
- const Hp = J['hip' + S], K = J['knee' + S], Hk = J['hock' + S], Mt = J['mtp' + S], Tt = J['htoe' + S];
306
- // broad muscular thigh with long "breeches" hair behind
307
- ellY({ tag: 'thigh', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.4), sx([0.004, 0, -0.03])), ydir: sub(K, Hp), lateral: lat, r: [0.042, 0.145, 0.095], k: 0.05, bias: 0.004 });
308
- m.cone({ tag: 'thighfront', bone: 'femur' + S, group: h, a: sx([0.04, 0.6, -0.15]), b: add(K, sx([-0.004, 0.055, 0.0])), ra: 0.038, rb: 0.024, k: 0.06 });
309
- m.cone({ tag: 'hamstring', bone: 'femur' + S, group: h, a: sx([0.045, 0.6, -0.36]), b: add(lerp(K, Hk, 0.28), [0, 0, -0.03]), ra: 0.046, rb: 0.028, k: 0.04 });
310
- ellY({ tag: 'breeches', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.62), sx([0.006, -0.01, -0.085])), ydir: sub(K, Hp), lateral: lat, r: [0.03, 0.08, 0.03], k: 0.035 });
311
- ellY({ tag: 'flankfold', bone: 'femur' + S, group: h, c: sx([0.05, 0.48, -0.13]), ydir: [-0.03, 0.16, 0.1], lateral: lat, r: [0.02, 0.08, 0.04], k: 0.06 });
312
- m.sphere({ tag: 'stifle', bone: 'tibia' + S, group: h, c: add(K, sx([0.002, 0.008, 0.006])), rad: 0.017, k: 0.04 });
313
- m.cone({ tag: 'shin', bone: 'tibia' + S, group: h, a: add(lerp(K, Hk, 0.06), sx([0.0, 0.0, 0.004])), b: Hk, ra: 0.023, rb: 0.017, k: 0.03 });
314
- ellY({ tag: 'calf', bone: 'tibia' + S, group: h, c: add(lerp(K, Hk, 0.3), sx([0.002, 0.01, -0.027])), ydir: sub(Hk, K), lateral: lat, r: [0.025, 0.07, 0.03], k: 0.035 });
315
- m.cone({ tag: 'achilles', bone: 'tibia' + S, group: h, a: add(lerp(K, Hk, 0.5), [0, 0.01, -0.034]), b: add(Hk, [0, 0.012, -0.029]), ra: 0.012, rb: 0.011, k: 0.015 });
316
- m.sphere({ tag: 'calcaneus', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.012, -0.025]), rad: 0.015, k: 0.012 });
317
- m.sphere({ tag: 'hock', bone: 'metatarsus' + S, group: h, c: Hk, rad: 0.02, k: 0.012 });
318
- m.cone({ tag: 'metatarsus', bone: 'metatarsus' + S, group: h, a: Hk, b: Mt, ra: 0.018, rb: 0.017, k: 0.012 });
319
- const dh = norm(sub(Tt, Mt));
320
- ellY({ tag: 'paw', bone: 'hpaw' + S, group: h, c: add(add(Mt, mul(dh, 0.02)), [0, -0.006, 0]), ydir: dh, lateral: lat, r: [0.027, 0.037, 0.018], k: 0.014 });
321
- m.sphere({ tag: 'pad', bone: 'hpaw' + S, group: h, c: add(Mt, [0, -0.025, 0.01]), rad: 0.012, k: 0.01 });
322
- for (let i = 0; i < 4; i++) m.sphere({ tag: 'toe', bone: 'hpaw' + S, group: h, c: [Tt[0] + toeX[i] * 0.92 * s, 0.0115, Tt[2] - 0.009 + toeZ[i]], rad: 0.0105, k: 0.007 });
323
- for (let i = 0; i < 4; i++) m.cone({ tag: 'claw', bone: 'hpaw' + S, group: h, a: [Tt[0] + toeX[i] * 0.88 * s, 0.01, Tt[2] + toeZ[i]], b: [Tt[0] + toeX[i] * 0.88 * s, 0.004, Tt[2] + 0.01 + toeZ[i]], ra: 0.003, rb: 0.0011, k: 0.002 });
324
- }
325
-
326
- // ---------------------------------------------------------------- TAIL
327
- // the brush: a dense, round tail thickest at a third of its length, not tapering to a point
328
- // (the long guard hair is carried partly by this volume, partly by the shells)
329
- const tb = params.tailBrush ?? 1;
330
- // spindle: narrow root, fullest at 50-60 % of the length, tapering to the tip
331
- // (the base mesh carries the brush at coarse tiers; it tapers to a point: the tip hair is the longest)
332
- const tailR = (t) => tb * (t < 0.15 ? 0.026 + 0.004 * (t / 0.15) : t < 0.55 ? 0.03 + 0.026 * smoothstep(0.15, 0.55, t) : 0.056 - 0.043 * Math.pow((t - 0.55) / 0.45, 1.6));
333
- m.cone({ tag: 'tailroot', bone: 'tail0', a: [0, 0.645, -0.285], b: J.tail1, ra: 0.032, rb: tailR(0.1), k: 0.04 });
334
- for (let i = 0; i < TAIL_SEGS; i++) {
335
- m.cone({ tag: 'tail', bone: 'tail' + i, a: J['tail' + i], b: J['tail' + (i + 1)], ra: tailR(i / TAIL_SEGS), rb: tailR((i + 1) / TAIL_SEGS), k: 0.012, thin: i >= TAIL_SEGS - 1 });
336
- }
337
-
338
- return m;
339
- }
@@ -1,29 +0,0 @@
1
- // Web Worker entry: builds one individual (or one meshing job of it) off the main thread.
2
- // Messages: { task: 'mesh', species, opts, job } -> { type: 'mesh', parts }
3
- // { task: 'finish', species, opts, jobs } -> { type: 'done', data }
4
- // { task: 'all', species, opts } -> { type: 'done', data }
5
- import { loadSpecies } from './registry.js';
6
- import { prepare, meshJob, finish, buildSync, transferables } from './core/build/pipeline.js';
7
- import { regionTransferables } from './core/build/mesh.js';
8
-
9
- self.onmessage = async (e) => {
10
- const m = e.data || {};
11
- try {
12
- const species = await loadSpecies(m.species);
13
- const t0 = performance.now();
14
- const progress = (msg) => self.postMessage({ type: 'progress', msg, t: performance.now() - t0 });
15
- if (m.task === 'mesh') {
16
- const parts = meshJob(species, m.opts, m.job);
17
- self.postMessage({ type: 'mesh', job: m.job, parts }, regionTransferables(parts));
18
- } else if (m.task === 'finish') {
19
- const prep = prepare(species, m.opts);
20
- const out = finish(species, m.opts, m.jobs, prep, progress);
21
- self.postMessage({ type: 'done', data: out }, transferables(out));
22
- } else {
23
- const out = buildSync(species, m.opts, progress);
24
- self.postMessage({ type: 'done', data: out }, transferables(out));
25
- }
26
- } catch (err) {
27
- self.postMessage({ type: 'error', message: String((err && err.stack) || err) });
28
- }
29
- };