Streets: sidewalks, road curves, junctions and roundabouts
Deploy web Studio / deploy (push) Successful in 4m31s

Roads take sidewalks (none, both, left, right): 12 cm high paving with
kerb faces drawn where they face the viewer. Road curve (radius, angle,
left/right) carries a street round a bend; centre lines are true arcs
when cut and offset, so curve joints are seamless. A road ending on
another road's centre line opens that road's kerb and sidewalk (T
junction); two roads ending at one corner close it. Roundabout: ring
radius and width, one or two lanes, up to four arms with flared kerbs,
splitter islands, give-way lines on the entry side (traffic on the left
or right) and a central island with apron.

Studio: a road run drawn corner by corner gets a curve in each corner
(prefab meta `corner`). Tooltips list the streets each one meets.
Engine: open `polyline` primitive (Prim::Poly open) so dashes run on
round curves. examples/streets and a Streets demo in the web Studio;
MCP guidance, docs. ROADMAP: backlog 5-7 done; multi-select and the
hosted copy marked done.

Tests: Rust 72, Studio 113, web 95.
This commit is contained in:
grabowski committed 2026-10-25 12:32:52 +07:00
1 parent 2442966743
commit c91d63663b
24 files changed
+989 -29

No files matched your search

+3 -1
View File
@@ -145,11 +145,13 @@ pub enum Prim {
two_sided: bool,
},
/// 3D polygons merged into one path with a fixed class (PV modules,
/// signs, markings on slopes): no shading, no culling.
/// signs, markings on slopes): no shading, no culling. `open`: lines
/// that are not closed (a dashed line round a curve stays one dash run).
Poly {
polys: Vec<Vec<P3>>,
cls: String,
id: Option<String>,
open: bool,
},
/// Screen-space circle of radius r (screen units) at a world point,
/// shifted by off (screen units): heads, lamps, LEDs, tree crowns.
+7 -2
View File
@@ -415,14 +415,19 @@ impl Ctx<'_> {
id,
two_sided,
} => self.face(pts, mat, id.as_deref(), *two_sided),
Prim::Poly { polys, cls, id } => {
Prim::Poly {
polys,
cls,
id,
open,
} => {
if polys.is_empty() {
return;
}
let p = self.p;
self.path_open(cls, id.as_deref());
for q in polys {
p.d(&mut self.out, q, false);
p.d(&mut self.out, q, *open);
}
self.out.push_str("\"/>");
}
+16
View File
@@ -447,6 +447,21 @@ impl Sprite {
/// 3D polygons as one path with a fixed class.
pub fn poly(&mut self, polys: Vec<Vec<P3>>, cls: &str, id: Option<String>) -> &mut Self {
self.poly_path(polys, cls, id, false)
}
/// Open 3D polylines as one path (markings along curves: one dash run).
pub fn polyline(&mut self, lines: Vec<Vec<P3>>, cls: &str, id: Option<String>) -> &mut Self {
self.poly_path(lines, cls, id, true)
}
fn poly_path(
&mut self,
polys: Vec<Vec<P3>>,
cls: &str,
id: Option<String>,
open: bool,
) -> &mut Self {
let polys: Vec<Vec<P3>> = polys
.into_iter()
.map(|p| p.into_iter().map(|q| self.xf.p3(q)).collect())
@@ -458,6 +473,7 @@ impl Sprite {
polys,
cls: cls.into(),
id,
open,
})
}