@bitefoundry/retrieviewer 0.0.9 → 0.0.11

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.
@@ -77,36 +77,72 @@
77
77
  "width": 320,
78
78
  "height": 320,
79
79
  "frames": 162,
80
- "uniqueFrames": 142,
80
+ "uniqueFrames": 126,
81
81
  "sourceSha256": "7918261fb64c57a91739729841fd84c6dd53891196c7fb76ee9c27712ab6ef5d",
82
82
  "sourcePackedSha256": "7785ef65b8a2a5bf6f7ce600fe9957a60d9af82066ff49275bba061eac5392d1",
83
- "encodedBytes": 6227235,
84
- "gzipBytes": 1426084,
85
- "residentPixelBytes": 3871386,
86
- "encodedSha256": "783eafb78fac8e004efd4d44c88776ba6cb486ed18092738a4aaf74a6906b8ed",
87
- "quantizedSha256": "f2d89ac5f6fb230b4d5ac6bcf09865185a00900947dbbfc7cbacc40f8f9719b6",
83
+ "encodedBytes": 5797825,
84
+ "gzipBytes": 1270759,
85
+ "residentPixelBytes": 3605128,
86
+ "encodedSha256": "cb045558ca490436c5d6703f6468daba26f20ed76bc886e829787889253ccbb9",
87
+ "quantizedSha256": "eb5007588c9f1843a9279fa21ee701e0f1ac3238d2dda70cc8e8c0c900e3c34b",
88
88
  "watchRegistration": {
89
- "sourceVersion": "0.0.5",
90
- "sourceEncodedSha256": "38cfdfcee9c857c56a14f0a9734cb3301b4a04cb8e1ae3fb2e2b2ffe3666240f",
91
- "canonicalSeat": 50,
92
- "method": "single-source-torso-envelope-and-paw-registration",
93
- "protectedFaceRows": 180,
94
- "torsoWarpStartY": 180,
95
- "torsoEnvelopeFullY": [
96
- 205,
97
- 265
89
+ "sourceSize": [
90
+ 1619,
91
+ 972
98
92
  ],
99
- "pawWarpFullFromY": 275,
100
- "groundRow": 300,
101
- "headTranslateY": 0,
102
- "neckConnected": true,
93
+ "scale": 0.7627737226277372,
94
+ "dx": 24.607664233576628,
95
+ "dy": 66.82846715328466,
96
+ "wholeBodyArt": true,
103
97
  "bodyCompositing": false,
104
- "paws": [
105
- 105,
106
- 138,
107
- 185,
108
- 218
98
+ "collarMatte": false,
99
+ "nonlinearWarp": false,
100
+ "perKeyScale": false,
101
+ "runtimeCompositing": false,
102
+ "pixelBlending": false,
103
+ "sourceKeys": [
104
+ 7,
105
+ 8,
106
+ 9,
107
+ 10,
108
+ 11,
109
+ 11,
110
+ 12,
111
+ 13,
112
+ 14,
113
+ 14,
114
+ 13,
115
+ 12,
116
+ 11,
117
+ 11,
118
+ 10,
119
+ 9,
120
+ 8,
121
+ 7,
122
+ 7,
123
+ 6,
124
+ 5,
125
+ 4,
126
+ 3,
127
+ 3,
128
+ 2,
129
+ 1,
130
+ 0,
131
+ 0,
132
+ 1,
133
+ 2,
134
+ 3,
135
+ 3,
136
+ 4,
137
+ 5,
138
+ 6,
139
+ 7
109
140
  ],
141
+ "keys": 15,
142
+ "frames": 36,
143
+ "sourceSheetSha256": "34b9bbca507f82c739c39e4b42e2390202863189d6d1b5e6e8ca7293555e19d3",
144
+ "canonicalSeat": 50,
145
+ "method": "authored-whole-body-connected-weight-transfer",
110
146
  "unchangedOtherFrames": true
111
147
  }
112
148
  },
@@ -1,5 +1,18 @@
1
1
  import {grayFrameReader,isGrayFrameSource} from './puppy-frame-store.mjs';
2
2
 
3
+ export function comfortMotionKey(progress,motion){
4
+ if(!Number.isFinite(progress)||progress<0||progress>1)throw Error('Invalid motion progress');
5
+ const age=progress*motion.duration;let key=0;
6
+ while(key<motion.times.length-1&&age>=motion.times[key+1])key++;
7
+ return key;
8
+ }
9
+ export function comfortMotion(pose,info){return pose?.clip==='scramble'?info.scramble:pose?.clip==='pickup'?info.pickups?.[pose.view]:pose?.clip==='carry-trot'?info.carryTrot:pose?.clip==='carry-settle'?info.carryTrot?.settle:null;}
10
+ function validateMotion(motion,count){
11
+ if(!motion||!Array.isArray(motion.frames)||!Array.isArray(motion.times)||motion.frames.length<2||motion.frames.length>32||motion.frames.length!==motion.times.length||motion.times[0]!==0||!Number.isFinite(motion.duration)||motion.duration<=0||motion.duration>3||(motion.cyclic?motion.times.at(-1)>=motion.duration:motion.times.at(-1)!==motion.duration)||motion.frames.some(i=>!Number.isInteger(i)||i<0||i>=count)||motion.times.some((t,i)=>!Number.isFinite(t)||(i&&t<=motion.times[i-1])))throw Error('Invalid fetch motion metadata');
12
+ if(!Array.isArray(motion.floorOffsets)||motion.floorOffsets.length!==motion.frames.length||motion.floorOffsets.some(v=>!Number.isFinite(v)||Math.abs(v)>64))throw Error('Invalid fetch floor metadata');
13
+ if(motion.mouth&&(!Array.isArray(motion.mouth)||motion.mouth.length!==motion.frames.length||motion.mouth.some(p=>!Array.isArray(p)||p.length!==2||p.some(v=>!Number.isFinite(v)||v<0||v>320))))throw Error('Invalid fetch mouth metadata');
14
+ }
15
+
3
16
  // A handful of authored keys, not an expanded 30fps film or a live image morph.
4
17
  // First/last keys are the exact shared seated source; all keys use one scale.
5
18
  export function comfortFrameIndex(progress,info){
@@ -13,14 +26,24 @@ export function comfortFrameIndex(progress,info){
13
26
  }
14
27
 
15
28
  export function createComfortSpriteSampler(bytes,info,output){
16
- if(!isGrayFrameSource(bytes)||![1,2].includes(info?.version)||info.width!==320||info.height!==320||info.frames!==(info.version===2?13:9))throw Error('Invalid comfort sprites');
29
+ if(!isGrayFrameSource(bytes)||![1,2,3,4,5,6].includes(info?.version)||info.width!==320||info.height!==320||(info.version>=4?(!Number.isInteger(info.frames)||info.frames<19||info.frames>64):info.frames!==({1:9,2:13,3:19}[info.version])))throw Error('Invalid comfort sprites');
30
+ if(info.version===3){
31
+ const s=info.scramble;
32
+ if(!s||!Array.isArray(s.frames)||!Array.isArray(s.times)||s.frames.length!==7||s.times.length!==7||s.times[0]!==0||!Number.isFinite(s.duration)||s.duration<=0||s.duration>3||s.times.at(-1)!==s.duration||s.frames[0]!==s.frames.at(-1)||s.frames.some(i=>!Number.isInteger(i)||i<0||i>=info.frames)||s.times.some((t,i)=>!Number.isFinite(t)||(i&&t<=s.times[i-1])))throw Error('Invalid scramble metadata');
33
+ }
34
+ if(info.version>=4){validateMotion(info.scramble,info.frames);for(const view of ['rear','front'])validateMotion(info.pickups?.[view],info.frames);}
35
+ if(info.version>=6){validateMotion(info.carryTrot,info.frames);validateMotion(info.carryTrot?.settle,info.frames);if(info.carryTrot.cyclic!==true||!info.carryTrot.mouth||!info.carryTrot.settle.mouth||!Array.isArray(info.carryTrot.feet)||info.carryTrot.feet.length!==info.carryTrot.frames.length||info.carryTrot.feet.some(list=>!Array.isArray(list)||list.some(f=>typeof f.id!=='string'||typeof f.stance!=='boolean'||![f.x,f.y].every(v=>Number.isFinite(v)&&v>=0&&v<320))))throw Error('Invalid carry metadata');}
17
36
  if(output!==undefined&&(!(output instanceof Uint8ClampedArray)||output.length!==320*320*4))throw Error('Invalid comfort output');
18
37
  const frames=grayFrameReader(bytes,320,320,info.frames);let rgba=output??null;
19
38
  if(rgba)for(let i=3;i<rgba.length;i+=4)rgba[i]=255;
20
39
  return pose=>{
21
- if(!['tug','sleep'].includes(pose?.clip))throw Error('Invalid comfort clip');
40
+ if(!['tug','sleep','scramble','pickup','carry-trot','carry-settle'].includes(pose?.clip))throw Error('Invalid comfort clip');
22
41
  if(!rgba){rgba=new Uint8ClampedArray(320*320*4);for(let i=3;i<rgba.length;i+=4)rgba[i]=255;}
23
- const index=pose.clip==='sleep'?info.sleep?.[pose.variant]:comfortFrameIndex(pose.progress,info);
42
+ let index;
43
+ if(['scramble','pickup','carry-trot','carry-settle'].includes(pose.clip)){
44
+ const motion=comfortMotion(pose,info);if(!motion)throw Error('Invalid scramble/pickup pose');
45
+ index=motion.frames[comfortMotionKey(pose.progress,motion)];
46
+ }else index=pose.clip==='sleep'?info.sleep?.[pose.variant]:comfortFrameIndex(pose.progress,info);
24
47
  if(!Number.isInteger(index))throw Error('Invalid sleep variant');
25
48
  const frame=frames.getFrame(index),base=pose.base,blend=pose.blend??1;
26
49
  if(!Number.isFinite(blend)||blend<0||blend>1)throw Error('Invalid sleep blend');
@@ -0,0 +1,36 @@
1
+ // Analytic ball travel in the same ground/camera as the dog. No physics engine,
2
+ // per-frame integration drift, timer, image or growing path-history allocation.
3
+ import {planPaperTravel} from './puppy-paper-world.mjs';
4
+ export function createBallPath({from={x:0,z:.2},landing={x:0,z:-4},stop=landing,flight=.65,peak=.75,deceleration=.6}={}){
5
+ if(![from.x,from.z,landing.x,landing.z,stop.x,stop.z,flight,peak,deceleration].every(Number.isFinite)||flight<0||peak<0||deceleration<=0)throw Error('Invalid ball path');
6
+ from=Object.freeze({...from});landing=Object.freeze({...landing});stop=Object.freeze({...stop});
7
+ const dx=stop.x-landing.x,dz=stop.z-landing.z,distance=Math.hypot(dx,dz),speed=Math.sqrt(2*deceleration*distance);
8
+ const roll=distance?planPaperTravel(distance,{speed,startSpeed:speed,acceleration:deceleration,deceleration}):null;
9
+ const duration=flight+(roll?.duration??0);
10
+ return Object.freeze({duration,from,landing,stop,
11
+ at(age){
12
+ if(!Number.isFinite(age))throw Error('Invalid ball age');
13
+ const t=Math.max(0,age);
14
+ if(t<flight){const p=t/flight;return{x:from.x+(landing.x-from.x)*p,z:from.z+(landing.z-from.z)*p,height:4*peak*p*(1-p),spin:0,speed:0,phase:'flight',settled:false};}
15
+ const state=roll?.at(t-flight),ratio=distance?state.distance/distance:1;
16
+ return{x:landing.x+dx*ratio,z:landing.z+dz*ratio,height:0,spin:(state?.distance??0)/.035,speed:state?.speed??0,phase:t>=duration?'rest':'roll',settled:t>=duration};
17
+ }
18
+ });
19
+ }
20
+
21
+ // First throw remains the familiar clean fetch. Later throws use a seeded
22
+ // weighted choice, no immediate repeat, and at least four throws between tumbles.
23
+ export function createFetchPatterns(random,{canTumble=false}={}){
24
+ if(typeof random!=='function')throw Error('Fetch pattern RNG is required');
25
+ let count=0,last=null,lastTumble=-4;
26
+ return(allowTumble=canTumble)=>{
27
+ if(typeof allowTumble!=='boolean')throw Error('Invalid tumble availability');
28
+ count++;
29
+ const weights={clean:4,rolling:3,miss:2,tumble:canTumble&&allowTumble&&count>=4&&count-lastTumble>=4?.6:0};
30
+ if(last)weights[last]=0;
31
+ let pattern='clean';
32
+ if(count>1){let pick=random()*Object.values(weights).reduce((a,b)=>a+b,0);pattern=Object.keys(weights).find(k=>(pick-=weights[k])<0)??'clean';}
33
+ if(pattern==='tumble')lastTumble=count;
34
+ last=pattern;return pattern;
35
+ };
36
+ }
Binary file
@@ -1,18 +1,22 @@
1
1
  {
2
- "version": 2,
2
+ "version": 6,
3
3
  "width": 320,
4
4
  "height": 320,
5
- "frames": 13,
5
+ "frames": 45,
6
6
  "clips": [
7
7
  "tug",
8
- "sleep"
8
+ "sleep",
9
+ "scramble",
10
+ "pickup-rear",
11
+ "pickup-side",
12
+ "carry-trot"
9
13
  ],
10
- "gzipBytes": 129148,
11
- "residentPixelBytes": 341139,
14
+ "gzipBytes": 457052,
15
+ "residentPixelBytes": 1302445,
12
16
  "sourceSheetSha256": "10fde9dacca5cb45acd68f387e7ed3bcf88cd72cf0f9c4220d31d819db91731e",
13
17
  "neutralSha256": "19f89a8bcb5a937e528593a8f36db846a0d45a5ee398f1b0873c056e5be2fa0a",
14
- "encodedSha256": "35327cc103b1f7ac2ff1b09ed98658fd8ff1dcef322de9c9b18071a4358b7b87",
15
- "method": "Built-in imagegen authored tug keys. One fixed scale, ground/center translation only. Exact existing Q5 seat first/last. No expanded tween film or live body deformation.",
18
+ "encodedSha256": "f4e7604e3a581f5b610eb0f7873deee317a6e2ad0ff548106db2e64f6bead89b",
19
+ "method": "Approved momentum poses, single depth pursuit, exact source endpoints; one common scale per authored batch, no runtime image warp or expanded film.",
16
20
  "registration": {
17
21
  "sheetSize": [
18
22
  1774,
@@ -136,5 +140,327 @@
136
140
  "belly": "e1967d9156a119d04e25fd6428fe5dcdbd2156c5e4eba0347217847874006109",
137
141
  "curl": "10c9a85701af3587c48c1d8158972a07026e79a18a2375d86108538eda05aae7",
138
142
  "chin": "e971eaa6861b597f0dba3a487403cee5f120cba0a9f9584d831b5634d096dee7"
143
+ },
144
+ "scramble": {
145
+ "frames": [
146
+ 25,
147
+ 26,
148
+ 27,
149
+ 28,
150
+ 29,
151
+ 30,
152
+ 31,
153
+ 32,
154
+ 33,
155
+ 34,
156
+ 35,
157
+ 36
158
+ ],
159
+ "times": [
160
+ 0,
161
+ 0.035,
162
+ 0.07,
163
+ 0.105,
164
+ 0.14,
165
+ 0.19,
166
+ 0.25,
167
+ 0.32,
168
+ 0.39,
169
+ 0.48,
170
+ 0.57,
171
+ 0.65
172
+ ],
173
+ "duration": 0.65,
174
+ "floorOffsets": [
175
+ 0,
176
+ 0,
177
+ 0,
178
+ 0,
179
+ 0,
180
+ 0,
181
+ 0,
182
+ 0,
183
+ 0,
184
+ 0,
185
+ 0,
186
+ 0
187
+ ],
188
+ "entryView": "rear",
189
+ "entryPhase": 0.25,
190
+ "trajectory": "single-pass-target",
191
+ "brakeSeconds": 0.19,
192
+ "catchSeconds": 0.29,
193
+ "catchSpeedRatio": 0.24,
194
+ "registration": {
195
+ "commonScale": 0.7160493827160493,
196
+ "commonDx": 39.580246913580254,
197
+ "wholeFrameFloorTranslations": true,
198
+ "approvedBatchScale": 0.47313240809175766,
199
+ "approvedBatchDx": 1.1088664421997976,
200
+ "approvedOriginalKeys": [
201
+ 2,
202
+ 4,
203
+ 6,
204
+ 8
205
+ ],
206
+ "perPoseScale": false,
207
+ "bodyCompositing": false,
208
+ "nonlinearWarp": false,
209
+ "exactSourceEndpoints": true
210
+ }
211
+ },
212
+ "pickups": {
213
+ "rear": {
214
+ "frames": [
215
+ 13,
216
+ 14,
217
+ 15,
218
+ 16,
219
+ 17,
220
+ 18
221
+ ],
222
+ "times": [
223
+ 0,
224
+ 0.09,
225
+ 0.18,
226
+ 0.28,
227
+ 0.4,
228
+ 0.52
229
+ ],
230
+ "duration": 0.52,
231
+ "floorOffsets": [
232
+ 0,
233
+ 2,
234
+ 2,
235
+ 1,
236
+ 1,
237
+ 0
238
+ ]
239
+ },
240
+ "front": {
241
+ "frames": [
242
+ 19,
243
+ 20,
244
+ 21,
245
+ 22,
246
+ 23,
247
+ 24
248
+ ],
249
+ "times": [
250
+ 0,
251
+ 0.09,
252
+ 0.18,
253
+ 0.28,
254
+ 0.4,
255
+ 0.52
256
+ ],
257
+ "duration": 0.52,
258
+ "floorOffsets": [
259
+ -4,
260
+ 1,
261
+ 1,
262
+ 1,
263
+ 1,
264
+ -4
265
+ ],
266
+ "mouth": [
267
+ [
268
+ 255,
269
+ 151
270
+ ],
271
+ [
272
+ 250,
273
+ 225
274
+ ],
275
+ [
276
+ 280,
277
+ 275
278
+ ],
279
+ [
280
+ 274,
281
+ 287
282
+ ],
283
+ [
284
+ 268,
285
+ 238
286
+ ],
287
+ [
288
+ 255,
289
+ 151
290
+ ]
291
+ ]
292
+ }
293
+ },
294
+ "sourceComfort": {
295
+ "ref": "9fc9628b6e42bd5da1efb02521ed4ab6e59f7763",
296
+ "encodedSha256": "35327cc103b1f7ac2ff1b09ed98658fd8ff1dcef322de9c9b18071a4358b7b87",
297
+ "frames": 13,
298
+ "gzipBytes": 129148
299
+ },
300
+ "approvedArtwork": "Owner-approved whole-body momentum study, 2026-10-23",
301
+ "sourceSheetHashes": {
302
+ "rear": "7beaf6a3250590563d98af860c369c91993d5aea6b7b901f368d3992d120b09b",
303
+ "breakdowns": "7a565ccf2aa54f38a89979826ecc7d2d77110abbcd1bfbe6ab2cb4d86093daf2"
304
+ },
305
+ "carryTrot": {
306
+ "frames": [
307
+ 37,
308
+ 38,
309
+ 39,
310
+ 40
311
+ ],
312
+ "times": [
313
+ 0,
314
+ 0.25,
315
+ 0.5,
316
+ 0.75
317
+ ],
318
+ "duration": 1,
319
+ "cyclic": true,
320
+ "floorOffsets": [
321
+ 0,
322
+ 0,
323
+ 0,
324
+ 0
325
+ ],
326
+ "mouth": [
327
+ [
328
+ 198,
329
+ 177
330
+ ],
331
+ [
332
+ 197,
333
+ 179
334
+ ],
335
+ [
336
+ 189,
337
+ 178
338
+ ],
339
+ [
340
+ 190,
341
+ 179
342
+ ]
343
+ ],
344
+ "registration": {
345
+ "commonScale": 0.6843853820598007,
346
+ "commonDx": 68.1594684385382,
347
+ "perPoseScale": false,
348
+ "bodyCompositing": false,
349
+ "wholeFrameFloorTranslation": true,
350
+ "selectedSourceKeys": [
351
+ 0,
352
+ 1,
353
+ 2,
354
+ 6,
355
+ 8,
356
+ 9,
357
+ 10,
358
+ 11
359
+ ]
360
+ },
361
+ "settle": {
362
+ "frames": [
363
+ 41,
364
+ 42,
365
+ 43,
366
+ 44
367
+ ],
368
+ "times": [
369
+ 0,
370
+ 0.07,
371
+ 0.14,
372
+ 0.22
373
+ ],
374
+ "duration": 0.22,
375
+ "floorOffsets": [
376
+ 0,
377
+ 0,
378
+ 0,
379
+ 0
380
+ ],
381
+ "mouth": [
382
+ [
383
+ 203,
384
+ 179
385
+ ],
386
+ [
387
+ 194,
388
+ 180
389
+ ],
390
+ [
391
+ 191,
392
+ 180
393
+ ],
394
+ [
395
+ 195,
396
+ 183
397
+ ]
398
+ ]
399
+ },
400
+ "feet": [
401
+ [
402
+ {
403
+ "id": "fore-left",
404
+ "x": 168,
405
+ "y": 260,
406
+ "stance": false
407
+ },
408
+ {
409
+ "id": "fore-right",
410
+ "x": 220,
411
+ "y": 298,
412
+ "stance": true
413
+ }
414
+ ],
415
+ [
416
+ {
417
+ "id": "fore-left",
418
+ "x": 174,
419
+ "y": 267,
420
+ "stance": false
421
+ },
422
+ {
423
+ "id": "fore-right",
424
+ "x": 210,
425
+ "y": 298,
426
+ "stance": true
427
+ }
428
+ ],
429
+ [
430
+ {
431
+ "id": "fore-left",
432
+ "x": 164,
433
+ "y": 298,
434
+ "stance": true
435
+ },
436
+ {
437
+ "id": "fore-right",
438
+ "x": 217,
439
+ "y": 260,
440
+ "stance": false
441
+ }
442
+ ],
443
+ [
444
+ {
445
+ "id": "fore-left",
446
+ "x": 160,
447
+ "y": 298,
448
+ "stance": true
449
+ },
450
+ {
451
+ "id": "fore-right",
452
+ "x": 216,
453
+ "y": 270,
454
+ "stance": false
455
+ }
456
+ ]
457
+ ]
458
+ },
459
+ "carrySource": {
460
+ "encodedSha256": "efa81c6f464646d101873223869cebf5ca28c80529ad4ab26380d3b97cc2af54",
461
+ "frames": 37,
462
+ "gzipBytes": 375968,
463
+ "residentPixelBytes": 1043388,
464
+ "sheetSha256": "43e41a678d348439bf989535bf30761cc23ac9d4ca049705a9a651e1e8b61ee0"
139
465
  }
140
466
  }