Diagonals: streets, walls, fences, hedges and house walls at any angle
Deploy web Studio / deploy (push) Successful in 1m5s

Object frames turn by any angle (quarter turns stay exact, so scenes along
the axes render byte for byte as before); a box in a turned frame is a
prism with its own corners. Rotation is kept as given (normalised to
0..360), no longer snapped to quarter turns.

Depth sort: sprites whose plan outline is not their box carry their convex
outline; when boxes overlap, an outline edge that separates two sprites
orders them. A long diagonal wall sorts right against both sides.

Joints (line_meet, end_cut, cut_box in util.rhai): where a piece off the
axes is involved, corners are mitred and T-junctions slanted; fences put a
corner post or pillar in its own sprite and mitre stone bases; Y joints let
the lowest id run through. Streets: every end part is a cut e + k*w, so
side streets come in at any angle (slanted sidewalk gaps), two streets meet
in a mitre with seam patches; fixes a curve's outer sidewalk that was
skipped when its kerb and back lines had different point counts.

Studio: runs and drags keep to x/y or snap to 45 degree diagonals, Ctrl
gives any angle; a road run turning 45 degrees gets a 45 degree curve;
Rotation is a number box. Streets demo: a diagonal lane with a hedge.
MCP guidance, SCRIPTING/README/ROADMAP.

Tests: Rust 76, Studio and web checks green.
This commit is contained in:
grabowski committed 2026-10-25 13:22:59 +07:00
1 parent c91d63663b
commit 96d472625f
18 files changed
+1051 -178

No files matched your search

+22 -7
View File
@@ -126,13 +126,26 @@ fn prefab_house_wall(o) {
gaps.push(opening_span(o, m.u, m.p.w));
}
gaps.sort(|a, b| if a[0] < b[0] { -1 } else if a[0] > b[0] { 1 } else { 0 });
let x = -p.w / 2.0 - wall_ext(o, -1.0);
let c0 = house_wall_end(o, -1.0);
let c1 = house_wall_end(o, 1.0);
let x = c0.x;
let k = c0.k;
for g in gaps {
if g[0] > x + 0.01 { wall_run(x, g[0], t, top, p.mat, cap); }
if g[0] > x + 0.01 { wall_run(x, k, g[0], 0.0, t, top, p.mat, cap); }
if g[1] > x { k = 0.0; }
x = fmax(x, g[1]);
}
let end = p.w / 2.0 + wall_ext(o, 1.0);
if end > x + 0.01 { wall_run(x, end, t, top, p.mat, cap); }
if c1.x > x + 0.01 { wall_run(x, k, c1.x, c1.k, t, top, p.mat, cap); }
}
/// Where house wall o ends at e (-1 or 1): #{ x, k } (see end_cut); mitred
/// at corners and slanted at T-junctions where a wall is diagonal.
fn house_wall_end(o, e) {
let m = line_meet(o, e, wall_types(), []);
if m.kind == "axis" { return end_cut(o.p.w, e, #{ ext: wall_ext(o, e), k: 0.0 }); }
if m.kind == "free" { return end_cut(o.p.w, e, #{ ext: o.p.t / 2.0, k: 0.0 }); }
if m.kind == "run" { return end_cut(o.p.w, e, #{ ext: 0.0, k: 0.0 }); }
end_cut(o.p.w, e, m)
}
/// How far wall o reaches past end e (-1 or 1) of its centre line. Alone:
@@ -162,12 +175,14 @@ fn wall_ext(o, e) {
ext
}
/// A solid stretch of wall from x0 to x1, one sprite per 30 m at most.
fn wall_run(x0, x1, t, top, mat, cap) {
/// A solid stretch of wall from x0 to x1 (ends slanted by k0, k1), one
/// sprite per 30 m at most.
fn wall_run(x0, k0, x1, k1, t, top, mat, cap) {
let n = ((x1 - x0) / 30.0).ceiling().to_int();
let L = (x1 - x0) / n;
for i in 0..n {
sprite().box(x0 + i * L, -t, x0 + (i + 1) * L, t, 0.0, top, mat, #{ top: cap });
sprite().cut_box(x0 + i * L, if i == 0 { k0 } else { 0.0 }, x0 + (i + 1) * L, if i == n - 1 { k1 } else { 0.0 },
-t, t, 0.0, top, mat, #{ top: cap });
}
}