@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,81 +0,0 @@
1
- // Cow motion data for the quadruped engine (schema: src/core/motion/quadruped.js).
2
- // Numbers are for the reference individual (params.size = 1, a 1.45 m Holstein cow); the engine
3
- // scales them by dynamic similarity for calves, Jerseys and bulls.
4
- //
5
- // Gaits: lateral-sequence 4-beat walk (preferred ~1.2 m/s, stride
6
- // ~0.8 Hz, duty 0.66), a short 2-beat trot (to feed, 2-3.5 m/s), and a clumsy canter / transverse
7
- // gallop (right lead) up to ~11 m/s that cattle rarely use. Walk -> trot at Froude ~0.4 (~1.8 m/s).
8
- import { behaviour } from './behaviour.js';
9
-
10
- const G = (name, v, f, D, off, o) => ({ name, v, f, D, off, ...o });
11
-
12
- export const motion = {
13
- maxSpeed: 11,
14
- accel: [2.2, 4],
15
- decel: 5,
16
- turnRate: 1.3,
17
- latAccelMax: 5,
18
- gears: { walk: 1.2, trot: 2.6, canter: 4.6, gallop: 8 },
19
- gallopBlend: [4.2, 8],
20
- // name v f D [FL, FR, HL, HR]
21
- gaits: [
22
- G('walk', 0.0, 0.72, 0.7, [0.25, 0.75, 0.0, 0.5], { liftF: 0.07, liftH: 0.06, bobF: 0.025, bobH: 0.03, flex: 0, lat: 0.03, fold: 0.6, heel: 28, tail: 0, drop: 0 }),
23
- G('walk', 1.5, 0.9, 0.64, [0.25, 0.75, 0.0, 0.5], { liftF: 0.09, liftH: 0.08, bobF: 0.045, bobH: 0.05, flex: 0, lat: 0.04, fold: 0.72, heel: 36, tail: 0.1, drop: 0 }),
24
- G('trot', 2.0, 1.35, 0.5, [0.52, 0.02, 0.0, 0.5], { liftF: 0.12, liftH: 0.1, bobF: 0.06, bobH: 0.06, flex: 0.01, lat: 0.0, fold: 0.95, heel: 45, tail: 0.3, drop: 0.015 }),
25
- G('trot', 3.3, 1.55, 0.43, [0.52, 0.02, 0.0, 0.5], { liftF: 0.15, liftH: 0.12, bobF: 0.07, bobH: 0.07, flex: 0.015, lat: 0.0, fold: 1.05, heel: 50, tail: 0.4, drop: 0.02 }),
26
- G('canter', 4.0, 1.7, 0.42, [0.3, 0.55, 0.0, 0.27], { liftF: 0.17, liftH: 0.14, bobF: 0.04, bobH: 0.055, flex: 0.05, lat: 0, fold: 1.1, heel: 50, tail: 0.55, drop: 0.03 }),
27
- G('canter', 5.6, 1.8, 0.37, [0.3, 0.55, 0.0, 0.25], { liftF: 0.19, liftH: 0.16, bobF: 0.045, bobH: 0.06, flex: 0.065, lat: 0, fold: 1.15, heel: 45, tail: 0.65, drop: 0.035 }),
28
- G('gallop', 7.0, 1.95, 0.31, [0.38, 0.52, 0.0, 0.12], { liftF: 0.21, liftH: 0.18, bobF: 0.05, bobH: 0.065, flex: 0.09, lat: 0, fold: 1.25, heel: 40, tail: 0.8, drop: 0.05 }),
29
- G('gallop', 11, 2.15, 0.25, [0.4, 0.52, 0.0, 0.13], { liftF: 0.23, liftH: 0.2, bobF: 0.05, bobH: 0.07, flex: 0.11, lat: 0, fold: 1.3, heel: 40, tail: 1.0, drop: 0.07 }),
30
- ],
31
- feet: {
32
- // cloven hooves on short pasterns: the fetlock sinks a little under load, the claws flip in swing
33
- front: { type: 'unguligrade', contact: 0.9, curl: 65, flex: 4, sink: 14, hoofFlex: 20, swingBack: 0.16 },
34
- hind: { type: 'unguligrade', contact: 0.9, curl: 65, flex: 4, sink: 12, hoofFlex: 20, swingBack: 0.04 },
35
- },
36
- stance: { width: 1, gallopWidth: 0.85, sprawl: 0, crouch: 0, scuff: [14, 24, 0] },
37
- // torso cross-sections (bind pose, reference size): centre height, half height, half width
38
- body: {
39
- chest: { y: 1.03, hh: 0.42, hw: 0.42 },
40
- mid: { y: 1.0, hh: 0.4, hw: 0.37 },
41
- pelvis: { y: 1.14, hh: 0.5, hw: 0.4 },
42
- back: 0.12,
43
- },
44
- head: {
45
- // [speed, raise (m), reach (m), pitch (rad, + = nose down)]: low and forward at the walk
46
- // (nodding, see the hook), a little higher at the trot, up and stretched in the gallop
47
- carriage: [[0, 0, 0, 0], [1.2, -0.05, 0.06, 0.04], [2.6, -0.02, 0.05, 0.0], [4.6, 0.02, 0.08, -0.05], [9, -0.04, 0.14, 0.0]],
48
- stab: [[0, 0], [1.2, 0.1], [2.6, 0.4], [4.6, 0.3], [9, 0.3]],
49
- neckBend: 34, neckPivot: 0.55, yaw: 0.7, pitchUp: 0.55, pitchDown: 0.9, mouth: 'jawTip',
50
- },
51
- tail: {
52
- // [speed, pitch per segment from horizontal (deg), tail head -> switch]: hangs straight down at
53
- // rest, lifts and streams out behind when running
54
- carriage: [
55
- [0.0, [-38, -68, -80, -85, -87, -88, -89, -89, -90, -90, -90, -90]],
56
- [1.2, [-34, -64, -78, -84, -86, -87, -88, -89, -89, -90, -90, -90]],
57
- [2.6, [-32, -48, -62, -71, -76, -80, -83, -85, -86, -87, -88, -88]],
58
- [4.6, [-30, -30, -42, -52, -58, -62, -66, -69, -72, -74, -76, -78]],
59
- [9, [-24, -10, -16, -24, -30, -34, -37, -40, -42, -44, -46, -48]],
60
- ],
61
- stiffness: 0.75, sway: 6, flick: 0, wag: 0, swish: 1, curl: 0, balance: 0,
62
- radius: [0.05, 0.05], // (bony tail, then the switch)
63
- },
64
- ears: { rest: [0, 0], mobility: 1.1, speedFlatten: 0.2, twitch: 1.3, prick: 0.4 },
65
- breath: { rate: 0.3, amp: 0.009, pant: false, pantRate: 1.4, pantAmp: 0.02, heatSpeed: 9 },
66
- actions: {
67
- // grazing / drinking: the long head hangs near vertical, the tongue wraps the grass (hook)
68
- eat: { style: 'graze', dropF: 0.05, dropH: 0, pitch: 1.0, ahead: 0.62 },
69
- drink: { dropF: 0.06, dropH: 0, pitch: 0.95, ahead: 0.62 },
70
- // cattle jump reluctantly: a low, heavy hop, landing on the forelegs
71
- jump: { height: 0.55, distance: 2.2, land: [0.22, 0.18] },
72
- sit: { dropH: 0.8 },
73
- // kneels on both front knees first, then lowers the hindquarters; rises hind end first
74
- lie: { down: 'front', up: 'hind' },
75
- sleep: { style: 'curl', down: 'front', up: 'hind' },
76
- // lying sternal: forelegs folded under the chest (carpus flexed, cannons back along the ground)
77
- fold: { front: { z: -0.24, k: 1.5, beta: 0.9 } },
78
- idle: { shift: 0.8 },
79
- },
80
- hooks: behaviour,
81
- };
@@ -1,98 +0,0 @@
1
- // Meshing plan: body at 12.2 mm (0.84 % of the withers height), head at 5.2 mm overlapping the body
2
- // across the upper neck and cross-faded, the four feet below the fetlocks at 5.5 mm (so the cleft
3
- // between the two claws and the dew claws read) cross-faded with the body, eyelid patches at 1.5 mm
4
- // (3 mm at medium: Safari and phones show medium), and rigid surfaces: the lower lip / chin (the mouth
5
- // opens and chews), the tongue, the horns, the ear tags and the nose ring.
6
- // Budgets (vertices, hero / high / medium / low / crowd: 130k / 80k / 32k / 15k / 8k): the cells of an
7
- // individual with more surface than the reference cow (a bull's crest, dewlap and thick legs, a beef
8
- // breed's width) grow with it, the small rigid parts (ear tags, nose ring) are dropped at low and crowd,
9
- // and the horns coarsen with the tier.
10
- import { EYE, hornPath } from './sculpt.js';
11
- import { HEAD_O, cowJoints } from './rig.js';
12
- import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
13
- import { norm, smoothstep } from '../../core/math/vec.js';
14
-
15
- const J = cowJoints();
16
- // the head hangs from the neck: cut just behind the poll - throat line
17
- export const NECK_CUT = { c: [0, 1.35, J.occiput[2] - 0.05], d: norm([0, 0.3, 1]), band: 0.024 };
18
- export const neckS = (x, y, z) => (x - NECK_CUT.c[0]) * NECK_CUT.d[0] + (y - NECK_CUT.c[1]) * NECK_CUT.d[1] + (z - NECK_CUT.c[2]) * NECK_CUT.d[2];
19
- // feet: below the fetlocks
20
- export const FOOT_CUT = { y: 0.115, band: 0.012 };
21
-
22
- export const EYE_PATCH = { R: 0.033, B: 0.006, h: 0.0015, centres: [1, -1].map((s) => eyeFrameOf(EYE, HEAD_O, s).c) };
23
- export function eyePatchWeight(x, y, z) {
24
- let dmin = 1e9;
25
- for (const c of EYE_PATCH.centres) dmin = Math.min(dmin, Math.hypot(x - c[0], y - c[1], z - c[2]));
26
- return 1 - smoothstep(EYE_PATCH.R - EYE_PATCH.B, EYE_PATCH.R + EYE_PATCH.B, dmin);
27
- }
28
-
29
- function boxOf(pts, pad) {
30
- const mn = [1e9, 1e9, 1e9], mx = [-1e9, -1e9, -1e9];
31
- for (const p of pts) for (let i = 0; i < 3; i++) { mn[i] = Math.min(mn[i], p[i] - pad); mx[i] = Math.max(mx[i], p[i] + pad); }
32
- return [mn, mx];
33
- }
34
-
35
- export function cowRegions(rig, params, Q) {
36
- const B = NECK_CUT.band, r = Q.res, FB = FOOT_CUT.band, FY = FOOT_CUT.y;
37
- const crowd = r >= 4, far = r >= 2.5; // (low / crowd: no tongue, ear-tag or ring surfaces at crowd, none of the small ones at low)
38
- // eyelid patches at hero / high and at medium (coarser there)
39
- const patches = Q.eyePatches || r <= 2 ? EYE_PATCH.centres.map((c) => ({ c, R: EYE_PATCH.R, B: EYE_PATCH.B, h: EYE_PATCH.h * Math.max(1, r) })) : [];
40
- // an individual's extra surface over the reference cow: bulls (crest, dewlap, sheath, thick legs and
41
- // neck: +8-10 % vertices), beef breeds (a wider barrel: +3 %)
42
- const kS = 1 + 0.045 * (params.bull || 0) + 0.02 * (1 - (params.dairy ?? 1));
43
- const rigid = [];
44
- if (params.horns) {
45
- const pts = [];
46
- for (const s of [1, -1]) { const hp = hornPath(params, s); pts.push(...hp.pts); }
47
- const [bmin, bmax] = boxOf(pts, params.horns.r * 1.3 + 0.01);
48
- rigid.push({ name: 'horn', part: 'horn', bmin, bmax, h: Math.max(0.0035, params.horns.r * 0.15) * (crowd ? 2.6 : Math.min(r, 2.2)), F: 6, rigidBone: 'head' });
49
- }
50
- if (params.earTags && !far) {
51
- for (const S of ['L', 'R']) {
52
- const a = rig.J['earBase' + S], b = rig.J['earTip' + S];
53
- const [bmin, bmax] = boxOf([a, b], 0.08);
54
- rigid.push({ name: 'tag' + S, part: 'tag' + S, bmin, bmax, h: r > 1 ? 0.0032 : 0.003, F: 6, rigidBone: 'ear' + S });
55
- }
56
- }
57
- if (params.noseRing && !far) {
58
- const c = rig.J.nose;
59
- rigid.push({ name: 'ring', part: 'ring', bmin: [c[0] - 0.05, c[1] - 0.12, c[2] - 0.08], bmax: [c[0] + 0.05, c[1] + 0.03, c[2] + 0.08], h: 0.002 * Math.min(r, 1.6), F: 6, rigidBone: 'head' });
60
- }
61
- const wide = params.shaggy ? 0.1 : 0;
62
- // feet: the clip keeps cells by their centre, so the mesh can end up to 1.5 cells below it; the
63
- // feet surface must reach past the middle of the band (FY, fade 0.5) at every tier, or its open
64
- // top ring shows (crowd: 18 mm cells, 65-87 exposed edges at the fetlocks); the box ends two cells
65
- // above the clip so it never cuts the surface first
66
- const hF = crowd ? 0.02 : 0.0055 * Math.min(r, 3);
67
- const feetTop = Math.max(FY + FB, FY + 1.5 * hF + 0.004);
68
- // the neck band likewise: each surface is kept to past the middle of the band (fade 0.5 at the cut)
69
- // by 1.5 of its own cells (low / crowd: the body's 37-51 mm cells ended in its visible half along
70
- // the upper neck, 7 exposed edges)
71
- const hB = 0.0122 * r * kS, hH = 0.0052 * r * kS;
72
- const bodyTo = Math.max(B, 1.5 * hB + 0.004), headFrom = -Math.max(B, 1.5 * hH + 0.004);
73
- const bodyFrom = Math.min(FY - FB, FY - 1.5 * hB - 0.004); // (and at the fetlocks)
74
- return {
75
- jobs: [
76
- [{ name: 'body', part: 'body', bmin: [-0.5 - wide, bodyFrom - 2 * hB, -1.14], bmax: [0.5 + wide, 1.72, 1.4], h: hB, F: 4, clip: (x, y, z) => neckS(x, y, z) < bodyTo && y > bodyFrom }],
77
- [
78
- { name: 'head', part: 'body', bmin: [-0.36 - wide, 0.84, 0.86], bmax: [0.36 + wide, 1.66, 1.62], h: hH, F: 6, clip: (x, y, z) => neckS(x, y, z) > headFrom, patches },
79
- { name: 'jaw', part: 'jaw', bmin: [-0.1, 0.84, 1.1], bmax: [0.1, 1.36, 1.62], h: 0.0046 * r, F: 6, rigidBone: 'jaw' },
80
- ...(crowd ? [] : [{ name: 'tongue', part: 'tongue', bmin: [-0.06, 0.84, 1.1], bmax: [0.06, 1.36, 1.62], h: 0.0055 * Math.min(r, 2), F: 6, rigidBone: 'tongue' }]),
81
- ],
82
- [{ name: 'feet', part: 'body', bmin: [-0.3, -0.01, -0.75], bmax: [0.3, feetTop + 2 * hF, 0.86], h: hF, F: 6, clip: (x, y, z) => y < feetTop }, ...rigid],
83
- ],
84
- fade(x, y, z, name, patch) {
85
- if (name === 'body') {
86
- const sN = neckS(x, y, z);
87
- return (1 - smoothstep(-B, B, sN)) * smoothstep(FY - FB, FY + FB, y);
88
- }
89
- if (name === 'feet') return 1 - smoothstep(FY - FB, FY + FB, y);
90
- if (name === 'head') {
91
- let f = smoothstep(-B, B, neckS(x, y, z));
92
- if (patches.length) { const ew = eyePatchWeight(x, y, z); f *= patch ? ew : 1 - ew; }
93
- return f;
94
- }
95
- return 1;
96
- },
97
- };
98
- }
@@ -1,116 +0,0 @@
1
- // Cow skeleton in bind pose (metres). Forward = +Z, up = +Y, the animal's left = +X.
2
- // Reference individual: a Holstein-Friesian cow, 1.45 m at the withers, ~650 kg.
3
- // Landmarks measured on the side references (side1_holstein.jpg: 1 px ~ 2.2 mm; side_natural.jpg for
4
- // the relaxed head carriage) and bovine osteology: point of shoulder 1.00 m, elbow
5
- // 0.72, carpus 0.40, fore fetlock 0.13, stifle 0.80, hock 0.53 (point of hock 0.60), body length
6
- // (point of shoulder -> pin bone) 1.72 m, hooks (tuber coxae) 1.47 m and 0.58 m apart.
7
- //
8
- // Hooves: like the horse, a hoof bone per leg below the pastern (fpaw / hpaw = pastern: fetlock ->
9
- // coffin joint; fhoof / hhoof = the claw pair: coffin joint -> toe), so the engine keeps the claws
10
- // flat while the fetlock sinks under load. Cattle are cloven: the sculpt splits each hoof into two
11
- // claws (digits III and IV) with two dew claws behind the fetlock; they share one hoof bone.
12
- //
13
- // Extra bones: a tongue (on the jaw, meshed as its own rigid surface, hidden in the mouth until it
14
- // wraps grass or licks the nose).
15
- import { buildRig } from '../../core/rig/rig.js';
16
- import { quadrupedBones, quadrupedLimbs, tailChain } from '../../core/rig/quadruped.js';
17
-
18
- export const TAIL_SEGS = 12; // 10 caudal segments (the bony tail to the hocks) + 2 of switch hair
19
- export const BONE_SEGS = 10;
20
- export const TAIL_PITCH = [-38, -68, -80, -85, -87, -88, -89, -89, -90, -90, -90, -90];
21
- export const TAIL_LENS = [0.075, 0.072, 0.07, 0.068, 0.066, 0.064, 0.062, 0.06, 0.058, 0.056, 0.16, 0.16];
22
-
23
- // Head frame: the head is modelled along its own axis (poll -> muzzle), pitched 50 degrees nose down
24
- // in the bind pose (relaxed standing carriage, research 3: nasal line 50-70 deg below horizontal).
25
- // Head-local coordinates: x lateral, y dorsal (forehead side), z along the nasal line toward the
26
- // muzzle; origin on the axis 0.17 m in front of the poll (level with the eyes).
27
- export const HEAD_PITCH = (50 * Math.PI) / 180;
28
- export const HZ = [0, -Math.sin(HEAD_PITCH), Math.cos(HEAD_PITCH)];
29
- export const HY = [0, Math.cos(HEAD_PITCH), Math.sin(HEAD_PITCH)];
30
- const POLL = [0, 1.46, 1.13]; // top of the poll (skin), bind
31
- export const HEAD_O = [0, POLL[1] + 0.17 * HZ[1], POLL[2] + 0.17 * HZ[2]];
32
- export const hl = (v) => [HEAD_O[0] + v[0], HEAD_O[1] + v[1] * HY[1] + v[2] * HZ[1], HEAD_O[2] + v[1] * HY[2] + v[2] * HZ[2]];
33
-
34
- export function cowJoints() {
35
- const J = {
36
- nose: hl([0, -0.01, 0.37]),
37
- occiput: hl([0, -0.035, -0.14]),
38
- neckMid: [0, 1.3, 0.9],
39
- neckBase: [0, 1.2, 0.64],
40
- chestMid: [0, 1.3, 0.34],
41
- thoraxRear: [0, 1.33, 0.0],
42
- lumbarMid: [0, 1.355, -0.27],
43
- lumbosacral: [0, 1.37, -0.5],
44
- tailBase: [0, 1.37, -0.88],
45
- // fore: scapula slopes ~55 deg, humerus down and back, radius and cannon near vertical, short
46
- // pastern ~50 deg to the ground
47
- scapTopL: [0.12, 1.36, 0.42],
48
- shoulderL: [0.19, 1.0, 0.74],
49
- elbowL: [0.18, 0.72, 0.58],
50
- wristL: [0.155, 0.4, 0.63],
51
- mcpL: [0.15, 0.13, 0.65],
52
- fcoffinL: [0.15, 0.052, 0.705],
53
- ftoeL: [0.15, 0.004, 0.785],
54
- // hind: hip joint, stifle, hock, fetlock, coffin joint, toe (the stifle well forward under the
55
- // flank, the straight-ish dairy hock)
56
- hipL: [0.2, 1.2, -0.6],
57
- kneeL: [0.235, 0.8, -0.4],
58
- hockL: [0.17, 0.53, -0.71],
59
- mtpL: [0.17, 0.13, -0.665],
60
- hcoffinL: [0.17, 0.052, -0.61],
61
- htoeL: [0.17, 0.004, -0.53],
62
- jawHinge: hl([0, -0.065, -0.075]),
63
- jawTip: hl([0, -0.118, 0.335]),
64
- tongueBase: hl([0, -0.085, 0.12]),
65
- tongueTip: hl([0, -0.09, 0.3]),
66
- // ears stick out sideways below the poll, nearly horizontal, tipped a little down and back
67
- earBaseL: hl([0.085, 0.01, -0.125]),
68
- };
69
- J.earTipL = [J.earBaseL[0] + 0.2, J.earBaseL[1] - 0.025, J.earBaseL[2] - 0.045];
70
- // the tail head is set high between the pin bones; the tail hangs straight down to the hocks,
71
- // the switch below it
72
- tailChain(J, 'tailBase', TAIL_PITCH, TAIL_LENS);
73
- return J;
74
- }
75
-
76
- export function cowBones() {
77
- const b = quadrupedBones({ tailSegs: TAIL_SEGS, ears: true, jaw: true });
78
- for (const d of b) {
79
- if (d[0] === 'fpaw{S}') { d[2] = 'fcoffin{S}'; d[4] = { region: 'limb', group: 'F{S}' }; }
80
- if (d[0] === 'hpaw{S}') { d[2] = 'hcoffin{S}'; d[4] = { region: 'limb', group: 'H{S}' }; }
81
- }
82
- b.push(
83
- ['fhoof{S}', 'fcoffin{S}', 'ftoe{S}', 'fpaw{S}', { region: 'foot', group: 'F{S}' }],
84
- ['hhoof{S}', 'hcoffin{S}', 'htoe{S}', 'hpaw{S}', { region: 'foot', group: 'H{S}' }],
85
- ['tongue', 'tongueBase', 'tongueTip', 'jaw', { region: 'jaw', group: 'jaw' }],
86
- );
87
- return b;
88
- }
89
-
90
- export function cowRig(J = cowJoints()) {
91
- const limbs = quadrupedLimbs({
92
- front: { tCore: 0.55, aCore: 0.055, aBody: -0.1, midX: 0.0 },
93
- hind: { tCore: 0.5, aCore: 0.022, aBody: -0.08, midX: 0.02 },
94
- });
95
- for (const L of Object.values(limbs)) { L.bones.push((L.front ? 'fhoof' : 'hhoof') + L.S); L.distal.push((L.front ? 'fhoof' : 'hhoof') + L.S); }
96
- return buildRig({
97
- joints: J,
98
- unit: 1.45 / 0.76,
99
- headOrigin: HEAD_O,
100
- bones: cowBones(),
101
- axial: {
102
- points: ['nose', 'occiput', 'neckMid', 'neckBase', 'chestMid', 'thoraxRear', 'lumbarMid', 'lumbosacral', 'tailBase', ...Array.from({ length: TAIL_SEGS }, (_, i) => 'tail' + (i + 1))],
103
- bones: ['head', 'neck2', 'neck1', 'chest', 'spine3', 'spine2', 'spine1', 'pelvis', ...Array.from({ length: TAIL_SEGS }, (_, i) => 'tail' + i)],
104
- // the skull's influence reaches back into the throat (the deep jowl and throat skin), the
105
- // neck base blends broadly into the chest and the dewlap
106
- blend: { 1: [-0.06, 0.12, 5], 3: [-0.12, 0.12, 2] },
107
- bodyTail: 0, // the tail hangs between the pin bones: body skin blends into it only at the tail head
108
- },
109
- limbs,
110
- appendages: [
111
- { group: 'earL', bone: 'earL', blend: [-0.003, 0.006] },
112
- { group: 'earR', bone: 'earR', blend: [-0.003, 0.006] },
113
- ],
114
- webTags: ['flankfold', 'thighfront', 'scapmuscle', 'pectoral', 'forearmweb'],
115
- });
116
- }
@@ -1,339 +0,0 @@
1
- // The cow, sculpted as a signed distance field.
2
- // Every primitive is attached to a bone (skinning) and a group (axial / limb / ear / jaw). The head
3
- // is modelled in its own frame (rig.js: hl()), measured from the profile and frontal references; the
4
- // body relative to the joints so the sculpt follows the skeleton.
5
- //
6
- // Individual knobs (params, see index.js): dairy (0 beef .. 1 dairy: angular frame, prominent hooks
7
- // and pins, visible ribs, big udder), bull (0 cow .. 1 bull: crest over the neck and shoulders, deep
8
- // forehead, sheath and scrotum instead of an udder), udder (size), dewlap, horns ({ style, len, r }),
9
- // polled peak, ear tags, nose ring, shaggy (Highland coat volume).
10
- import { HEAD_O, HZ, HY, hl, TAIL_SEGS, BONE_SEGS } from './rig.js';
11
- import { add, sub, mul, lerp, norm, cross, rng } from '../../core/math/vec.js';
12
- import { sculptEyeSocket, apertureTiltAlong } from '../../core/sdf/eyeSocket.js';
13
-
14
- // Eye (head-local centre -> offset from HEAD_O). Globe ~34 mm axial, visible opening ~35 x 22 mm,
15
- // set at the edge of the broad flat forehead and bulging out of the head's outline in front view.
16
- const EYE_LOCAL = [0.111, 0.004, 0.0];
17
- const eyeDir = (() => {
18
- // lateral, 18 degrees forward along the nasal line, a little up
19
- const a = (18 * Math.PI) / 180;
20
- return norm(add(add([Math.cos(a), 0, 0], mul(HZ, Math.sin(a))), mul(HY, 0.12)));
21
- })();
22
- const EYE_BASE = {
23
- c: sub(hl(EYE_LOCAL), HEAD_O), r: 0.02, back: 0.004, yaw: Math.atan2(eyeDir[0], eyeDir[2]), pitch: Math.asin(eyeDir[1]),
24
- lid: 0.0024, R: 0.0215, d: 0.0094, off: -0.0008, tilt: 0, irisZ: 0.011, irisR: 0.0146,
25
- };
26
- // the almond's roll: in the relaxed carriage (nasal line 50 deg below horizontal) the slit is about
27
- // level, the inner corner a little lower (side1_holstein.jpg, head raised to ~30 deg: the slit rises
28
- // ~20 deg toward the nose); i.e. 0.85 rad up from the nasal line, the upper lid up
29
- EYE_BASE.tilt = apertureTiltAlong(EYE_BASE, HEAD_O, HZ) - 0.85;
30
- export const EYE = EYE_BASE;
31
-
32
- const X = [1, 0, 0];
33
- const UP = [0, 1, 0];
34
- const FWD = [0, 0, 1];
35
-
36
- // horn base on the poll (head-local) and the horn's centre line (world, bind), per side
37
- export function hornPath(params, s) {
38
- const H = params.horns;
39
- if (!H) return null;
40
- const base = hl([0.082 * s, 0.035, -0.158]);
41
- const out = [s, 0, 0];
42
- // the second direction the horn curls toward: lyre (cow: up and forward), 'down' (Hereford:
43
- // forward and down), 'bull' (short, forward), 'wide' (Highland: out, then up)
44
- const tw = {
45
- lyre: norm([0, 0.8, 0.55]), down: norm([0, -0.55, 0.85]), bull: norm([0, 0.35, 0.95]), wide: norm([0, 0.95, 0.3]),
46
- }[H.style] || norm([0, 0.8, 0.55]);
47
- const a0 = (H.a0 ?? 12) * Math.PI / 180, a1 = (H.a1 ?? 95) * Math.PI / 180;
48
- const n = 7, pts = [], rads = [];
49
- let p = add(base, mul(out, -0.03)); // rooted inside the skull
50
- pts.push(p); rads.push(H.r * 1.05);
51
- for (let i = 1; i <= n; i++) {
52
- const t = (i - 0.5) / n;
53
- const a = a0 + (a1 - a0) * Math.pow(t, H.curlPow ?? 1.3);
54
- const d = norm(add(mul(out, Math.cos(a)), mul(tw, Math.sin(a))));
55
- const seg = (H.len + 0.03) / n;
56
- p = add(p, mul(d, seg));
57
- pts.push(p);
58
- const u = i / n;
59
- rads.push(Math.max(0.004, H.r * (1 - 0.86 * Math.pow(u, 1.25))));
60
- }
61
- return { pts, rads, base };
62
- }
63
-
64
- export function sculptCow(m, rig, params = {}) {
65
- const J = rig.J;
66
- const calf = params.age === 'juvenile';
67
- const dairy = params.dairy ?? 1;
68
- const bull = params.bull ?? 0;
69
- const beef = 1 - dairy;
70
- const R = rng(5113 + (params.coatSeed || 0));
71
-
72
- const ellY = (o) => {
73
- const y = norm(o.ydir);
74
- const lat = o.lateral || X;
75
- const z = norm(cross(lat, y));
76
- return m.ell({ ...o, axis: z, up: y, r: [o.r[0], o.r[1], o.r[2]] });
77
- };
78
- // ellipsoid in head-local coordinates: r = [lateral, dorsal, along the head]
79
- const H = { bone: 'head' };
80
- const hdir = (v) => [v[0], v[1] * HY[1] + v[2] * HZ[1], v[1] * HY[2] + v[2] * HZ[2]];
81
- const hell = (o) => m.ell({ ...H, ...o, c: hl(o.c), axis: o.axisL ? norm(hdir(o.axisL)) : HZ, up: o.upL ? norm(hdir(o.upL)) : HY });
82
-
83
- // ---------------------------------------------------------------- TORSO
84
- // the barrel: deep (chest depth ~0.8 m, belly ~0.63 m above the ground), wide (0.62 m), deepest
85
- // behind the ribs; the dairy wedge: the body gets deeper and wider toward the rear
86
- const wB = 1 + 0.06 * beef + 0.04 * bull;
87
- m.ell({ tag: 'ribcage', bone: 'spine3', c: [0, 1.04, 0.12], r: [0.3 * wB, 0.4, 0.56], axis: norm([0, 0.04, 1]), k: 0 });
88
- m.ell({ tag: 'girth', bone: 'chest', c: [0, 1.02, 0.44], r: [0.25 * wB, 0.33, 0.24], k: 0.1 });
89
- m.ell({ tag: 'abdomen', bone: 'spine2', c: [0, 1.0, -0.22], r: [0.33 * wB, 0.37, 0.36], axis: norm([0, 0.1, -1]), k: 0.12 });
90
- m.ell({ tag: 'belly', bone: 'spine2', c: [0, 0.84, -0.08], r: [0.27 * wB, 0.2, 0.42], k: 0.16 });
91
- m.ell({ tag: 'flank', bone: 'spine1', c: [0, 1.17, -0.45], r: [0.28 * wB, 0.24, 0.24], k: 0.12 });
92
- // withers: the dorsal spines of T2-T6 between the scapulae, sharper in dairy cows
93
- m.ell({ tag: 'withers', bone: 'chest', c: [0, 1.36, 0.4], r: [0.085 + 0.03 * beef, 0.1, 0.24], axis: norm([0, -0.12, 1]), k: 0.1 });
94
- m.ell({ tag: 'back', bone: 'spine3', c: [0, 1.35, 0.04], r: [0.17 + 0.05 * beef, 0.08, 0.42], k: 0.12 });
95
- m.ell({ tag: 'loin', bone: 'spine1', c: [0, 1.38, -0.28], r: [0.21 + 0.04 * beef, 0.07, 0.24], k: 0.12 });
96
- // hindquarters: the rump runs level from the hooks (tuber coxae) to the pins (tuber ischii); the
97
- // tail head sits between the pins
98
- m.ell({ tag: 'pelvis', bone: 'pelvis', c: [0, 1.25, -0.68], r: [0.25 * wB, 0.2, 0.28], k: 0.1 });
99
- m.ell({ tag: 'rump', bone: 'pelvis', c: [0, 1.38, -0.67], r: [0.2 + 0.05 * beef, 0.08, 0.28], axis: norm([0, -0.06, 1]), k: 0.1 });
100
- for (const s of [1, -1]) {
101
- m.sphere({ tag: 'hook', bone: 'pelvis', c: [0.27 * s, 1.37 + 0.012 * dairy, -0.46], rad: 0.055, k: 0.07 + 0.04 * beef });
102
- m.sphere({ tag: 'pin', bone: 'pelvis', c: [0.095 * s, 1.34, -0.915], rad: 0.05, k: 0.07 + 0.03 * beef });
103
- // quarters: the muscle between hook, pin and stifle (full and round in beef breeds)
104
- m.ell({ tag: 'quarter', bone: 'pelvis', c: [0.14 * s, 1.2, -0.78], r: [0.13 + 0.03 * beef, 0.2, 0.14 + 0.02 * beef], k: 0.1 });
105
- }
106
- m.ell({ tag: 'tailhead', bone: 'pelvis', c: [0, 1.39, -0.86], r: [0.06, 0.055, 0.08], k: 0.06 });
107
- // chest front: the brisket (sternum and its fat pad) between and in front of the forelegs
108
- m.ell({ tag: 'brisket', bone: 'chest', c: [0, 0.83, 0.6], r: [0.17 * wB, 0.17, 0.17], k: 0.1 });
109
- for (const s of [1, -1]) {
110
- m.ell({ tag: 'pectoral', bone: 'chest', c: [0.1 * s, 0.86, 0.7], r: [0.1, 0.15, 0.1], k: 0.08 });
111
- }
112
- if (!calf && bull < 0.5 && (params.udder ?? 1) > 0.05) sculptUdder(m, params.udder ?? 1);
113
- if (bull >= 0.5) {
114
- // sheath (prepuce) under the belly, scrotum between the hind legs
115
- m.ell({ tag: 'sheath', bone: 'spine2', c: [0, 0.66, -0.05], r: [0.045, 0.06, 0.14], axis: norm([0, 0.35, 1]), k: 0.06 });
116
- m.ell({ tag: 'sheath', bone: 'spine2', c: [0, 0.6, 0.08], r: [0.03, 0.04, 0.05], k: 0.04 });
117
- if (!calf) m.ell({ tag: 'scrotum', bone: 'pelvis', c: [0, 0.66, -0.66], r: [0.075, 0.13, 0.08], axis: norm([0, 0.1, 1]), k: 0.05 });
118
- }
119
-
120
- // ---------------------------------------------------------------- NECK
121
- // thin and long in dairy cows, short and thick in beef breeds, massive with a crest in bulls
122
- const nb = J.neckBase, nm = J.neckMid, occ = J.occiput;
123
- const nd1 = norm(sub(nm, nb)), nd2 = norm(sub(occ, nm));
124
- const nk = 1 + 0.1 * beef + 0.45 * bull;
125
- m.ell({ tag: 'neck', bone: 'neck1', c: [0, 1.16 + 0.03 * bull, 0.64], r: [0.2 * nk, 0.27 * (1 + 0.15 * bull), 0.24], axis: nd1, up: [0, 1, -0.3], k: 0.12 });
126
- m.ell({ tag: 'neck', bone: 'neck1', c: [0, 1.25, 0.84], r: [0.14 * nk, 0.21 * (1 + 0.1 * bull), 0.2], axis: nd1, up: [0, 1, -0.3], k: 0.1 });
127
- m.ell({ tag: 'neck', bone: 'neck2', c: [0, 1.31, 1.0], r: [0.11 * (1 + 0.15 * bull), 0.16, 0.14], axis: nd2, up: [0, 1, -0.2], k: 0.09 });
128
- // top of the neck from the withers to the poll; the bull's crest rises over the shoulders
129
- m.ell({ tag: 'crest', bone: 'neck1', c: [0, 1.37 + 0.07 * bull, 0.72], r: [0.08 + 0.12 * bull, 0.07 + 0.11 * bull, 0.3 + 0.04 * bull], axis: norm([0, 0.02, 1]), k: 0.1 + 0.04 * bull });
130
- m.ell({ tag: 'crest', bone: 'neck2', c: [0, 1.41 + 0.02 * bull, 0.98], r: [0.06 + 0.04 * bull, 0.05 + 0.03 * bull, 0.14], axis: FWD, k: 0.08 });
131
- if (bull > 0) m.ell({ tag: 'crest', bone: 'chest', c: [0, 1.37 + 0.03 * bull, 0.5], r: [0.2 * bull + 0.02, 0.13 * bull + 0.01, 0.28], k: 0.14 });
132
- // underline: trachea, then the dewlap, a hanging fold of skin from the throat to the brisket
133
- m.cone({ tag: 'throat', bone: 'neck1', a: [0, 1.02, 0.76], b: [0, 1.16, 0.98], ra: 0.09, rb: 0.075, k: 0.1 });
134
- m.cone({ tag: 'throat', bone: 'neck2', a: [0, 1.15, 0.98], b: hl([0, -0.16, -0.06]), ra: 0.07, rb: 0.06, k: 0.08 });
135
- const dw = params.dewlap ?? 0.5;
136
- if (dw > 0.02) {
137
- const a = [0, 0.99, 0.95], b = [0, 0.76, 0.66], mid = lerp(a, b, 0.55);
138
- const dd = norm(sub(b, a));
139
- m.ell({ tag: 'dewlap', bone: 'neck1', c: add(mid, [0, -0.04 * dw, 0.02 * dw]), r: [0.035 + 0.015 * dw, 0.07 + 0.08 * dw, 0.2], axis: dd, up: norm([0, 1, 0.9]), k: 0.07 });
140
- }
141
-
142
- // ---------------------------------------------------------------- HEAD (head-local, see rig.js)
143
- // landmarks (head_profile of face.jpg / side photos): poll ridge top y 0.06 at z -0.16, flat
144
- // forehead y 0.075 at z 0, nasal bridge 0.055 at 0.18, muzzle top 0.03 at 0.33, nose tip z 0.39,
145
- // chin y -0.14 at 0.3, jaw angle y -0.2 at z -0.03, eyes (+-0.098, 0.004, 0)
146
- const hb = 1 + 0.08 * bull; // broader, deeper bull head
147
- hell({ tag: 'poll', c: [0, 0.015, -0.14], r: [0.095 * hb, 0.055, 0.055], k: 0.05 });
148
- hell({ tag: 'cranium', c: [0, 0.0, -0.06], r: [0.1 * hb, 0.075, 0.11], k: 0.05 });
149
- hell({ tag: 'forehead', c: [0, 0.035, 0.0], r: [0.104 * hb, 0.04, 0.12], k: 0.04 });
150
- hell({ tag: 'face', c: [0, 0.01, 0.2], r: [0.08 * hb, 0.045, 0.19], k: 0.05 });
151
- hell({ tag: 'lowerface', c: [0, -0.07, 0.18], r: [0.078 * hb, 0.08, 0.17], k: 0.05 });
152
- hell({ tag: 'muzzle', c: [0, -0.04, 0.33], r: [0.086 * hb, 0.066, 0.064], k: 0.05 });
153
- hell({ tag: 'upperlip', c: [0, -0.1, 0.345], r: [0.068 * hb, 0.032, 0.046], k: 0.03 });
154
- if (params.polledPeak) hell({ tag: 'polltop', c: [0, 0.05, -0.15], r: [0.04, 0.035 + 0.01 * params.polledPeak, 0.04], k: 0.04 });
155
- if (bull > 0) hell({ tag: 'forehead', c: [0, 0.045, -0.06], r: [0.09, 0.03 * bull + 0.01, 0.1], k: 0.05 }); // curly bull forehead mat
156
- if (params.dished) hell({ tag: 'face', c: [0, 0.035, 0.1], r: [0.05, 0.02, 0.05], k: 0.03, carve: true });
157
- for (const s of [1, -1]) {
158
- // masseter: the big flat cheek; the mandible's lower border from the jaw angle to the chin
159
- hell({ tag: 'cheek', c: [0.06 * s * hb, -0.105, -0.0], r: [0.048, 0.1, 0.11], k: 0.05 });
160
- m.cone({ ...H, tag: 'mandible', a: hl([0.052 * s, -0.19, -0.04]), b: hl([0.036 * s, -0.135, 0.27]), ra: 0.036, rb: 0.024, k: 0.05 });
161
- // bony eye arch (the orbit rim stands out of the forehead's outline) and the temporal fossa
162
- hell({ tag: 'brow', c: [0.1 * s, 0.03, -0.006], r: [0.03, 0.022, 0.04], k: 0.03 });
163
- hell({ tag: 'nostrilwing', c: [0.055 * s, -0.03, 0.35], r: [0.03, 0.035, 0.038], k: 0.03 });
164
- // eye: orbit hollow, lids, almond aperture
165
- sculptEyeSocket(m, EYE, HEAD_O, s, { orbit: { r: [0.022, 0.016, 0.012], at: [0.0, 0.002, 0.022], k: 0.01 } });
166
- // nostrils: comma-shaped openings on the front corners of the moist muzzle plate
167
- hell({ tag: 'nostril', c: [0.043 * s, -0.035, 0.392], r: [0.011, 0.022, 0.02], axisL: [-0.35 * s, 0.1, 1], upL: [0.35 * s, 1, 0], k: 0.006, carve: true });
168
- // ear base muscles on the side of the poll
169
- m.sphere({ ...H, tag: 'earbase', c: add(J['earBase' + (s > 0 ? 'L' : 'R')], [-0.012 * s, 0, 0]), rad: 0.032, k: 0.03 });
170
- }
171
- // philtrum groove down the middle of the muzzle plate
172
- hell({ tag: 'philtrum', c: [0, -0.07, 0.405], r: [0.004, 0.03, 0.012], k: 0.006, carve: true });
173
- // mouth line: a shallow slit under the upper lip (the jaw part fills it)
174
- hell({ tag: 'mouthcut', c: [0, -0.13, 0.31], r: [0.06, 0.012, 0.07], k: 0.008, carve: true });
175
-
176
- // ---------------------------------------------------------------- JAW (lower lip and chin: the mouth opens, chews sideways)
177
- const JW = { bone: 'jaw', group: 'jaw', part: 'jaw' };
178
- m.ell({ ...JW, tag: 'chin', c: hl([0, -0.138, 0.3]), r: [0.05, 0.038, 0.06], axis: HZ, up: HY, k: 0 });
179
- m.ell({ ...JW, tag: 'lowerlip', c: hl([0, -0.125, 0.345]), r: [0.056, 0.026, 0.035], axis: HZ, up: HY, k: 0.02 });
180
- for (const s of [1, -1]) {
181
- m.cone({ ...JW, tag: 'mandible', a: hl([0.036 * s, -0.14, 0.2]), b: hl([0.03 * s, -0.135, 0.3]), ra: 0.022, rb: 0.028, k: 0.03 });
182
- }
183
- // tongue: its own rigid surface in the mouth (see regions.js); it wraps grass and licks the nose
184
- const TG = { bone: 'tongue', group: 'jaw', part: 'tongue' };
185
- m.cone({ ...TG, tag: 'tongue', a: J.tongueBase, b: J.tongueTip, ra: 0.03, rb: 0.024, k: 0.02 });
186
- m.ell({ ...TG, tag: 'tongue', c: lerp(J.tongueBase, J.tongueTip, 0.7), r: [0.036, 0.016, 0.07], axis: norm(sub(J.tongueTip, J.tongueBase)), up: HY, k: 0.02 });
187
-
188
- // ---------------------------------------------------------------- EARS (broad leaves, carried sideways)
189
- for (const S of ['L', 'R']) {
190
- const s = S === 'L' ? 1 : -1;
191
- const base = J['earBase' + S], tip = J['earTip' + S];
192
- const along = norm(sub(tip, base));
193
- // the pinna opens forward and a little down
194
- const facing = norm([0, -0.25, 1]);
195
- const lat = norm(cross(along, facing));
196
- const ear = { bone: 'ear' + S, group: 'ear' + S, thin: true };
197
- const eK = calf ? 1.1 : 1;
198
- ellY({ ...ear, tag: 'ear', c: lerp(base, tip, 0.5), ydir: along, lateral: lat, r: [0.056 * eK, 0.108 * eK, 0.018], k: 0.02 });
199
- ellY({ ...ear, tag: 'ear', c: lerp(base, tip, 0.15), ydir: along, lateral: lat, r: [0.03, 0.05, 0.026], k: 0.02 });
200
- ellY({ ...ear, tag: 'earinner', c: add(lerp(base, tip, 0.55), mul(facing, 0.016)), ydir: along, lateral: lat, r: [0.042 * eK, 0.09 * eK, 0.013], k: 0.006, carve: true });
201
- }
202
-
203
- // ---------------------------------------------------------------- LEGS
204
- const legK = 1 + 0.08 * beef + 0.1 * bull; // bone and muscle thickness
205
- for (const S of ['L', 'R']) {
206
- const s = S === 'L' ? 1 : -1;
207
- const lat = [s, 0, 0];
208
- const sx = (v) => [v[0] * s, v[1], v[2]];
209
- // front
210
- const g = 'F' + S;
211
- const Sc = J['scapTop' + S], Sh = J['shoulder' + S], E = J['elbow' + S], W = J['wrist' + S], M = J['mcp' + S], C = J['fcoffin' + S], T = J['ftoe' + S];
212
- ellY({ tag: 'scapmuscle', bone: 'scapula' + S, group: g, c: add(lerp(Sc, Sh, 0.45), sx([0.06, 0, -0.02])), ydir: sub(Sh, Sc), lateral: lat, r: [0.08 + 0.03 * bull, 0.25, 0.15], k: 0.12, bias: 0.006 });
213
- m.sphere({ tag: 'shoulderpoint', bone: 'humerus' + S, group: g, c: add(Sh, sx([0.015, 0.0, 0.03])), rad: 0.075 * legK, k: 0.09 });
214
- m.cone({ tag: 'upperarm', bone: 'humerus' + S, group: g, a: Sh, b: E, ra: 0.1 * legK, rb: 0.085 * legK, k: 0.09 });
215
- ellY({ tag: 'triceps', bone: 'humerus' + S, group: g, c: add(lerp(Sh, E, 0.55), sx([0.01, 0.02, -0.09])), ydir: sub(E, Sh), lateral: lat, r: [0.1 * legK, 0.17, 0.12], k: 0.09 });
216
- m.sphere({ tag: 'olecranon', bone: 'radius' + S, group: g, c: add(E, [0, 0.02, -0.07]), rad: 0.055, k: 0.05 });
217
- m.cone({ tag: 'forearm', bone: 'radius' + S, group: g, a: add(E, [0, 0, -0.01]), b: W, ra: 0.08 * legK, rb: 0.048 * legK, k: 0.05 });
218
- ellY({ tag: 'forearmmuscle', bone: 'radius' + S, group: g, c: add(lerp(E, W, 0.25), sx([0.008, 0, 0.012])), ydir: sub(W, E), lateral: lat, r: [0.074 * legK, 0.14, 0.08 * legK], k: 0.05 });
219
- m.cone({ tag: 'forearmweb', bone: 'radius' + S, group: g, a: add(E, sx([-0.04, 0.06, -0.02])), b: add(lerp(E, W, 0.3), sx([-0.02, 0, 0])), ra: 0.06, rb: 0.045, k: 0.06 });
220
- // knee (carpus): broad, the accessory carpal bone behind
221
- ellY({ tag: 'knee', bone: 'metacarpus' + S, group: g, c: add(W, [0, 0.0, 0.004]), ydir: UP, lateral: lat, r: [0.053 * legK, 0.058, 0.048 * legK], k: 0.025 });
222
- m.sphere({ tag: 'accessory', bone: 'metacarpus' + S, group: g, c: add(W, [0, 0.03, -0.042]), rad: 0.024, k: 0.02 });
223
- // cannon (fused metacarpals III+IV): short, broad, flat in front
224
- m.cone({ tag: 'cannon', bone: 'metacarpus' + S, group: g, a: add(W, [0, -0.02, 0.0]), b: add(M, [0, 0.02, 0.0]), ra: 0.036 * legK, rb: 0.034 * legK, k: 0.02 });
225
- m.cone({ tag: 'tendon', bone: 'metacarpus' + S, group: g, a: add(W, [0, -0.03, -0.028]), b: add(M, [0, 0.03, -0.03]), ra: 0.022 * legK, rb: 0.026 * legK, k: 0.02 });
226
- ellY({ tag: 'fetlock', bone: 'metacarpus' + S, group: g, c: add(M, [0, 0.0, -0.01]), ydir: [0, 1, 0.3], lateral: lat, r: [0.048 * legK, 0.048, 0.05], k: 0.02 });
227
- sculptDigits(m, 'fpaw' + S, 'fhoof' + S, g, M, C, T, s, 1.0 * legK);
228
-
229
- // hind
230
- const h = 'H' + S;
231
- const Hp = J['hip' + S], K = J['knee' + S], Hk = J['hock' + S], Mt = J['mtp' + S], Ch = J['hcoffin' + S], Tt = J['htoe' + S];
232
- // thigh: flat and lean in dairy cows (the "incurving thigh"), full and round in beef breeds
233
- ellY({ tag: 'thigh', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.42), sx([0.02 + 0.02 * beef, 0, -0.05])), ydir: sub(K, Hp), lateral: lat, r: [0.08 + 0.04 * beef, 0.3, 0.2 + 0.03 * beef], k: 0.1, bias: 0.006 });
234
- m.cone({ tag: 'thighfront', bone: 'femur' + S, group: h, a: sx([0.22, 1.25, -0.46]), b: add(K, sx([0.0, 0.06, 0.04])), ra: 0.11, rb: 0.07, k: 0.1 });
235
- m.cone({ tag: 'hamstring', bone: 'femur' + S, group: h, a: sx([0.11, 1.26, -0.84]), b: add(lerp(K, Hk, 0.3), [0, 0, -0.08]), ra: 0.1 + 0.02 * beef, rb: 0.06, k: 0.08 });
236
- ellY({ tag: 'flankfold', bone: 'femur' + S, group: h, c: sx([0.22, 0.92, -0.3]), ydir: [-0.1, 0.3, 0.12], lateral: lat, r: [0.05, 0.15, 0.08], k: 0.1 });
237
- m.sphere({ tag: 'stifle', bone: 'tibia' + S, group: h, c: add(K, sx([0.01, 0.01, 0.03])), rad: 0.06, k: 0.07 });
238
- ellY({ tag: 'gaskin', bone: 'tibia' + S, group: h, c: add(lerp(K, Hk, 0.32), sx([0.006, 0, -0.045])), ydir: sub(Hk, K), lateral: lat, r: [0.072 * legK, 0.16, 0.09 * legK], k: 0.06 });
239
- m.cone({ tag: 'shin', bone: 'tibia' + S, group: h, a: lerp(K, Hk, 0.1), b: Hk, ra: 0.06 * legK, rb: 0.046 * legK, k: 0.05 });
240
- m.cone({ tag: 'achilles', bone: 'tibia' + S, group: h, a: add(lerp(K, Hk, 0.45), [0, 0, -0.075]), b: add(Hk, [0, 0.07, -0.07]), ra: 0.03, rb: 0.024, k: 0.03 });
241
- m.sphere({ tag: 'calcaneus', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.07, -0.066]), rad: 0.034, k: 0.025 });
242
- ellY({ tag: 'hock', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.01, -0.005]), ydir: [0, 1, 0.25], lateral: lat, r: [0.052 * legK, 0.075, 0.055], k: 0.03 });
243
- m.cone({ tag: 'cannon', bone: 'metatarsus' + S, group: h, a: add(Hk, [0, -0.04, 0.005]), b: add(Mt, [0, 0.02, 0.0]), ra: 0.036 * legK, rb: 0.033 * legK, k: 0.02 });
244
- m.cone({ tag: 'tendon', bone: 'metatarsus' + S, group: h, a: add(Hk, [0, -0.04, -0.034]), b: add(Mt, [0, 0.03, -0.032]), ra: 0.022 * legK, rb: 0.026 * legK, k: 0.02 });
245
- ellY({ tag: 'fetlock', bone: 'metatarsus' + S, group: h, c: add(Mt, [0, 0.0, -0.01]), ydir: [0, 1, 0.3], lateral: lat, r: [0.047 * legK, 0.048, 0.05], k: 0.02 });
246
- sculptDigits(m, 'hpaw' + S, 'hhoof' + S, h, Mt, Ch, Tt, s, 0.97 * legK);
247
- }
248
-
249
- // ---------------------------------------------------------------- TAIL: tail head, bony tail, switch
250
- const tailK = calf ? 0.9 : 1;
251
- for (let i = 0; i < BONE_SEGS; i++) {
252
- const t0 = i / BONE_SEGS, t1 = (i + 1) / BONE_SEGS;
253
- const r = (t) => (0.05 - 0.032 * Math.pow(t, 0.7)) * tailK;
254
- m.cone({ tag: 'tail', bone: 'tail' + i, a: J['tail' + i], b: J['tail' + (i + 1)], ra: r(t0), rb: r(t1), k: i === 0 ? 0.06 : 0.01 });
255
- }
256
- // switch: a long tassel of hair from the end of the bony tail
257
- const sw = (params.switchLen ?? 1) * (calf ? 0.4 : 1);
258
- const b10 = J['tail' + BONE_SEGS], b11 = J['tail' + (BONE_SEGS + 1)], b12 = J['tail' + TAIL_SEGS];
259
- const sw1 = lerp(b10, b11, Math.min(1, sw)), sw2 = lerp(b11, b12, Math.max(0, Math.min(1, sw * 1.0)));
260
- m.cone({ tag: 'switch', bone: 'tail' + (BONE_SEGS - 1), a: lerp(J['tail' + (BONE_SEGS - 1)], b10, 0.3), b: b10, ra: 0.022, rb: 0.04, k: 0.015 });
261
- m.cone({ tag: 'switch', bone: 'tail' + BONE_SEGS, a: b10, b: sw1, ra: 0.042, rb: 0.048 * Math.max(0.6, sw), k: 0.015 });
262
- if (sw > 0.5) m.cone({ tag: 'switch', bone: 'tail' + (BONE_SEGS + 1), a: sw1, b: sw2, ra: 0.048, rb: 0.012, k: 0.015 });
263
-
264
- // ---------------------------------------------------------------- HORNS (own rigid surface on the head)
265
- if (params.horns) {
266
- for (const s of [1, -1]) {
267
- const hp = hornPath(params, s);
268
- for (let i = 0; i < hp.pts.length - 1; i++) {
269
- m.cone({ bone: 'head', group: 'axial', part: 'horn', tag: 'horn', a: hp.pts[i], b: hp.pts[i + 1], ra: hp.rads[i], rb: hp.rads[i + 1], k: 0.01 });
270
- }
271
- // horn base boss on the poll (skin)
272
- m.sphere({ ...H, tag: 'hornbase', c: add(hp.base, [0.006 * s, 0, 0]), rad: params.horns.r * 1.05, k: 0.03 });
273
- }
274
- }
275
-
276
- // ---------------------------------------------------------------- EAR TAGS, NOSE RING (own rigid surfaces)
277
- if (params.earTags) {
278
- for (const S of ['L', 'R']) {
279
- const s = S === 'L' ? 1 : -1;
280
- const base = J['earBase' + S], tip = J['earTip' + S];
281
- const q = add(lerp(base, tip, 0.42), [0, -0.01, 0.02]);
282
- m.cone({ bone: 'ear' + S, group: 'ear' + S, part: 'tag' + S, tag: 'eartag', a: add(q, [0, 0.012, 0]), b: add(q, [0, 0.012, 0.012]), ra: 0.007, rb: 0.007, k: 0.002 });
283
- m.ell({ bone: 'ear' + S, group: 'ear' + S, part: 'tag' + S, tag: 'eartag', c: add(q, [0, -0.028, 0.017]), r: [0.028, 0.034, 0.0028], axis: norm([0, 0.1, 1]), k: 0.004 });
284
- }
285
- }
286
- if (params.noseRing) {
287
- const c = hl([0, -0.078, 0.402]), rr = 0.03, n = 14;
288
- const pt = (i) => { const a = (i / n) * Math.PI * 2; return add(c, add(mul(HY, Math.cos(a) * rr), mul(HZ, Math.sin(a) * rr * 0.85))); };
289
- for (let i = 0; i < n; i++) m.cone({ bone: 'head', part: 'ring', tag: 'ring', a: pt(i), b: pt(i + 1), ra: 0.0042, rb: 0.0042, k: 0.001 });
290
- }
291
- void R;
292
- return m;
293
- }
294
-
295
- // Udder: four quarters under the pelvis between the thighs, rear attachment high up between the
296
- // thighs, fore udder blending forward into the belly, four teats.
297
- function sculptUdder(m, k) {
298
- const U = { bone: 'spine1', tag: 'udder' };
299
- const sz = 0.75 + 0.35 * k;
300
- m.ell({ ...U, c: [0, 0.73 + 0.04 * (1 - k), -0.55], r: [0.15 * sz, 0.15 * sz, 0.22 * sz], k: 0.06 });
301
- m.ell({ ...U, bone: 'pelvis', c: [0, 0.92, -0.74], r: [0.12 * sz, 0.2, 0.1], k: 0.08 });
302
- m.ell({ ...U, c: [0, 0.76, -0.36], r: [0.13 * sz, 0.1 * sz, 0.18 * sz], k: 0.1 });
303
- const bottom = 0.73 + 0.04 * (1 - k) - 0.15 * sz;
304
- for (const s of [1, -1]) {
305
- // quarters bulge a little on each side (the median suspensory ligament makes a groove)
306
- m.ell({ ...U, c: [0.075 * s, bottom + 0.1, -0.62], r: [0.085 * sz, 0.1 * sz, 0.12 * sz], k: 0.05 });
307
- m.ell({ ...U, c: [0.07 * s, bottom + 0.1, -0.45], r: [0.075 * sz, 0.09 * sz, 0.1 * sz], k: 0.05 });
308
- for (const z of [-0.64, -0.44]) {
309
- m.cone({ tag: 'teat', bone: 'spine1', c: undefined, a: [0.06 * s, bottom + 0.03, z], b: [0.062 * s, bottom - 0.05, z + 0.005], ra: 0.017, rb: 0.013, k: 0.02 });
310
- }
311
- }
312
- }
313
-
314
- // Cloven hoof: pastern with two dew claws behind the fetlock, then two claws (digits III and IV),
315
- // each a hoof-wall half-cone with its front wall parallel to the pastern, heel bulbs behind, flat on
316
- // the ground (carved at y = 0); the interdigital cleft is carved between them.
317
- function sculptDigits(m, paw, hoof, group, M, C, T, s, w) {
318
- m.cone({ tag: 'pastern', bone: paw, group, a: M, b: C, ra: 0.038 * w, rb: 0.04 * w, k: 0.02 });
319
- // dew claws: two small horny nubs behind and above the fetlock... (cattle: just above the heels)
320
- for (const d of [1, -1]) {
321
- m.ell({ tag: 'dewclaw', bone: paw, group, c: add(M, [0.022 * d, -0.035, -0.05]), r: [0.013, 0.017, 0.016], axis: norm([0, -0.5, -1]), k: 0.012 });
322
- }
323
- const zMid = (C[2] + T[2]) * 0.5;
324
- const dir = Math.sign(T[2] - C[2]);
325
- for (const d of [1, -1]) {
326
- const off = 0.026 * d * w;
327
- const top = add(C, [off, 0.018, -0.012 * dir]);
328
- const base = [C[0] + off * 1.1, 0.0, zMid - 0.004 * dir];
329
- m.cone({ tag: 'hoof', bone: hoof, group, a: top, b: base, ra: 0.026 * w, rb: 0.036 * w, k: 0.012 });
330
- // the toe: a pointed front of each claw
331
- m.ell({ tag: 'hoof', bone: hoof, group, c: [C[0] + off * 0.9, 0.022, T[2] - 0.028 * dir], r: [0.024 * w, 0.022, 0.034], axis: norm([0, -0.25, dir]), k: 0.012 });
332
- m.sphere({ tag: 'heelbulb', bone: hoof, group, c: add(C, [off * 0.9, -0.026, -0.042 * dir]), rad: 0.026 * w, k: 0.02 });
333
- }
334
- m.cone({ tag: 'coronet', bone: hoof, group, a: add(C, [0, 0.024, -0.02 * dir]), b: add(C, [0, 0.012, 0.02 * dir]), ra: 0.043 * w, rb: 0.043 * w, k: 0.015 });
335
- // interdigital cleft: a V opening toward the front and the ground
336
- m.ell({ tag: 'cleft', bone: hoof, group, c: [C[0], 0.0, T[2] - 0.004 * dir], r: [0.005, 0.045, 0.075], k: 0.004, carve: true });
337
- m.ell({ tag: 'cleft', bone: hoof, group, c: [C[0], -0.005, zMid], r: [0.004, 0.03, 0.08], k: 0.003, carve: true });
338
- m.ell({ tag: 'sole', bone: hoof, group, c: [C[0], -0.2 + 0.002, zMid], r: [0.2, 0.2, 0.2], k: 0.004, carve: true });
339
- }