@bitefoundry/retrieviewer 0.0.0-stage → 0.0.1

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 (292) hide show
  1. package/ASSET-PROVENANCE.md +43 -0
  2. package/ASSETS-LICENSE.md +58 -0
  3. package/ATTRIBUTION.md +54 -0
  4. package/EMBEDDING.md +96 -0
  5. package/INSTALLING.md +107 -0
  6. package/LICENSE +16 -0
  7. package/LICENSE-CODE +21 -0
  8. package/PLUGIN.md +110 -0
  9. package/PUPPY-MEMORY.md +57 -0
  10. package/PUPPY-Q5-COMPARISON.md +73 -0
  11. package/README.md +61 -2
  12. package/RELEASING.md +61 -0
  13. package/THIRD-PARTY-NOTICES.md +17 -0
  14. package/TRADEMARKS.md +21 -0
  15. package/braille.mjs +45 -0
  16. package/examples/bridge.py +62 -0
  17. package/examples/embed.mjs +15 -0
  18. package/examples/live.mjs +40 -0
  19. package/examples/plugin.mjs +21 -0
  20. package/integrations/opencode/runtime.mjs +195 -0
  21. package/integrations/opencode/tui.tsx +59 -0
  22. package/locomotion-core.mjs +21 -0
  23. package/locomotion-puppy.animal +0 -0
  24. package/package.json +125 -4
  25. package/pet-q5-manifest.json +118 -0
  26. package/puppy-clean.mjs +51 -0
  27. package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
  28. package/puppy-companion-interactions-v3.json +1 -0
  29. package/puppy-companion-life.mjs +182 -0
  30. package/puppy-companion-objects.mjs +30 -0
  31. package/puppy-companion-sprites.mjs +14 -0
  32. package/puppy-continuous.mjs +57 -0
  33. package/puppy-face.mjs +46 -0
  34. package/puppy-frame-predictive.mjs +147 -0
  35. package/puppy-frame-store.mjs +108 -0
  36. package/puppy-mouth-anchor.json +1 -0
  37. package/puppy-mouth-anchor.mjs +33 -0
  38. package/puppy-paper-actions-v9-q5.bin.gz +0 -0
  39. package/puppy-paper-actions-v9.json +1 -0
  40. package/puppy-paper-actions.mjs +48 -0
  41. package/puppy-paper-approach-q5.bin.gz +0 -0
  42. package/puppy-paper-approach.json +1 -0
  43. package/puppy-paper-area.mjs +15 -0
  44. package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
  45. package/puppy-paper-arrival-seat.json +1 -0
  46. package/puppy-paper-behaviors-q5.bin.gz +0 -0
  47. package/puppy-paper-behaviors.json +1 -0
  48. package/puppy-paper-behaviors.mjs +8 -0
  49. package/puppy-paper-contact.mjs +103 -0
  50. package/puppy-paper-director.mjs +21 -0
  51. package/puppy-paper-paw-transport.mjs +76 -0
  52. package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
  53. package/puppy-paper-rear-gallop.json +1 -0
  54. package/puppy-paper-seat-q5.bin.gz +0 -0
  55. package/puppy-paper-seat.json +14 -0
  56. package/puppy-paper-seat.mjs +13 -0
  57. package/puppy-paper-trick-timing.mjs +58 -0
  58. package/puppy-paper-world.mjs +108 -0
  59. package/puppy-paper.mjs +271 -0
  60. package/puppy-stable-meta.json +1606 -0
  61. package/puppy-stable.mjs +72 -0
  62. package/puppy-stride-meta.json +343 -0
  63. package/puppy-stride-tweens-q5.bin.gz +0 -0
  64. package/puppy-zoomies.mjs +27 -0
  65. package/puppy-zoomies30.mjs +12 -0
  66. package/retrieviewer-cli.mjs +81 -0
  67. package/retrieviewer-config.mjs +49 -0
  68. package/retrieviewer-install.mjs +157 -0
  69. package/retrieviewer-plugin.d.mts +44 -0
  70. package/retrieviewer-plugin.mjs +97 -0
  71. package/retrieviewer-stdio.mjs +38 -0
  72. package/retrieviewer.d.mts +85 -0
  73. package/retrieviewer.mjs +142 -0
  74. package/vendor/procedural-animals/LICENSE +21 -0
  75. package/vendor/procedural-animals/package.json +40 -0
  76. package/vendor/procedural-animals/src/animal.js +146 -0
  77. package/vendor/procedural-animals/src/bake.js +51 -0
  78. package/vendor/procedural-animals/src/build.js +81 -0
  79. package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
  80. package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
  81. package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
  82. package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
  83. package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
  84. package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
  85. package/vendor/procedural-animals/src/core/build/seams.js +183 -0
  86. package/vendor/procedural-animals/src/core/build/warp.js +112 -0
  87. package/vendor/procedural-animals/src/core/build/weights.js +393 -0
  88. package/vendor/procedural-animals/src/core/math/vec.js +81 -0
  89. package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
  90. package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
  91. package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
  92. package/vendor/procedural-animals/src/core/motion/common.js +158 -0
  93. package/vendor/procedural-animals/src/core/motion/index.js +22 -0
  94. package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
  95. package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
  96. package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
  97. package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
  98. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
  99. package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
  100. package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
  101. package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
  102. package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
  103. package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
  104. package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
  105. package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
  106. package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
  107. package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
  108. package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
  109. package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
  110. package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
  111. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
  112. package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
  113. package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
  114. package/vendor/procedural-animals/src/index.d.ts +98 -0
  115. package/vendor/procedural-animals/src/index.js +29 -0
  116. package/vendor/procedural-animals/src/registry.js +48 -0
  117. package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
  118. package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
  119. package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
  120. package/vendor/procedural-animals/src/species/bear/index.js +139 -0
  121. package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
  122. package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
  123. package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
  124. package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
  125. package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
  126. package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
  127. package/vendor/procedural-animals/src/species/boar/index.js +110 -0
  128. package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
  129. package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
  130. package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
  131. package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
  132. package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
  133. package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
  134. package/vendor/procedural-animals/src/species/cat/index.js +211 -0
  135. package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
  136. package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
  137. package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
  138. package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
  139. package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
  140. package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
  141. package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
  142. package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
  143. package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
  144. package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
  145. package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
  146. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
  147. package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
  148. package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
  149. package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
  150. package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
  151. package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
  152. package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
  153. package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
  154. package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
  155. package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
  156. package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
  157. package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
  158. package/vendor/procedural-animals/src/species/cow/index.js +118 -0
  159. package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
  160. package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
  161. package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
  162. package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
  163. package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
  164. package/vendor/procedural-animals/src/species/crow/index.js +70 -0
  165. package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
  166. package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
  167. package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
  168. package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
  169. package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
  170. package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
  171. package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
  172. package/vendor/procedural-animals/src/species/deer/index.js +106 -0
  173. package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
  174. package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
  175. package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
  176. package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
  177. package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
  178. package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
  179. package/vendor/procedural-animals/src/species/dog/index.js +144 -0
  180. package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
  181. package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
  182. package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
  183. package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
  184. package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
  185. package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
  186. package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
  187. package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
  188. package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
  189. package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
  190. package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
  191. package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
  192. package/vendor/procedural-animals/src/species/fish/index.js +88 -0
  193. package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
  194. package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
  195. package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
  196. package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
  197. package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
  198. package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
  199. package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
  200. package/vendor/procedural-animals/src/species/fox/index.js +120 -0
  201. package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
  202. package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
  203. package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
  204. package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
  205. package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
  206. package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
  207. package/vendor/procedural-animals/src/species/frog/index.js +97 -0
  208. package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
  209. package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
  210. package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
  211. package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
  212. package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
  213. package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
  214. package/vendor/procedural-animals/src/species/goat/index.js +149 -0
  215. package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
  216. package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
  217. package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
  218. package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
  219. package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
  220. package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
  221. package/vendor/procedural-animals/src/species/horse/index.js +80 -0
  222. package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
  223. package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
  224. package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
  225. package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
  226. package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
  227. package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
  228. package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
  229. package/vendor/procedural-animals/src/species/lion/index.js +111 -0
  230. package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
  231. package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
  232. package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
  233. package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
  234. package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
  235. package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
  236. package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
  237. package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
  238. package/vendor/procedural-animals/src/species/pig/index.js +161 -0
  239. package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
  240. package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
  241. package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
  242. package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
  243. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
  244. package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
  245. package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
  246. package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
  247. package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
  248. package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
  249. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
  250. package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
  251. package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
  252. package/vendor/procedural-animals/src/species/rat/index.js +111 -0
  253. package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
  254. package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
  255. package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
  256. package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
  257. package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
  258. package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
  259. package/vendor/procedural-animals/src/species/shark/index.js +104 -0
  260. package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
  261. package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
  262. package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
  263. package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
  264. package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
  265. package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
  266. package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
  267. package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
  268. package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
  269. package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
  270. package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
  271. package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
  272. package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
  273. package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
  274. package/vendor/procedural-animals/src/species/snake/index.js +73 -0
  275. package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
  276. package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
  277. package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
  278. package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
  279. package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
  280. package/vendor/procedural-animals/src/species/spider/index.js +78 -0
  281. package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
  282. package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
  283. package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
  284. package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
  285. package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
  286. package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
  287. package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
  288. package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
  289. package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
  290. package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
  291. package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
  292. package/vendor/procedural-animals/src/worker.js +29 -0
package/package.json CHANGED
@@ -1,6 +1,127 @@
1
1
  {
2
2
  "name": "@bitefoundry/retrieviewer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.0.1",
4
+ "description": "Embeddable autonomous Braille retriever for TUI panes; Q5 art included, host-owned input and screen.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "homepage": "https://github.com/BiteFoundry",
7
+ "keywords": ["tui", "braille", "pet", "retriever", "opencode"],
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org/",
11
+ "tag": "latest"
12
+ },
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "private": false,
17
+ "type": "module",
18
+ "types": "./retrieviewer.d.mts",
19
+ "bin": {
20
+ "retrieviewer": "retrieviewer-cli.mjs",
21
+ "retrieviewer-stdio": "retrieviewer-stdio.mjs"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "types": "./retrieviewer.d.mts",
26
+ "default": "./retrieviewer.mjs"
27
+ },
28
+ "./plugin": {
29
+ "types": "./retrieviewer-plugin.d.mts",
30
+ "default": "./retrieviewer-plugin.mjs"
31
+ },
32
+ "./tui": "./integrations/opencode/tui.tsx",
33
+ "./opencode/tui": "./integrations/opencode/tui.tsx",
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "LICENSE",
38
+ "LICENSE-CODE",
39
+ "ASSETS-LICENSE.md",
40
+ "ATTRIBUTION.md",
41
+ "ASSET-PROVENANCE.md",
42
+ "THIRD-PARTY-NOTICES.md",
43
+ "TRADEMARKS.md",
44
+ "RELEASING.md",
45
+ "retrieviewer.mjs",
46
+ "retrieviewer.d.mts",
47
+ "retrieviewer-stdio.mjs",
48
+ "retrieviewer-plugin.mjs",
49
+ "retrieviewer-plugin.d.mts",
50
+ "retrieviewer-cli.mjs",
51
+ "retrieviewer-install.mjs",
52
+ "retrieviewer-config.mjs",
53
+ "integrations/opencode/tui.tsx",
54
+ "integrations/opencode/runtime.mjs",
55
+ "INSTALLING.md",
56
+ "PLUGIN.md",
57
+ "EMBEDDING.md",
58
+ "PUPPY-MEMORY.md",
59
+ "PUPPY-Q5-COMPARISON.md",
60
+ "examples/embed.mjs",
61
+ "examples/live.mjs",
62
+ "examples/plugin.mjs",
63
+ "examples/bridge.py",
64
+ "locomotion-core.mjs",
65
+ "locomotion-puppy.animal",
66
+ "braille.mjs",
67
+ "puppy-frame-store.mjs",
68
+ "puppy-frame-predictive.mjs",
69
+ "pet-q5-manifest.json",
70
+ "puppy-paper.mjs",
71
+ "puppy-paper-world.mjs",
72
+ "puppy-paper-contact.mjs",
73
+ "puppy-paper-seat.mjs",
74
+ "puppy-paper-director.mjs",
75
+ "puppy-paper-behaviors.mjs",
76
+ "puppy-paper-area.mjs",
77
+ "puppy-paper-actions.mjs",
78
+ "puppy-paper-trick-timing.mjs",
79
+ "puppy-paper-paw-transport.mjs",
80
+ "puppy-companion-life.mjs",
81
+ "puppy-companion-objects.mjs",
82
+ "puppy-companion-sprites.mjs",
83
+ "puppy-mouth-anchor.mjs",
84
+ "puppy-mouth-anchor.json",
85
+ "puppy-clean.mjs",
86
+ "puppy-stable.mjs",
87
+ "puppy-face.mjs",
88
+ "puppy-continuous.mjs",
89
+ "puppy-zoomies.mjs",
90
+ "puppy-zoomies30.mjs",
91
+ "puppy-stride-tweens-q5.bin.gz",
92
+ "puppy-stride-meta.json",
93
+ "puppy-stable-meta.json",
94
+ "puppy-paper-rear-gallop-q5.bin.gz",
95
+ "puppy-paper-rear-gallop.json",
96
+ "puppy-paper-approach-q5.bin.gz",
97
+ "puppy-paper-approach.json",
98
+ "puppy-paper-seat-q5.bin.gz",
99
+ "puppy-paper-seat.json",
100
+ "puppy-paper-arrival-seat-q5.bin.gz",
101
+ "puppy-paper-arrival-seat.json",
102
+ "puppy-paper-behaviors-q5.bin.gz",
103
+ "puppy-paper-behaviors.json",
104
+ "puppy-paper-actions-v9-q5.bin.gz",
105
+ "puppy-paper-actions-v9.json",
106
+ "puppy-companion-interactions-v3-q5.bin.gz",
107
+ "puppy-companion-interactions-v3.json",
108
+ "vendor/procedural-animals/src",
109
+ "vendor/procedural-animals/LICENSE",
110
+ "vendor/procedural-animals/package.json"
111
+ ],
112
+ "scripts": {
113
+ "start": "node server.mjs",
114
+ "terminal": "node terminal.mjs",
115
+ "pet": "node puppy-terminal.mjs",
116
+ "pet:companion": "node examples/live.mjs",
117
+ "test": "node --test \"*.test.mjs\"",
118
+ "test:locomotion": "node check-locomotion.mjs",
119
+ "test:package": "node check-pet-package.mjs",
120
+ "release:check": "node tools/check-release.mjs",
121
+ "prepublishOnly": "npm run release:check"
122
+ },
123
+ "dependencies": {
124
+ "jsonc-parser": "3.3.1",
125
+ "three": "0.186.1"
126
+ }
127
+ }
@@ -0,0 +1,118 @@
1
+ {
2
+ "version": 1,
3
+ "levels": 32,
4
+ "entries": {
5
+ "puppy-stride-tweens": {
6
+ "file": "puppy-stride-tweens-q5.bin.gz",
7
+ "width": 320,
8
+ "height": 320,
9
+ "frames": 799,
10
+ "uniqueFrames": 683,
11
+ "sourceSha256": "15e8ef49947cec4a1dcb3393d43a2ad48125d1bcd30f01275087c3df61627392",
12
+ "sourcePackedSha256": "439c8d235492ebb1e11cdb2f4cb1078b7840f14c0586c15544cb984d06d1047b",
13
+ "encodedBytes": 44585401,
14
+ "gzipBytes": 7564304,
15
+ "residentPixelBytes": 27752723,
16
+ "encodedSha256": "eecf92fd402843bef43aa4b154db6eba999959dd16cd8e34c54e42c88eaade10",
17
+ "quantizedSha256": "a74614dc210b245e5f8a4dba6a8cfb9b11b8da8dba9e2537fab53458ba27f02c"
18
+ },
19
+ "puppy-paper-rear-gallop": {
20
+ "file": "puppy-paper-rear-gallop-q5.bin.gz",
21
+ "width": 320,
22
+ "height": 320,
23
+ "frames": 162,
24
+ "uniqueFrames": 143,
25
+ "sourceSha256": "75972598be42314b92e64487106e3103ad477f897705dda869fc0b10cf68e256",
26
+ "sourcePackedSha256": "0c2d70643ef5807f1fa3e489abd65e0e9871041a8b45ea1027f94439601a3677",
27
+ "encodedBytes": 8770545,
28
+ "gzipBytes": 1468318,
29
+ "residentPixelBytes": 5456694,
30
+ "encodedSha256": "fe96e31d30348cc7d1fc674f40778d2312c9d1b40c57e634dcc397a35cc5ccf4",
31
+ "quantizedSha256": "a072f3a3f729f96556767b93d4921f76e32bc517aa06fb50f87fcb27fd05396c"
32
+ },
33
+ "puppy-paper-approach": {
34
+ "file": "puppy-paper-approach-q5.bin.gz",
35
+ "width": 320,
36
+ "height": 320,
37
+ "frames": 162,
38
+ "uniqueFrames": 139,
39
+ "sourceSha256": "f5558c57d5a445d396e06c060f9f8d64e6ce0ff5b9b92fa6d77ef9bb2984a2fe",
40
+ "sourcePackedSha256": "53bd77c5d06012b9b99884d2b38c499f0bcb9a42007736ef3d1cfffaba3c6ebf",
41
+ "encodedBytes": 7089140,
42
+ "gzipBytes": 1442754,
43
+ "residentPixelBytes": 4407560,
44
+ "encodedSha256": "6f57a5f506eabea41c00f02738c8124a4ae8e05471bdcd76e0a6e9ed739ca1a3",
45
+ "quantizedSha256": "c6e12fc09a7c314e65f7a115afb1e9cb865069cdd321c6f5483a92aa69517024"
46
+ },
47
+ "puppy-paper-seat": {
48
+ "file": "puppy-paper-seat-q5.bin.gz",
49
+ "width": 320,
50
+ "height": 320,
51
+ "frames": 51,
52
+ "uniqueFrames": 49,
53
+ "sourceSha256": "613dbeeeaec11804e80db0216922c8f62294745514ee6283bdce61a6d784dd5e",
54
+ "sourcePackedSha256": "2c34d1a34323e48e4ec73f60613ddacab9ff3814851275663fe573b00469e485",
55
+ "encodedBytes": 3326771,
56
+ "gzipBytes": 478093,
57
+ "residentPixelBytes": 2070312,
58
+ "encodedSha256": "b21aa11039b314ed3dec96818e571d54b6102bd0bc1f76e0a99ceb6d2b5ae097",
59
+ "quantizedSha256": "be0876275d124005ecb758b953f3a553a7f2748f5e3fd522612059f6fd81149a"
60
+ },
61
+ "puppy-paper-arrival-seat": {
62
+ "file": "puppy-paper-arrival-seat-q5.bin.gz",
63
+ "width": 320,
64
+ "height": 320,
65
+ "frames": 51,
66
+ "uniqueFrames": 49,
67
+ "sourceSha256": "13acb62fd573ff7ef97bcf59be80757897fe9d1784f187ab278990ff94a643b9",
68
+ "sourcePackedSha256": "014ee6e77802de49e9bcb77666867619b03688fd1a57040701904385a35dcd16",
69
+ "encodedBytes": 3332894,
70
+ "gzipBytes": 489164,
71
+ "residentPixelBytes": 2074316,
72
+ "encodedSha256": "3b2ef9e322e625340c42a549f17165492cc14f724f4593bf298f67195677bb6e",
73
+ "quantizedSha256": "77eb55a8922fda7f9a39c2b247eeb7098a3bacbf4c38d4652e7686650d9396aa"
74
+ },
75
+ "puppy-paper-behaviors": {
76
+ "file": "puppy-paper-behaviors-q5.bin.gz",
77
+ "width": 320,
78
+ "height": 320,
79
+ "frames": 162,
80
+ "uniqueFrames": 142,
81
+ "sourceSha256": "7918261fb64c57a91739729841fd84c6dd53891196c7fb76ee9c27712ab6ef5d",
82
+ "sourcePackedSha256": "7785ef65b8a2a5bf6f7ce600fe9957a60d9af82066ff49275bba061eac5392d1",
83
+ "encodedBytes": 7031343,
84
+ "gzipBytes": 1450156,
85
+ "residentPixelBytes": 4373034,
86
+ "encodedSha256": "38cfdfcee9c857c56a14f0a9734cb3301b4a04cb8e1ae3fb2e2b2ffe3666240f",
87
+ "quantizedSha256": "3c8f40416d2fff1c1031d3ad8917ea885998a786126d5973dfee4102893a0a53"
88
+ },
89
+ "puppy-paper-actions-v9": {
90
+ "file": "puppy-paper-actions-v9-q5.bin.gz",
91
+ "width": 480,
92
+ "height": 480,
93
+ "frames": 594,
94
+ "uniqueFrames": 474,
95
+ "sourceSha256": "1e0db7138d087292a3e70c246a1d56af590b3fb50928687a026e7080d7657c38",
96
+ "sourcePackedSha256": "ce5426d2fb9b2bb5d20b4f206215252954f9cc20d830d309a2d74ed441e37f02",
97
+ "encodedBytes": 24733003,
98
+ "gzipBytes": 4614142,
99
+ "residentPixelBytes": 15388998,
100
+ "encodedSha256": "615fb04961a38e592a661a20ad50fad45e1e4205e34198fa87a155ace966ea77",
101
+ "quantizedSha256": "fd1f23fe5dbd79bcf3cb9e060b9adfa0948cd4423e4b19e5810671e42ee952be"
102
+ },
103
+ "puppy-companion-interactions-v3": {
104
+ "file": "puppy-companion-interactions-v3-q5.bin.gz",
105
+ "width": 320,
106
+ "height": 320,
107
+ "frames": 360,
108
+ "uniqueFrames": 124,
109
+ "sourceSha256": "8438ee6cfc52b28f8e327bf37453c8e5ad7d568b2569cd4c5ed1e051d7026905",
110
+ "sourcePackedSha256": "deb8a3618451087fdae102f16ef7528c79c91b249844db1bfa9f9399c5355051",
111
+ "encodedBytes": 8023311,
112
+ "gzipBytes": 1150175,
113
+ "residentPixelBytes": 4992197,
114
+ "encodedSha256": "5ebfc58c9cfeaf349d30bc65be2e4663f7a3b8be9f35ca5d6da323172624de7a",
115
+ "quantizedSha256": "ebf86e2671234feb46a0933bb9c6d367c353d1b38a5bbf01e07c7b7f6183c1ea"
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,51 @@
1
+ import {stableAt,groundOffset,createStableZoomies} from './puppy-stable.mjs';
2
+ import {createFaceMotion} from './puppy-face.mjs';
3
+ import {grayFrameReader,isGrayFrameSource} from './puppy-frame-store.mjs';
4
+ export const CLEAN_BYTES=47*17*320*320;
5
+ export const STRIDE_TEMPO=.72;
6
+ export function retimeRun(phase,knots){
7
+ if(!knots)return phase;
8
+ const count=knots.length-1;
9
+ if(count<2||count>20||knots[0]!==0||knots[count]!==20||knots.some((x,i)=>!Number.isFinite(x)||(i&&x<=knots[i-1])))throw Error('Invalid gait timing');
10
+ const tangent=i=>{i%=count;const before=i?knots[i]-knots[i-1]:20-knots[count-1],after=knots[i+1]-knots[i],w1=2*after+before,w2=after+2*before;return (w1+w2)/(w1*before+w2*after);};
11
+ let index=0;while(index<count-1&&phase>knots[index+1])index++;
12
+ const h=knots[index+1]-knots[index],p=Math.max(0,Math.min(1,(phase-knots[index])/h)),p2=p*p,p3=p2*p;
13
+ return (2*p3-3*p2+1)*index+(p3-2*p2+p)*h*tangent(index)+(-2*p3+3*p2)*(index+1)+(p3-p2)*h*tangent(index+1);
14
+ }
15
+ export function cleanAt(seconds,options={}){
16
+ const state=stableAt(seconds,{...options,tempo:options.tempo??(options.sequence?STRIDE_TEMPO:.6),quickLaunch:options.quickLaunch??!!options.sequence,easedTurns:true,rampRest:true});
17
+ if(state.state==='run'){
18
+ const phase=retimeRun(state.pair+state.blend,options.timing),index=Math.floor(phase),count=options.sequence?.length??20;
19
+ state.pair=index%count;state.pose=options.sequence?.[state.pair]??state.pair;state.blend=phase-index;
20
+ }
21
+ return state;
22
+ }
23
+ export function cleanGroundOffset(state,meta){
24
+ if(state.state==='run'&&meta.run_sequence){
25
+ const seq=meta.run_sequence,n=seq.length,i=state.pair,p=state.blend,v=meta.ground_offsets;
26
+ const a=v[seq[(i+n-1)%n]],b=v[seq[i]],c=v[seq[(i+1)%n]],d=v[seq[(i+2)%n]];
27
+ return .5*(2*b+(-a+c)*p+(2*a-5*b+4*c-d)*p*p+(-a+3*b-3*c+d)*p*p*p);
28
+ }
29
+ if(state.pair<30||state.pair>40)return groundOffset(state,meta);
30
+ const i=state.pair,p=state.blend,v=meta.ground_offsets;
31
+ const a=v[Math.max(30,i-1)],b=v[i],c=v[i+1],d=v[Math.min(41,i+2)];
32
+ return .5*(2*b+(-a+c)*p+(2*a-5*b+4*c-d)*p*p+(-a+3*b-3*c+d)*p*p*p);
33
+ }
34
+ export function createCachedTween(cache,info){
35
+ if(!isGrayFrameSource(cache)||cache.byteLength!==CLEAN_BYTES||info.pairs!==47||info.samples!==17||info.width!==320||info.height!==320||info.channels!==1||info.bytes!==CLEAN_BYTES)throw Error('Invalid baked tween cache');
36
+ const frames=grayFrameReader(cache,320,320,47*17),output=new Uint8ClampedArray(320*320*4),stride=320*320;
37
+ for(let i=3;i<output.length;i+=4)output[i]=255;
38
+ return state=>{
39
+ if(!Number.isInteger(state.pair)||state.pair<0||state.pair>=47||!Number.isFinite(state.blend))throw Error('Invalid baked tween state');
40
+ const phase=Math.max(0,Math.min(1,state.blend))*16,index=Math.min(15,Math.floor(phase)),alpha=phase-index;
41
+ const a=frames.getFrame(state.pair*17+index),b=frames.getFrame(state.pair*17+index+1);
42
+ for(let i=0,j=0;i<stride;i++,j+=4){const value=a[i]*(1-alpha)+b[i]*alpha;output[j]=output[j+1]=output[j+2]=value;}
43
+ return output;
44
+ };
45
+ }
46
+ export function createCleanZoomies(atlas,cache,meta,cacheInfo,width,height,{secondaryMotion=true,quickLaunch=!!cacheInfo.run_sequence,sampleState=cleanAt,frameTransform,groundAt=cleanGroundOffset}={}){
47
+ const renderMeta=cacheInfo.pair_map?{...meta,pairs:cacheInfo.pair_map,run_sequence:cacheInfo.run_sequence}:meta;
48
+ if(cacheInfo.run_sequence){const seq=cacheInfo.run_sequence;if(seq.some((v,i)=>!Number.isInteger(v)||v<0||v>=20||renderMeta.pairs[i][0]!==v||renderMeta.pairs[i][1]!==seq[(i+1)%seq.length]))throw Error('Invalid curated gait');}
49
+ const tween=createCachedTween(cache,cacheInfo),face=secondaryMotion&&cacheInfo.run_sequence?createFaceMotion():null;
50
+ return createStableZoomies(atlas,null,renderMeta,width,height,{sampleState:(s,o)=>sampleState(s,{...o,quickLaunch,timing:cacheInfo.run_timing,sequence:cacheInfo.run_sequence}),frameProvider:state=>{const image=tween(state),moved=face?face(image,state):image;return frameTransform?frameTransform(moved,state):moved;},groundAt});
51
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"width":320,"height":320,"samples":9,"pairs":40,"bytes":36864000,"sha256":"8438ee6cfc52b28f8e327bf37453c8e5ad7d568b2569cd4c5ed1e051d7026905","clips":{"treat-eat":[30,31,32,33,34,35,36,37,38,39],"ball-pickup":[13,14,15,16,17,18,19,20,21,22],"ball-drop":[23,24,25,26,27,28,29]},"edges":[[0,2],[2,3],[3,4],[4,5],[5,4],[5,6],[6,0],[0,7],[7,8],[8,9],[9,10],[10,11],[11,0],[0,1],[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9],[9,10],[10,11],[11,12],[12,13],[13,14],[14,15],[15,16],[16,0]],"ballSourceKeys":{"13":[0,1],"14":[1,2],"15":[2,3],"16":[3,4],"17":[4,5],"18":[5,6],"19":[6,7],"20":[7,8],"21":[8,9],"22":[9,10],"23":[10,11],"24":[11,12],"25":[12,13],"26":[13,14],"27":[14,15],"28":[15,16],"29":[16,0]},"sourceSequences":{"ball-pickup":[0,1,2,3,4,5,6,7,8,9,10],"ball-drop":[10,11,12,13,14,15,16,0]},"scale":0.7870722433460076,"registration":[{"cell":0,"matrix":[[0.7870722433460076,0.0,13.04942965779469],[0.0,0.7870722433460076,75.68441064638785]]},{"cell":1,"matrix":[[0.7870722433460076,0.0,-232.12357414448667],[0.0,0.7870722433460076,75.68441064638785]]},{"cell":2,"matrix":[[0.7870722433460076,0.0,-475.32889733840295],[0.0,0.7870722433460076,75.68441064638785]]},{"cell":3,"matrix":[[0.7870722433460076,0.0,-718.5342205323193],[0.0,0.7870722433460076,74.89733840304183]]},{"cell":4,"matrix":[[0.7870722433460076,0.0,10.688212927756666],[0.0,0.7870722433460076,-162.01140684410643]]},{"cell":5,"matrix":[[0.7870722433460076,0.0,-232.12357414448667],[0.0,0.7870722433460076,-162.01140684410643]]},{"cell":6,"matrix":[[0.7870722433460076,0.0,-476.115969581749],[0.0,0.7870722433460076,-162.01140684410643]]},{"cell":7,"matrix":[[0.7870722433460076,0.0,-718.9277566539923],[0.0,0.7870722433460076,-162.01140684410643]]},{"cell":8,"matrix":[[0.7870722433460076,0.0,12.655893536121681],[0.0,0.7870722433460076,-407.57794676806077]]},{"cell":9,"matrix":[[0.7870722433460076,0.0,-231.7300380228137],[0.0,0.7870722433460076,-407.57794676806077]]},{"cell":10,"matrix":[[0.7870722433460076,0.0,-474.93536121673003],[0.0,0.7870722433460076,-407.57794676806077]]},{"cell":11,"matrix":[[0.7870722433460076,0.0,-718.5342205323193],[0.0,0.7870722433460076,-407.57794676806077]]},{"cell":12,"matrix":[[0.7870722433460076,0.0,12.262357414448672],[0.0,0.7870722433460076,-647.6349809885932]]},{"cell":13,"matrix":[[0.7870722433460076,0.0,-232.12357414448667],[0.0,0.7870722433460076,-646.8479087452471]]},{"cell":14,"matrix":[[0.7870722433460076,0.0,-474.14828897338396],[0.0,0.7870722433460076,-647.6349809885932]]},{"cell":15,"matrix":[[0.7870722433460076,0.0,-718.9277566539923],[0.0,0.7870722433460076,-647.6349809885932]]}],"neutralSha256":"0b0584bd032ddf41d62f02c5064547d667e84eea9b8a74e3ff2d7043abb5eb78","sourceHashes":{"puppy-companion-interactions-v2.bin.gz":"75d682c0eaab31dd01038f36fd6da425eaee6cb92afc8857e06e3013e3307724","puppy-interactions12-v1-sheet.png":"e3931afbd935355734f9448f12495edb269547cadba30dcd36265ea9e4cf7afd","puppy-paper-seat.bin.gz":"a863225bef502fa3d60fe97c85007a9e85b818d53e5c3b5490fddd80b0c100d9","prepare-companion-interactions-v3.py":"d0d195683d2869f9bf5a94e8c8195d00cb5caf3caa4099c49cc20e384592da73"},"method":"v2 ball sprites byte-exact; treat is full original generated sprite, one source scale, no head/torso mask and no ghost-frame morph","treatSourceKeys":{"30":[0,1],"31":[1,2],"32":[2,3],"33":[3,4],"34":[4,5],"35":[5,4],"36":[4,5],"37":[5,6],"38":[6,1],"39":[1,0]},"treatKeyHashes":["0b0584bd032ddf41d62f02c5064547d667e84eea9b8a74e3ff2d7043abb5eb78","0870c83bbd95da39680c4297fb2a7c08fafb296578f7e872de31ace3736e303d","fe95b5930e429cb1e42ca5c6c06e500c4c30446164ece075fd580d85f8a014e0","2533e5d855f56867c2d0650da1644a175e414145c03a29e43281996094e14e67","a75805e1a40004160a07275db03cc74310950bb8200ca6ca71566706e6a8d776","51b81c3433ba2ee74127a39386bf725d654c72bffbf6cfcbe475107cf1d13cdb","d11f159c000b2e432a77dd3d922d7f51435db5f2a9799f55290d3dedc9c88636"],"treatScale":0.6124260355029586,"treatRegistration":[{"cell":0,"matrix":[[0.6124260355029586,0.0,36.39497041420118],[0.0,0.6124260355029586,57.47928994082841]]},{"cell":1,"matrix":[[0.6124260355029586,0.0,-149.47633136094674],[0.0,0.6124260355029586,56.25443786982248]]},{"cell":2,"matrix":[[0.6124260355029586,0.0,-335.3476331360947],[0.0,0.6124260355029586,56.86686390532543]]},{"cell":3,"matrix":[[0.6124260355029586,0.0,-521.8313609467456],[0.0,0.6124260355029586,56.25443786982248]]},{"cell":4,"matrix":[[0.6124260355029586,0.0,37.00739644970413],[0.0,0.6124260355029586,-181.36686390532543]]},{"cell":5,"matrix":[[0.6124260355029586,0.0,-148.8639053254438],[0.0,0.6124260355029586,-181.36686390532543]]}]}
@@ -0,0 +1,182 @@
1
+ // Autonomous companion "life" controller layered over a paper puppy actor.
2
+ // The parent calls actor.update(dt) THEN life.update(dt). Commands are only issued
3
+ // while the actor rests at a shared safe endpoint (idle/down); everything else waits.
4
+ // Exception: an accepted ball throw reacts at once via actor.chase (the actor owns safe
5
+ // interruption); actors without chase fall back to go at the next safe endpoint.
6
+ // Parameters are authored for feel, not canine science.
7
+
8
+ const SAFE=new Set(['idle','down']);
9
+ const HOME={x:0,z:.2};
10
+ const BALL_TARGET={x:0,z:-4};
11
+ const OFFER={x:0,z:.3};
12
+ // Hand-fed treat marker, relative to the puppy's current root (no travel for treats).
13
+ const TREAT_OFFSET={z:.08,height:.25};
14
+ const COOLDOWN={ball:6,treat:15};
15
+ const FLIGHT=.65,BALL_PEAK=.75,CARRY_HEIGHT=.19,SETTLE_FALLBACK=.6,MAX_DT=.25,ARRIVE=.05;
16
+ const LABEL={
17
+ start:'기다리는 중',pause:'잠시 느긋하게 둘러보는 중',lie:'엎드리는 중',rest:'엎드려 쉬는 중',recover:'앉아서 기운 차리는 중',
18
+ sit:'앉는 중',watch:'앉아서 지켜보는 중',look:'고개를 돌려 살피는 중',observe:'가만히 주변 관찰 중',
19
+ stroll:'옆으로 살짝 걸어가는 중',sniff:'잠깐 멈춰 주변을 살피는 중',dash:'저 멀리 달려가는 중',far:'멀리서 둘러보는 중',home:'제자리로 돌아오는 중',
20
+ fetch:'공을 쫓아가는 중',pickup:'공을 무는 중',carry:'공을 물고 돌아오는 중',drop:'공을 내려놓는 중',offer:'공을 내려놓고 기다리는 중',
21
+ treatSit:'간식 앞에 앉는 중',chew:'간식 먹는 중'
22
+ };
23
+ const PENDING_LABEL={treat:'곧 간식'};
24
+ // Baked interaction clips, played only while holding at the shared seated endpoint.
25
+ const GESTURE={'ball-pickup':1.4,'ball-drop':1.2,'treat-eat':2.4};
26
+ const clamp=(v,a,b)=>Math.min(b,Math.max(a,v));
27
+
28
+ function mulberry32(seed){
29
+ let a=seed>>>0;
30
+ return()=>{a=a+0x6d2b79f5>>>0;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};
31
+ }
32
+
33
+ export function createCompanionLife(actor,{seed=731,energy=.8}={}){
34
+ for(const name of ['update','sample','go','come','sit','lie','look','walk','roll','rest'])
35
+ if(!actor||typeof actor[name]!=='function')throw TypeError(`actor.${name} must be a function`);
36
+ if(!Number.isInteger(seed))throw TypeError('seed must be an integer');
37
+ if(!Number.isFinite(energy)||energy<0||energy>1)throw RangeError('energy must be within 0..1');
38
+
39
+ const rand=mulberry32(seed),range=(a,b)=>a+(b-a)*rand(),side=()=>rand()<.5?'left':'right';
40
+ const needs={energy,social:.5,curiosity:.5};
41
+ const cooldowns={ball:0,treat:0};
42
+ let clock=0,decisions=0,lastEpisode=null;
43
+ let queue=[],hold=0,holdIntent=null,waiting=null,gesture=null;
44
+ let intent='start',label=LABEL.start,pending=null,active=null,ball=null,treat=null;
45
+
46
+ const cmd=(name,args,step,when)=>({cmd:name,args,intent:step,label:LABEL[step],when});
47
+ const wait=(seconds,step)=>({hold:seconds,intent:step,label:LABEL[step]});
48
+ const play=(clip,step)=>({hold:GESTURE[clip],gesture:clip,intent:step,label:LABEL[step]});
49
+ // Skip sit() when the actor already rests in the canonical front-seated endpoint.
50
+ const unseated=()=>{const s=actor.sample(clock);return !(actor.state==='idle'&&s.seatProgress===1&&s.view==='approach'&&(s.seatVariant??'default')==='default');};
51
+ // Treats accept any seat variant at the front-seated endpoint: the hand comes to the puppy.
52
+ const seatedAnyVariant=()=>{const s=actor.sample(clock);return actor.state==='idle'&&s.seatProgress===1&&s.view==='approach';};
53
+ const fx=fn=>({fx:fn});
54
+ const home=()=>cmd('go',[HOME.x,HOME.z],'home');
55
+
56
+ // Each episode is a short coherent plan; a pause always leads in so autonomy never stalls.
57
+ const EPISODES={
58
+ rest:()=>[cmd('lie',[],'lie'),wait(range(10,18),'rest'),cmd('sit',[],'recover'),wait(range(3,6),'recover')],
59
+ watch:()=>[cmd('sit',[],'sit'),cmd('look',[side()],'look'),wait(range(3,9),'watch'),fx(()=>{needs.social-=.3;needs.curiosity+=.1;})],
60
+ observe:()=>[cmd('look',[side()],'look'),wait(range(3,9),'observe'),fx(()=>{needs.curiosity-=.15;})],
61
+ stroll:()=>[cmd('walk',[side(),range(.08,.18)],'stroll'),wait(range(2,4),'sniff'),home(),fx(()=>{needs.energy-=.08;needs.curiosity-=.2;})],
62
+ dash:()=>[cmd('go',[(rand()*2-1)*.18,-range(3,8)],'dash'),wait(range(1.5,3),'far'),home(),fx(()=>{needs.energy-=.3;needs.curiosity-=.4;})]
63
+ };
64
+ const weights=()=>{
65
+ const {energy:e,social:s,curiosity:c}=needs;
66
+ return {rest:e<.35?6:(1-e)*1.2,watch:.8+s,observe:.6+c*.6,stroll:1.2*c*e,dash:e>.55?1.2*e*c:0};
67
+ };
68
+ function decide(){
69
+ const w=weights();if(lastEpisode)w[lastEpisode]=0;
70
+ const kinds=Object.keys(w),total=kinds.reduce((sum,k)=>sum+w[k],0);
71
+ let pick=rand()*total,kind=kinds.find(k=>(pick-=w[k])<0)??'observe';
72
+ lastEpisode=kind;decisions++;
73
+ queue=[wait(range(3,9),'pause'),...EPISODES[kind]()];
74
+ }
75
+
76
+ const landed=()=>ball?.mode==='flight'&&clock-ball.start>=FLIGHT;
77
+ const arrived=()=>{const s=actor.sample(clock);return SAFE.has(actor.state)&&Math.hypot(s.x-BALL_TARGET.x,s.z-BALL_TARGET.z)<ARRIVE;};
78
+ // The leading go is dropped when interact() already dispatched the chase; pickup waits for both arrival and landing.
79
+ const BALL_STEPS=()=>[
80
+ cmd('go',[BALL_TARGET.x,BALL_TARGET.z],'fetch'),{until:()=>landed()&&arrived()},
81
+ fx(()=>{ball={mode:'ground',x:BALL_TARGET.x,z:BALL_TARGET.z};}),
82
+ cmd('sit',[],'pickup'),play('ball-pickup','pickup'),fx(()=>{ball={mode:'carried'};}),cmd('go',[HOME.x,HOME.z],'carry'),
83
+ cmd('sit',[],'drop',unseated),play('ball-drop','drop'),
84
+ fx(()=>{ball={mode:'ground',x:OFFER.x,z:OFFER.z};needs.social-=.4;}),wait(3,'offer'),fx(()=>{ball=null;})
85
+ ];
86
+ // Hand-feed where the puppy already is: sit only if not front-seated, never go/walk/chase/come.
87
+ const TREAT_STEPS=()=>[
88
+ fx(()=>{treat=true;}),cmd('sit',[],'treatSit',()=>!seatedAnyVariant()),
89
+ play('treat-eat','chew'),fx(()=>{treat=null;needs.energy+=.25;needs.social-=.3;})
90
+ ];
91
+
92
+ function startInteraction(){
93
+ active=pending;pending=null;decisions++;hold=0;
94
+ queue=active==='ball'?BALL_STEPS():TREAT_STEPS();
95
+ }
96
+ function finishInteraction(){cooldowns[active]=COOLDOWN[active];active=null;}
97
+
98
+ function drift(dt){
99
+ const resting=holdIntent==='rest'||holdIntent==='recover';
100
+ needs.energy+=resting?.04*dt:-.004*dt;
101
+ if(!active){needs.curiosity+=.01*dt;needs.social+=.008*dt;}
102
+ for(const k in needs)needs[k]=clamp(needs[k],0,1);
103
+ }
104
+
105
+ function update(dt){
106
+ if(typeof dt!=='number'||!Number.isFinite(dt)||dt<0)throw RangeError('dt must be a finite non-negative number');
107
+ dt=Math.min(dt,MAX_DT);clock+=dt;
108
+ for(const k in cooldowns)cooldowns[k]=Math.max(0,cooldowns[k]-dt);
109
+ drift(dt);
110
+ const safe=SAFE.has(actor.state);
111
+ if(waiting){
112
+ waiting.age+=dt;if(!safe)waiting.left=true;
113
+ if(!safe||(!waiting.left&&waiting.age<SETTLE_FALLBACK))return;
114
+ waiting=null;
115
+ }
116
+ if(hold>0){
117
+ hold-=dt;if(gesture)gesture.elapsed+=dt;
118
+ if(pending&&!active&&safe)hold=0;
119
+ if(hold>0)return;
120
+ }
121
+ holdIntent=null;gesture=null;
122
+ if(!safe)return;
123
+ if(pending&&!active)startInteraction();
124
+ // Run instant effects; stop at the first hold or issued command (at most one command per update).
125
+ for(;;){
126
+ if(!queue.length){
127
+ if(active){finishInteraction();if(pending)startInteraction();else decide();}
128
+ else decide();
129
+ }
130
+ const step=queue.shift();
131
+ if(step.fx){step.fx();for(const k in needs)needs[k]=clamp(needs[k],0,1);continue;}
132
+ if(step.until){if(step.until())continue;queue.unshift(step);return;}
133
+ if(step.when&&!step.when())continue;
134
+ intent=step.intent;label=step.label;
135
+ if(step.hold!==undefined){hold=step.hold;holdIntent=step.intent;if(step.gesture)gesture={clip:step.gesture,duration:step.hold,elapsed:0};return;}
136
+ if(actor[step.cmd](...step.args)!==false)waiting={age:0,left:false};
137
+ return;
138
+ }
139
+ }
140
+
141
+ function interact(kind){
142
+ if(kind!=='ball'&&kind!=='treat')return false;
143
+ if(pending||active||cooldowns[kind]>0)return false;
144
+ if(kind==='treat'){pending=kind;return true;}
145
+ // Ball: launch now and drop obsolete controller plans; the actor's own state is left to chase.
146
+ active='ball';decisions++;ball={mode:'flight',start:clock};
147
+ queue=BALL_STEPS();hold=0;holdIntent=null;gesture=null;waiting=null;
148
+ intent='fetch';label=LABEL.fetch;
149
+ const urgent=typeof actor.chase==='function';
150
+ if((urgent||SAFE.has(actor.state))&&actor[urgent?'chase':'go'](BALL_TARGET.x,BALL_TARGET.z)!==false)queue.shift();
151
+ return true;
152
+ }
153
+
154
+ // Recomputed from the live pose so the marker stays truthful before and after sitting.
155
+ function treatReport(){
156
+ if(!treat)return null;
157
+ const s=actor.sample(clock);return {x:s.x,z:s.z+TREAT_OFFSET.z,height:TREAT_OFFSET.height};
158
+ }
159
+ function ballReport(){
160
+ if(!ball)return null;
161
+ if(ball.mode==='flight'){
162
+ const p=clamp((clock-ball.start)/FLIGHT,0,1);
163
+ if(p===1)return {x:BALL_TARGET.x,z:BALL_TARGET.z,height:0,carried:false};
164
+ return {x:HOME.x+(BALL_TARGET.x-HOME.x)*p,z:HOME.z+(BALL_TARGET.z-HOME.z)*p,height:4*BALL_PEAK*p*(1-p),carried:false};
165
+ }
166
+ if(ball.mode==='carried'){const s=actor.sample(clock);return {x:s.x,z:s.z,height:CARRY_HEIGHT,carried:true};}
167
+ return {x:ball.x,z:ball.z,height:0,carried:false};
168
+ }
169
+
170
+ return {
171
+ update,interact,
172
+ get report(){
173
+ const interaction=active??pending;
174
+ return {
175
+ intent,label:pending?`${label} · ${PENDING_LABEL[pending]}`:label,interaction,busy:interaction!==null,
176
+ ball:ballReport(),treat:treatReport(),energy:needs.energy,decisions,cooldowns:{...cooldowns},
177
+ // Local hold clock only: progress freezes between updates; null outside the stationary hold.
178
+ gesture:gesture&&SAFE.has(actor.state)?{clip:gesture.clip,progress:clamp(gesture.elapsed/gesture.duration,0,1)}:null
179
+ };
180
+ }
181
+ };
182
+ }
@@ -0,0 +1,30 @@
1
+ // Small physical toys in the SAME fixed camera as the puppy, before Braille
2
+ // conversion. No minimum thumbnail size, DOM overlay, sprite zoom or new dog art.
3
+ // mouth: renderer.lastMouth for the frame just painted. A carried ball sits on that
4
+ // measured drawn mouth only; without one (side/rear/unmeasured pose) it is hidden.
5
+ export function paintCompanionObjects(pixels,width,height,projection,report,state,mouth=null){
6
+ const objects=[];
7
+ // Interaction drawings already include the toy/food. Never paint a second
8
+ // floating ball over the baked mouth, or a biscuit through the puppy's chest.
9
+ if(report.gesture)return pixels;
10
+ if(report.ball?.carried){if(mouth&&[mouth.x,mouth.y,mouth.rx,mouth.ry].every(Number.isFinite)&&mouth.rx>0&&mouth.ry>0)objects.push({kind:'ball',screen:mouth});}
11
+ else if(report.ball)objects.push({...report.ball,radius:.035,kind:'ball'});
12
+ if(report.treat)objects.push({...report.treat,height:report.treat.height??.018,radius:.022,kind:'treat'});
13
+ for(const o of objects){
14
+ let cx,cy,rx,ry;
15
+ if(o.screen)({x:cx,y:cy,rx,ry}=o.screen);
16
+ else{
17
+ const p=projection.project(o.x,o.height??0,o.z),edge=projection.project(o.x+o.radius,o.height??0,o.z);
18
+ cx=p.x*width;cy=p.y*height;rx=Math.abs(edge.x-p.x)*width;
19
+ const up=projection.project(o.x,(o.height??0)+o.radius,o.z);ry=Math.abs(up.y-p.y)*height;
20
+ }
21
+ if(![cx,cy,rx,ry].every(Number.isFinite)||rx<=0||ry<=0)continue;
22
+ for(let y=Math.max(0,Math.floor(cy-ry));y<Math.min(height,Math.ceil(cy+ry));y++)
23
+ for(let x=Math.max(0,Math.floor(cx-rx));x<Math.min(width,Math.ceil(cx+rx));x++){
24
+ const u=(x+.5-cx)/rx,v=(y+.5-cy)/ry;if(u*u+v*v>1)continue;
25
+ const tone=o.kind==='treat'?((x+y)%3?190:85):Math.abs(v+.25*u)<.18?85:225;
26
+ const j=(y*width+x)*4;pixels[j]=pixels[j+1]=pixels[j+2]=tone;
27
+ }
28
+ }
29
+ return pixels;
30
+ }
@@ -0,0 +1,14 @@
1
+ import {grayFrameReader,isGrayFrameSource} from './puppy-frame-store.mjs';
2
+ // Prebaked interaction samples; the original seat is the exact shared endpoint.
3
+ // Pure sampling, no live image morph, root motion, camera or pose-size fitting.
4
+ export function createCompanionSpriteSampler(bytes,info){
5
+ const n=320*320;
6
+ if(!isGrayFrameSource(bytes)||info?.width!==320||info.height!==320||info.samples!==9||!Number.isInteger(info.pairs)||info.pairs<1||info.pairs>128||bytes.byteLength!==info.pairs*9*n)throw Error('Invalid companion sprite cache');
7
+ for(const key of ['treat-eat','ball-pickup','ball-drop'])if(!Array.isArray(info.clips?.[key])||!info.clips[key].length||!info.clips[key].every(p=>Number.isInteger(p)&&p>=0&&p<info.pairs))throw Error('Invalid companion sprite clip');
8
+ const frames=grayFrameReader(bytes,320,320,info.pairs*9),rgba=new Uint8ClampedArray(n*4);for(let i=3;i<rgba.length;i+=4)rgba[i]=255;
9
+ return({clip,progress})=>{
10
+ const list=info.clips[clip];if(!list||!Number.isFinite(progress)||progress<0||progress>1)throw Error('Invalid companion sprite pose');
11
+ const q=progress*list.length,index=Math.min(list.length-1,Math.floor(q)),s=Math.round((q-index)*8),frame=frames.getFrame(list[index]*9+s);
12
+ for(let i=0,j=0;i<n;i++,j+=4)rgba[j]=rgba[j+1]=rgba[j+2]=frame[i];return rgba;
13
+ };
14
+ }
@@ -0,0 +1,57 @@
1
+ import {createZoomies} from './puppy-zoomies.mjs';
2
+ import {ZOOMIES30} from './puppy-zoomies30.mjs';
3
+ export const FLOW_SPEC={width:80,height:80,pairs:34,components:4,quantization:32};
4
+ export const CONTINUOUS_CYCLE=20;
5
+ const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
6
+ const mix=(a,b,x)=>a+(b-a)*x;
7
+ // A deliberate 2D card-turn, not a fabricated double-headed morph or true 3D turn.
8
+ const turn=(p,x,fromLeft)=>{const c=Math.cos(Math.PI*ease(p));return blend({state:'turn',x,floor:.86,scale:.64,widthScale:Math.abs(c)<1e-7?0:Math.abs(c)},0,0,fromLeft?c>=0:c<0);};
9
+ const blend=(state,pair,alpha,flip=false)=>({...state,pair,blend:alpha,flip,pose:pair<30?pair:pair===31||pair===33?20:0});
10
+ const run=(time,start,end,from,to,flip)=>{
11
+ const phase=(time-start)*30,index=Math.floor(phase),pose=index%20;
12
+ return blend({state:'run',x:mix(from,to,ease((time-start)/(end-start))),floor:.86,scale:.64},pose,phase-Math.floor(phase),flip);
13
+ };
14
+ export function continuousAt(seconds,{mode='auto'}={}){
15
+ if(!Number.isFinite(seconds)||seconds<0||!['auto','run','pant'].includes(mode))throw Error('Invalid animation time/mode');
16
+ const t=(seconds*ZOOMIES30.tempo)%CONTINUOUS_CYCLE;
17
+ const pant=(phase)=>{const p=phase*15,i=Math.floor(p);return blend({state:'pant',x:.58,floor:.96,scale:.9},20+i%10,p-i);};
18
+ if(mode==='pant')return pant(seconds*ZOOMIES30.tempo);
19
+ if(mode==='run')return run(seconds*ZOOMIES30.tempo,0,1,.58,.58,false);
20
+ // All segment endpoints match in location, scale AND rendered sprite.
21
+ if(t<1){const a=ease(t);return blend({state:'launch',x:.58,floor:mix(.96,.86,a),scale:mix(.9,.64,a)},33,a);}
22
+ if(t<3)return run(t,1,3,.58,.78,false);
23
+ if(t<4)return turn(t-3,.78,false);
24
+ if(t<22/3)return run(t,4,22/3,.78,.22,true);
25
+ if(t<25/3)return turn(t-22/3,.22,true);
26
+ if(t<31/3)return run(t,25/3,31/3,.22,.58,false);
27
+ if(t<34/3){const a=ease(t-31/3);return blend({state:'settle',x:.58,floor:mix(.86,.96,a),scale:mix(.64,.9,a)},30,a);}
28
+ return pant(t-34/3);
29
+ }
30
+ const endpoints=Array.from({length:34},(_,i)=>i<20?[i,(i+1)%20,false,false]:i<30?[i,20+(i-19)%10,false,false]:[[0,20,false,false],[20,0,false,true],[0,20,true,false],[20,0,false,false]][i-30]);
31
+ export function createSpriteTween(atlas,binary,{count=30,pairs=endpoints}={}){
32
+ if(atlas.length!==count*409600||binary.length!==pairs.length*80*80*4*2)throw Error('Invalid tween assets');
33
+ const fields=new Int16Array(binary.buffer,binary.byteOffset,binary.byteLength/2),out=new Uint8ClampedArray(409600),stride=409600;
34
+ const sample=(frame,x,y,flip)=>{
35
+ if(flip)x=319-x;if(x<0||y<0||x>319||y>319)return 0;
36
+ const x0=x|0,y0=y|0,x1=Math.min(319,x0+1),y1=Math.min(319,y0+1),fx=x-x0,fy=y-y0,o=frame*stride;
37
+ return (atlas[o+(y0*320+x0)*4]*(1-fx)+atlas[o+(y0*320+x1)*4]*fx)*(1-fy)+(atlas[o+(y1*320+x0)*4]*(1-fx)+atlas[o+(y1*320+x1)*4]*fx)*fy;
38
+ };
39
+ return function tween(state){
40
+ const {pair}=state,a=Math.max(0,Math.min(1,state.blend)),[fa,fb,flipA,flipB]=pairs[pair];
41
+ if(a<1e-7&&!flipA)return atlas.subarray(fa*stride,(fa+1)*stride);
42
+ if(a>1-1e-7&&!flipB)return atlas.subarray(fb*stride,(fb+1)*stride);
43
+ const offset=pair*80*80*4;
44
+ for(let y=0;y<320;y++)for(let x=0;x<320;x++){
45
+ const gx=x*79/319,gy=y*79/319,x0=gx|0,y0=gy|0,x1=Math.min(79,x0+1),y1=Math.min(79,y0+1),fx=gx-x0,fy=gy-y0;
46
+ const p=offset+(y0*80+x0)*4,q=offset+(y0*80+x1)*4,r=offset+(y1*80+x0)*4,s=offset+(y1*80+x1)*4;
47
+ const flow=k=>((fields[p+k]*(1-fx)+fields[q+k]*fx)*(1-fy)+(fields[r+k]*(1-fx)+fields[s+k]*fx)*fy)/32;
48
+ const av=sample(fa,x-a*flow(0),y-a*flow(1),flipA),bv=sample(fb,x-(1-a)*flow(2),y-(1-a)*flow(3),flipB),v=av*(1-a)+bv*a,j=(y*320+x)*4;
49
+ out[j]=out[j+1]=out[j+2]=v;out[j+3]=255;
50
+ }
51
+ return out;
52
+ };
53
+ }
54
+ export function createContinuousZoomies(atlas,fields,width,height){
55
+ const tween=createSpriteTween(atlas,fields);
56
+ return createZoomies(atlas,width,height,{spec:ZOOMIES30,stateAt:continuousAt,frameProvider:tween});
57
+ }
package/puppy-face.mjs ADDED
@@ -0,0 +1,46 @@
1
+ // Small deterministic 2D secondary motion, NOT recovered anatomy/physics.
2
+ // Coordinates track the eight approved, body-registered run keys (320px).
3
+ const HEADS=[[243,145],[254,136],[252,145],[253,129],[246,152],[247,155],[248,139],[251,135]];
4
+ const oval=(x,y,cx,cy,rx,ry)=>Math.max(0,1-((x-cx)/rx)**2-((y-cy)/ry)**2)**2;
5
+ const bump=(p,center,radius)=>{const u=Math.abs(p-center)/radius;return u<1?(1+Math.cos(Math.PI*u))/2:0;};
6
+ export function faceAt(state){
7
+ if(state.state!=='run')return {cheek:0,ear:0,mouth:0};
8
+ const phase=state.pair+state.blend;
9
+ // No extra oscillations at extension. One landing squash, one soft recovery.
10
+ // All pulses and their first derivatives are zero at the gait/segment ends.
11
+ return {cheek:3.5*bump(phase,4.75,1.25)-.85*bump(phase,6.05,.95),
12
+ ear:-5*bump(phase,2.3,1.6)+7*bump(phase,5.12,1.48)-1.4*bump(phase,6.65,.85),
13
+ mouth:2.2*bump(phase,5.02,1.32)-.75*bump(phase,6.25,1)};
14
+ }
15
+ export function headAt(state){
16
+ const i=state.pair,p=state.blend,n=HEADS.length;
17
+ if(!Number.isInteger(i)||i<0||i>=n||!Number.isFinite(p)||p<0||p>1)throw Error('Invalid facial gait state');
18
+ return [0,1].map(axis=>{const a=HEADS[(i+n-1)%n][axis],b=HEADS[i][axis],c=HEADS[(i+1)%n][axis],d=HEADS[(i+2)%n][axis];
19
+ return .5*(2*b+(-a+c)*p+(2*a-5*b+4*c-d)*p*p+(-a+3*b-3*c+d)*p*p*p);});
20
+ }
21
+ export function createFaceMotion(){
22
+ const output=new Uint8ClampedArray(320*320*4);
23
+ return (image,state)=>{
24
+ const m=faceAt(state);
25
+ if(!m.cheek&&!m.ear&&!m.mouth)return image;
26
+ if(image.length!==output.length)throw Error('Invalid facial image');
27
+ const [hx,hy]=headAt(state);output.set(image);
28
+ for(let y=Math.max(0,Math.floor(hy-40));y<Math.min(320,hy+65);y++)for(let x=Math.max(0,Math.floor(hx-65));x<Math.min(320,hx+49);x++){
29
+ const dx=x-hx,dy=y-hy;
30
+ // Compact supports leave body, legs, tail and background outside ROI exact.
31
+ const ear=oval(dx,dy,-37,7,23,36)+oval(dx,dy,29,12,16,30);
32
+ const cheek=oval(dx,dy,-18,17,24,24)+oval(dx,dy,23,19,19,23);
33
+ const mouth=oval(dx,dy,6,31,24,21);
34
+ const protect=Math.min(1,oval(dx,dy,-9,-9,14,12)+oval(dx,dy,14,-8,12,11)+oval(dx,dy,12,9,15,12));
35
+ const weight=1-protect;
36
+ const sx=x-m.cheek*dx/30*cheek*.35*weight-m.ear*ear*.18;
37
+ const sy=y+(m.cheek*cheek-m.mouth*mouth)*weight-m.ear*ear;
38
+ if(sx===x&&sy===y)continue;
39
+ const px=Math.max(0,Math.min(319,sx)),py=Math.max(0,Math.min(319,sy)),x0=Math.floor(px),y0=Math.floor(py),x1=Math.min(319,x0+1),y1=Math.min(319,y0+1),fx=px-x0,fy=py-y0;
40
+ const a=(y0*320+x0)*4,b=(y0*320+x1)*4,c=(y1*320+x0)*4,d=(y1*320+x1)*4,j=(y*320+x)*4;
41
+ // Grayscale remains grayscale; one inverse sample avoids crossfade ghosts.
42
+ output[j]=output[j+1]=output[j+2]=(image[a]*(1-fx)+image[b]*fx)*(1-fy)+(image[c]*(1-fx)+image[d]*fx)*fy;
43
+ }
44
+ return output;
45
+ };
46
+ }