Point handles for routes and outlines; Area corners; bus goes round right
Deploy web Studio / deploy (push) Successful in 1m4s
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:
1 parent
540b37020c
commit
c8becc2e59
12 files changed
+189
-11
No files matched your search
@@ -155,6 +155,10 @@ pub struct PrefabMeta {
|
||||
/// Studio puts one in each corner of a run drawn corner by corner.
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub corner: String,
|
||||
/// The prefab draws the object's `pts` as its outline when it has them (areas):
|
||||
/// the Studio offers to turn its rectangle into corners that can be dragged.
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
pub outline: bool,
|
||||
#[serde(default)]
|
||||
pub props: Vec<PropDef>,
|
||||
/// Module the prefab comes from ("isokit/site", "lib/kiosk.rhai").
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,8 +26,8 @@ fn prefab_site(o) {
|
||||
}
|
||||
|
||||
fn meta_area() {
|
||||
#{ label: "Area", category: "Ground", icon: "▱", shape: "rect",
|
||||
help: "A paved or planted area lying on the ground.",
|
||||
#{ label: "Area", category: "Ground", icon: "▱", shape: "rect", outline: true,
|
||||
help: "A paved or planted area lying on the ground. Edit corners turns the rectangle into points you can drag, add (drag a +) and remove (double-click), for any outline.",
|
||||
props: [
|
||||
#{ key: "w", type: "float", def: 10.0, min: 0.5, max: 1000.0, step: 0.5, label: "Width (m)" },
|
||||
#{ key: "d", type: "float", def: 6.0, min: 0.5, max: 1000.0, step: 0.5, label: "Depth (m)" },
|
||||
|
||||
@@ -84,7 +84,8 @@ fn route_pts(o, closed) {
|
||||
out
|
||||
}
|
||||
|
||||
/// The line `d` metres to the left of a path (mitred corners).
|
||||
/// The line `d` metres to the left of a path (mitred corners). Where the path
|
||||
/// doubles back, the offset line gets a point on each side: a U-turn once rounded.
|
||||
fn rt_offset(pts, d, closed) {
|
||||
if d == 0.0 { return pts; }
|
||||
let n = pts.len();
|
||||
@@ -97,8 +98,12 @@ fn rt_offset(pts, d, closed) {
|
||||
if type_of(n1) == "()" { n1 = n2; }
|
||||
if type_of(n2) == "()" { n2 = n1; }
|
||||
let s = 1.0 + n1[0] * n2[0] + n1[1] * n2[1];
|
||||
let m = if s < 0.3 { n2 } else { rt_mul(rt_add(n1, n2), 1.0 / s) };
|
||||
out.push(rt_add(pts[i], rt_mul(m, d)));
|
||||
if s < 0.3 {
|
||||
out.push(rt_add(pts[i], rt_mul(n1, d)));
|
||||
out.push(rt_add(pts[i], rt_mul(n2, d)));
|
||||
} else {
|
||||
out.push(rt_add(pts[i], rt_mul(rt_mul(rt_add(n1, n2), 1.0 / s), d)));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user