@bitefoundry/retrieviewer 0.0.11 → 0.0.13

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/INSTALLING.md CHANGED
@@ -99,6 +99,31 @@ Inside OpenCode, `/retrieviewer enable`, `/retrieviewer disable`,
99
99
  `/retrieviewer ball`, `/retrieviewer treat`, `/retrieviewer status` control the native
100
100
  pet. The adapter owns no raw keyboard or stdout; the host owns drawing and layout.
101
101
 
102
+ ## Recovering an interrupted settings change
103
+
104
+ Settings writes use an exclusive `settings.lock`. New locks record the owning
105
+ PID, hostname, start time and a random token. A busy error prints the exact lock
106
+ path and PID when known. An old or empty lock may have no owner information.
107
+ Locks are deliberately **not** deleted just because they are old or a PID probe
108
+ fails: a long operation, PID reuse or a shared directory can make that unsafe.
109
+
110
+ If an operation crashed:
111
+
112
+ 1. Stop all Retrieviewer enable/disable/disconnect commands and close all host
113
+ agents that can change its settings. If the directory is shared, stop writers
114
+ on every machine. Inspect the exact lock named in the error; an active owner
115
+ must finish first. Do not kill an unrelated process just because its PID matches.
116
+ 2. With **all writers stopped**, move only that `settings.lock` aside in the same
117
+ directory (for example to `settings.lock.recovered`). Keep it as a backup;
118
+ do not overwrite an existing backup, touch `settings.json`, remove the whole
119
+ directory, or follow a symlink. Then retry the original command. A fresh lock
120
+ is acquired normally and the existing settings are preserved.
121
+
122
+ Default directory: `%USERPROFILE%\.config\retrieviewer` on Windows,
123
+ `~/.config/retrieviewer` on macOS/Linux. `RETRIEVIEWER_HOME` overrides it, so use
124
+ the path in the error rather than assuming the default. This is an **offline
125
+ manual recovery**, not a command to remove a live lock or automatic stale reaping.
126
+
102
127
  ## Evidence, not a misleading green check
103
128
 
104
129
  Registration means the config contains our plugin URL. A runtime receipt means
@@ -0,0 +1,63 @@
1
+ # Precomputed motion tables (0.0.12)
2
+
3
+ `npm run bake:motion` compiles the current approved sources into
4
+ `puppy-motion-tables.generated.mjs`. It does not change a picture, camera,
5
+ trajectory, animation duration or source metadata. `npm run check:motion` checks
6
+ the exact deterministic output without writing. Tests and the publication guard
7
+ reject stale output after source metadata or witnessed art changes.
8
+
9
+ ## What is precomputed
10
+
11
+ - Source support episodes, including continuous stance across a cycle seam and
12
+ a complete swing skipped between rendered frames. No stance-history discovery
13
+ occurs while building a default renderer, including after a resize.
14
+ - Clip lists, weights and totals used for action-pose addressing. The runtime no
15
+ longer validates/rebuilds these same lists and totals for each default pose.
16
+ - A dense table of72 facing/seat preparation cases, sharing8 step descriptors.
17
+ Lookup requires a stationary root; it does not insert a turn at running speed.
18
+ - Ten exact-pixel connection witnesses with view, speed and (where applicable)
19
+ gait-phase/velocity-match requirements. These cover walk entry/exit, rear run
20
+ into the authored impact, impact into rear pickup, carry-gather into arrival
21
+ seat, tug into the canonical seat, and four watch endpoints into that seat.
22
+ - SHA256 of every input metadata/art file used by the bake. JSON text is hashed
23
+ with LF line endings; binary artwork is hashed byte-for-byte. Generated text
24
+ checks also accept CRLF checkouts. No source images,
25
+ source hashes or graph search are computed by the running pet.
26
+
27
+ The generated routes preserve the existing approved preparation order. This is
28
+ a bounded compilation of existing paths, not unrestricted motion matching or a
29
+ new animation blend engine. Other actor-specific recovery rules stay in place.
30
+ Unregistered arbitrary-phase joins are not certified: in particular an urgently
31
+ interrupted walk may use its existing recovery rather than a proven phase0 join.
32
+
33
+ ## What remains dynamic
34
+
35
+ World displacement, actual velocity, unwrapped gait phase, perspective and the
36
+ small paw-to-world anchor correction still depend on live state. Those cannot
37
+ be baked as fixed screen coordinates while allowing free travel and resizing.
38
+ Support tables address the original source coordinates; they do not stretch the
39
+ head/torso, replace drawings or cause a camera orbit.
40
+
41
+ Paw labels are visible image-space proxies, not anatomical motion capture. Some
42
+ resting endpoints have exact-pose evidence but no individually labeled paws.
43
+ The authored run-to-impact entry is **airborne**, not a planted-paw handoff:
44
+ its gate requires the correct phase, positive speed and matched velocity.
45
+ Neither exact pixels nor a green numerical test proves general animal realism.
46
+
47
+ ## Compatibility and resource scope
48
+
49
+ The bundled Q5/default-loader path uses the compiled tables. Optional original
50
+ art and custom loaders retain the general metadata-driven implementation instead
51
+ of accidentally applying the bundled pet's bake to unrelated art. The general
52
+ helpers remain available for authoring and equivalence tests.
53
+
54
+ The generated module is about11KB source, shared between instances; there is no
55
+ extra image cache, dependency, timer, worker or asset-fetch round trip. It removes
56
+ repeat discovery/allocation work, not the roughly20MB retained image payload or
57
+ Node/native process baseline. It is not a large-RAM-reduction claim.
58
+
59
+ Verification compares every baked support sample and wrap, all action-addressing
60
+ boundaries, both actor paths through seeded fetches and rare impacts, and the
61
+ public renderer's text, contacts and mouth anchors. Use
62
+ `node --test puppy-motion-tables.test.mjs`. The owner authorized publication of
63
+ this work as0.0.12 on2026-10-24. Earlier published artifacts remain unchanged.
package/RELEASING.md CHANGED
@@ -1,4 +1,48 @@
1
- # Retrieviewer 0.0.11 release process
1
+ # Retrieviewer 0.0.13 release process
2
+
3
+ Current target: `@bitefoundry/retrieviewer@0.0.13`, public `latest`.
4
+ The owner authorized npm publication and website deployment on2026-10-24.
5
+ This release contains the reviewed reliability fixes, with unchanged art/motion:
6
+
7
+ - Canonical LF text/hash checks and CRLF regression coverage; binary art stays
8
+ byte-for-byte unchanged. Git attributes and existing JSON text use LF.
9
+ - Exclusive settings locks record the owner PID/host/time/token. Busy errors
10
+ identify the lock and link to the offline manual recovery in INSTALLING.md.
11
+ There is no automatic removal of possibly live locks.
12
+ - Workers require ready plus an acknowledged detach to survive launcher loss.
13
+ Startup disconnect/launcher-death tests cover worker, painter and session cleanup.
14
+ - Oversized Windows grids hide until they fit again; plugin timer delays cannot
15
+ overflow Node's signed32-bit timeout bound into a1ms loop.
16
+ - Windows20 / Linux22 Node CI on push and PR, including installed-consumer checks
17
+ and an owned Windows native diagnostic. Website copy discloses page visit and
18
+ performance analytics separately from the desktop pet.
19
+
20
+ Build and install a fresh exact tarball outside the source tree, compare installed
21
+ bytes and test its native/browser runtimes. Publish that same artifact after the
22
+ new remote CI succeeds; verify registry integrity and matching Pages runtime.
23
+ No new runtime dependency, process, background timer or art cache. No claim of
24
+ new macOS/Linux GUI certification or a material RAM reduction. Preserve all
25
+ earlier npm artifacts, MIT code / CC BY-SA4.0 art and the user's running pet.
26
+
27
+ ## Previous 0.0.12 scope (historical)
28
+
29
+ Current target: `@bitefoundry/retrieviewer@0.0.12`, public `latest`.
30
+ On2026-10-24 the owner authorized npm publication and website deployment of the
31
+ reviewed precomputed motion tables. Support episodes, action addressing and
32
+ bounded preparation routes are compiled offline; live travel, perspective and
33
+ paw correction remain dynamic. Default Q5 uses the tables; custom loaders and
34
+ optional original art retain their metadata-driven path. See MOTION-TABLES.md.
35
+ No new artwork, runtime dependency, process or timer. This removes repeated
36
+ CPU/allocation work, not the retained image payload or Node/native baseline.
37
+ Do not claim a large RAM reduction or new Linux/macOS live GUI certification.
38
+
39
+ Build and verify a fresh exact0.0.12 tarball outside the source tree, including
40
+ its owned Windows native lifecycle and bilingual browser runtime. Publish that
41
+ same tarball, verify registry integrity and matching GitHub Pages runtime bytes.
42
+ Do not restart the user's live pet or change host/agent configuration. Earlier
43
+ npm artifacts remain immutable; MIT code / CC BY-SA4.0 artwork stay unchanged.
44
+
45
+ ## Previous 0.0.11 scope (historical)
2
46
 
3
47
  Current target: `@bitefoundry/retrieviewer@0.0.11`, public `latest`.
4
48
  On2026-10-24 the owner accepted the reviewed candidate and authorized immediate
@@ -168,8 +168,10 @@ sealed class Companion : ApplicationContext {
168
168
  if(target!=null&&!Native.IsIconic(target.Window)&&Native.IsWindowVisible(target.Window)){
169
169
  next=Native.ClientBounds(target.Window);IntPtr foreground=Native.GetForegroundWindow();show=!picking&&(testMode||foreground==target.Window||foreground==bar.Handle)&&next.Width>=160&&next.Height>=120;
170
170
  }
171
- int nextCell=8;while(next.Width/nextCell*(next.Height/(nextCell*2))>125000&&nextCell<16)nextCell+=2;
172
- if(nextCell==14)nextCell=16;
171
+ int nextCell=Program.GridCell(next.Width,next.Height);
172
+ // A huge target may exceed the transport budget even at the largest cell.
173
+ // Hide until its size fits again; never send an invalid visible grid.
174
+ if(nextCell==0){show=false;nextCell=16;}
173
175
  int columns=next.Width/nextCell,rows=next.Height/(nextCell*2);
174
176
  if(force||show!=visible||next!=canvas||nextCell!=cell){
175
177
  visible=show;canvas=next;cell=nextCell;revision++;pet.Hide();
@@ -237,6 +239,13 @@ static class Program {
237
239
  using(Companion context=new Companion(single,ipcTest))Application.Run(context);return 0;
238
240
  }catch(Exception error){Emit(new {type="error",message=error.Message});return 1;}
239
241
  }
242
+ internal static int GridCell(int width,int height){
243
+ foreach(int size in new int[]{8,10,12,16}){
244
+ long columns=(long)width/size,rows=(long)height/(size*2);
245
+ if(columns>0&&rows>0&&columns*rows<=125000)return size;
246
+ }
247
+ return 0;
248
+ }
240
249
  static int SelfTest(string proof){
241
250
  // Uses only our own off-screen windows. Does NOT click/type in or capture a user application.
242
251
  IntPtr foreground=Native.GetForegroundWindow();
@@ -253,8 +262,9 @@ static class Program {
253
262
  Native.ShowWindow(target.Handle,7);Application.DoEvents();bool minimized=Native.IsIconic(target.Handle);pet.Hide();bool hidden=!pet.Visible;
254
263
  Native.ShowWindow(target.Handle,4);Application.DoEvents();bool restored=!Native.IsIconic(target.Handle);
255
264
  bool focus=Native.GetForegroundWindow()==foreground;
256
- bool passed=clickThrough&&noActivate&&layered&&follows&&minimized&&hidden&&restored&&focus&&pet.PaintedDots>0;
257
- Emit(new {type="self-test",passed=passed,clickThroughStyle=clickThrough,noActivateStyle=noActivate,layered=layered,followGeometry=follows,expectedX=expectedX,expectedY=expectedY,actualX=actual.Left,actualY=actual.Top,minimize=minimized,hide=hidden,restore=restored,foregroundPreserved=focus,dots=pet.PaintedDots,proof=proof});return passed?0:1;
265
+ bool gridBounds=GridCell(3840,2160)==8&&GridCell(7680,4320)==12&&GridCell(32000,18000)==0&&GridCell(int.MaxValue,int.MaxValue)==0&&GridCell(0,0)==0;
266
+ bool passed=clickThrough&&noActivate&&layered&&follows&&minimized&&hidden&&restored&&focus&&pet.PaintedDots>0&&gridBounds;
267
+ Emit(new {type="self-test",passed=passed,gridBounds=gridBounds,clickThroughStyle=clickThrough,noActivateStyle=noActivate,layered=layered,followGeometry=follows,expectedX=expectedX,expectedY=expectedY,actualX=actual.Left,actualY=actual.Top,minimize=minimized,hide=hidden,restore=restored,foregroundPreserved=focus,dots=pet.PaintedDots,proof=proof});return passed?0:1;
258
268
  }
259
269
  }
260
270
  }
@@ -102,9 +102,15 @@ export async function startDesktop(options={}){
102
102
  const helper=await desktopHelper(options);
103
103
  return new Promise((resolve,reject)=>{
104
104
  const child=spawn(process.execPath,[fileURLToPath(new URL('./worker.mjs',import.meta.url)),'--helper-spec',JSON.stringify(helper)],{detached:true,windowsHide:true,stdio:['ignore','ignore','ignore','ipc'],env:{...process.env,RETRIEVIEWER_HOME:getConfigPaths(options).root,RETRIEVIEWER_INK:ink,RETRIEVIEWER_RETURN_STYLE:returnStyle}});
105
- let settled=false;const finish=(error,message)=>{if(settled)return;settled=true;clearTimeout(timer);if(child.connected)child.disconnect();child.unref();error?reject(error):resolve(message);};
106
- const timer=setTimeout(()=>{if(child.connected)child.send({command:'abort'});finish(Error('Desktop startup timed out; no agent settings changed'));},20000);
107
- child.on('error',error=>finish(error));child.on('message',message=>message.error?finish(Error(message.error)):finish(null,message));child.on('exit',code=>finish(Error('Desktop exited during startup ('+code+')')));
105
+ let settled=false,ready=null;const finish=(error,message)=>{if(settled)return;settled=true;clearTimeout(timer);if(child.connected)child.disconnect();child.unref();error?reject(error):resolve(message);};
106
+ // Disconnect on failure aborts startup. Only an acknowledged detach may live on.
107
+ const timer=setTimeout(()=>finish(Error('Desktop startup timed out; no agent settings changed')),20000);
108
+ child.on('error',error=>finish(error));child.on('message',message=>{
109
+ if(settled)return;
110
+ if(message?.error)return finish(Error(message.error));
111
+ if(message?.detached===true&&ready)return finish(null,ready);
112
+ if(message?.running===true&&!ready){ready=message;child.send({command:'detach'},error=>{if(error)finish(error);});}
113
+ });child.on('disconnect',()=>{if(!settled)finish(Error('Desktop disconnected during startup'));});child.on('exit',code=>finish(Error('Desktop exited during startup ('+code+')')));
108
114
  });
109
115
  }
110
116
  export async function cleanDesktop(options={}){
@@ -9,25 +9,32 @@ import {createFramePump} from './protocol.mjs';
9
9
  import {controlAddress,parseHelperSpec} from '../desktop/platform.mjs';
10
10
 
11
11
  let helper,pump,timer,server,session,paths,owned=false,stopping=false,frames=0,paintedFrames=0,visible=false,selected=false,pickerVisible=false,pendingViewport=null,lastInteraction=null;
12
- let nativeError=null;
12
+ let nativeError=null,ready=false,detached=false;
13
13
  const returnStyle=process.env.RETRIEVIEWER_RETURN_STYLE??'auto';
14
14
  let nativeStderr='';
15
- const sendParent=message=>{if(process.connected)process.send(message);};
15
+ const sendParent=message=>{if(process.connected)try{process.send(message,error=>{if(error&&!detached)stop();});}catch{if(!detached)stop();}};
16
+ const starting=()=>{if(stopping||(!process.connected&&!detached))throw Error('Desktop launcher closed during startup');};
16
17
  async function stop(){
17
- if(stopping)return;stopping=true;clearInterval(timer);pump?.dispose();
18
+ const first=!stopping;stopping=true;clearInterval(timer);pump?.dispose();
18
19
  if(helper&&!helper.killed){try{helper.stdin.end(JSON.stringify({type:'quit'})+'\n');}catch{}}
19
- if(server)server.close();
20
+ if(server?.listening)server.close();
20
21
  if(owned)try{const current=JSON.parse(await readFile(paths.session,'utf8'));if(current.token===session.token)await unlink(paths.session);}catch{}
21
22
  // A wedged painter is our own child only, never the selected terminal.
22
- const watchdog=setTimeout(()=>{helper?.kill();process.exit(0);},2500);watchdog.unref();
23
+ if(first){const watchdog=setTimeout(()=>{helper?.kill();process.exit(0);},2500);watchdog.unref();}
23
24
  if(!helper||helper.exitCode!==null)process.exitCode=0;
24
25
  }
25
- process.on('SIGTERM',stop);process.on('SIGINT',stop);process.on('message',m=>{if(m?.command==='abort')stop();});
26
+ process.on('SIGTERM',stop);process.on('SIGINT',stop);
27
+ process.on('disconnect',()=>{if(!detached)stop();});
28
+ process.on('message',m=>{
29
+ if(m?.command==='abort')stop();
30
+ else if(m?.command==='detach'&&ready&&!stopping){detached=true;sendParent({detached:true});}
31
+ });
26
32
  try{
33
+ starting();
27
34
  const spec=parseHelperSpec(process.argv.slice(2));
28
- paths=await prepareRoot();await noLink(paths.session);
35
+ paths=await prepareRoot();await noLink(paths.session);starting();
29
36
  session={owner:'bitefoundry-retrieviewer-overlay-v1',pid:process.pid,pipe:controlAddress({uuid:randomUUID()}),token:randomBytes(32).toString('hex')};
30
- const record=await open(paths.session,'wx',0o600);owned=true;try{await record.writeFile(JSON.stringify(session)+'\n');}finally{await record.close();}
37
+ const record=await open(paths.session,'wx',0o600);owned=true;try{await record.writeFile(JSON.stringify(session)+'\n');}finally{await record.close();}starting();
31
38
  server=createServer(socket=>{
32
39
  socket.setTimeout(1500,()=>socket.destroy());let body='';
33
40
  socket.on('error',()=>{});socket.on('data',bytes=>{
@@ -40,6 +47,7 @@ try{
40
47
  });
41
48
  await new Promise((resolve,reject)=>{server.once('error',reject);server.listen(session.pipe,resolve);});
42
49
  if(process.platform!=='win32')await chmod(session.pipe,0o600);
50
+ starting();
43
51
  // GUI-subsystem exe: no console to hide. SW_HIDE here also hides its first UI.
44
52
  helper=spawn(spec.command,spec.args,{windowsHide:false,stdio:['pipe','pipe','pipe']});
45
53
  helper.stdin.on('error',()=>stop());helper.stderr.setEncoding('utf8');helper.stderr.on('data',s=>{nativeStderr=(nativeStderr+s).slice(-4096);});helper.once('error',error=>{sendParent({error:error.message});stop();});
@@ -49,7 +57,7 @@ try{
49
57
  reader.on('line',line=>{
50
58
  if(line.length>100000)return stop();let m;try{m=JSON.parse(line);}catch{return stop();}
51
59
  try{
52
- if(m.type==='ready'){pickerVisible=m.pickerVisible===true;sendParent({running:true,windowPicker:pickerVisible,mode:spec.mode,returnStyle,pid:process.pid});}
60
+ if(m.type==='ready'&&!stopping){ready=true;pickerVisible=m.pickerVisible===true;sendParent({running:true,windowPicker:pickerVisible,mode:spec.mode,returnStyle,pid:process.pid});}
53
61
  else if(m.type==='error'){nativeError=String(m.message??'Desktop painter failed');sendParent({error:nativeError});stop();}
54
62
  else if(m.type==='picker')pickerVisible=m.visible===true;
55
63
  else if(m.type==='viewport'){pendingViewport=m;visible=m.visible;selected=m.selected===true;pump?.viewport(m);}
@@ -59,7 +67,7 @@ try{
59
67
  else if(m.type==='quit')stop();
60
68
  }catch(error){write({type:'error',message:error.message});stop();}
61
69
  });
62
- const {createPet}=await import('../../retrieviewer.mjs');const pet=await createPet({returnStyle});
70
+ starting();const {createPet}=await import('../../retrieviewer.mjs');starting();const pet=await createPet({returnStyle});
63
71
  if(stopping)pet.dispose();else{
64
72
  pump=createFramePump({pet,send:write});if(pendingViewport)pump.viewport(pendingViewport);
65
73
  write({type:'loaded',ink:process.env.RETRIEVIEWER_INK??'cream'});timer=setInterval(()=>{try{pump.tick();}catch(error){write({type:'error',message:error.message});stop();}},1000/spec.fps);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitefoundry/retrieviewer",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "A fluffy Braille desktop pet for Windows and Linux X11, with experimental macOS support and an embeddable TUI engine.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://bitefoundry.github.io/retrieviewer/",
@@ -90,6 +90,7 @@
90
90
  "PUPPY-Q5-COMPARISON.md",
91
91
  "COMFORT-ART.md",
92
92
  "WATCH-REDRAW.md",
93
+ "MOTION-TABLES.md",
93
94
  "BUTTERFLY.md",
94
95
  "examples/embed.mjs",
95
96
  "examples/live.mjs",
@@ -102,6 +103,8 @@
102
103
  "puppy-paper.mjs",
103
104
  "puppy-paper-world.mjs",
104
105
  "puppy-paper-contact.mjs",
106
+ "puppy-motion-lookup.mjs",
107
+ "puppy-motion-tables.generated.mjs",
105
108
  "puppy-paper-seat.mjs",
106
109
  "puppy-paper-director.mjs",
107
110
  "puppy-paper-behaviors.mjs",
@@ -144,13 +147,15 @@
144
147
  "vendor/procedural-animals/LICENSE"
145
148
  ],
146
149
  "scripts": {
150
+ "bake:motion": "node tools/bake-pet-motion.mjs",
151
+ "check:motion": "node tools/bake-pet-motion.mjs --check",
147
152
  "start": "node tools/preview-site.mjs",
148
153
  "build:site": "node tools/build-site.mjs",
149
154
  "pet:companion": "node examples/live.mjs",
150
155
  "pet:desktop": "node retrieviewer-cli.mjs desktop",
151
- "test": "node --test \"*.test.mjs\"",
156
+ "test": "node --test",
152
157
  "test:package": "node check-pet-package.mjs",
153
- "release:check": "node tools/check-release.mjs",
158
+ "release:check": "npm run check:motion && node tools/check-release.mjs",
154
159
  "prepublishOnly": "npm run release:check"
155
160
  },
156
161
  "dependencies": {
@@ -1 +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]]}]}
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,20 @@
1
+ // Pure lookups over offline data. No source analysis, graph search or I/O here.
2
+ const views=Object.freeze({front:0,rear:1,approach:2});
3
+ export function preparationRoute(tables,kind,{view,flip,seatProgress=0,desired,nextFlip=false,speed=0}){
4
+ if(!Number.isFinite(speed)||Math.abs(speed)>tables.routes.maxPreparationSpeed)throw Error('Motion preparation requires a stopped root');
5
+ const viewIndex=views[view],seat=seatProgress===0?0:seatProgress===1?2:1;
6
+ const target=kind==='action'?(desired==='stand'?0:desired==='seat'?1:undefined):views[desired];
7
+ if(viewIndex===undefined||target===undefined||typeof flip!=='boolean'||typeof nextFlip!=='boolean')throw Error('Unknown baked motion route');
8
+ const index=kind==='action'?((viewIndex*2+ +flip)*3+seat)*2+target:((viewIndex*2+ +flip)*3+target)*2+ +nextFlip;
9
+ const code=tables.routes[kind]?.[index],route=tables.routes.steps[code];if(!route)throw Error('Unknown baked motion route');
10
+ return route;
11
+ }
12
+
13
+ export function canJoinMotion(tables,name,{view,speed,phase=0,nextSpeed=speed}){
14
+ const join=tables.joins[name],epsilon=1e-7;
15
+ if(!join||join.view!==view||![speed,phase,nextSpeed].every(Number.isFinite))return false;
16
+ if(speed<join.speed[0]-epsilon||speed>join.speed[1]+epsilon||nextSpeed<join.speed[0]-epsilon||nextSpeed>join.speed[1]+epsilon||join.positiveSpeed&&(speed<=0||nextSpeed<=0))return false;
17
+ if(join.phase!==undefined){const d=phase-join.phase;if(Math.abs(d-Math.round(d))>epsilon)return false;}
18
+ if(join.matchVelocity&&Math.abs(speed-nextSpeed)>epsilon)return false;
19
+ return true;
20
+ }
@@ -0,0 +1,4 @@
1
+ // Generated by tools/bake-pet-motion.mjs. Do not edit by hand.
2
+ // Source paw labels are image-space proxies, not anatomical motion capture.
3
+ const freeze=v=>{if(v&&typeof v==='object'){for(const x of Object.values(v))freeze(x);Object.freeze(v);}return v;};
4
+ export const BAKED_MOTION=freeze({"version":1,"sources":{"puppy-stride-meta.json":"30fd474f3ed9337851b2071037d88d60b37cb2bb1c12d48b28c34ebc126b7e3a","puppy-paper-rear-gallop.json":"fe9b1bf0b88f2a366f28deee6cb63964b8a94d6faa99b111524112832e80705d","puppy-paper-approach.json":"e7e02589622cecde679de79bb79c71b77494fd2fd850793c2fba9754b46aa474","puppy-paper-actions-v9.json":"38b35496c552443c7ea99beceac3cd25c670bc91836b22509b1cc97da3d0a9de","puppy-paper-behaviors.json":"67d8633bc61a2d1c46206969b9aeaaf30073fcc0f0ae7dc9954957d5522edb32","puppy-companion-comfort.json":"5234cf9566957c91ad0aa76c5dae4c9449f6c34171dcb581bf6d114b607c4a18","puppy-paper-actions-v9-q5.bin.gz":"abb4677468206c62937c0247dc4c5823e0a7c4a3a197da40194a08dd594aeef9","puppy-paper-rear-gallop-q5.bin.gz":"ca731e1979a37ec2e005a80365e3b58358f640b8b26e7036b0e931940a7b1264","puppy-companion-comfort-q5.bin.gz":"9f8c89bf4764eb2f1f17131e2413d941f1eae33ae9a15ffbe76cc33f2d18103a","puppy-paper-arrival-seat-q5.bin.gz":"7effa0a2150e8ef9ed37d5b6cb10089672e0ce32599c06db21ad4769144bf246","puppy-paper-seat-q5.bin.gz":"5480c994de0d034d70eff6ea86095f3722cf87bde72f364dd369b94b1ad66f84","puppy-paper-behaviors-q5.bin.gz":"e4b79387aca974ceec74a02dc3f02780eb53d38af6ec9ebd69994de744b39716"},"support":{"front":{"step":16,"period":128,"cyclic":true,"offsets":{"0":0,"1":16,"2":32,"3":48,"4":64,"5":80,"6":96,"7":112},"starts":{"hind-near":[123,123,123,123,123,123,123,123,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,123,123,123,123,123],"fore-far":[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],"fore-near":[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],"hind-far":[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]}},"rear":{"step":8,"period":64,"cyclic":true,"offsets":{"5":0,"6":8,"7":16,"8":24,"9":32,"10":40,"11":48,"12":56},"starts":{"hind-far":[60,60,60,60,60,60,60,60,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,60,60,60,60],"hind-near":[62,62,62,62,62,62,62,62,62,62,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,62]}},"approach":{"step":8,"period":64,"cyclic":true,"offsets":{"5":0,"6":8,"7":16,"8":24,"9":32,"10":40,"11":48,"12":56},"starts":{"fore-left":[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,39,39,39,39,39,39,-1,-1,-1,-1,-1,-1,-1,-1,-1,54,54,54,54,54,-1,-1,-1,-1,-1],"fore-right":[61,61,61,61,61,61,61,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,-1,-1,-1,-1,55,55,55,55,55,-1,61,61,61]}},"walk":{"step":8,"period":64,"cyclic":true,"offsets":{"42":0,"43":8,"44":16,"45":24,"46":32,"47":40,"48":48,"49":56},"starts":{"hind-far":[52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,52,52,52,52,52,52,52,52,52,52,52,52],"hind-near":[58,58,58,58,58,58,58,58,58,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,24,24,24,24,24,24,24,24,24,24,24,24,24,24,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,58,58,58,58,58,58],"fore-far":[-1,-1,-1,3,3,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,21,21,21,21,21,-1,-1,-1,-1,-1,31,31,31,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,47,47,47,47,47,-1,-1,-1,-1,-1,-1,-1,59,59,59,-1,-1],"fore-near":[-1,-1,-1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-1,-1,-1,-1,-1,-1,-1,33,-1,-1,-1,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,-1,-1]}},"behavior/walk":{"step":8,"period":32,"cyclic":true,"offsets":{"0":0,"1":8,"2":16,"3":24},"starts":{"fore-left":[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,24,24,-1,-1,-1,-1,-1,-1],"fore-right":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]}},"behavior/walk-enter":{"step":8,"period":8,"cyclic":false,"offsets":{"4":0},"starts":{"fore-left":[-1,-1,-1,-1,-1,-1,-1,-1,-1],"fore-right":[-1,-1,-1,-1,4,4,4,4,4]}},"behavior/brake":{"step":8,"period":32,"cyclic":false,"offsets":{"5":0,"6":8,"7":16,"8":24},"starts":{"fore-left":[-1,-1,-1,-1,-1,-1,-1,-1,-1,9,9,9,9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,23,23,23,23,23,23,23,23,23],"fore-right":[0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,15,15,15,15,15,15,15,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]}},"behavior/finish":{"step":8,"period":8,"cyclic":false,"offsets":{"9":0},"starts":{}},"behavior/watch-left":{"step":8,"period":16,"cyclic":false,"offsets":{"10":0,"11":8},"starts":{}},"behavior/watch-right":{"step":8,"period":16,"cyclic":false,"offsets":{"12":0,"13":8},"starts":{}},"behavior/rest":{"step":8,"period":32,"cyclic":false,"offsets":{"14":0,"15":8,"16":16,"17":24},"starts":{}}},"actionClips":{"spin":{"list":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],"weights":[1.1168,0.8052,0.827,0.7936,1.3421,1.4204,0.9015,0.5275,1.1081,0.7701,0.723,1.0494,1.4422,1.3325,1.0787,0.8964,0.8655],"total":17},"roll":{"list":[17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"weights":[1.122,1.4089,1.6479,0.6752,0.753,0.8756,0.9957,0.9586,0.7325,0.8218,1.0968,0.9962,0.8959,0.6589,1.0191,0.8352,0.4557,1.1818,1.1101,0.905,0.6752,1.6479,1.4089,1.122],"total":23.9999},"roll-down":{"list":[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"weights":[1.6886,0.6919,0.7717,0.8973,1.0203,0.9823,0.7506,0.8422,1.124,1.0208,0.918,0.6752,1.0443,0.8559,0.467,1.211,1.1376,0.9274,0.6919,1.6886,1.4438,1.1498],"total":22.0002},"lie-down":{"list":[17,18],"weights":[0.8866,1.1134],"total":2},"lie-up":{"list":[39,40],"weights":[1.1134,0.8866],"total":2},"walk-enter":{"list":[41],"weights":[1],"total":1},"walk":{"list":[42,43,44,45,46,47,48,49],"weights":[1,1,1,1,1,1,1,1],"total":8},"walk-exit":{"list":[50],"weights":[1],"total":1},"walk-face-front":{"list":[51],"weights":[1],"total":1},"walk-face-rear":{"list":[52],"weights":[1],"total":1},"walk-turn":{"list":[53,54,55,56,57,58,59,60,61,62,63,64,65],"weights":[1,1,1,1,1,1,1,1,1,1,1,1,1],"total":13}},"routes":{"maxPreparationSpeed":1e-8,"steps":[["ready"],["turn",true],["view-turn","rear"],["view-turn","approach"],["turn",false],["view-turn","front"],["rise"],["sit-down"]],"direction":[0,1,2,2,3,3,4,0,2,2,3,3,5,5,0,0,5,5,5,5,0,0,5,5,5,5,5,5,0,0,5,5,5,5,0,0],"action":[0,3,6,3,6,3,4,4,6,4,6,4,5,5,6,5,6,5,5,5,6,5,6,5,5,7,6,7,6,0,5,5,6,5,6,5]},"joins":{"walk-enter/walk":{"from":["puppy-paper-actions-v9",377],"to":["puppy-paper-actions-v9",378],"poseSha256":"81361614df56226b64be437f189c2e135c13498324286880db0fc06c5df3e86a","view":"front","speed":[0,0],"supportEvidence":"shared-pixels-with-labeled-walk-pose","supports":["hind-far","hind-near"],"phase":0},"walk/walk-exit":{"from":["puppy-paper-actions-v9",449],"to":["puppy-paper-actions-v9",450],"poseSha256":"81361614df56226b64be437f189c2e135c13498324286880db0fc06c5df3e86a","view":"front","speed":[0,0],"supportEvidence":"shared-pixels-with-labeled-walk-pose","supports":["hind-far","hind-near"],"phase":0},"rear-run/scramble":{"from":["puppy-paper-rear-gallop",63],"to":["puppy-companion-comfort",25],"poseSha256":"fa79a8762b57c0d5b70586a687f10e4a313e227e11e2435f6d7af0cd0866cc83","view":"rear","phase":0.25,"speed":[0,4.556913271064087],"positiveSpeed":true,"matchVelocity":true,"supports":[],"supportEvidence":"source-paw-proxies","airborne":true},"scramble/pickup-rear":{"from":["puppy-companion-comfort",36],"to":["puppy-companion-comfort",13],"poseSha256":"8192c504552a3984c80e1c3216e134605d4a930ec14cd7f778036e629e334601","view":"rear","speed":[0,0],"supports":["hind-far","hind-near"],"supportEvidence":"shared-pixels-with-labeled-rear-pose"},"carry-settle/arrival-seat":{"from":["puppy-companion-comfort",44],"to":["puppy-paper-arrival-seat",0],"poseSha256":"5ccf5acd9ca9ea81a0c9fc59d1ab2a2ddab148c942e31009f1a4c18de347fae8","view":"approach","speed":[0,0],"supportEvidence":"shared-pose-only"},"tug/seat":{"from":["puppy-companion-comfort",0],"to":["puppy-paper-seat",50],"poseSha256":"19f89a8bcb5a937e528593a8f36db846a0d45a5ee398f1b0873c056e5be2fa0a","view":"approach","speed":[0,0],"supportEvidence":"shared-pose-only"},"watch-90/seat":{"from":["puppy-paper-behaviors",90],"to":["puppy-paper-seat",50],"poseSha256":"19f89a8bcb5a937e528593a8f36db846a0d45a5ee398f1b0873c056e5be2fa0a","view":"approach","speed":[0,0],"supportEvidence":"shared-pose-only"},"watch-107/seat":{"from":["puppy-paper-behaviors",107],"to":["puppy-paper-seat",50],"poseSha256":"19f89a8bcb5a937e528593a8f36db846a0d45a5ee398f1b0873c056e5be2fa0a","view":"approach","speed":[0,0],"supportEvidence":"shared-pose-only"},"watch-108/seat":{"from":["puppy-paper-behaviors",108],"to":["puppy-paper-seat",50],"poseSha256":"19f89a8bcb5a937e528593a8f36db846a0d45a5ee398f1b0873c056e5be2fa0a","view":"approach","speed":[0,0],"supportEvidence":"shared-pose-only"},"watch-125/seat":{"from":["puppy-paper-behaviors",125],"to":["puppy-paper-seat",50],"poseSha256":"19f89a8bcb5a937e528593a8f36db846a0d45a5ee398f1b0873c056e5be2fa0a","view":"approach","speed":[0,0],"supportEvidence":"shared-pose-only"}}});