@bitefoundry/retrieviewer 0.0.0-stage → 0.0.2

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 (297) hide show
  1. package/ASSET-PROVENANCE.md +43 -0
  2. package/ASSETS-LICENSE.md +58 -0
  3. package/ATTRIBUTION.md +54 -0
  4. package/EMBEDDING.md +96 -0
  5. package/INSTALLING.md +126 -0
  6. package/LICENSE +16 -0
  7. package/LICENSE-CODE +21 -0
  8. package/PLUGIN.md +110 -0
  9. package/PUPPY-MEMORY.md +57 -0
  10. package/PUPPY-Q5-COMPARISON.md +73 -0
  11. package/README.md +86 -2
  12. package/RELEASING.md +70 -0
  13. package/THIRD-PARTY-NOTICES.md +17 -0
  14. package/TRADEMARKS.md +21 -0
  15. package/WINDOWS-COMPANION.md +113 -0
  16. package/braille.mjs +45 -0
  17. package/examples/bridge.py +62 -0
  18. package/examples/embed.mjs +15 -0
  19. package/examples/live.mjs +40 -0
  20. package/examples/plugin.mjs +21 -0
  21. package/integrations/opencode/runtime.mjs +195 -0
  22. package/integrations/opencode/tui.tsx +59 -0
  23. package/integrations/windows-overlay/Overlay.cs +259 -0
  24. package/integrations/windows-overlay/desktop.mjs +87 -0
  25. package/integrations/windows-overlay/protocol.mjs +39 -0
  26. package/integrations/windows-overlay/worker.mjs +60 -0
  27. package/locomotion-core.mjs +21 -0
  28. package/locomotion-puppy.animal +0 -0
  29. package/package.json +131 -4
  30. package/pet-q5-manifest.json +118 -0
  31. package/puppy-clean.mjs +51 -0
  32. package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
  33. package/puppy-companion-interactions-v3.json +1 -0
  34. package/puppy-companion-life.mjs +182 -0
  35. package/puppy-companion-objects.mjs +30 -0
  36. package/puppy-companion-sprites.mjs +14 -0
  37. package/puppy-continuous.mjs +57 -0
  38. package/puppy-face.mjs +46 -0
  39. package/puppy-frame-predictive.mjs +147 -0
  40. package/puppy-frame-store.mjs +108 -0
  41. package/puppy-mouth-anchor.json +1 -0
  42. package/puppy-mouth-anchor.mjs +33 -0
  43. package/puppy-paper-actions-v9-q5.bin.gz +0 -0
  44. package/puppy-paper-actions-v9.json +1 -0
  45. package/puppy-paper-actions.mjs +48 -0
  46. package/puppy-paper-approach-q5.bin.gz +0 -0
  47. package/puppy-paper-approach.json +1 -0
  48. package/puppy-paper-area.mjs +15 -0
  49. package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
  50. package/puppy-paper-arrival-seat.json +1 -0
  51. package/puppy-paper-behaviors-q5.bin.gz +0 -0
  52. package/puppy-paper-behaviors.json +1 -0
  53. package/puppy-paper-behaviors.mjs +8 -0
  54. package/puppy-paper-contact.mjs +103 -0
  55. package/puppy-paper-director.mjs +21 -0
  56. package/puppy-paper-paw-transport.mjs +76 -0
  57. package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
  58. package/puppy-paper-rear-gallop.json +1 -0
  59. package/puppy-paper-seat-q5.bin.gz +0 -0
  60. package/puppy-paper-seat.json +14 -0
  61. package/puppy-paper-seat.mjs +13 -0
  62. package/puppy-paper-trick-timing.mjs +58 -0
  63. package/puppy-paper-world.mjs +108 -0
  64. package/puppy-paper.mjs +271 -0
  65. package/puppy-stable-meta.json +1606 -0
  66. package/puppy-stable.mjs +72 -0
  67. package/puppy-stride-meta.json +343 -0
  68. package/puppy-stride-tweens-q5.bin.gz +0 -0
  69. package/puppy-zoomies.mjs +27 -0
  70. package/puppy-zoomies30.mjs +12 -0
  71. package/retrieviewer-cli.mjs +94 -0
  72. package/retrieviewer-config.mjs +49 -0
  73. package/retrieviewer-install.mjs +157 -0
  74. package/retrieviewer-plugin.d.mts +44 -0
  75. package/retrieviewer-plugin.mjs +97 -0
  76. package/retrieviewer-stdio.mjs +38 -0
  77. package/retrieviewer.d.mts +85 -0
  78. package/retrieviewer.mjs +142 -0
  79. package/vendor/procedural-animals/LICENSE +21 -0
  80. package/vendor/procedural-animals/package.json +40 -0
  81. package/vendor/procedural-animals/src/animal.js +146 -0
  82. package/vendor/procedural-animals/src/bake.js +51 -0
  83. package/vendor/procedural-animals/src/build.js +81 -0
  84. package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
  85. package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
  86. package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
  87. package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
  88. package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
  89. package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
  90. package/vendor/procedural-animals/src/core/build/seams.js +183 -0
  91. package/vendor/procedural-animals/src/core/build/warp.js +112 -0
  92. package/vendor/procedural-animals/src/core/build/weights.js +393 -0
  93. package/vendor/procedural-animals/src/core/math/vec.js +81 -0
  94. package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
  95. package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
  96. package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
  97. package/vendor/procedural-animals/src/core/motion/common.js +158 -0
  98. package/vendor/procedural-animals/src/core/motion/index.js +22 -0
  99. package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
  100. package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
  101. package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
  102. package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
  103. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
  104. package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
  105. package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
  106. package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
  107. package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
  108. package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
  109. package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
  110. package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
  111. package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
  112. package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
  113. package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
  114. package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
  115. package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
  116. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
  117. package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
  118. package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
  119. package/vendor/procedural-animals/src/index.d.ts +98 -0
  120. package/vendor/procedural-animals/src/index.js +29 -0
  121. package/vendor/procedural-animals/src/registry.js +48 -0
  122. package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
  123. package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
  124. package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
  125. package/vendor/procedural-animals/src/species/bear/index.js +139 -0
  126. package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
  127. package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
  128. package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
  129. package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
  130. package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
  131. package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
  132. package/vendor/procedural-animals/src/species/boar/index.js +110 -0
  133. package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
  134. package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
  135. package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
  136. package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
  137. package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
  138. package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
  139. package/vendor/procedural-animals/src/species/cat/index.js +211 -0
  140. package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
  141. package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
  142. package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
  143. package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
  144. package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
  145. package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
  146. package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
  147. package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
  148. package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
  149. package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
  150. package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
  151. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
  152. package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
  153. package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
  154. package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
  155. package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
  156. package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
  157. package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
  158. package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
  159. package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
  160. package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
  161. package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
  162. package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
  163. package/vendor/procedural-animals/src/species/cow/index.js +118 -0
  164. package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
  165. package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
  166. package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
  167. package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
  168. package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
  169. package/vendor/procedural-animals/src/species/crow/index.js +70 -0
  170. package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
  171. package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
  172. package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
  173. package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
  174. package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
  175. package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
  176. package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
  177. package/vendor/procedural-animals/src/species/deer/index.js +106 -0
  178. package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
  179. package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
  180. package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
  181. package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
  182. package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
  183. package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
  184. package/vendor/procedural-animals/src/species/dog/index.js +144 -0
  185. package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
  186. package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
  187. package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
  188. package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
  189. package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
  190. package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
  191. package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
  192. package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
  193. package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
  194. package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
  195. package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
  196. package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
  197. package/vendor/procedural-animals/src/species/fish/index.js +88 -0
  198. package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
  199. package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
  200. package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
  201. package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
  202. package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
  203. package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
  204. package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
  205. package/vendor/procedural-animals/src/species/fox/index.js +120 -0
  206. package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
  207. package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
  208. package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
  209. package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
  210. package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
  211. package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
  212. package/vendor/procedural-animals/src/species/frog/index.js +97 -0
  213. package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
  214. package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
  215. package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
  216. package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
  217. package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
  218. package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
  219. package/vendor/procedural-animals/src/species/goat/index.js +149 -0
  220. package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
  221. package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
  222. package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
  223. package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
  224. package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
  225. package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
  226. package/vendor/procedural-animals/src/species/horse/index.js +80 -0
  227. package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
  228. package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
  229. package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
  230. package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
  231. package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
  232. package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
  233. package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
  234. package/vendor/procedural-animals/src/species/lion/index.js +111 -0
  235. package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
  236. package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
  237. package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
  238. package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
  239. package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
  240. package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
  241. package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
  242. package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
  243. package/vendor/procedural-animals/src/species/pig/index.js +161 -0
  244. package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
  245. package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
  246. package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
  247. package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
  248. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
  249. package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
  250. package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
  251. package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
  252. package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
  253. package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
  254. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
  255. package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
  256. package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
  257. package/vendor/procedural-animals/src/species/rat/index.js +111 -0
  258. package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
  259. package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
  260. package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
  261. package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
  262. package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
  263. package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
  264. package/vendor/procedural-animals/src/species/shark/index.js +104 -0
  265. package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
  266. package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
  267. package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
  268. package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
  269. package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
  270. package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
  271. package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
  272. package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
  273. package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
  274. package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
  275. package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
  276. package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
  277. package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
  278. package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
  279. package/vendor/procedural-animals/src/species/snake/index.js +73 -0
  280. package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
  281. package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
  282. package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
  283. package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
  284. package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
  285. package/vendor/procedural-animals/src/species/spider/index.js +78 -0
  286. package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
  287. package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
  288. package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
  289. package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
  290. package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
  291. package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
  292. package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
  293. package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
  294. package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
  295. package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
  296. package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
  297. package/vendor/procedural-animals/src/worker.js +29 -0
@@ -0,0 +1,339 @@
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
+ }
@@ -0,0 +1,29 @@
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
+ };