@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,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
- };