@bitefoundry/retrieviewer 0.0.2 → 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.
- package/ASSET-PROVENANCE.md +6 -0
- package/ASSETS-LICENSE.md +2 -1
- package/ATTRIBUTION.md +6 -1
- package/COMFORT-ART.md +65 -0
- package/DESKTOP-PLATFORMS.md +75 -0
- package/INSTALLING.md +8 -5
- package/PLUGIN.md +13 -10
- package/PREREQUISITES.md +95 -0
- package/PUPPY-MEMORY.md +102 -1
- package/README.ko.md +110 -0
- package/README.md +119 -58
- package/RELEASING.md +27 -14
- package/THIRD-PARTY-NOTICES.md +5 -3
- package/braille.mjs +35 -9
- package/examples/live.mjs +2 -2
- package/integrations/desktop/platform.mjs +27 -0
- package/integrations/linux-overlay/README.md +83 -0
- package/integrations/linux-overlay/overlay.py +1094 -0
- package/integrations/macos-overlay/README.md +83 -0
- package/integrations/macos-overlay/overlay.jxa +639 -0
- package/integrations/opencode/runtime.mjs +4 -4
- package/integrations/opencode/tui.tsx +1 -1
- package/integrations/windows-overlay/Overlay.cs +4 -4
- package/integrations/windows-overlay/desktop.mjs +40 -8
- package/integrations/windows-overlay/protocol.mjs +11 -7
- package/integrations/windows-overlay/worker.mjs +13 -7
- package/package.json +41 -14
- package/puppy-comfort-sprites.mjs +25 -0
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-comfort.json +77 -0
- package/puppy-companion-life.mjs +49 -16
- package/puppy-companion-objects.mjs +12 -0
- package/puppy-frame-predictive.mjs +15 -9
- package/puppy-frame-store.mjs +2 -2
- package/puppy-paper-world.mjs +14 -0
- package/puppy-paper.mjs +65 -14
- package/retrieviewer-cli.mjs +10 -3
- package/retrieviewer-plugin.d.mts +1 -1
- package/retrieviewer-plugin.mjs +1 -1
- package/retrieviewer.d.mts +10 -4
- package/retrieviewer.mjs +20 -14
- package/locomotion-core.mjs +0 -21
- package/locomotion-puppy.animal +0 -0
- package/vendor/procedural-animals/package.json +0 -40
- package/vendor/procedural-animals/src/animal.js +0 -146
- package/vendor/procedural-animals/src/bake.js +0 -51
- package/vendor/procedural-animals/src/build.js +0 -81
- package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
- package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
- package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
- package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
- package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
- package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
- package/vendor/procedural-animals/src/core/build/seams.js +0 -183
- package/vendor/procedural-animals/src/core/build/warp.js +0 -112
- package/vendor/procedural-animals/src/core/build/weights.js +0 -393
- package/vendor/procedural-animals/src/core/math/vec.js +0 -81
- package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
- package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
- package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
- package/vendor/procedural-animals/src/core/motion/common.js +0 -158
- package/vendor/procedural-animals/src/core/motion/index.js +0 -22
- package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
- package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
- package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
- package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
- package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
- package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
- package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
- package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
- package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
- package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
- package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
- package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
- package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
- package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
- package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
- package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
- package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
- package/vendor/procedural-animals/src/index.d.ts +0 -98
- package/vendor/procedural-animals/src/index.js +0 -29
- package/vendor/procedural-animals/src/registry.js +0 -48
- package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
- package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
- package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
- package/vendor/procedural-animals/src/species/bear/index.js +0 -139
- package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
- package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
- package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
- package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
- package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
- package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
- package/vendor/procedural-animals/src/species/boar/index.js +0 -110
- package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
- package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
- package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
- package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
- package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
- package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
- package/vendor/procedural-animals/src/species/cat/index.js +0 -211
- package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
- package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
- package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
- package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
- package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
- package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
- package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
- package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
- package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
- package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
- package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
- package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
- package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
- package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
- package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
- package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
- package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
- package/vendor/procedural-animals/src/species/cow/index.js +0 -118
- package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
- package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
- package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
- package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
- package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
- package/vendor/procedural-animals/src/species/crow/index.js +0 -70
- package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
- package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
- package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
- package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
- package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
- package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
- package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
- package/vendor/procedural-animals/src/species/deer/index.js +0 -106
- package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
- package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
- package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
- package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
- package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
- package/vendor/procedural-animals/src/species/dog/index.js +0 -144
- package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
- package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
- package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
- package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
- package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
- package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
- package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
- package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
- package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
- package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
- package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
- package/vendor/procedural-animals/src/species/fish/index.js +0 -88
- package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
- package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
- package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
- package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
- package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
- package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
- package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
- package/vendor/procedural-animals/src/species/fox/index.js +0 -120
- package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
- package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
- package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
- package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
- package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
- package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
- package/vendor/procedural-animals/src/species/frog/index.js +0 -97
- package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
- package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
- package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
- package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
- package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
- package/vendor/procedural-animals/src/species/goat/index.js +0 -149
- package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
- package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
- package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
- package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
- package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
- package/vendor/procedural-animals/src/species/horse/index.js +0 -80
- package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
- package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
- package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
- package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
- package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
- package/vendor/procedural-animals/src/species/lion/index.js +0 -111
- package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
- package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
- package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
- package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
- package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
- package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
- package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
- package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
- package/vendor/procedural-animals/src/species/pig/index.js +0 -161
- package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
- package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
- package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
- package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
- package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
- package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
- package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
- package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
- package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
- package/vendor/procedural-animals/src/species/rat/index.js +0 -111
- package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
- package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
- package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
- package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
- package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
- package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
- package/vendor/procedural-animals/src/species/shark/index.js +0 -104
- package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
- package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
- package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
- package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
- package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
- package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
- package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
- package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
- package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
- package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
- package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
- package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
- package/vendor/procedural-animals/src/species/snake/index.js +0 -73
- package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
- package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
- package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
- package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
- package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
- package/vendor/procedural-animals/src/species/spider/index.js +0 -78
- package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
- package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
- package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
- package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
- package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
- package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
- package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
- package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
- package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
- package/vendor/procedural-animals/src/worker.js +0 -29
|
@@ -80,7 +80,7 @@ function unfilterRow(type,src,at,cur,prev,w){
|
|
|
80
80
|
|
|
81
81
|
// Validate and unfilter. Only the returned typed arrays survive; `bytes` is
|
|
82
82
|
// not referenced by the store built from them.
|
|
83
|
-
function decode(bytes){
|
|
83
|
+
function decode(bytes,{frameIndices}={}){
|
|
84
84
|
if(!(bytes instanceof Uint8Array))fail('bytes');
|
|
85
85
|
if(!isPredictiveFrames(bytes)||bytes.length<HEADER)fail('magic');
|
|
86
86
|
const view=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength),u32=at=>view.getUint32(at,true);
|
|
@@ -89,8 +89,13 @@ function decode(bytes){
|
|
|
89
89
|
if(unique<1||unique>frameCount)fail('unique count');
|
|
90
90
|
const data=HEADER+4*frameCount+RECORD*unique;
|
|
91
91
|
if(bytes.length<data)fail('truncated');
|
|
92
|
-
const
|
|
93
|
-
|
|
92
|
+
const retained=frameIndices===undefined?null:new Uint8Array(frameCount);
|
|
93
|
+
if(retained){
|
|
94
|
+
if(!Array.isArray(frameIndices)||!frameIndices.length)fail('retained frames');
|
|
95
|
+
for(const f of frameIndices){if(!Number.isInteger(f)||f<0||f>=frameCount)fail('retained frame index');retained[f]=1;}
|
|
96
|
+
}
|
|
97
|
+
const map=new Uint32Array(frameCount),used=new Uint8Array(unique),needed=retained?new Uint8Array(unique):null;
|
|
98
|
+
for(let f=0;f<frameCount;f++){const u=u32(HEADER+4*f);if(u>=unique)fail('map');map[f]=u;used[u]=1;if(retained?.[f])needed[u]=1;}
|
|
94
99
|
if(used.includes(0))fail('unreferenced frame');
|
|
95
100
|
// x,y,w,h,first pixel index in the packed stream
|
|
96
101
|
const rects=new Float64Array(unique*5);let total=0,pixels=0;
|
|
@@ -98,7 +103,7 @@ function decode(bytes){
|
|
|
98
103
|
const at=HEADER+4*frameCount+RECORD*u,x=u32(at),y=u32(at+4),w=u32(at+8),h=u32(at+12),offset=u32(at+16),length=u32(at+20);
|
|
99
104
|
if((w===0)!==(h===0)||(w===0&&(x||y))||x+w>width||y+h>height)fail('rectangle');
|
|
100
105
|
if(length!==h*(w+1)||offset!==total)fail('offset');
|
|
101
|
-
rects.set([x,y,w,h,pixels],u*5);total+=length;pixels+=w*h;
|
|
106
|
+
rects.set([x,y,w,h,pixels],u*5);total+=length;if(!needed||needed[u])pixels+=w*h;
|
|
102
107
|
}
|
|
103
108
|
if(bytes.length!==data+total)fail('pixel length');
|
|
104
109
|
if(pixels*BITS>0xffffffff)fail('pixel count');
|
|
@@ -109,23 +114,24 @@ function decode(bytes){
|
|
|
109
114
|
for(let y=0;y<h;y++){
|
|
110
115
|
unfilterRow(bytes[at],bytes,at+1,cur,prev,w);at+=w+1;
|
|
111
116
|
for(let x=0;x<w;x++){
|
|
112
|
-
const q=cur[x];if(q>31)fail('quantized value');
|
|
117
|
+
const q=cur[x];if(q>31)fail('quantized value');if(needed&&!needed[u])continue;
|
|
113
118
|
const i=pos>>>3,s=pos&7;packed[i]|=(q<<s)&255;if(s>3)packed[i+1]|=q>>(8-s);pos+=BITS;
|
|
114
119
|
}
|
|
115
120
|
const t=prev;prev=cur;cur=t;
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
|
-
return{width,height,frameCount,unique,map,rects,packed};
|
|
123
|
+
return{width,height,frameCount,unique,map,rects,packed,retained};
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
function createStore({width,height,frameCount,unique,map,rects,packed}){
|
|
126
|
+
function createStore({width,height,frameCount,unique,map,rects,packed,retained}){
|
|
122
127
|
const n=width*height,slots=[{unique:-1,frame:null},{unique:-1,frame:null}];let decodes=0,hits=0;
|
|
123
128
|
return Object.freeze({
|
|
124
129
|
width,height,frameCount,uniqueCount:unique,byteLength:n*frameCount,residentPixelBytes:packed.byteLength,
|
|
125
|
-
get residentBytes(){return packed.byteLength+map.byteLength+rects.byteLength+slots.reduce((s,e)=>s+(e.frame?.byteLength??0),0);},
|
|
130
|
+
get residentBytes(){return packed.byteLength+map.byteLength+rects.byteLength+(retained?.byteLength??0)+slots.reduce((s,e)=>s+(e.frame?.byteLength??0),0);},
|
|
126
131
|
stats(){return{decodes,hits,cachedFrames:slots.filter(e=>e.frame).length};},
|
|
127
132
|
getFrame(index){
|
|
128
133
|
if(!Number.isInteger(index)||index<0||index>=frameCount)fail('frame index');
|
|
134
|
+
if(retained&&!retained[index])fail('frame was not retained');
|
|
129
135
|
const u=map[index];
|
|
130
136
|
if(slots[0].unique===u){hits++;return slots[0].frame;}
|
|
131
137
|
if(slots[1].unique===u){hits++;slots.reverse();return slots[0].frame;}
|
|
@@ -144,4 +150,4 @@ function createStore({width,height,frameCount,unique,map,rects,packed}){
|
|
|
144
150
|
|
|
145
151
|
// Parse, validate and unpack an RVQ5 store. A returned frame is valid until two
|
|
146
152
|
// other unique frames have been requested.
|
|
147
|
-
export function readPredictiveFrames(bytes){return createStore(decode(bytes));}
|
|
153
|
+
export function readPredictiveFrames(bytes,options){return createStore(decode(bytes,options));}
|
package/puppy-frame-store.mjs
CHANGED
|
@@ -52,9 +52,9 @@ function checkShape(width,height,frameCount){
|
|
|
52
52
|
// resident (crops only) and reconstructs at most two full frames at a time.
|
|
53
53
|
// A returned frame is valid until two other unique frames have been requested.
|
|
54
54
|
// An 'RVQ5' store (lossy 32-level comparison codec) is delegated and branded.
|
|
55
|
-
export function readPackedFrames(bytes){
|
|
55
|
+
export function readPackedFrames(bytes,options){
|
|
56
56
|
if(!(bytes instanceof Uint8Array))throw Error('Invalid packed frames: bytes');
|
|
57
|
-
if(isPredictiveFrames(bytes)){const store=readPredictiveFrames(bytes);stores.add(store);return store;}
|
|
57
|
+
if(isPredictiveFrames(bytes)){const store=readPredictiveFrames(bytes,options);stores.add(store);return store;}
|
|
58
58
|
if(bytes.length<HEADER||MAGIC.some((v,i)=>bytes[i]!==v))throw Error('Invalid packed frames: magic');
|
|
59
59
|
const view=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength),u32=at=>view.getUint32(at,true);
|
|
60
60
|
const width=u32(4),height=u32(8),frameCount=u32(12),unique=u32(16);
|
package/puppy-paper-world.mjs
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
// Design calibration, not a measurement of the user's current head position.
|
|
2
2
|
// Sources: OpenCV pinhole model; PsychoPy viewtools physical-screen frustums.
|
|
3
3
|
const diagonal=.0254*32;
|
|
4
|
+
// The paper actor already integrates its own calibrated travel profile and foot
|
|
5
|
+
// contacts. Its old 3D Animal.follow() merely copied that root's x/z, then also
|
|
6
|
+
// updated an invisible skinned mesh, IK, fur and materials. Keep just the follow
|
|
7
|
+
// contract here; speed/stride/projection remain owned by the existing planner.
|
|
8
|
+
export function createPaperBody(){
|
|
9
|
+
const position={x:0,y:0,z:0};let target=null,disposed=false;
|
|
10
|
+
const alive=()=>{if(disposed)throw Error('Paper body is disposed');};
|
|
11
|
+
return{
|
|
12
|
+
position,
|
|
13
|
+
follow(point){alive();if(!point||![point.x,point.y,point.z].every(Number.isFinite))throw Error('Invalid paper body position');target={x:point.x,y:point.y,z:point.z};},
|
|
14
|
+
update(dt){alive();if(!Number.isFinite(dt)||dt<0||dt>.1)throw Error('Invalid paper body step');if(target){position.x=target.x;position.z=target.z;}},
|
|
15
|
+
dispose(){disposed=true;target=null;},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
4
18
|
export const PAPER_SCREEN=Object.freeze({widthMeters:diagonal*16/Math.hypot(16,9),heightMeters:diagonal*9/Math.hypot(16,9),viewDistance:.70});
|
|
5
19
|
const eyeHeight=.45,eyeZ=1.65,pitch=9*Math.PI/180;
|
|
6
20
|
export const PAPER_CAMERA=Object.freeze({fov:2*Math.atan(PAPER_SCREEN.heightMeters/(2*PAPER_SCREEN.viewDistance))*180/Math.PI,aspect:16/9,position:Object.freeze([0,eyeHeight,eyeZ]),target:Object.freeze([0,.12,eyeZ-(eyeHeight-.12)/Math.tan(pitch)]),canvasMeters:.64});
|
package/puppy-paper.mjs
CHANGED
|
@@ -8,13 +8,31 @@ import {createPaperAreaSampler} from './puppy-paper-area.mjs';
|
|
|
8
8
|
import {PAPER_ACTION_DURATIONS,createPaperActionSampler,paperActionPose} from './puppy-paper-actions.mjs';
|
|
9
9
|
import {createPawTransport} from './puppy-paper-paw-transport.mjs';
|
|
10
10
|
import {createCompanionSpriteSampler} from './puppy-companion-sprites.mjs';
|
|
11
|
+
import {createComfortSpriteSampler} from './puppy-comfort-sprites.mjs';
|
|
11
12
|
import {mouthSourceFrame,mouthAnchor,projectMouth} from './puppy-mouth-anchor.mjs';
|
|
13
|
+
import {trickClipEdges} from './puppy-paper-trick-timing.mjs';
|
|
12
14
|
|
|
13
15
|
// Fixed viewer: slightly closer/wider lens gives depth without changing the art.
|
|
14
16
|
export {PAPER_CAMERA} from './puppy-paper-world.mjs';
|
|
15
17
|
|
|
18
|
+
// Comfort poses reuse existing clips only. Belly holds the calibrated roll-down
|
|
19
|
+
// source exactly at the start of the edge leaving `key` (paws-up belly), easing
|
|
20
|
+
// in/out with zero speed at the hold. `hold` defaults to the v9 keyOrder/edge
|
|
21
|
+
// boundary of roll7; createPaperComfortTimings re-derives it from action info.
|
|
22
|
+
export const PAPER_COMFORT_LIMITS=Object.freeze({belly:30,sleep:120,tug:30});
|
|
23
|
+
export const PAPER_COMFORT_TIMINGS=Object.freeze({belly:Object.freeze({key:'roll7',hold:.4449823183425605,enter:1.3,exit:1.7}),tugExit:.35});
|
|
24
|
+
export function createPaperComfortTimings(actionInfo,{key=PAPER_COMFORT_TIMINGS.belly.key}={}){
|
|
25
|
+
const {keys,bounds}=trickClipEdges('roll-down',actionInfo),i=keys.findIndex(k=>k[0]===key);
|
|
26
|
+
if(i<=0)throw Error(`Invalid comfort timing: no roll-down edge leaves ${key}`);
|
|
27
|
+
return Object.freeze({...PAPER_COMFORT_TIMINGS,belly:Object.freeze({...PAPER_COMFORT_TIMINGS.belly,key,hold:bounds[i]})});
|
|
28
|
+
}
|
|
29
|
+
const comfortDuration=(name,seconds)=>{if(typeof seconds!=='number'||!Number.isFinite(seconds)||seconds<=0||seconds>PAPER_COMFORT_LIMITS[name])throw Error(`Invalid ${name} duration`);};
|
|
30
|
+
const comfortEase=u=>u<=0?0:u>=1?1:u*u*(3-2*u);
|
|
31
|
+
|
|
16
32
|
// Original artwork only. Physics supplies ground travel; no procedural dog is drawn.
|
|
17
|
-
export function createPaperActor(animal,info,{manualTricks=false,trickTimings=null,walkTransitions=false}={}){
|
|
33
|
+
export function createPaperActor(animal,info,{manualTricks=false,trickTimings=null,walkTransitions=false,comfortTimings=PAPER_COMFORT_TIMINGS}={}){
|
|
34
|
+
const bellyTiming=comfortTimings?.belly;
|
|
35
|
+
if(!bellyTiming||![bellyTiming.hold,bellyTiming.enter,bellyTiming.exit,comfortTimings.tugExit].every(Number.isFinite)||bellyTiming.hold<=0||bellyTiming.hold>=1||bellyTiming.enter<=0||bellyTiming.exit<=0||comfortTimings.tugExit<=0)throw Error('Invalid comfort timing');
|
|
18
36
|
let state='idle',age=0,phase=0,flip=false,nextFlip=false,target=null,route=false,routeIndex=0,view='front',nextView='front',turnView='front',turnFlip=false;
|
|
19
37
|
let held=null,waypoints=[];
|
|
20
38
|
let preparationRate=1;
|
|
@@ -22,6 +40,7 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
22
40
|
let straight=false,seatProgress=0,seatVariant='default',travel=null,approachPlan=null,travelAge=0,travelDistance=0,motionSpeed=0,motionStage='rest',completed=false;
|
|
23
41
|
let watchSide='left',restProgress=0,idleHold=2.5;
|
|
24
42
|
let pendingAction=null,activeAction=null,actionDone=null,deferred=null,movementMode='run',stayDown=false,manualDown=false,spinReady=false;
|
|
43
|
+
let comfort=null,comfortSeconds=0;
|
|
25
44
|
const director=createPaperDirector();
|
|
26
45
|
const points=[[.12,PAPER_GROUND.away]];
|
|
27
46
|
const enter=s=>{state=s;age=0;};
|
|
@@ -33,13 +52,31 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
33
52
|
const turnSeconds=()=>snappyPreparation()?.22:.85;
|
|
34
53
|
const launchSeconds=()=>snappyPreparation()?.10:.25;
|
|
35
54
|
const dispatch=(fn,{keepDown=false}={})=>{
|
|
36
|
-
if(activeAction||(movementMode==='walk'&&state==='run')){route=false;deferred=()=>dispatch(fn,{keepDown});return true;}
|
|
55
|
+
if(activeAction||state==='tug'||(movementMode==='walk'&&state==='run')){route=false;deferred=()=>dispatch(fn,{keepDown});hurryComfort();return true;}
|
|
37
56
|
// The calibrated trick down pose has its own byte-shared recovery; never
|
|
38
57
|
// jump back into the older/larger rest illustration on another command.
|
|
39
58
|
if(manualDown&&!keepDown){route=false;deferred=fn;playAction('lie-up',()=>{manualDown=false;view='approach';seatProgress=1;seatVariant='default';enter('idle');drain();},.55);return true;}
|
|
40
59
|
return fn();
|
|
41
60
|
};
|
|
42
61
|
const drain=()=>{const fn=deferred;deferred=null;if(fn){fn();return true;}return false;};
|
|
62
|
+
// Belly: 0 -> hold eased to rest, held exact source sample, eased hold -> 1.
|
|
63
|
+
// Queued commands cut the remaining hold only; the roll always finishes at seat.
|
|
64
|
+
const bellyAction=seconds=>{
|
|
65
|
+
const {hold:H,enter:a,exit:b}=bellyTiming,t={comfort:'belly',hold:[a,a+seconds],duration:a+seconds+b};
|
|
66
|
+
t.progressAt=age=>{if(!Number.isFinite(age))throw Error('Invalid comfort age');return age<t.hold[0]?H*comfortEase(age/a):age<t.hold[1]?H:H+(1-H)*comfortEase((age-t.hold[1])/b);};
|
|
67
|
+
return t;
|
|
68
|
+
};
|
|
69
|
+
const startBelly=seconds=>{
|
|
70
|
+
manualDown=false;stayDown=false;restProgress=0;comfort=null;
|
|
71
|
+
const timing=bellyAction(seconds);activeAction={name:'roll-down',duration:timing.duration,timing,comfort:'belly'};actionDone=()=>finishTrick('roll');stop();enter('action');
|
|
72
|
+
if(deferred)hurryComfort();
|
|
73
|
+
};
|
|
74
|
+
const tugProgress=()=>{if(!comfort||comfort.kind!=='tug')return 0;const h=comfort.hurry;return h?(h.length>0?Math.min(1,h.progress+(1-h.progress)*(age-h.age)/h.length):1):Math.min(1,age/comfort.seconds);};
|
|
75
|
+
function hurryComfort(){
|
|
76
|
+
const t=activeAction?.timing;
|
|
77
|
+
if(t?.comfort==='belly'&&age<t.hold[1]){t.hold[1]=Math.max(t.hold[0],age);t.duration=activeAction.duration=t.hold[1]+bellyTiming.exit;}
|
|
78
|
+
else if(state==='tug'&&comfort&&!comfort.hurry){const progress=tugProgress();comfort.hurry={progress,age,length:Math.min(comfortTimings.tugExit,(1-progress)*comfort.seconds)};}
|
|
79
|
+
}
|
|
43
80
|
const playAction=(name,done,duration=PAPER_ACTION_DURATIONS[name])=>{const timing=trickTimings?.[name];duration=timing?.duration??duration;if(!Number.isFinite(duration)||duration<=0)throw Error('Invalid paper action duration');activeAction={name,duration,timing};actionDone=done;stop();enter('action');};
|
|
44
81
|
const finishTrick=name=>{if(name!=='spin'){view='approach';flip=false;seatProgress=1;seatVariant='default';enter('idle');}else{view='front';flip=false;seatProgress=0;enter('settle');}drain();};
|
|
45
82
|
const issue=(skipEntry=false)=>{
|
|
@@ -61,6 +98,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
61
98
|
if(walkTransitions&&name==='spin'&&!spinReady){spinReady=true;enter('launch');return;}
|
|
62
99
|
pendingAction=null;
|
|
63
100
|
if(name==='sit'){enter('idle');return;}
|
|
101
|
+
if(name==='belly'){const seconds=comfortSeconds;playAction('lie-down',()=>startBelly(seconds),.65);return;}
|
|
102
|
+
if(name==='sleep'){stayDown=false;comfort={kind:'sleep',seconds:comfortSeconds};enter('recline');return;}
|
|
103
|
+
if(name==='tug'){stop();comfort={kind:'tug',seconds:comfortSeconds};enter('tug');return;}
|
|
64
104
|
if(name==='lie'){
|
|
65
105
|
if(manualTricks){playAction('lie-down',()=>{manualDown=true;enter('down');drain();},.65);}
|
|
66
106
|
else{stayDown=true;enter('recline');}
|
|
@@ -73,7 +113,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
73
113
|
preparationRate=1;
|
|
74
114
|
if(name==='roll'&&manualDown&&state==='down'){route=false;target=null;waypoints=[];pendingAction=null;stayDown=false;manualDown=false;restProgress=0;playAction('roll-down',()=>finishTrick('roll'));return true;}
|
|
75
115
|
if(name==='roll'&&state==='down'&&restProgress===.25){route=false;target=null;waypoints=[];pendingAction=null;stayDown=false;restProgress=0;playAction('roll-down',()=>finishTrick('roll'));return true;}
|
|
76
|
-
route=false;target=null;waypoints=[];pendingAction=
|
|
116
|
+
if(name==='belly'&&state==='down'&&(manualDown||restProgress===.25)){route=false;target=null;waypoints=[];pendingAction=null;startBelly(comfortSeconds);return true;}
|
|
117
|
+
if(name==='sleep'&&state==='sleep'&&comfort?.kind==='sleep'){comfort.seconds=age+comfortSeconds;return true;}
|
|
118
|
+
comfort=null;route=false;target=null;waypoints=[];pendingAction=name;spinReady=false;completed=false;stayDown=false;movementMode='run';stop();
|
|
77
119
|
if(restProgress>0||state==='recline'||state==='wake')enter('wake');
|
|
78
120
|
else if(['watch','view-turn','turn','arrival-exit'].includes(state)){/* reach a shared endpoint */}
|
|
79
121
|
else if(['run','finish','launch','settle'].includes(state))enter('finish');
|
|
@@ -87,7 +129,7 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
87
129
|
if(![x,z].every(Number.isFinite))throw Error('Invalid paper target');
|
|
88
130
|
preparationRate=urgent?3:1;
|
|
89
131
|
target=paperGroundTarget(x,z);
|
|
90
|
-
pendingAction=null;stayDown=false;movementMode=walking?'walk':'run';
|
|
132
|
+
pendingAction=null;stayDown=false;comfort=null;movementMode=walking?'walk':'run';
|
|
91
133
|
const dz=target[1]-animal.position.z,dx=target[0]-animal.position.x;
|
|
92
134
|
waypoints=[];
|
|
93
135
|
// Do not drag a straight rear/front drawing diagonally across the floor.
|
|
@@ -116,10 +158,14 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
116
158
|
walk(side,distance=1.05){if(!['left','right'].includes(side)||!Number.isFinite(distance)||distance<=0)throw Error('Invalid walk command');return dispatch(()=>{const [x,z]=paperWalkTarget(animal.position.x,animal.position.z,side==='left'?-1:1,distance);if(Math.abs(x-animal.position.x)<1e-6)return false;route=false;go(x,z,{walking:true});return true;});},
|
|
117
159
|
look(side='left'){if(state!=='idle'||seatProgress!==1)return false;route=false;watchSide=side==='right'?'right':'left';enter('watch');return true;},
|
|
118
160
|
lie(){const name=manualDown||activeAction?.name==='lie-down'||restProgress>0?'sit':'lie';return dispatch(()=>requestAction(name));},
|
|
119
|
-
|
|
161
|
+
// Comfort poses: bounded finite seconds; same dispatch safety as tricks.
|
|
162
|
+
belly(seconds=6){comfortDuration('belly',seconds);return dispatch(()=>{comfortSeconds=seconds;return requestAction('belly');},{keepDown:true});},
|
|
163
|
+
sleep(seconds=25){comfortDuration('sleep',seconds);return dispatch(()=>{comfortSeconds=seconds;return requestAction('sleep');});},
|
|
164
|
+
tug(seconds=6){comfortDuration('tug',seconds);return dispatch(()=>{comfortSeconds=seconds;return requestAction('tug');});},
|
|
165
|
+
rest(){return dispatch(()=>{route=false;target=null;waypoints=[];pendingAction=null;stayDown=false;comfort=null;stop();if(restProgress>0){enter('wake');}else if(state==='watch'||state==='arrival-exit'){/* finish shared return */}else if(seatProgress>0||state==='rise'||state==='sit-down')enter('sit-down');else if(!['idle','view-turn','turn'].includes(state))enter('finish');});},
|
|
120
166
|
update(dt){
|
|
121
167
|
if(!Number.isFinite(dt)||dt<0||dt>.1)throw Error('Invalid paper step');
|
|
122
|
-
const
|
|
168
|
+
const beforeX=animal.position.x,beforeZ=animal.position.z;
|
|
123
169
|
if(state==='run'&&travel&&target){
|
|
124
170
|
travelAge+=dt;const p=travel.plan.at(travelAge),ratio=travel.plan.distance?p.distance/travel.plan.distance:1;
|
|
125
171
|
travelDistance=p.distance;motionSpeed=p.speed;motionStage=p.stage;
|
|
@@ -130,7 +176,7 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
130
176
|
animal.update(dt);
|
|
131
177
|
const prepareDt=dt*preparationRate;
|
|
132
178
|
age+=['view-turn','turn','rise','watch','arrival-exit','launch','wake'].includes(state)?prepareDt:dt;
|
|
133
|
-
const distance=Math.hypot(animal.position.x-
|
|
179
|
+
const distance=Math.hypot(animal.position.x-beforeX,animal.position.z-beforeZ);
|
|
134
180
|
director.update(dt,state==='run',distance);
|
|
135
181
|
if(state==='run'){
|
|
136
182
|
phase+=distance/travel.stride;
|
|
@@ -151,7 +197,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
151
197
|
else if(state==='rise'){seatProgress=Math.max(0,seatProgress-prepareDt/(snappyPreparation()?.28:.65));if(seatProgress===0){if(seatVariant==='arrival')enter('arrival-exit');else prepareDirection();}}
|
|
152
198
|
else if(state==='arrival-exit'&&age>=.22){seatVariant='default';prepareDirection();}
|
|
153
199
|
else if(state==='watch'&&age>=1.4){if(pendingAction)prepareDirection();else if(target)enter('rise');else enter('idle');}
|
|
154
|
-
else if(state==='recline'){const end=stayDown?.25:1;restProgress=Math.min(end,restProgress+dt/1.8);if(restProgress===end)enter(stayDown?'down':'nap');}
|
|
200
|
+
else if(state==='recline'){const end=stayDown?.25:1;restProgress=Math.min(end,restProgress+dt/1.8);if(restProgress===end)enter(stayDown?'down':comfort?.kind==='sleep'?'sleep':'nap');}
|
|
201
|
+
else if(state==='sleep'&&age>=comfort.seconds){comfort=null;enter('wake');}
|
|
202
|
+
else if(state==='tug'&&tugProgress()>=1){comfort=null;enter('idle');drain();}
|
|
155
203
|
else if(state==='nap'&&age>=5&&!stayDown){enter('wake');}
|
|
156
204
|
else if(state==='wake'){restProgress=Math.max(0,restProgress-prepareDt/1.2);if(restProgress===0){if(pendingAction)prepareDirection();else if(target)enter('rise');else enter('idle');}}
|
|
157
205
|
else if(state==='idle'&&route&&age>=idleHold)nextRoute();
|
|
@@ -161,7 +209,8 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
161
209
|
if(![12,24,30].includes(poseFps))throw Error('Invalid pose cadence');
|
|
162
210
|
let pair=20,blend=0,kind=state,rearPair=null,approachPair=null;
|
|
163
211
|
let seat=state==='sit-down'||state==='rise'||seatProgress>0?Math.round(seatProgress*48)/48:null;
|
|
164
|
-
let behavior=null,behaviorProgress=0;
|
|
212
|
+
let behavior=null,behaviorProgress=0,comfortPose=state==='tug'?{clip:'tug',progress:tugProgress()}:null;
|
|
213
|
+
if(activeAction?.comfort)kind=activeAction.comfort;
|
|
165
214
|
let actionName=activeAction?.name??(state==='run'&&movementMode==='walk'?'walk':null),actionProgress=activeAction?(activeAction.timing?activeAction.timing.progressAt(age):Math.min(1,age/activeAction.duration)):phase;
|
|
166
215
|
if(manualDown&&state==='down'){actionName='lie-down';actionProgress=1;}
|
|
167
216
|
if(walkTransitions&&state==='view-turn'&&turnView==='front'){
|
|
@@ -192,13 +241,13 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
192
241
|
// Deliberate held source poses: no live frame blending or face/body deformation.
|
|
193
242
|
blend=Math.round(blend*16)/16;
|
|
194
243
|
const key=`${state}/${actionName}/${view}/${turnView}/${poseFps}/${Math.floor(clock*poseFps)}`;
|
|
195
|
-
if(held?.key===key){pair=held.pair;blend=held.blend;rearPair=held.rearPair;approachPair=held.approachPair;seat=held.seat;behavior=held.behavior;behaviorProgress=held.behaviorProgress;actionName=held.actionName;actionProgress=held.actionProgress;}else held={key,pair,blend,rearPair,approachPair,seat,behavior,behaviorProgress,actionName,actionProgress,posePhase:phase};
|
|
196
|
-
return{state:state==='run'||state==='finish'?'run':kind,pair,blend,rearPair,approachPair,seatProgress:seat,seatVariant,behavior,behaviorProgress,actionName,actionProgress,queued:!!deferred,view,straight,flip:state==='turn'&&!walkTransitions?false:flip,phase,posePhase:held.posePhase,stride:travel?.stride??PAPER_GAITS.front.stride,heading:travel?.heading??0,speed:motionSpeed,motionStage,travelDistance,energy:director.report.energy,x:animal.position.x,z:animal.position.z};
|
|
244
|
+
if(held?.key===key){pair=held.pair;blend=held.blend;rearPair=held.rearPair;approachPair=held.approachPair;seat=held.seat;behavior=held.behavior;behaviorProgress=held.behaviorProgress;actionName=held.actionName;actionProgress=held.actionProgress;comfortPose=held.comfortPose;}else held={key,pair,blend,rearPair,approachPair,seat,behavior,behaviorProgress,actionName,actionProgress,comfortPose,posePhase:phase};
|
|
245
|
+
return{state:state==='run'||state==='finish'?'run':kind,pair,blend,rearPair,approachPair,seatProgress:seat,seatVariant,behavior,behaviorProgress,actionName,actionProgress,queued:!!deferred,view,straight,flip:state==='turn'&&!walkTransitions?false:flip,phase,posePhase:held.posePhase,stride:travel?.stride??PAPER_GAITS.front.stride,heading:travel?.heading??0,speed:motionSpeed,motionStage,travelDistance,energy:director.report.energy,x:animal.position.x,z:animal.position.z,...(comfortPose?{comfortPose:{...comfortPose}}:{})};
|
|
197
246
|
},get state(){return state;}
|
|
198
247
|
};
|
|
199
248
|
}
|
|
200
249
|
|
|
201
|
-
export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,seat,arrivalSeat,behaviors,actions,interactions,contacts=true,pawTransport=false}={}){
|
|
250
|
+
export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,seat,arrivalSeat,behaviors,actions,interactions,comfort,contacts=true,pawTransport=false}={}){
|
|
202
251
|
if(![width,height].every(Number.isInteger)||width<2||height<2||width*height>1_000_000)throw Error('Invalid paper grid');
|
|
203
252
|
const tween=createCachedTween(cache,info),renderMeta={...meta,pairs:info.pair_map,run_sequence:info.run_sequence};
|
|
204
253
|
const out=new Uint8ClampedArray(width*height*4),area=createPaperAreaSampler();
|
|
@@ -208,6 +257,7 @@ export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,
|
|
|
208
257
|
const arrivalSeatSample=arrivalSeat?createPaperSeatSampler(arrivalSeat.bytes,arrivalSeat.info):null;
|
|
209
258
|
const behaviorSample=behaviors?createPaperBehaviorSampler(behaviors.bytes,behaviors.info):null;
|
|
210
259
|
const interactionSample=interactions?createCompanionSpriteSampler(interactions.bytes,interactions.info):null;
|
|
260
|
+
const comfortSample=comfort?createComfortSpriteSampler(comfort.bytes,comfort.info):null;
|
|
211
261
|
const actionSample=actions?createPaperActionSampler(actions.bytes,actions.info):null,actionArea=actions?createPaperAreaSampler(actions.info.width):null;
|
|
212
262
|
const walkEpisodes=actions?createPaperSupportEpisodes(actions.info.feet,actions.info.clips.walk,9):null;
|
|
213
263
|
const sourceEpisodes=rear?{
|
|
@@ -222,10 +272,11 @@ export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,
|
|
|
222
272
|
const isApproach=state.approachPair!==null&&state.approachPair!==undefined&&approachSample;
|
|
223
273
|
const isSeat=state.seatProgress!==null&&state.seatProgress!==undefined&&seatSample;
|
|
224
274
|
if(!Number.isFinite(pixelAspect)||pixelAspect<.1||pixelAspect>10)throw Error('Invalid dot pixel aspect');
|
|
225
|
-
const
|
|
275
|
+
const isComfort=state.comfortPose&&comfortSample;
|
|
276
|
+
const isInteraction=isComfort||state.interactionPose&&interactionSample;
|
|
226
277
|
const isBehavior=!isInteraction&&state.behavior&&behaviorSample;
|
|
227
278
|
const isAction=!isInteraction&&state.actionName&&actionSample,sourceN=isAction?actions.info.width:320,originX=(sourceN-320)/2,originY=(sourceN/320-1)*300;
|
|
228
|
-
const image=isInteraction?interactionSample(state.interactionPose):isAction?actionSample(state):isBehavior?behaviorSample(state):isSeat?(state.seatVariant==='arrival'&&arrivalSeatSample?arrivalSeatSample:seatSample)(state.seatProgress):isApproach?approachSample(state.approachPair,state.blend):isRear?rearSample(state.rearPair,state.blend):tween(state),side=height*size,sideX=side/pixelAspect;
|
|
279
|
+
const image=isComfort?comfortSample(state.comfortPose):isInteraction?interactionSample(state.interactionPose):isAction?actionSample(state):isBehavior?behaviorSample(state):isSeat?(state.seatVariant==='arrival'&&arrivalSeatSample?arrivalSeatSample:seatSample)(state.seatProgress):isApproach?approachSample(state.approachPair,state.blend):isRear?rearSample(state.rearPair,state.blend):tween(state),side=height*size,sideX=side/pixelAspect;
|
|
229
280
|
// Quantize source lookup only, never the projected rectangle. Rounding side,
|
|
230
281
|
// left and top first introduced whole-body scale/position jumps at thresholds.
|
|
231
282
|
const left=width*x-sideX/2,floor=isInteraction||isAction||isBehavior||isSeat||isRear||isApproach?0:cleanGroundOffset(state,renderMeta),top=height*y-(300-floor)/320*side;
|
package/retrieviewer-cli.mjs
CHANGED
|
@@ -23,8 +23,10 @@ Enable opens a first-run selection/confirmation wizard on a real terminal.
|
|
|
23
23
|
Non-interactive changes require --agent and --yes. Unsupported native renderers
|
|
24
24
|
are refused: MCP connectivity alone is never treated as in-screen rendering.
|
|
25
25
|
No command changes credentials, shell profiles or agent executables.
|
|
26
|
-
Desktop is a separate
|
|
26
|
+
Desktop is a separate native overlay, NOT a Codex plugin. Choose a window once;
|
|
27
27
|
use Codex normally. Exit from its tray icon or desktop-stop. No auto-start is added.
|
|
28
|
+
Windows is verified; macOS and Linux X11 adapters are experimental. Native Wayland
|
|
29
|
+
app tracking is not supported. Linux targets AlmaLinux8/GLIBC2.28, system Python3.6+.
|
|
28
30
|
`;
|
|
29
31
|
export function parseArguments(args){
|
|
30
32
|
const [command='help',...rest]=args,options={};
|
|
@@ -88,7 +90,12 @@ export async function runCli(args,{input=process.stdin,output=process.stdout,err
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
// CLI is deliberately a separate module from the side-effect-free library.
|
|
91
|
-
import {
|
|
92
|
-
|
|
93
|
+
import {fileURLToPath} from 'node:url';
|
|
94
|
+
import {realpathSync} from 'node:fs';
|
|
95
|
+
// npm global bins are symlinks on Unix. import.meta resolves the real module,
|
|
96
|
+
// whereas argv[1] keeps the bin link; comparing those raw URLs silently did nothing.
|
|
97
|
+
let main=false;
|
|
98
|
+
try{main=!!process.argv[1]&&realpathSync(process.argv[1])===realpathSync(fileURLToPath(import.meta.url));}catch{}
|
|
99
|
+
if(main){
|
|
93
100
|
try{process.exitCode=await runCli(process.argv.slice(2));}catch(e){process.stderr.write('Retrieviewer: '+e.message+'\n');process.exitCode=1;}
|
|
94
101
|
}
|
|
@@ -36,7 +36,7 @@ export interface PetPlugin {
|
|
|
36
36
|
/** Host-owned loop: advance seconds then render. False while hidden. Throws while running. */
|
|
37
37
|
tick(dtSeconds: number): boolean;
|
|
38
38
|
/** False when busy or cooling down. */
|
|
39
|
-
interact(kind: 'ball' | 'treat'): boolean;
|
|
39
|
+
interact(kind: 'ball' | 'treat' | 'tug'): boolean;
|
|
40
40
|
readonly running: boolean;
|
|
41
41
|
readonly disposed: boolean;
|
|
42
42
|
dispose(): void;
|
package/retrieviewer-plugin.mjs
CHANGED
|
@@ -82,7 +82,7 @@ export async function createPetPlugin({host,petOptions,fps=30,scheduler}={}){
|
|
|
82
82
|
if(typeof dtSeconds!=='number'||!Number.isFinite(dtSeconds)||dtSeconds<0)throw RangeError('dtSeconds must be finite non-negative seconds');
|
|
83
83
|
try{return draw(dtSeconds);}catch(error){halt();throw error;}
|
|
84
84
|
},
|
|
85
|
-
// 'ball' | 'treat'. False when busy or cooling down; shows on the next frame.
|
|
85
|
+
// 'ball' | 'treat' | 'tug'. False when busy or cooling down; shows on the next frame.
|
|
86
86
|
interact(kind){alive();return pet.interact(kind);},
|
|
87
87
|
get running(){return running;},
|
|
88
88
|
get disposed(){return disposed;},
|
package/retrieviewer.d.mts
CHANGED
|
@@ -35,16 +35,22 @@ export interface PetState {
|
|
|
35
35
|
[key: string]: unknown;
|
|
36
36
|
}
|
|
37
37
|
export interface LifeReport {
|
|
38
|
+
/** Last selected autonomous episode; null before the first decision. */
|
|
39
|
+
episode: string | null;
|
|
38
40
|
intent: string;
|
|
39
41
|
label: string;
|
|
40
|
-
interaction: 'ball' | 'treat' | null;
|
|
42
|
+
interaction: 'ball' | 'treat' | 'tug' | null;
|
|
41
43
|
busy: boolean;
|
|
42
44
|
energy: number;
|
|
43
45
|
decisions: number;
|
|
44
|
-
cooldowns: {ball: number; treat: number};
|
|
46
|
+
cooldowns: {ball: number; treat: number; tug: number};
|
|
45
47
|
gesture: {clip: string; progress: number} | null;
|
|
46
48
|
ball: {x: number; z: number; height: number; carried: boolean} | null;
|
|
47
49
|
treat: {x: number; z: number; height: number} | null;
|
|
50
|
+
/** Tug-of-war state while a tug interaction is active. */
|
|
51
|
+
tug: {progress: number; grip: number} | null;
|
|
52
|
+
/** Silent rising sleep cue, normalized cycle progress; no audio process. */
|
|
53
|
+
snore: number | null;
|
|
48
54
|
}
|
|
49
55
|
export interface PetFrame {
|
|
50
56
|
artProfile: 'original' | 'q5';
|
|
@@ -70,7 +76,7 @@ export interface Pet {
|
|
|
70
76
|
step(dt: number): number;
|
|
71
77
|
frame(options: FrameOptions): PetFrame;
|
|
72
78
|
/** False when busy or cooling down. */
|
|
73
|
-
interact(kind: 'ball' | 'treat'): boolean;
|
|
79
|
+
interact(kind: 'ball' | 'treat' | 'tug'): boolean;
|
|
74
80
|
readonly disposed: boolean;
|
|
75
81
|
dispose(): void;
|
|
76
82
|
}
|
|
@@ -78,7 +84,7 @@ export function createPet(options?: PetOptions): Promise<Pet>;
|
|
|
78
84
|
export function defaultLoadAsset(name: string): Promise<ArrayBuffer | Uint8Array>;
|
|
79
85
|
export const STEP_SECONDS: number;
|
|
80
86
|
export const MAX_CATCH_UP_SECONDS: number;
|
|
81
|
-
export const INTERACTIONS: readonly ['ball', 'treat'];
|
|
87
|
+
export const INTERACTIONS: readonly ['ball', 'treat', 'tug'];
|
|
82
88
|
export const ART_PROFILES: readonly ['original', 'q5'];
|
|
83
89
|
export const DEFAULT_ART_PROFILE: 'q5';
|
|
84
90
|
export function getPetAssetFiles(artProfile?: 'original' | 'q5'): readonly string[];
|
package/retrieviewer.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {createPaperProjection} from './puppy-paper-world.mjs';
|
|
1
|
+
import {createPaperActor,createPaperRenderer,createPaperComfortTimings} from './puppy-paper.mjs';
|
|
2
|
+
import {createPaperProjection,createPaperBody} from './puppy-paper-world.mjs';
|
|
4
3
|
import {rgbaToBraille} from './braille.mjs';
|
|
5
4
|
import {createSnappyTrickTimings} from './puppy-paper-trick-timing.mjs';
|
|
6
5
|
import {createCompanionLife} from './puppy-companion-life.mjs';
|
|
@@ -11,24 +10,25 @@ import {readPackedFrames} from './puppy-frame-store.mjs';
|
|
|
11
10
|
// or listener side effects: the host owns the loop and calls step() then frame().
|
|
12
11
|
export const STEP_SECONDS=1/60;
|
|
13
12
|
export const MAX_CATCH_UP_SECONDS=.1;
|
|
14
|
-
export const INTERACTIONS=Object.freeze(['ball','treat']);
|
|
13
|
+
export const INTERACTIONS=Object.freeze(['ball','treat','tug']);
|
|
15
14
|
// Approved 32-level art ships by default; preserved original art is optional.
|
|
16
15
|
export const DEFAULT_ART_PROFILE='q5';
|
|
17
16
|
export const ART_PROFILES=Object.freeze(['original','q5']);
|
|
18
17
|
// Current companion runtime: v9 tricks + interactions-v3, same as puppy-paper.html.
|
|
19
18
|
export const PET_ASSETS=Object.freeze({
|
|
19
|
+
// Kept as a legacy identifier only; createPet/getPetAssetFiles no longer load it.
|
|
20
20
|
animal:'locomotion-puppy.animal',
|
|
21
21
|
stride:'puppy-stride-tweens-q5.bin.gz',strideInfo:'puppy-stride-meta.json',stableInfo:'puppy-stable-meta.json',
|
|
22
22
|
rear:'puppy-paper-rear-gallop',approach:'puppy-paper-approach',seat:'puppy-paper-seat',arrivalSeat:'puppy-paper-arrival-seat',
|
|
23
|
-
behaviors:'puppy-paper-behaviors',actions:'puppy-paper-actions-v9',interactions:'puppy-companion-interactions-v3',
|
|
23
|
+
behaviors:'puppy-paper-behaviors',actions:'puppy-paper-actions-v9',interactions:'puppy-companion-interactions-v3',comfort:'puppy-companion-comfort',
|
|
24
24
|
});
|
|
25
|
-
const BUNDLES=['rear','approach','seat','arrivalSeat','behaviors','actions','interactions'];
|
|
25
|
+
const BUNDLES=['rear','approach','seat','arrivalSeat','behaviors','actions','interactions','comfort'];
|
|
26
26
|
const frameFile=(base,profile)=>base+(profile==='q5'?'-q5.bin.gz':'-compact.bin.gz');
|
|
27
27
|
/** Exact files a host/bundler must copy, including metadata. Never loads assets. */
|
|
28
28
|
export function getPetAssetFiles(artProfile=DEFAULT_ART_PROFILE){
|
|
29
29
|
if(!ART_PROFILES.includes(artProfile))throw RangeError('artProfile must be original or q5');
|
|
30
30
|
return Object.freeze([PET_ASSETS.strideInfo,PET_ASSETS.stableInfo,frameFile('puppy-stride-tweens',artProfile),
|
|
31
|
-
...BUNDLES.flatMap(key=>[PET_ASSETS[key]+'.json',frameFile(PET_ASSETS[key],artProfile)])
|
|
31
|
+
...BUNDLES.flatMap(key=>[PET_ASSETS[key]+'.json',frameFile(PET_ASSETS[key],artProfile)])]);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Relative to this module, never the process cwd: file: URLs in Node, HTTP in browsers.
|
|
@@ -62,13 +62,19 @@ const sharedAssets=new Map();
|
|
|
62
62
|
async function loadPetAssets(loadAsset,artProfile){
|
|
63
63
|
const load=async name=>bytesOf(name,await loadAsset(name));
|
|
64
64
|
const json=async name=>JSON.parse(new TextDecoder().decode(await load(name)));
|
|
65
|
-
const frames=async name=>readPackedFrames(await gunzip(await load(name)));
|
|
65
|
+
const frames=async (name,options)=>readPackedFrames(await gunzip(await load(name)),options);
|
|
66
66
|
// One family at a time: never inflate all eight legacy-sized buffers together.
|
|
67
67
|
const data={assets:{}};
|
|
68
68
|
data.info=await json(PET_ASSETS.strideInfo);data.meta=await json(PET_ASSETS.stableInfo);
|
|
69
69
|
data.cache=await frames(frameFile('puppy-stride-tweens',artProfile));
|
|
70
|
-
for(const key of BUNDLES){
|
|
71
|
-
|
|
70
|
+
for(const key of BUNDLES){
|
|
71
|
+
const base=PET_ASSETS[key],info=await json(base+'.json');
|
|
72
|
+
// Old interaction sheets include preparation frames no runtime clip can reach.
|
|
73
|
+
// Keep exact active samples only, leaving room for small new gestures without
|
|
74
|
+
// keeping another multi-megabyte art family resident. Original RVF1 unchanged.
|
|
75
|
+
const options=key==='interactions'&&artProfile==='q5'?{frameIndices:[...new Set(Object.values(info.clips).flatMap(list=>list.flatMap(p=>Array.from({length:info.samples},(_,s)=>p*info.samples+s))))]}:undefined;
|
|
76
|
+
data.assets[key]={info,bytes:await frames(frameFile(base,artProfile),options)};
|
|
77
|
+
}
|
|
72
78
|
return data;
|
|
73
79
|
}
|
|
74
80
|
async function acquireAssets(loadAsset,artProfile){
|
|
@@ -90,12 +96,12 @@ export async function createPet({loadAsset=defaultLoadAsset,seed,energy,pawTrans
|
|
|
90
96
|
if(!ART_PROFILES.includes(artProfile))throw RangeError('artProfile must be original or q5');
|
|
91
97
|
let lease=await acquireAssets(loadAsset,artProfile),data=lease.data;
|
|
92
98
|
const release=lease.release;lease=null;
|
|
93
|
-
let {cache,info,meta
|
|
99
|
+
let {cache,info,meta}=data;const assets={...data.assets};data=null;
|
|
94
100
|
let animal,actor,life;
|
|
95
101
|
try{
|
|
96
102
|
if(!assets.actions.info.clips?.['lie-down']||!assets.actions.info.clips?.['lie-up'])throw Error('v9 action cache lacks lie-down/lie-up clips');
|
|
97
|
-
animal=
|
|
98
|
-
actor=createPaperActor(animal,info,{manualTricks:true,trickTimings:createSnappyTrickTimings(assets.actions.info),walkTransitions:true});
|
|
103
|
+
animal=createPaperBody();
|
|
104
|
+
actor=createPaperActor(animal,info,{manualTricks:true,trickTimings:createSnappyTrickTimings(assets.actions.info),comfortTimings:createPaperComfortTimings(assets.actions.info),walkTransitions:true});
|
|
99
105
|
life=createCompanionLife(actor,{seed,...energy===undefined?{}:{energy}});
|
|
100
106
|
}catch(e){animal?.dispose?.();release();throw e;}
|
|
101
107
|
let paint=null,paintKey='',time=0,acc=0,disposed=false;
|
|
@@ -127,7 +133,7 @@ export async function createPet({loadAsset=defaultLoadAsset,seed,energy,pawTrans
|
|
|
127
133
|
const {text}=rgbaToBraille(pixels,width,height,{method:'stable',brightness:1.18,contrast:1.1});
|
|
128
134
|
return {text,columns,rows,pixels,state,report,placement,projection,artProfile,mouth:paint.lastMouth,contacts:paint.report()};
|
|
129
135
|
},
|
|
130
|
-
// 'ball' | 'treat'. False when busy or cooling down; unknown kinds throw.
|
|
136
|
+
// 'ball' | 'treat' | 'tug'. False when busy or cooling down; unknown kinds throw.
|
|
131
137
|
interact(kind){alive();if(!INTERACTIONS.includes(kind))throw TypeError(`Unknown interaction: ${kind}`);return life.interact(kind);},
|
|
132
138
|
get disposed(){return disposed;},
|
|
133
139
|
dispose(){
|
package/locomotion-core.mjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
import {createAnimal} from './vendor/procedural-animals/src/index.js';
|
|
3
|
-
export const LAB_OPTIONS={variant:'retriever',age:'juvenile',seed:2,quality:'low',worker:false,overrides:{colour:'yellow',stanceSoftMargin:.005}};
|
|
4
|
-
export async function makeLabAnimal(extra={}){
|
|
5
|
-
return createAnimal('dog',{...LAB_OPTIONS,...extra,overrides:{...LAB_OPTIONS.overrides,...extra.overrides}});
|
|
6
|
-
}
|
|
7
|
-
// Never translate the mesh externally. The gait engine owns root motion + IK.
|
|
8
|
-
export function makeLabController(animal){
|
|
9
|
-
let target=null,speed=animal.gears.walk;
|
|
10
|
-
return {get target(){return target;},get speed(){return speed;},setSpeed(kind){
|
|
11
|
-
if(!['walk','trot','gallop'].includes(kind))throw Error('Unknown gait');
|
|
12
|
-
speed=animal.gears[kind];if(target)animal.moveTo(target,{speed});
|
|
13
|
-
},go(x,z){
|
|
14
|
-
if(![x,z].every(Number.isFinite))throw Error('Invalid ground target');
|
|
15
|
-
target=new THREE.Vector3(Math.max(-.8,Math.min(.8,x)),0,Math.max(-1.7,Math.min(.3,z)));
|
|
16
|
-
animal.stopAction();animal.moveTo(target,{speed});
|
|
17
|
-
},stop(){target=null;animal.stop();},update(dt){
|
|
18
|
-
if(!Number.isFinite(dt)||dt<0||dt>.1)throw Error('Invalid lab step');animal.update(dt);
|
|
19
|
-
if(target&&animal.position.distanceTo(target)<.025&&animal.speed<.03){target=null;animal.stop();}
|
|
20
|
-
}};
|
|
21
|
-
}
|
package/locomotion-puppy.animal
DELETED
|
Binary file
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "procedural-animals",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "24 fully procedural animals for three.js: SDF-sculpted bodies, dual-quaternion skinning, GPU fur, feathers and scales, IK locomotion and actions. No models, no textures, no downloads.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Majid Manzarpour",
|
|
7
|
-
"homepage": "https://majidmanzarpour.github.io/threejs-procedural-animals/",
|
|
8
|
-
"repository": { "type": "git", "url": "git+https://github.com/majidmanzarpour/threejs-procedural-animals.git" },
|
|
9
|
-
"bugs": { "url": "https://github.com/majidmanzarpour/threejs-procedural-animals/issues" },
|
|
10
|
-
"type": "module",
|
|
11
|
-
"main": "./src/index.js",
|
|
12
|
-
"module": "./src/index.js",
|
|
13
|
-
"types": "./src/index.d.ts",
|
|
14
|
-
"exports": {
|
|
15
|
-
".": { "types": "./src/index.d.ts", "default": "./src/index.js" },
|
|
16
|
-
"./species/*": "./src/species/*/index.js",
|
|
17
|
-
"./worker": "./src/worker.js",
|
|
18
|
-
"./bake": "./src/bake.js",
|
|
19
|
-
"./package.json": "./package.json"
|
|
20
|
-
},
|
|
21
|
-
"bin": { "procedural-animals-bake": "./tools/bake.mjs" },
|
|
22
|
-
"sideEffects": ["./src/worker.js"],
|
|
23
|
-
"files": [
|
|
24
|
-
"src/**/*.js",
|
|
25
|
-
"src/index.d.ts",
|
|
26
|
-
"tools/bake.mjs",
|
|
27
|
-
"README.md",
|
|
28
|
-
"LICENSE",
|
|
29
|
-
"llms.txt"
|
|
30
|
-
],
|
|
31
|
-
"engines": { "node": ">=18" },
|
|
32
|
-
"peerDependencies": { "three": ">=0.160.0" },
|
|
33
|
-
"devDependencies": { "esbuild": "^0.28.2", "playwright-core": "1.56", "three": "^0.186.0" },
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build:showcase": "node showcase/build.mjs",
|
|
36
|
-
"build:pages": "node showcase/build.mjs --pages",
|
|
37
|
-
"test": "node tools/test.mjs"
|
|
38
|
-
},
|
|
39
|
-
"keywords": ["three", "threejs", "procedural", "animals", "animation", "ik", "fur", "sdf", "creatures", "gamedev", "webgl"]
|
|
40
|
-
}
|