Diagonals: streets, walls, fences, hedges and house walls at any angle
Deploy web Studio / deploy (push) Successful in 1m5s
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:
1 parent
c91d63663b
commit
96d472625f
18 files changed
+1051
-178
No files matched your search
@@ -14,7 +14,8 @@ pub const PROTOCOL: &str = "2025-06-18";
|
||||
|
||||
const INSTRUCTIONS: &str = "isokit edits an isometric scene (a site plan drawn as a SimCity-style 3D view). \
|
||||
World coordinates are metres: x east, y north, z up. The scene is a list of objects; each has a type (a prefab), \
|
||||
a position (x, y = centre of its footprint), a rotation in 90 degree steps and props from the prefab's schema. \
|
||||
a position (x, y = centre of its footprint), a rotation in degrees (any angle, counter-clockwise from east; \
|
||||
90 degree steps keep things along the axes) and props from the prefab's schema. \
|
||||
Start with project_info and list_prefabs, then add_object / update_object. Use render_view (summary) to see where \
|
||||
everything ended up and check_project to find problems. Edits are saved at once and show live in an open Studio. \
|
||||
Houses are built from parts (categories House and Furniture): a floor per room (w x d), a house_wall along each edge \
|
||||
@@ -23,7 +24,9 @@ then windows and doors with x, y on a wall line (they sit on that wall and cut t
|
||||
the wall's +y side, rot + 180 swings it the other way), a roof (w x d = outer size of the walls) and furniture \
|
||||
(fronts face local -y, backs go against a wall). Level gf shows the house cut open; a part's storey is its z / 3 m. \
|
||||
Fences (props.style: mesh, wood, bars, half stone, cobble, brick) and site walls join where their centre-line ends \
|
||||
meet, so for a plot boundary put each piece's ends exactly on the next piece's end or line. \
|
||||
meet, so for a plot boundary put each piece's ends exactly on the next piece's end or line. Line pieces (roads, walls, \
|
||||
fences, hedges, house walls) can run at any angle: a piece from (x0, y0) to (x1, y1) has x, y = the midpoint, \
|
||||
w = the distance and rot = atan2(y1 - y0, x1 - x0) in degrees; where diagonal pieces meet, corners are mitred. \
|
||||
Production lines (category Industry): a producer (props.rate per minute, product) puts goods out on its local +x \
|
||||
side; a conveyor (rot 0 runs towards +x, 90 +y, 180 -x, 270 -y; w = length, props.speed m/s) starting exactly \
|
||||
at that side carries them, and a conveyor whose start is at another's end carries on with them (corners join). \
|
||||
@@ -32,8 +35,8 @@ A robot_arm (props.reach, default 2 m) picks goods off the end of the nearest co
|
||||
them on the start of another conveyor within reach, or on a pallet beside it; place it 1.5 to 2 m past a belt's end. \
|
||||
Streets (category Ground) join by their centre lines: a road (x, y = middle, rot 0 along x, w = length, d = width, \
|
||||
props.walk none/both/left/right adds raised sidewalks walk_w wide) ending on another road's centre line makes a \
|
||||
junction there; a road_curve starts at its x, y heading along local +x (rot 0/90/180/270), bends props.angle degrees \
|
||||
left or right on radius r and ends where the next road starts; two roads ending at one point make a sharp corner. \
|
||||
junction there (at any angle); a road_curve starts at its x, y heading along its rot, bends props.angle degrees \
|
||||
left or right on radius r and ends where the next road starts; two roads ending at one point make a mitred corner. \
|
||||
A roundabout (props.r ring radius, arm_e/n/w/s, arm_len) has its arm ends r + arm_len from its centre: put the \
|
||||
roads' ends exactly there, with the same width as road_d.";
|
||||
|
||||
@@ -47,7 +50,7 @@ pub fn tools() -> Vec<Value> {
|
||||
"x": { "type": "number", "description": "east, metres (centre of the footprint)" },
|
||||
"y": { "type": "number", "description": "north, metres" },
|
||||
"z": { "type": "number", "description": "base height, metres (default 0)" },
|
||||
"rot": { "type": "number", "description": "rotation in degrees, multiples of 90" },
|
||||
"rot": { "type": "number", "description": "rotation in degrees counter-clockwise from east, any angle (diagonal streets, walls, fences, hedges); 0, 90, 180, 270 keep to the axes" },
|
||||
"props": { "type": "object", "description": "prefab properties (see list_prefabs); in updates they merge, null removes" },
|
||||
"name": { "type": "string", "description": "name shown in the tooltip" },
|
||||
"info": { "type": "string", "description": "tooltip text" },
|
||||
|
||||
@@ -84,8 +84,19 @@ fn finite(v: f64, what: &str) -> Result<f64, String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Any angle, in [0, 360) to 4 decimals; within 0.001 degrees of a quarter
|
||||
/// turn it is that quarter turn exactly (pieces along the axes stay exact).
|
||||
fn norm_rot(r: f64) -> f64 {
|
||||
(((r / 90.0).round() as i64).rem_euclid(4) * 90) as f64
|
||||
let q = (r / 90.0).round();
|
||||
if (r - q * 90.0).abs() < 1e-3 {
|
||||
return ((q as i64).rem_euclid(4) * 90) as f64;
|
||||
}
|
||||
let a = (r.rem_euclid(360.0) * 1e4).round() / 1e4;
|
||||
if a >= 360.0 {
|
||||
0.0
|
||||
} else {
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
/// Check property values against the prefab schema (unknown keys pass:
|
||||
@@ -364,7 +375,28 @@ mod tests {
|
||||
.id
|
||||
.unwrap();
|
||||
assert_eq!(id, "tree");
|
||||
// any angle stays (diagonal streets and walls), normalised to [0, 360)
|
||||
assert_eq!(d.objects[0].rot, 100.0);
|
||||
apply(
|
||||
&mut d,
|
||||
&c,
|
||||
op(json!({"op": "update", "id": "tree", "patch": {"rot": -45.000001}})),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(d.objects[0].rot, 315.0);
|
||||
apply(
|
||||
&mut d,
|
||||
&c,
|
||||
op(json!({"op": "update", "id": "tree", "patch": {"rot": 450.0004}})),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(d.objects[0].rot, 90.0);
|
||||
apply(
|
||||
&mut d,
|
||||
&c,
|
||||
op(json!({"op": "update", "id": "tree", "patch": {"rot": 100}})),
|
||||
)
|
||||
.unwrap();
|
||||
apply(&mut d, &c, op(json!({"op": "update", "id": "tree", "patch": {"x": 7.5, "props": {"r": 2.0}, "tag": "Oak"}}))).unwrap();
|
||||
assert_eq!(d.objects[0].x, 7.5);
|
||||
assert_eq!(d.objects[0].props["r"], json!(2.0));
|
||||
|
||||
Reference in new issue
Block a user