Spaces:
Running on Zero
Running on Zero
Draw track preview with native cubic curves
#4
by TiGa-RCE - opened
- RELEASE_MANIFEST.json +1 -1
- track-map.js +19 -3
RELEASE_MANIFEST.json
CHANGED
|
@@ -83,7 +83,7 @@
|
|
| 83 |
"star_check.py": "5c17080957b81ffccf0de8314635b471562e55c92c97a1207d9034a99279b2c3",
|
| 84 |
"steam_leaderboard.py": "d76397c39cae58f636ddf9a9be1a46ae3d77fefd6b7269feac73d34d97165481",
|
| 85 |
"track-library.js": "351ec4c026b49420ce963f4310cb000d9e987fd28e6dac5ecd0176509340f6e6",
|
| 86 |
-
"track-map.js": "
|
| 87 |
"training_config.py": "6eb7282bbee3b3a6273bddc29c949d9904067414c26e501d815012f033173a1c",
|
| 88 |
"vendor/gradio-client/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
|
| 89 |
"vendor/gradio-client/dist/index.min.js": "fdd776db8633354f118fe2908c980592283968737e6b91dc3aa86d648da5c3fd",
|
|
|
|
| 83 |
"star_check.py": "5c17080957b81ffccf0de8314635b471562e55c92c97a1207d9034a99279b2c3",
|
| 84 |
"steam_leaderboard.py": "d76397c39cae58f636ddf9a9be1a46ae3d77fefd6b7269feac73d34d97165481",
|
| 85 |
"track-library.js": "351ec4c026b49420ce963f4310cb000d9e987fd28e6dac5ecd0176509340f6e6",
|
| 86 |
+
"track-map.js": "a5e76598d5988c0446ad3b5796b601467cf9423e6daddf21753258b6eee0c195",
|
| 87 |
"training_config.py": "6eb7282bbee3b3a6273bddc29c949d9904067414c26e501d815012f033173a1c",
|
| 88 |
"vendor/gradio-client/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
|
| 89 |
"vendor/gradio-client/dist/index.min.js": "fdd776db8633354f118fe2908c980592283968737e6b91dc3aa86d648da5c3fd",
|
track-map.js
CHANGED
|
@@ -33,6 +33,20 @@
|
|
| 33 |
case 3:return [x+size,y+offset];
|
| 34 |
}
|
| 35 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
async function decode(file){
|
| 37 |
if(!file||file.size<1||file.size>1048576)throw Error('Choose a .track file under 1 MB');
|
| 38 |
if(typeof DecompressionStream!=='function')throw Error('This browser cannot preview gzip tracks');
|
|
@@ -89,9 +103,11 @@
|
|
| 89 |
context.strokeStyle='#d7e8f2';context.lineWidth=Math.max(1,size*.018);
|
| 90 |
context.stroke();
|
| 91 |
if(Array.isArray(tile.connections)&&tile.connections.length>=2){
|
| 92 |
-
const
|
|
|
|
|
|
|
| 93 |
context.beginPath();context.moveTo(...a);
|
| 94 |
-
context.
|
| 95 |
context.strokeStyle='#f3a967';context.lineWidth=Math.max(3,size*.055);
|
| 96 |
context.lineCap='round';context.stroke();
|
| 97 |
}
|
|
@@ -125,7 +141,7 @@
|
|
| 125 |
status.textContent=source+' · '+track.tiles.length+' pieces · green dot marks the saved starting point';
|
| 126 |
legend.textContent=NAMES.filter((_,i)=>counts[i]).map((name,index)=>{
|
| 127 |
const i=NAMES.indexOf(name);return name+' '+counts[i];
|
| 128 |
-
}).join(' · ')+' · Orange line
|
| 129 |
}catch(error){
|
| 130 |
if(current!==version)return;
|
| 131 |
renderEmpty();status.textContent='Map unavailable: '+error.message;legend.textContent='Training selection is unaffected.';
|
|
|
|
| 33 |
case 3:return [x+size,y+offset];
|
| 34 |
}
|
| 35 |
}
|
| 36 |
+
function connectionControl(own,other,point,target){
|
| 37 |
+
// Native TrackUtils.GetPathCurve (random_track.rs, b90b8af): each
|
| 38 |
+
// connection's inward normal sets its cubic handle, adjusted by port type.
|
| 39 |
+
// At the exit this is the next tile's negated entry normal, so the same
|
| 40 |
+
// per-tile construction also preserves the curve when endpoints reverse.
|
| 41 |
+
const [nx,ny]=[[0,1],[0,-1],[1,0],[-1,0]][own.side];
|
| 42 |
+
const dx=target[0]-point[0],dy=target[1]-point[1];
|
| 43 |
+
const angle=own.type===1?Math.PI/4:own.type===3?Math.atan2(128,384):own.type===4?Math.atan2(384,128):0;
|
| 44 |
+
const rotation=Math.sign(nx*dy-ny*dx)*angle;
|
| 45 |
+
const c=Math.cos(rotation),s=Math.sin(rotation);
|
| 46 |
+
const smoothness=[0,2,100].includes(own.type)&&[1,3,4].includes(other.type)?0.2:0.5;
|
| 47 |
+
const reach=Math.hypot(dx,dy)*smoothness;
|
| 48 |
+
return [point[0]+(nx*c-ny*s)*reach,point[1]+(nx*s+ny*c)*reach];
|
| 49 |
+
}
|
| 50 |
async function decode(file){
|
| 51 |
if(!file||file.size<1||file.size>1048576)throw Error('Choose a .track file under 1 MB');
|
| 52 |
if(typeof DecompressionStream!=='function')throw Error('This browser cannot preview gzip tracks');
|
|
|
|
| 103 |
context.strokeStyle='#d7e8f2';context.lineWidth=Math.max(1,size*.018);
|
| 104 |
context.stroke();
|
| 105 |
if(Array.isArray(tile.connections)&&tile.connections.length>=2){
|
| 106 |
+
const [entry,exit]=tile.connections;
|
| 107 |
+
const a=connectionPoint(entry,x,y,size),z=connectionPoint(exit,x,y,size);
|
| 108 |
+
const p1=connectionControl(entry,exit,a,z),p2=connectionControl(exit,entry,z,a);
|
| 109 |
context.beginPath();context.moveTo(...a);
|
| 110 |
+
context.bezierCurveTo(...p1,...p2,...z);
|
| 111 |
context.strokeStyle='#f3a967';context.lineWidth=Math.max(3,size*.055);
|
| 112 |
context.lineCap='round';context.stroke();
|
| 113 |
}
|
|
|
|
| 141 |
status.textContent=source+' · '+track.tiles.length+' pieces · green dot marks the saved starting point';
|
| 142 |
legend.textContent=NAMES.filter((_,i)=>counts[i]).map((name,index)=>{
|
| 143 |
const i=NAMES.indexOf(name);return name+' '+counts[i];
|
| 144 |
+
}).join(' · ')+' · Orange line follows the track centerline; collision shapes and car positions are not shown.';
|
| 145 |
}catch(error){
|
| 146 |
if(current!==version)return;
|
| 147 |
renderEmpty();status.textContent='Map unavailable: '+error.message;legend.textContent='Training selection is unaffected.';
|