Joins follow the travel direction: the next piece goes right after the end (t67 phase 2b)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 14:09:28 -04:00
co-authored by Claude Opus 5.5
parent fd35bf334f
commit 45538cc18e
16 changed files with 363 additions and 236 deletions
+37 -2
View File
@@ -97,7 +97,7 @@ with sync_playwright() as p:
}''')
check('sidewall stays behind the grain colour in pixels and smooth',
wall and wall['colors'][0] == wall['colors'][1] and wall['grainClass'] == 2, wall)
# Shared joins: E elbows bend inside the straight block; other placements keep collars.
# Travel joins: edge elbows in diagonals, E elbows in straight receivers, otherwise collars.
pg.set_viewport_size({"width": 2100, "height": 1500})
ev("part.fill(0); blockP.fill(0); occ.fill(0); mach.fill(0); ents=[]; ghostBlocks.clear();")
ev("() => {" + (root / "tools/scenes/joins.js").read_text() + "}")
@@ -153,7 +153,7 @@ with sync_playwright() as p:
const bad=[], now=performance.now.bind(performance),frozen=now();
performance.now=()=>frozen;
try {
for(const look of ['pixel','smooth'])for(const j of JOIN_CASES.filter(j=>j.code===BP_SCREW && j.spot===2 && j.end===1 && (j.v===5&&!j.vertical || j.v===4&&j.vertical))) {
for(const look of ['pixel','smooth'])for(const j of JOIN_CASES.filter(j=>j.code===BP_SCREW && j.joined && j.kind>=5 && (j.v===5&&!j.vertical || j.v===4&&j.vertical))) {
SET.look=look;
const side=j.end?1:-1,slope=j.v===4||j.v===6?-1:1;
const ux=2+(j.vertical?side:-side)*1.8,uy=2+(j.vertical?-side*slope:side*slope)*1.8;
@@ -196,6 +196,41 @@ with sync_playwright() as p:
} finally {performance.now=now;}
}''')
check('smooth: trees behind a straight screw cannot become elbow join bits', not tree_tags, tree_tags)
drops = ev("""() => {
const detail=slopeDetailAt,now=performance.now.bind(performance),frozen=now(),out={};
const j=JOIN_CASES.find(j=>j.v===4&&j.end===0&&j.code===BP_BELT&&j.spot===0);
try {
slopeDetailAt=()=>0;performance.now=()=>frozen;
const sample=()=>{const p=worldToScreenPx(j.bx*BS+0.4,j.by*BS+2.5);return Array.from(R.ctx.getImageData(Math.floor(p.x),Math.floor(p.y),1,1).data);};
for(const look of ['pixel','smooth']) {
SET.look=look;SM.scale=1;drawWorld();const joined=sample();
removeBlock(j.nx,j.ny);drawWorld();const free=sample();
placeBlock(BP_BELT,j.straight,j.nx,j.ny,true);
out[look]=[joined,free];
}
return out;
} finally {slopeDetailAt=detail;performance.now=now;}
}""")
for look, (joined, free) in drops.items():
check(f'{look}: the same-row uphill inlet draws its drop above the low face', joined != free and joined[2] < free[2], [joined, free])
filter_plans = ev("""() => {
const saved={...GAME},original=slopeBeltConnections,seen=[];
try {
GAME.tool=TOOLS.findIndex(t=>t.id==='build');GAME.pieceIdx=PIECES.findIndex(p=>p.id==='filter');
GAME.selDrag=null;GAME.paste=false;GAME.filterCfg={m:[],inv:0};GAME.pieceDir={filter:1};
slopeBeltConnections=(x,y,c,p)=>{
if(p)seen.push([c,p.get(y*BW+x)?.[1]]);
return original(x,y,c,p);
};
for(const grade of [-1,1]) {
GAME.drag={bx:50,by:30};GAME.aim={x:52*BS,y:(30+2*grade)*BS};
drawPreviews();
}
return seen;
} finally {slopeBeltConnections=original;Object.assign(GAME,saved);}
}""")
check('filter drag previews pass stored travel bits to the join rule',
bool(filter_plans) and all(v == (128 if c == ev('BP_FILTER_DOWN') else 0) for c, v in filter_plans), filter_plans)
# a browser without hardware drawing gets the pixel look and a note
pg2 = b.new_page(viewport={'width': 800, 'height': 500})
pg2.add_init_script('localStorage.clear();')