@bitefoundry/retrieviewer 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/ASSET-PROVENANCE.md +6 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +65 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/PLUGIN.md +13 -10
  7. package/PREREQUISITES.md +95 -0
  8. package/PUPPY-MEMORY.md +102 -1
  9. package/README.ko.md +110 -0
  10. package/README.md +117 -59
  11. package/RELEASING.md +23 -12
  12. package/THIRD-PARTY-NOTICES.md +5 -3
  13. package/braille.mjs +35 -9
  14. package/examples/live.mjs +2 -2
  15. package/integrations/linux-overlay/overlay.py +2 -2
  16. package/integrations/macos-overlay/overlay.jxa +8 -6
  17. package/integrations/opencode/runtime.mjs +4 -4
  18. package/integrations/opencode/tui.tsx +1 -1
  19. package/integrations/windows-overlay/Overlay.cs +4 -4
  20. package/integrations/windows-overlay/protocol.mjs +11 -7
  21. package/package.json +34 -13
  22. package/puppy-comfort-sprites.mjs +25 -0
  23. package/puppy-companion-comfort-q5.bin.gz +0 -0
  24. package/puppy-companion-comfort.json +77 -0
  25. package/puppy-companion-life.mjs +49 -16
  26. package/puppy-companion-objects.mjs +12 -0
  27. package/puppy-frame-predictive.mjs +15 -9
  28. package/puppy-frame-store.mjs +2 -2
  29. package/puppy-paper-world.mjs +14 -0
  30. package/puppy-paper.mjs +65 -14
  31. package/retrieviewer-plugin.d.mts +1 -1
  32. package/retrieviewer-plugin.mjs +1 -1
  33. package/retrieviewer.d.mts +10 -4
  34. package/retrieviewer.mjs +20 -14
  35. package/locomotion-core.mjs +0 -21
  36. package/locomotion-puppy.animal +0 -0
  37. package/vendor/procedural-animals/package.json +0 -40
  38. package/vendor/procedural-animals/src/animal.js +0 -146
  39. package/vendor/procedural-animals/src/bake.js +0 -51
  40. package/vendor/procedural-animals/src/build.js +0 -81
  41. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  42. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  43. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  44. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  45. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  46. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  47. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  48. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  49. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  50. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  51. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  52. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  53. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  54. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  55. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  56. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  57. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  58. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  59. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  60. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  61. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  62. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  63. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  64. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  65. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  66. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  67. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  68. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  69. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  70. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  71. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  72. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  73. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  74. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  75. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  76. package/vendor/procedural-animals/src/index.d.ts +0 -98
  77. package/vendor/procedural-animals/src/index.js +0 -29
  78. package/vendor/procedural-animals/src/registry.js +0 -48
  79. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  80. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  81. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  82. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  83. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  84. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  85. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  86. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  87. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  88. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  89. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  90. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  91. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  92. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  93. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  94. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  95. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  96. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  97. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  98. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  99. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  100. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  101. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  102. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  103. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  104. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  105. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  106. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  107. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  108. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  109. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  110. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  111. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  112. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  113. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  114. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  115. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  116. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  117. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  118. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  119. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  120. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  121. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  122. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  123. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  124. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  125. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  126. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  127. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  128. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  129. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  130. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  131. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  132. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  133. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  134. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  135. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  136. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  137. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  138. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  139. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  140. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  141. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  142. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  143. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  144. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  145. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  146. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  147. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  148. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  149. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  150. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  151. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  152. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  153. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  154. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  155. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  156. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  157. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  158. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  159. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  160. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  161. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  162. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  163. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  164. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  165. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  166. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  167. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  168. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  169. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  170. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  171. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  172. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  173. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  174. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  175. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  176. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  177. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  178. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  179. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  180. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  181. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  182. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  183. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  184. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  185. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  186. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  187. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  188. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  189. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  190. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  191. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  192. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  193. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  194. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  195. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  196. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  197. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  198. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  199. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  200. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  201. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  202. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  203. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  204. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  205. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  206. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  207. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  208. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  209. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  210. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  211. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  212. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  213. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  214. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  215. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  216. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  217. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  218. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  219. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  220. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  221. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  222. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  223. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  224. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  225. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  226. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  227. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  228. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  229. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  230. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  231. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  232. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  233. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  234. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  235. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  236. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  237. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  238. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  239. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  240. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  241. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  242. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  243. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  244. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  245. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  246. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  247. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  248. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  249. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  250. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  251. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  252. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  253. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  254. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,547 +0,0 @@
1
- // The goat, sculpted as a signed distance field.
2
- // Every primitive is attached to a bone (skinning) and a group (axial / limb / ear / jaw). The head is
3
- // modelled in its own frame (rig.js: hl()), measured from the profile and frontal references (side2,
4
- // side3, face1, face3); the body relative to the joints so the sculpt follows the skeleton.
5
- // Separate surfaces ("parts"): 'jaw' (lower lip, chin and the beard: the mouth opens and the beard
6
- // swings with it) and 'horn' (keratin horns, rigid on the skull).
7
- import { HEAD_O, HZ, HY, hl, hdir, TAIL_SEGS, hqOf, faceZ, KID_FACE } from './rig.js';
8
- import { add, sub, mul, lerp, norm, cross, rng, clamp, smoothstep } from '../../core/math/vec.js';
9
- import { apertureTiltAlong, sculptEyeSocket } from '../../core/sdf/eyeSocket.js';
10
- import { SDFModel } from '../../core/sdf/sdf.js';
11
-
12
- // Eye: globe ~29 mm across, visible opening ~24 x 16 mm, set high on the side
13
- // of the head, prominent, looking out and a little forward.
14
- const EYE_LOCAL = [0.057, -0.004, 0.0];
15
- const eyeDir = (() => {
16
- const a = (24 * Math.PI) / 180;
17
- return norm(add([Math.cos(a), 0, 0], mul(HZ, Math.sin(a))));
18
- })();
19
- const EYE_BASE = {
20
- c: sub(hl(EYE_LOCAL), HEAD_O), r: 0.0145, back: 0.003, yaw: Math.atan2(eyeDir[0], eyeDir[2]), pitch: Math.asin(eyeDir[1]),
21
- lid: 0.0017, R: 0.0132, d: 0.0052, off: -0.0006, tilt: 0, irisZ: 0.0081, irisR: 0.0112,
22
- };
23
- // roll the almond: its long axis ~20 deg from the horizontal with the inner corner low (side2, face4,
24
- // walk1), i.e. rolled back ~22 deg from the head axis (a search over a limited range stood it on end)
25
- EYE_BASE.tilt = apertureTiltAlong(EYE_BASE, HEAD_O, HZ) - 0.38;
26
- export const EYE = EYE_BASE;
27
-
28
- const X = [1, 0, 0];
29
-
30
- // The horn's mesh cell at this individual's quality tier (params.tierRes): its girth sets it (regions.js)
31
- export const hornCell = (params, res = params.tierRes ?? 1.3) => clamp((params.horns?.base || 0.012) * 0.13, 0.0025, 0.0039) * Math.min(res, 3);
32
-
33
- // Growth ridges round the horn's base half (face2, face4: transverse corrugations, the keratin added ring by ring
34
- // at the base), shared by the sculpt (raised collars where the tier's cells resolve them) and the coat (a dark
35
- // groove before each ridge at every tier). Spacing S along the horn (m), height amp x the horn radius, fading out
36
- // toward tEnd (fraction of the length). null for horn buds.
37
- export function hornRidges(params) {
38
- const H = params.horns;
39
- if (!H || H.len < 0.06) return null;
40
- const buck = H.base > 0.02;
41
- const S = clamp(0.45 * H.base, 0.0075, 0.0135);
42
- // (resolved: at least ~2.6 cells per ridge; at the medium tier and coarser the ridges are painted only)
43
- const geo = smoothstep(2.4, 3.0, S / hornCell(params));
44
- // (s0: the first crest, clear of the skin collar round the base: buried in it, a ridge lifted the hair)
45
- return { S, s0: 0.012 + 0.5 * S, amp: buck ? 0.075 : 0.06, tEnd: buck ? 0.62 : 0.5, geo };
46
- }
47
-
48
- // At the low and crowd tiers the horn is meshed as two closed solids: the base ('horn', the tier's horn cells)
49
- // and the tapering outer part ('horntip', at the medium tier's horn cells), the tip's start buried in the base
50
- // and a hair thinner, so the base's rounded end meets it as a fine ring. Returns the first path index of the
51
- // base's end (the tip runs from two segments before it), or -1 when the horn is one solid.
52
- // (one solid at the low tier's ~1 cm cells: the last fifth of a buck's horn, 3-9 mm thick, meshed as a pinched
53
- // neck and a twisted, ragged flake at the tip, the tip read as snapped off; kept ~0.45 of a cell thick, it was a
54
- // sub-cell tube the grid could not resolve)
55
- export function hornSplit(params, pth) {
56
- if ((params.tierRes ?? 1.3) < 2.5 || !pth || pth.length < 6 || params.horns.len < 0.06) return -1;
57
- const hc = hornCell(params);
58
- let j = 1;
59
- while (j < pth.length - 3 && (pth[j].t < 0.3 || pth[j].r > 1.3 * hc) && pth[j].t < 0.75) j++;
60
- return j;
61
- }
62
- export const hornTipCell = (params) => hornCell(params, (params.tierRes ?? 1.3) > 3.5 ? 2.25 : 2);
63
-
64
- // Horn centre line (reference space), shared by the sculpt and the coat: a scimitar arc from the
65
- // poll, up and back, sweeping further back and out along its length. Returns [{ p, r, t }].
66
- // (at the medium tier and coarser the horn is never thinner than ~0.8 of its cell (the tip solid's cell at the low
67
- // and crowd tiers, hornSplit): at the crowd tier's 16 mm cells the tapering half of a buck's horn, 2-8 mm thick,
68
- // broke into ragged fragments along its length, and with ~0.45 of a cell (and at medium with none) the last
69
- // centimetre ended in a black chisel facet or a needle-thin twisted flake past a pinch, a tip that read as snapped
70
- // off; at ~0.8 the tip is a blunt rounded end, as at the high tier)
71
- export function hornPath(params, s) {
72
- const H = params.horns;
73
- if (!H || H.len <= 0) return [];
74
- const rMin = (params.tierRes ?? 1.3) >= 1.9 ? 0.8 * hornTipCell(params) : 0;
75
- const base = hl([0.021 * s, 0.035, -0.052]);
76
- // sagittal start direction (head-local y, z) and the backward-rotated perpendicular
77
- const d0 = norm([0, 0.15 + 0.35 * (H.upright || 0), -1]); // up and back from the poll
78
- const v0 = [0, d0[2], -d0[1]]; // d0 rotated 90 degrees backward in the sagittal plane (back and down)
79
- // (~1 cm segments: at 2.5-3 cm the cones made a faceted curve that caught the light segment by segment)
80
- const n = Math.max(8, Math.round(20 * Math.min(1.6, H.len / 0.2)));
81
- const out = [];
82
- let p = base.slice();
83
- const ds = H.len / n;
84
- for (let i = 0; i <= n; i++) {
85
- const t = i / n;
86
- const th = H.curve * Math.pow(t, 1.15);
87
- const dl = [
88
- s * (H.spread * (0.35 + 1.1 * t)),
89
- d0[1] * Math.cos(th) + v0[1] * Math.sin(th),
90
- d0[2] * Math.cos(th) + v0[2] * Math.sin(th),
91
- ];
92
- const r = Math.max(rMin, H.base * Math.pow(1 - 0.92 * t, 0.85) + 0.0015);
93
- out.push({ p: p.slice(), r, t });
94
- p = add(p, mul(norm(hdir(dl)), ds));
95
- }
96
- return out;
97
- }
98
-
99
- // the direction the inner (cupped) side of an ear faces: forward, a little out and down (erect), toward the
100
- // face and a little forward (lop); shared with the coat (inner-ear skin, the rim of a lop ear)
101
- export function earFacing(params, s) {
102
- return params.ear === 'lop' ? norm(add([-0.9 * s, 0, 0], mul(hdir([0, 0, 1]), 0.4))) : norm([0.25 * s, -0.3, 1]);
103
- }
104
-
105
- export function sculptGoat(m, rig, params = {}) {
106
- const J = rig.J;
107
- const kid = params.age === 'juvenile';
108
- const HQ = hqOf(params);
109
- const male = params.sex === 'male';
110
- const buck = male && !kid;
111
- const heavy = params.heavy ?? (buck ? 1 : 0); // 0 doe .. 1 heavy buck
112
- const R = rng(5311 + (params.coatSeed || 0));
113
- const roman = params.roman || 0; // Nubian / Boer convex nose
114
- void R;
115
-
116
- const ellY = (o) => {
117
- const y = norm(o.ydir);
118
- const lat = o.lateral || X;
119
- const z = norm(cross(lat, y));
120
- return m.ell({ ...o, axis: z, up: y, r: [o.r[0], o.r[1], o.r[2]] });
121
- };
122
- const H = { bone: 'head' };
123
- // (head-local points in adult coordinates; a kid's face in front of the eyes is shorter, rig.js faceZ)
124
- const hk = (v) => hl([v[0], v[1], faceZ(v[2], kid)]);
125
- const shortR = (o) => (kid && o.c[2] > 0.02 ? [o.r[0], o.r[1], o.r[2] * KID_FACE] : o.r);
126
- const hell = (o) => m.ell({ ...H, ...o, c: hk(o.c), r: shortR(o), axis: o.axisL ? norm(hdir(o.axisL)) : HZ, up: o.upL ? norm(hdir(o.upL)) : HY });
127
-
128
- // ---------------------------------------------------------------- TORSO
129
- // dairy type: deep barrel, sharp withers, level back, prominent hip bones and pin bones, sloping rump
130
- const gw = 1 + 0.12 * heavy; // bucks are broader
131
- m.ell({ tag: 'ribcage', bone: 'spine3', c: [0, 0.585, 0.105], r: [0.122 * gw, 0.18, 0.25], axis: norm([0, 0.08, 1]), k: 0 });
132
- m.ell({ tag: 'girth', bone: 'chest', c: [0, 0.575, 0.28], r: [0.1 * gw, 0.15, 0.12], k: 0.08 });
133
- // (a doe is deep through the belly, deeper in milk: side2, side3; the barrel hung 2-5 cm higher than theirs;
134
- // the Boer's barrel is already deep through its girth, and deeper still its short forelegs could not fold
135
- // under the chest when it lay down)
136
- const milk = !male && !kid ? (params.udder ?? 0.5) * (params.variant === 'boer' ? 0.7 : 1) : 0;
137
- const doe = !male && !kid && params.variant !== 'boer' ? 1 : 0;
138
- // (the trunk pieces blend over 8-10 cm: one smooth barrel with a straight top line, threequarter1 / side2;
139
- // at 5-7 cm the withers, back, loin and flank showed as separate bumps, and the coat parted over the
140
- // junctions)
141
- m.ell({ tag: 'abdomen', bone: 'spine2', c: [0, 0.57 - 0.015 * milk, -0.085], r: [0.14 * gw, 0.19 - 0.02 * heavy + 0.02 * milk, 0.165], axis: norm([0, 0.1, -1]), k: 0.09 });
142
- // (the deeper belly of a doe hangs between the elbows and the stifles, clear of the stifles: lowered as a
143
- // whole, the belly's side took the stifle's place and the swinging stifle poked through it at every step)
144
- if (doe) m.ell({ tag: 'abdomen', bone: 'spine2', c: [0, 0.43 - 0.008 * milk, -0.02], r: [0.1, 0.075 + 0.012 * milk, 0.125], k: 0.08 });
145
- m.ell({ tag: 'flank', bone: 'spine1', c: [0, 0.62, -0.19], r: [0.12 * gw, 0.12, 0.11], k: 0.09 });
146
- m.ell({ tag: 'withers', bone: 'chest', c: [0, 0.735, 0.22], r: [0.045, 0.05, 0.15], axis: norm([0, -0.12, 1]), k: 0.09 });
147
- m.ell({ tag: 'back', bone: 'spine3', c: [0, 0.72, 0.05], r: [0.09 * gw, 0.05, 0.2], k: 0.1 });
148
- m.ell({ tag: 'loin', bone: 'spine1', c: [0, 0.725, -0.13], r: [0.1 * gw, 0.045, 0.12], k: 0.1 });
149
- // hindquarters: croup sloping to the tail head, hooks (hip points) and pins
150
- m.ell({ tag: 'pelvis', bone: 'pelvis', c: [0, 0.645, -0.3], r: [0.115 * gw, 0.115, 0.105], k: 0.05 });
151
- m.ell({ tag: 'croup', bone: 'pelvis', c: [0, 0.715, -0.29], r: [0.085 * gw, 0.045, 0.11], axis: norm([0, -0.25, 1]), k: 0.05 });
152
- for (const s of [1, -1]) {
153
- m.sphere({ tag: 'hippoint', bone: 'pelvis', c: [0.095 * s * gw, 0.715, -0.195], rad: 0.021, k: 0.05 });
154
- m.sphere({ tag: 'pinbone', bone: 'pelvis', c: [0.045 * s, 0.69, -0.362], rad: 0.022, k: 0.04 });
155
- m.ell({ tag: 'rump', bone: 'pelvis', c: [0.05 * s, 0.6, -0.335], r: [0.055, 0.1, 0.055], k: 0.06 });
156
- }
157
- // chest front: the breast between the forelegs, running smoothly up into the throat (side2: the point
158
- // of the shoulder shows, the breast does not bulge; it stood 6 cm ahead of the shoulder joints as a knob)
159
- // (front1, front2, threequarter1: one rounded front, the neck flaring into it, the points of the shoulder
160
- // at most a gentle angle at its sides. A breast as wide as the shoulders with the humerus heads at its
161
- // corners read as a box with two round lumps in every front and 3/4 view: the shoulder cones stood
162
- // 1.2 cm proud of a groove, and 2 cm outside every primitive there, where five of them blended. Now the
163
- // breast is narrower and centred, the shoulders 2.5-3 cm behind it; the chest front stands 0.06 / 0.09 /
164
- // 0.11 SH ahead of the forearm at 0.54 / 0.56 / 0.59 SH, side3 0.09 / 0.13 / 0.14 with its near leg)
165
- m.ell({ tag: 'brisket', bone: 'chest', c: [0, 0.482, 0.272], r: [0.075, 0.07, 0.07], k: 0.05 });
166
- for (const s of [1, -1]) m.ell({ tag: 'pectoral', bone: 'chest', c: [0.042 * s, 0.475, 0.3], r: [0.04, 0.055, 0.026], k: 0.05 });
167
- // the breast muscles over the sternum
168
- // (bias: its skin counts as the chest's for the weights, so a goat lying on its side rests on it: skinned
169
- // half to the neck and legs, the fuller chest front went 13 mm into the ground in death)
170
- m.ell({ tag: 'pectoral', bone: 'chest', c: [0, 0.525, 0.325], r: [0.075, 0.065, 0.065], k: 0.05, bias: 0.03 });
171
- // udder (adult does): two halves and teats, pink skin (size: dry doe .. in milk); a buck's scrotum.
172
- // Their own closed surface ('udder' part), rigid on the udder bone, its top sunk into the belly: meshed
173
- // with the body, the udder hangs between the gaskins and took 74 % tibia weights (the core gives the limbs
174
- // their share before an appendage), so its skin crumpled and the hair shells over it tore into dark shards
175
- // at every step. Narrow enough to hang between the thighs (their inner skin is ~5 cm off the midline).
176
- const UD = { bone: 'udder', group: 'udder', part: 'udder' };
177
- if (!male && !kid && (params.udder ?? 0.5) > 0.05) {
178
- const u = params.udder ?? 0.5;
179
- for (const s of [1, -1]) {
180
- m.ell({ ...UD, tag: 'udder', c: [0.025 * s, 0.35 - 0.03 * u, -0.338 + HQ], r: [0.032 + 0.013 * u, 0.047 + 0.03 * u, 0.052 + 0.022 * u], k: 0.03 });
181
- const a = [0.033 * s, 0.315 - 0.055 * u, -0.322 + HQ], b = add(a, [0.01 * s, -0.035 - 0.01 * u, 0.013]);
182
- m.cone({ ...UD, tag: 'teat', a, b, ra: 0.0105, rb: 0.0078, k: 0.012 });
183
- }
184
- // the attachment: from the udder up into the belly (hidden inside the body above the groin)
185
- m.ell({ ...UD, tag: 'udder', c: [0, 0.43, -0.345 + HQ], r: [0.045, 0.085, 0.072], k: 0.035 });
186
- }
187
- if (male && !kid) {
188
- // scrotum between the hind legs, under the groin (walk1: a short-haired pendulous bag hanging behind the stifles to
189
- // about the middle of the gaskin): a neck of skin from deep in the groin, narrowing to 4.2 cm below the underline,
190
- // and a pear-shaped bag of two testes side by side (7 cm across, 8 cm deep, 26 cm round once scaled to a buck:
191
- // dairy bucks 26-34 cm), its bottom 18 cm below the groin. The neck is anchored 7 cm inside the body (the old
192
- // sausage's top was 1 cm inside: as the groin skin moved with the thighs its end showed, a separate object). Inside
193
- // the thighs' inner skin (x 4.8-6.4 cm off the midline beside it) with 1.3 cm to spare. Sheath under the belly.
194
- const zc = -0.385 + HQ;
195
- // (widening up into the groin; a flare filling the top of the arch between the thighs read as a second bulb from behind)
196
- m.cone({ ...UD, tag: 'scrotum', a: [0, 0.575, zc + 0.012], b: [0, 0.448, zc - 0.002], ra: 0.034, rb: 0.021, k: 0.02 });
197
- m.ell({ ...UD, tag: 'scrotum', c: [0, 0.392, zc - 0.004], r: [0.03, 0.05, 0.036], k: 0.03 });
198
- for (const s of [1, -1]) m.ell({ ...UD, tag: 'scrotum', c: [0.013 * s, 0.375, zc - 0.006], r: [0.022, 0.05, 0.04], axis: norm([0, 0.12, 1]), k: 0.02 });
199
- m.ell({ tag: 'sheath', bone: 'spine2', c: [0, 0.375, -0.02], r: [0.017, 0.02, 0.045], axis: norm([0, 0.3, 1]), k: 0.03 });
200
- }
201
-
202
- // ---------------------------------------------------------------- NECK
203
- // long and slender, flattened sideways, deep at the base; a buck's neck is thick with a crest
204
- const nb = J.neckBase, nm = J.neckMid, occ = J.occiput;
205
- const nd1 = norm(sub(nm, nb)), nd2 = norm(sub(occ, nm));
206
- // (perpendiculars in the sagittal plane: fwd = toward the throat, back = toward the crest)
207
- const fwdN = (d) => [0, -d[2], d[1]], backN = (d) => [0, d[2], -d[1]];
208
- const ndM = norm(add(nd1, nd2));
209
- const nk = 1 + 0.3 * heavy;
210
- // (the pieces blend over 7 cm: over a sharper junction the 9 mm coat fanned open along a ragged dark line)
211
- m.ell({ tag: 'neck', bone: 'neck1', c: add(nb, [0, 0.008, 0.006]), r: [0.072 * nk, 0.1, 0.095], axis: nd1, up: [0, 1, -0.6], k: 0.07 });
212
- m.ell({ tag: 'neck', bone: 'neck1', c: add(lerp(nb, nm, 0.72), [0, 0, 0.012]), r: [0.052 * nk, 0.075 * (1 + 0.15 * heavy), 0.1], axis: nd1, up: [0, 1, -0.6], k: 0.07 });
213
- // (a bridge at the mid neck: the neck tapers continuously instead of stepping from one piece to the next)
214
- m.ell({ tag: 'neck', bone: 'neck2', c: add(nm, [0, 0, 0.018]), r: [0.047 * nk, 0.064 * (1 + 0.15 * heavy), 0.075], axis: ndM, up: [0, 1, -0.6], k: 0.07 });
215
- m.ell({ tag: 'neck', bone: 'neck2', c: add(lerp(nm, occ, 0.39), [0, 0, 0.028]), r: [0.042 * nk, 0.052 * (1 + 0.15 * heavy), 0.08], axis: nd2, up: [0, 1, -0.6], k: 0.06 });
216
- // a buck's crest: the thick upper neck behind the axis (a narrow crest stood out as a ridge along the side
217
- // of a doe's neck, where the coat parted)
218
- if (heavy > 0.05) m.ell({ tag: 'crest', bone: 'neck1', c: add(lerp(nb, nm, 0.9), mul(backN(nd1), 0.045)), r: [0.05 + 0.02 * heavy, 0.035 + 0.025 * heavy, 0.13], axis: norm(add(nd1, [0, 0.1, 0])), k: 0.07 });
219
- // underline of the neck (trachea) to the throat
220
- const thr = add(nm, mul(fwdN(ndM), 0.07));
221
- m.cone({ tag: 'throat', bone: 'neck1', a: add(add(nb, mul(nd1, -0.02)), mul(fwdN(nd1), 0.07)), b: thr, ra: 0.038, rb: 0.031, k: 0.045 });
222
- m.cone({ tag: 'throat', bone: 'neck2', a: add(thr, mul(ndM, -0.01)), b: hl([0, -0.105 + (kid ? 0.015 : 0), -0.06]), ra: 0.03, rb: 0.027, k: 0.04 });
223
- // the throatlatch: the larynx fills the corner between the underside of the jaw and the throat (a sharp
224
- // notch there folded over as the head moved, back faces showing at the throat outline); not in kids (their
225
- // throats stretched 3x on 4.2 % of the mesh when grazing)
226
- if (!kid) m.ell({ tag: 'throat', bone: 'neck2', c: hl([0, -0.13, 0.036]), r: [0.02, 0.015, 0.022], k: 0.03 });
227
- // wattles (toggles): two hair-covered tassels hanging from the throat, behind and below the jaw angle
228
- // (at head-local y -0.082 they hung from the cheek beside the eye once the head was carried higher)
229
- if (params.wattles) {
230
- for (const s of [1, -1]) {
231
- const a = hl([0.018 * s, -0.128, -0.068]);
232
- const b = add(a, [0.006 * s, -0.052, -0.006]);
233
- m.cone({ tag: 'wattle', bone: 'neck2', part: 'wattle', a, b, ra: 0.0065, rb: 0.0095, k: 0.008 });
234
- }
235
- }
236
-
237
- // ---------------------------------------------------------------- HEAD (head-local, see rig.js)
238
- // A goat's head from the side is a wedge (side2, walk1, face2, face4; skull anatomy): the eye sits high
239
- // on the side of a deep head, the flat, broad forehead rises above it to the horn bases, the straight
240
- // nasal line runs steeply down (~60 deg below the horizontal, 20 deg below the eye -> nose line) to a
241
- // compact nose, and the lower border of the jaw runs straight from the chin back and down to a deep jaw
242
- // angle behind and 13 cm below the eye (the ramus); the cheek is a flat masseter plate, not a jowl. The
243
- // lips and chin sit well below the nose. From the front the head is widest at the eyes and tapers to
244
- // the muzzle (face1, face3).
245
- // landmarks: poll (0, 0, -0.07), eyes (+-0.057, -0.004, 0), forehead top y 0.052 at z 0.02, nose top
246
- // (0, -0.004, 0.17), nose tip (0, -0.022, 0.205), mouth line y -0.066, chin bottom (0, -0.097, 0.165),
247
- // jaw angle (+-0.031, -0.138, -0.045)
248
- // (kd: a kid's head: a domed braincase, a shallow jaw without the adult's masseter plate)
249
- const kd = kid ? 1 : 0;
250
- hell({ tag: 'cranium', c: [0, 0.012 + 0.004 * kd, -0.042], r: [0.055, 0.041 + 0.01 * kd, 0.058], k: 0.03 });
251
- hell({ tag: 'poll', c: [0, -0.014, -0.068], r: [0.035, 0.037, 0.03], k: 0.03 });
252
- // the dorsal line: one straight line from the forehead between the horn bases to the nose, 16 deg below
253
- // the head axis (58 deg below the horizontal in the bind carriage; side2 55, walk1 60): the forehead
254
- // plate and the nasal bridge are laid along it (a level forehead dropping onto a lower nasal bridge
255
- // made a dog's stop)
256
- const NL = [0, -0.29, 1];
257
- hell({ tag: 'forehead', c: [0, 0.026, 0.012], r: [0.062, 0.018, 0.07], axisL: NL, k: 0.025 });
258
- // nasal bridge: straight / slightly dished in Swiss breeds, convex (Roman) in Nubians and Boers
259
- // (the arch: a separate ridge at mid-face, up to 26 mm for roman = 1)
260
- hell({ tag: 'face', c: [0, -0.004, 0.105], r: [0.029, 0.023, 0.095], axisL: NL, k: 0.025 });
261
- if (roman > 0.05) hell({ tag: 'face', c: [0, 0.0 + 0.013 * roman, 0.105], r: [0.022 + 0.004 * roman, 0.016 + 0.012 * roman, 0.07], axisL: NL, k: 0.03 });
262
- // (the muzzle is broad: face1 / face3, the nose nearly as wide as the face at the mouth; at 5 cm across it
263
- // made a horse's narrow nose)
264
- hell({ tag: 'lowerface', c: [0, -0.042 + 0.004 * kd, 0.082], r: [0.035, 0.043 - 0.006 * kd, 0.088], k: 0.03 });
265
- hell({ tag: 'muzzle', c: [0, -0.032, 0.17], r: [0.031 + 0.003 * roman, 0.027, 0.03], k: 0.024 });
266
- hell({ tag: 'upperlip', c: [0, -0.053, 0.181], r: [0.025, 0.014, 0.021], k: 0.016 });
267
- for (const s of [1, -1]) {
268
- // the face below the eye (maxilla, facial crest): broad under the eyes, tapering to the muzzle
269
- // (face1, face3: the face narrowed straight from the eyes into a horse's nose)
270
- hell({ tag: 'maxilla', c: [0.026 * s, -0.03, 0.045], r: [0.018, 0.033, 0.065], axisL: [0.18 * s, 0, 1], k: 0.03 });
271
- // masseter: a flat swell over the ramus, from below the eye down to the jaw angle
272
- // (its rear edge ended 7 cm behind the eye as a 16 mm step against the jaw angle and throat, blended
273
- // over only 3 cm: a dark strap from under the eye down to the throat on every adult; face1 / face3 /
274
- // side2 show a flat cheek running into the throat, the masseter only a gentle swell)
275
- // (a kid has no plate: its small one ended 6 cm behind the eye in an earlier strap from the eye to the
276
- // throat, and its outline stood as a knob on the throat behind the jaw; the adults' plate moved forward
277
- // stretched a kid's throat 3x on 4.15 % of the mesh when grazing, the skin of the lower head riding on
278
- // the neck. Without it a kid's > 3x is lower, 3.49-3.54 %)
279
- // ... only a fill inside the jaw's hollow below the eye (between the braincase, the maxilla and the jaw,
280
- // raised on a kid: without anything there the kid's head had a pit down to the midline behind the eye)
281
- if (kd) hell({ tag: 'cheek', c: [0.012 * s, -0.055, -0.02], r: [0.02, 0.028, 0.03], k: 0.03 });
282
- else {
283
- hell({ tag: 'cheek', c: [0.035 * s, -0.072, 0.0], r: [0.012, 0.056, 0.045], k: 0.05 });
284
- // behind the ramus (parotid): the side of the head slopes from the cheek into the neck over 9 cm (the
285
- // back of the jaw fell 3-4 cm in as many, a crease from below the eye down to the throat in every
286
- // 3/4 view)
287
- hell({ tag: 'parotid', c: [0.022 * s, -0.072, -0.085], r: [0.024, 0.05, 0.05], k: 0.035 });
288
- }
289
- // the lower border of the jaw, from the jaw angle forward (the front of the mandible is the jaw part)
290
- // (it runs forward over the sides of the chin: the jaw part shows only its lower lip at the front and
291
- // the chin underneath, so the seam between the two surfaces lies along the mouth line and under the
292
- // chin, not on the side of the jaw)
293
- // (a kid's jaw angle 2.2 cm higher, its end 4 mm further in and blended over 3.7 cm: at the side of the
294
- // throat it poked out as a small bare knob, a white fleck at the jaw angle; lower, the throat stretched)
295
- m.cone({ ...H, tag: 'mandible', a: hk([0.029 * s - 0.004 * s * kd, -0.124 + 0.022 * kd, -0.045 + 0.01 * kd]), b: hk([0.013 * s, -0.083, 0.17]), ra: 0.014 - 0.002 * kd, rb: 0.0085, k: 0.025 + 0.012 * kd });
296
- // the cheek between the upper and lower jaws (buccinator), flush from the eye to the mouth corner: the
297
- // face showed a 5-7 mm groove from below the eye forward to the chin between the maxilla and the
298
- // mandible, a long crease in every front and 3/4 view
299
- // (a kid's shallow jaw: the fill stays above its jaw line)
300
- hell({ tag: 'buccal', c: [0.022 * s, -0.073 + 0.005 * kd, 0.085], r: [0.012, 0.019 - 0.004 * kd, 0.07], axisL: [-0.14 * s, 0.14, 1], k: 0.025 });
301
- hell({ tag: 'brow', c: [0.048 * s, 0.017, -0.005], r: [0.012, 0.008, 0.016], k: 0.015 });
302
- hell({ tag: 'nostrilwing', c: [0.02 * s, -0.026, 0.177], r: [0.012, 0.014, 0.014], k: 0.013 });
303
- sculptEyeSocket(m, EYE, HEAD_O, s, { orbit: { r: [0.016, 0.012, 0.01], at: [0.0, 0.001, 0.017], k: 0.007 } });
304
- // nostril: a comma-shaped slit on the front of the nose, the lower ends toward the philtrum and the
305
- // upper ends turned out (face3)
306
- // (its middle, not its tip, breaks the surface: a slit 2 cm long, not a round hole)
307
- hell({ tag: 'nostril', c: [0.018 * s, -0.026, 0.199], r: [0.0026, 0.0105, 0.0065], axisL: [-0.4 * s, 0.15, 1], upL: [0.5 * s, 1, 0], k: 0.0025, carve: true });
308
- }
309
- // the soft floor between the two mandibles (a midline notch under the jaw between the mandible cones)
310
- hell({ tag: 'intermandible', c: [0, -0.094 + 0.008 * kd, 0.068 + 0.012 * kd], r: [0.018, 0.012 - 0.003 * kd, 0.062 - 0.012 * kd], axisL: [0, 0.18, 1], k: 0.02 });
311
- // (no philtrum groove: at mesh resolution its notch at the lip margin showed the lower lip behind it as a
312
- // white fang)
313
-
314
- // ---------------------------------------------------------------- JAW (lower lip, chin, beard)
315
- const JW = { bone: 'jaw', group: 'jaw', part: 'jaw' };
316
- // a small lower lip tucked under the upper lip, and a rounded chin below it (walk1, face2)
317
- // (the lower lip reaches 6 mm up inside the upper lip, so the mouth stays shut while the jaw grinds)
318
- m.ell({ ...JW, tag: 'chin', c: hk([0, -0.086, 0.168]), r: [0.017, 0.013, 0.02], axis: HZ, up: HY, k: 0 });
319
- m.ell({ ...JW, tag: 'lowerlip', c: hk([0, -0.071, 0.182]), r: [0.02, 0.0105, 0.015], axis: HZ, up: HY, k: 0.012 });
320
- // (the front of the mandible, inside the head's jaw: it carries the chin when the mouth opens; its
321
- // ends reached out between the lips as two white incisors)
322
- for (const s of [1, -1]) m.cone({ ...JW, tag: 'mandible', a: hk([0.009 * s, -0.085, 0.115]), b: hk([0.008 * s, -0.084, 0.152]), ra: 0.006, rb: 0.0065, k: 0.015 });
323
- const beard = params.beard || 0; // length (m)
324
- if (beard > 0.01) {
325
- // hangs from under the chin in three loose locks (face2, face4: a beard of separate locks, the middle one longest),
326
- // each a thin round core with long hair: one 3 cm wide core read as a flat, smooth ribbon inside the hair
327
- const a = hk([0, -0.094, 0.12]);
328
- const down = norm([0, -1, -0.05]);
329
- const nB = 4;
330
- // (each lock a chain of round cones: ellipsoid segments ended in points thinner than a medium-tier cell, whose
331
- // tips folded over)
332
- for (const j of [-1, 0, 1]) {
333
- const L = beard * (j === 0 ? 1 : 0.78);
334
- const at = (t) => add(add(a, mul(down, L * t)), [j * (0.006 + 0.008 * t), 0, -0.024 * t - 0.004 * Math.abs(j)]);
335
- const rad = (t) => (0.0045 + 0.0015 * Math.min(1, beard / 0.15)) * (1 - 0.5 * t);
336
- for (let i = 0; i < nB; i++) {
337
- const t0 = i / nB, t1 = (i + 1) / nB;
338
- m.cone({ ...JW, bone: 'beard', part: 'beard', tag: 'beard', thin: true, a: at(t0), b: at(t1), ra: rad(t0), rb: rad(t1), k: 0.008 });
339
- }
340
- }
341
- // the root of the beard under the chin (a 9 cm lock along the underside of the jaw hung below the
342
- // mandibles as a pale strap)
343
- m.ell({ ...JW, tag: 'beard', c: hk([0, -0.093, 0.13]), r: [0.019, 0.013, 0.026 * (kid ? KID_FACE : 1)], axis: HZ, up: HY, k: 0.016 });
344
- }
345
-
346
- // ---------------------------------------------------------------- EARS
347
- const lop = params.ear === 'lop';
348
- for (const S of ['L', 'R']) {
349
- const s = S === 'L' ? 1 : -1;
350
- const base = J['earBase' + S], tip = J['earTip' + S];
351
- const up = norm(sub(tip, base));
352
- const len = Math.hypot(...sub(tip, base));
353
- // the cup opens forward and down (erect), inward toward the face (lop)
354
- // (erect: the broad inner side faces forward, a little out and down, as in face1 / face3 / front1;
355
- // it faced straight down, so the ears showed their edge from the front)
356
- const facing = earFacing(params, s);
357
- const lat = norm(cross(up, facing));
358
- const fc = norm(cross(lat, up));
359
- const ear = { bone: 'ear' + S, group: 'ear' + S, thin: true };
360
- // a leaf: a rolled, narrow base, the blade widest at ~40 % of the length, a pointed tip (Swiss:
361
- // ~6 cm wide on 14-16 cm, face1 / face3 / front2); the lop ear a broad drape with a rounded tip
362
- // (it was an 8.4 cm wide disc: mouse ears)
363
- // (a kid's lop ear: its thickness, root roll and rim scaled with the ear's length, x0.82, and a little
364
- // narrower: at the adult's 7.5 mm blades and 5 cm width the blades and the rolled root read through the
365
- // outer face of the short ear as lumps and a raised pad, round the kid's face like a bonnet)
366
- const ek = lop && kid ? 0.82 : 1;
367
- const wd = lop ? 0.05 * (kid ? 0.88 : 1) : 0.029;
368
- if (lop) {
369
- // Nubian / Boer: a long, broad, pendulous ear (the Nubian standard: close to the head at the temple,
370
- // flaring out and forward to the rounded tip, a bell round the face; side_boer). It was one flat oval
371
- // blade pasted on the cheek: short, no roll at the root, no rim.
372
- const out = [s, 0, 0];
373
- const fwd = lat[2] >= 0 ? lat : mul(lat, -1); // the blade's front edge direction
374
- const fwdF = norm(add(fwd, mul(out, 0.5))); // ... turned out in the lower, flaring half
375
- // the root: rolled and narrow, leaving the skull sideways before it drops
376
- // (a 36 mm thick end on the 15 mm blade stood out as a lump with a shadowed step below it)
377
- m.cone({ ...ear, tag: 'ear', a: add(base, mul(up, 0.004)), b: add(lerp(base, tip, 0.28), mul(out, 0.003 * ek)), ra: 0.0115 * ek, rb: 0.0062 * ek, k: 0.018 * ek });
378
- // the upper blade, along the side of the head behind the eye
379
- // (the blades follow the ear bone, which already hangs out 24 deg: set further out on their own they
380
- // stepped out one after the other, an oval lump on the outside of the ear)
381
- ellY({ ...ear, tag: 'ear', c: add(lerp(base, tip, 0.44), mul(fwd, 0.002)), ydir: up, lateral: fwd, r: [wd * 0.84, len * 0.32, 0.0075 * ek], k: 0.016 });
382
- // the lower blade and the rounded tip, turned a little out
383
- ellY({ ...ear, tag: 'ear', c: add(lerp(base, tip, 0.69), mul(out, 0.0015 * ek)), ydir: up, lateral: fwdF, r: [wd, len * 0.28, 0.0072 * ek], k: 0.018 });
384
- ellY({ ...ear, tag: 'eartip', c: add(lerp(base, tip, 0.86), mul(out, 0.002 * ek)), ydir: up, lateral: fwdF, r: [wd * 0.8, len * 0.15, 0.0065 * ek], k: 0.016 });
385
- // the front edge rolled out into a thick rim
386
- m.cone({ ...ear, tag: 'ear', a: add(lerp(base, tip, 0.14), mul(fwd, wd * 0.4)), b: add(lerp(base, tip, 0.64), add(mul(fwdF, wd * 0.86), mul(out, 0.003 * ek))), ra: 0.003 * ek, rb: 0.0055 * ek, k: 0.01 * ek });
387
- // the cup: the inner face hollowed down the middle of the blade (short of the root: medium-tier hole)
388
- // (a kid's cup as deep as an adult's: the coarse tiers inflate thin blades to 1 cm, not carvers, and a cup
389
- // scaled with the kid's blade only grazed the inflated face there, a ragged speckle over the inner face)
390
- ellY({ ...ear, tag: 'earinner', c: add(lerp(base, tip, 0.55), mul(fc, 0.0068)), ydir: up, lateral: fwd, r: [wd * 0.6, len * 0.28, 0.0052], k: 0.004, carve: true });
391
- } else {
392
- // (the rolled base no thicker than the blade where it ends: a 32 mm round end on the 17 mm blade folded
393
- // the skin over at the medium tier, back faces showing through the ear)
394
- m.cone({ ...ear, tag: 'ear', a: add(base, mul(up, 0.004)), b: lerp(base, tip, 0.27), ra: 0.011, rb: 0.0105, k: 0.014 });
395
- ellY({ ...ear, tag: 'ear', c: lerp(base, tip, 0.44), ydir: up, lateral: lat, r: [wd, len * 0.42, 0.0085], k: 0.014 });
396
- ellY({ ...ear, tag: 'eartip', c: lerp(base, tip, 0.78), ydir: up, lateral: lat, r: [wd * 0.5, len * 0.24, 0.0065], k: 0.012 });
397
- // (the cup stops short of the narrow root: reaching it, it broke through the ear's floor at the medium
398
- // tier, a ragged hole beside the root; it leaves a 12.5 mm floor at hero and 14 mm at medium: 9.5-12 mm,
399
- // about two medium cells, still showed pinholes through the ear)
400
- // (its root end soft, k 8 mm, and 1 cm further from the root: with a 4 mm blend the end of the carve was a
401
- // corner the medium tier's 5.6 mm cells folded into a white notch)
402
- ellY({ ...ear, tag: 'earinner', c: add(lerp(base, tip, 0.6), mul(fc, 0.0085)), ydir: up, lateral: lat, r: [wd * 0.66, len * 0.28, 0.0045], k: 0.008, carve: true });
403
- }
404
- m.sphere({ ...H, tag: 'earbase', c: add(base, mul(up, -0.006)), rad: 0.016, k: 0.02 });
405
- }
406
-
407
- // ---------------------------------------------------------------- HORNS (own rigid surface)
408
- for (const s of [1, -1]) {
409
- const pth = hornPath(params, s);
410
- const J = hornSplit(params, pth);
411
- for (let i = 0; i + 1 < pth.length; i++) {
412
- if (J < 0 || i < J) m.cone({ bone: 'head', part: 'horn', group: 'axial', tag: 'horn', a: pth[i].p, b: pth[i + 1].p, ra: pth[i].r, rb: pth[i + 1].r, k: 0.004 });
413
- if (J >= 0 && i >= J - 2) m.cone({ bone: 'head', part: 'horntip', group: 'axial', tag: 'horn', a: pth[i].p, b: pth[i + 1].p, ra: pth[i].r * (i <= J ? 0.96 : 1), rb: pth[i + 1].r * (i + 1 <= J ? 0.96 : 1), k: 0.004 });
414
- }
415
- // a horn is flattened sideways at its base (a keel along the front)
416
- if (pth.length) {
417
- // (over the first ~2.5 cm of the horn)
418
- let j = 1;
419
- while (j + 1 < pth.length && Math.hypot(...sub(pth[j].p, pth[0].p)) < 0.025) j++;
420
- m.ell({ bone: 'head', part: 'horn', group: 'axial', tag: 'horn', c: lerp(pth[0].p, pth[j].p, 0.5), r: [pth[0].r * 0.75, pth[0].r * 1.15, Math.hypot(...sub(pth[j].p, pth[0].p)) * 0.8], axis: norm(sub(pth[j].p, pth[0].p)), up: HZ, k: 0.004 });
421
- }
422
- // growth ridges: a raised collar every S along the base half, rising over a third of the spacing to its crest and
423
- // falling back to the horn over the rest toward the tip (two round cones about the crest), lower toward tEnd
424
- const RG = hornRidges(params);
425
- if (pth.length && RG && RG.geo > 0.01) {
426
- const L = params.horns.len, n = pth.length - 1;
427
- const at = (sArc) => {
428
- const u = clamp(sArc / L, 0, 1) * n, i = Math.min(n - 1, Math.floor(u)), f = u - i;
429
- return { p: lerp(pth[i].p, pth[i + 1].p, f), r: pth[i].r + (pth[i + 1].r - pth[i].r) * f, t: u / n };
430
- };
431
- for (let sA = RG.s0; sA < RG.tEnd * L; sA += RG.S) {
432
- const c = at(sA), a = at(sA - 0.35 * RG.S), b = at(sA + 0.6 * RG.S);
433
- const h = RG.amp * RG.geo * (1 - smoothstep(0.55 * RG.tEnd, RG.tEnd, c.t));
434
- if (h < 0.01) continue;
435
- const R = { bone: 'head', part: 'horn', group: 'axial', tag: 'horn', k: 0.0015 };
436
- m.cone({ ...R, a: a.p, b: c.p, ra: a.r * (1 + 0.15 * h), rb: c.r * (1 + h) });
437
- m.cone({ ...R, a: c.p, b: b.p, ra: c.r * (1 + h), rb: b.r * (1 + 0.1 * h) });
438
- }
439
- }
440
- // horn base boss on the skull (also the polled goat's small knobs)
441
- if (params.horns && params.horns.len > 0) m.sphere({ ...H, tag: 'hornboss', c: hl([0.021 * s, 0.03, -0.05]), rad: Math.min(0.016, params.horns.base * 0.9), k: 0.015 });
442
- }
443
-
444
- // ---------------------------------------------------------------- MANE (bucks: long hair along the crest and back)
445
- if (buck && (params.mane || 0) > 0.05) {
446
- const mn = params.mane;
447
- // the crest line: the skin of the neck behind its axis (marched out from the axis), then the back
448
- const body = m.prims.filter((q) => (q.part || 'body') === 'body' && !q.carve);
449
- const skin = (p, d) => { let t = 0; while (t < 0.25 && SDFModel.evalList(body, p[0] + d[0] * t, p[1] + d[1] * t, p[2] + d[2] * t) < 0) t += 0.002; return add(p, mul(d, t - 0.012)); };
450
- const line = [
451
- skin(lerp(nm, occ, 0.75), backN(nd2)), skin(lerp(nm, occ, 0.35), backN(nd2)), skin(lerp(nb, nm, 0.85), backN(nd1)),
452
- skin(lerp(nb, nm, 0.4), backN(nd1)), [0, 0.765, 0.2], [0, 0.745, 0.08],
453
- ];
454
- const bones = ['neck2', 'neck2', 'neck1', 'neck1', 'spine3', 'spine3'];
455
- for (let i = 0; i < line.length; i++) {
456
- m.ell({ tag: 'mane', bone: bones[i], c: line[i], r: [0.028 * mn + 0.01, 0.022 * mn + 0.006, 0.055], axis: i + 1 < line.length ? norm(sub(line[i], line[i + 1])) : [0, 0, 1], k: 0.03 });
457
- }
458
- }
459
-
460
- // ---------------------------------------------------------------- LEGS (slender, long cannons)
461
- const lk = 1 + 0.12 * heavy;
462
- for (const S of ['L', 'R']) {
463
- const s = S === 'L' ? 1 : -1;
464
- const lat = [s, 0, 0];
465
- const sx = (v) => [v[0] * s, v[1], v[2]];
466
- const g = 'F' + S;
467
- 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];
468
- ellY({ tag: 'scapmuscle', bone: 'scapula' + S, group: g, c: add(lerp(Sc, Sh, 0.45), sx([0.03, 0, -0.01])), ydir: sub(Sh, Sc), lateral: lat, r: [0.022 * lk, 0.1, 0.062], k: 0.06 });
469
- // (the point of the shoulder only a gentle angle: no ball on the humerus head, and the upper arm's cone
470
- // starts a quarter of the way down: seen end-on from the front, a cone from the joint drew a round disc)
471
- m.cone({ tag: 'upperarm', bone: 'humerus' + S, group: g, a: lerp(Sh, E, 0.25), b: E, ra: 0.036 * lk, rb: 0.032 * lk, k: 0.04 });
472
- ellY({ tag: 'triceps', bone: 'humerus' + S, group: g, c: add(lerp(Sh, E, 0.55), sx([0.005, 0.01, -0.045])), ydir: sub(E, Sh), lateral: lat, r: [0.03 * lk, 0.07, 0.048], k: 0.05 });
473
- m.sphere({ tag: 'olecranon', bone: 'radius' + S, group: g, c: add(E, [0, 0.012, -0.034]), rad: 0.024, k: 0.025 });
474
- m.cone({ tag: 'forearm', bone: 'radius' + S, group: g, a: add(E, [0, 0, -0.005]), b: W, ra: 0.034 * lk, rb: 0.019, k: 0.025 });
475
- ellY({ tag: 'forearmmuscle', bone: 'radius' + S, group: g, c: add(lerp(E, W, 0.25), sx([0.004, 0, 0.006])), ydir: sub(W, E), lateral: lat, r: [0.032 * lk, 0.075, 0.035 * lk], k: 0.025 });
476
- m.cone({ tag: 'forearmweb', bone: 'radius' + S, group: g, a: add(E, sx([-0.02, 0.03, -0.01])), b: add(lerp(E, W, 0.3), sx([-0.01, 0, 0])), ra: 0.028, rb: 0.02, k: 0.03 });
477
- // knee (carpus, with its callus in front)
478
- ellY({ tag: 'knee', bone: 'metacarpus' + S, group: g, c: add(W, [0, 0.0, 0.002]), ydir: [0, 1, 0], lateral: lat, r: [0.021, 0.026, 0.021], k: 0.012 });
479
- m.cone({ tag: 'cannon', bone: 'metacarpus' + S, group: g, a: add(W, [0, -0.01, 0.0]), b: add(M, [0, 0.01, 0.0]), ra: 0.0145, rb: 0.0135, k: 0.01 });
480
- m.cone({ tag: 'tendon', bone: 'metacarpus' + S, group: g, a: add(W, [0, -0.015, -0.012]), b: add(M, [0, 0.015, -0.013]), ra: 0.009, rb: 0.011, k: 0.01 });
481
- ellY({ tag: 'fetlock', bone: 'metacarpus' + S, group: g, c: add(M, [0, 0.0, -0.004]), ydir: [0, 1, 0.3], lateral: lat, r: [0.019, 0.02, 0.021], k: 0.01 });
482
- sculptDewclaws(m, 'fpaw' + S, g, M, s);
483
- m.cone({ tag: 'pastern', bone: 'fpaw' + S, group: g, a: M, b: C, ra: 0.0155, rb: 0.0165, k: 0.01 });
484
- sculptHoof(m, 'fhoof' + S, g, C, T, 1.0, s);
485
-
486
- const h = 'H' + S;
487
- 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];
488
- ellY({ tag: 'thigh', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.42), sx([0.02, 0, -0.025])), ydir: sub(K, Hp), lateral: lat, r: [0.032 * lk, 0.13, 0.095], k: 0.06 });
489
- m.cone({ tag: 'thighfront', bone: 'femur' + S, group: h, a: sx([0.075, 0.66, -0.3 + HQ]), b: add(K, sx([0.0, 0.03, 0.02])), ra: 0.048, rb: 0.03, k: 0.05 });
490
- m.cone({ tag: 'hamstring', bone: 'femur' + S, group: h, a: sx([0.045, 0.64, -0.345]), b: add(lerp(K, Hk, 0.3), [0, 0, -0.04]), ra: 0.045, rb: 0.026, k: 0.04 });
491
- ellY({ tag: 'flankfold', bone: 'femur' + S, group: h, c: sx([0.09, 0.47, -0.2 + HQ]), ydir: [-0.1, 0.3, 0.12], lateral: lat, r: [0.024, 0.07, 0.038], k: 0.05 });
492
- m.sphere({ tag: 'stifle', bone: 'tibia' + S, group: h, c: add(K, sx([0.004, 0.005, 0.014])), rad: 0.027, k: 0.035 });
493
- // (a broad gaskin, side2: the lower thigh read thin next to the barrel)
494
- ellY({ tag: 'gaskin', bone: 'tibia' + S, group: h, c: add(lerp(K, Hk, 0.3), sx([0.004, 0, -0.024])), ydir: sub(Hk, K), lateral: lat, r: [0.037 * lk, 0.085, 0.048], k: 0.03 });
495
- m.cone({ tag: 'shin', bone: 'tibia' + S, group: h, a: lerp(K, Hk, 0.1), b: Hk, ra: 0.026, rb: 0.018, k: 0.025 });
496
- m.cone({ tag: 'achilles', bone: 'tibia' + S, group: h, a: add(lerp(K, Hk, 0.45), [0, 0, -0.035]), b: add(Hk, [0, 0.03, -0.03]), ra: 0.012, rb: 0.01, k: 0.015 });
497
- m.sphere({ tag: 'calcaneus', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.028, -0.028]), rad: 0.014, k: 0.012 });
498
- ellY({ tag: 'hock', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.004, -0.002]), ydir: [0, 1, 0.25], lateral: lat, r: [0.02, 0.03, 0.022], k: 0.015 });
499
- m.cone({ tag: 'cannon', bone: 'metatarsus' + S, group: h, a: add(Hk, [0, -0.016, 0.002]), b: add(Mt, [0, 0.01, 0.0]), ra: 0.015, rb: 0.0135, k: 0.01 });
500
- m.cone({ tag: 'tendon', bone: 'metatarsus' + S, group: h, a: add(Hk, [0, -0.016, -0.013]), b: add(Mt, [0, 0.015, -0.013]), ra: 0.009, rb: 0.011, k: 0.01 });
501
- ellY({ tag: 'fetlock', bone: 'metatarsus' + S, group: h, c: add(Mt, [0, 0.0, -0.004]), ydir: [0, 1, 0.3], lateral: lat, r: [0.019, 0.02, 0.021], k: 0.01 });
502
- sculptDewclaws(m, 'hpaw' + S, h, Mt, s);
503
- m.cone({ tag: 'pastern', bone: 'hpaw' + S, group: h, a: Mt, b: Ch, ra: 0.015, rb: 0.016, k: 0.01 });
504
- sculptHoof(m, 'hhoof' + S, h, Ch, Tt, 0.95, s);
505
- }
506
-
507
- // ---------------------------------------------------------------- TAIL: short, flat, triangular, carried up
508
- // a thin tail with a flat brush of hair that widens to the tip (side2, walk1)
509
- // (the hair pieces overlap along the tail and blend over 2 cm: five short separate ones left knobs between
510
- // them, a caterpillar)
511
- for (let i = 0; i < TAIL_SEGS; i++) {
512
- const t0 = i / TAIL_SEGS, t1 = (i + 1) / TAIL_SEGS;
513
- const a = J['tail' + i], b = J['tail' + (i + 1)];
514
- const w0 = 0.024 - 0.016 * t0, w1 = 0.024 - 0.016 * t1;
515
- m.cone({ tag: i === 0 ? 'tailhead' : 'tail', bone: 'tail' + i, a, b, ra: w0 * 0.72, rb: w1 * 0.72, k: i === 0 ? 0.03 : 0.012 });
516
- const d = norm(sub(b, a)), tm = (t0 + t1) * 0.5, L = Math.hypot(...sub(b, a));
517
- m.ell({ tag: 'tailhair', bone: 'tail' + i, c: lerp(a, b, 0.55), r: [0.015 + 0.01 * tm, 0.0095 + 0.002 * tm, L * 0.95], axis: d, up: norm(cross(d, X)), k: 0.02 });
518
- }
519
- void kid;
520
- return m;
521
- }
522
-
523
- // Dewclaws: two small horny knobs behind the fetlock
524
- function sculptDewclaws(m, bone, group, M, s) {
525
- for (const k of [1, -1]) m.sphere({ tag: 'dewclaw', bone, group, c: add(M, [0.009 * k * s, -0.006, -0.021]), rad: 0.0055, k: 0.004 });
526
- }
527
-
528
- // Cloven hoof: two claws (digits III and IV) with a cleft between them, the front wall parallel to
529
- // the pastern, heel bulbs behind, flat on the ground.
530
- function sculptHoof(m, bone, group, C, T, w, s) {
531
- const zc = (C[2] + T[2]) * 0.5;
532
- for (const k of [1, -1]) {
533
- const x = C[0] + 0.0105 * k * w;
534
- const top = [x, C[1] + 0.008, C[2] - 0.004];
535
- const toe = [x - 0.002 * k, 0.006, T[2] - 0.004];
536
- const heel = [x, 0.008, C[2] - 0.016];
537
- m.cone({ tag: 'hoof', bone, group, a: top, b: toe, ra: 0.0115 * w, rb: 0.0055 * w, k: 0.006 });
538
- m.cone({ tag: 'hoof', bone, group, a: heel, b: [toe[0], 0.006, zc + 0.004], ra: 0.011 * w, rb: 0.009 * w, k: 0.008 });
539
- m.sphere({ tag: 'heelbulb', bone, group, c: [x, 0.013, C[2] - 0.02], rad: 0.0105 * w, k: 0.008 });
540
- }
541
- m.cone({ tag: 'coronet', bone, group, a: add(C, [0, 0.012, -0.01]), b: add(C, [0, 0.006, 0.008]), ra: 0.017 * w, rb: 0.017 * w, k: 0.008 });
542
- // the cleft between the claws, open at the toe
543
- m.ell({ tag: 'cleft', bone, group, c: [C[0], 0.006, zc + 0.012], r: [0.0022, 0.02, 0.03], k: 0.002, carve: true });
544
- // flat sole
545
- m.ell({ tag: 'sole', bone, group, c: [C[0], -0.1 + 0.001, zc], r: [0.1, 0.1, 0.1], k: 0.002, carve: true });
546
- void s;
547
- }