Point handles for routes and outlines; Area corners; bus goes round right
Deploy web Studio / deploy (push) Successful in 1m4s

Studio: one selected object with pts (a route, an outlined area) shows a
handle on each point and a + in the middle of each side: drag a point to
move it, drag a + to add one there, double-click a point to remove it
(open routes keep 2, outlines 3). Points snap to the grid and onto wall,
fence and road ends, at any angle; one undo step each. Handles keep their
screen size (new viewer callback onView on every redraw) and follow
re-renders and selection. The viewer captures the pointer, so a double-
click targets the drawing: the last point pressed is remembered instead.

Prefab meta `outline`: the prefab draws pts as its outline. Area has it:
Edit corners turns its rectangle into four points, Rectangle again
removes them.

Routes: where a path doubles back, the lane offset puts a point on each
side, so it rounds into a U-turn instead of a diagonal across the lanes.
The streets demo bus now runs east, three quarters round the roundabout
(clockwise, left-hand traffic), down south street, turns and comes back
by the short way, instead of going round the wrong way on its way out.

Tests: Rust 81 (a doubling-back route: each mover once, no sort cycles),
Studio 134 (corners, move, add, remove, undo), web 100.
This commit is contained in:
grabowski committed 2026-10-25 18:46:47 +07:00
1 parent 540b37020c
commit c8becc2e59
12 files changed
+189 -11

No files matched your search

+18 -2
View File
@@ -936,8 +936,24 @@ fn route_movers_show_once_each_at_every_moment() {
);
deer.rot = 30.0;
deer.pts = Some(vec![[-6.0, 0.0], [6.0, 0.0]]);
// a loop that doubles back twice (a spur up to a stop and back, a U-turn at the west end):
// each way keeps to its side, so the strips never overlap
let mut spur = obj(
"spur",
"route",
0.0,
40.0,
json!({"kind": "bus", "n": 2, "closed": true}),
);
spur.pts = Some(vec![[-20.0, 0.0], [20.0, 0.0], [20.0, 15.0], [20.0, 0.0]]);
let doc = Doc {
objects: vec![cars, walk, deer, obj("t1", "tree", 0.0, -14.0, json!({}))],
objects: vec![
cars,
walk,
deer,
spur,
obj("t1", "tree", 0.0, -14.0, json!({})),
],
..Default::default()
};
for view in 0..4u8 {
@@ -947,7 +963,7 @@ fn route_movers_show_once_each_at_every_moment() {
assert_eq!(out.cycles, 0, "view {view}: depth-sort cycles");
// one <style> with every keyframe set, each used
assert_eq!(out.html.matches("<style>").count(), 1);
for (id, n) in [("cars", 4), ("walk", 3), ("deer", 2)] {
for (id, n) in [("cars", 4), ("walk", 3), ("deer", 2), ("spur", 2)] {
let wins = travel_windows(&b, id);
assert!(!wins.is_empty(), "{id}");
// at any moment, each mover is on exactly one piece