fix(street): 0.1.1 collision patch — flush sidewalk edge, graded crossing ramps (#21)

Merged PR #21: 0.1.1 collision patch (flush sidewalk edge, graded ramps)

Co-authored-by: Zwitschi <zwitschi82@gmail.com>
This commit was merged in pull request #21.
This commit is contained in:
zwitschi authored and zwitschi committed 2026-11-08 16:19:11 +01:00
1 parent 0dd4a55c87
commit e75e5500a7
38 files changed
+162 -83

No files matched your search

+51 -6
View File
@@ -68,8 +68,13 @@ def parse_args():
ap.add_argument("--cross", type=float, default=0.75)
ap.add_argument("--kerb", type=float, default=0.15)
ap.add_argument("--ramp-half-width", type=float, default=2.5)
ap.add_argument("--ramp-blend", type=float, default=1.5)
return ap.parse_args(argv)
ap.add_argument("--ramp-blend", type=float, default=None,
help="override the ramp run; default derives it from --max-ramp-grade")
ap.add_argument("--max-ramp-grade", type=float, default=0.08,
help="maximum crossing-ramp slope (0.08 = 8%%)")
args = ap.parse_args(argv)
args.ramp_blend = ramp_blend(args.kerb, args.max_ramp_grade, args.ramp_blend)
return args
def find_proj():
@@ -125,7 +130,13 @@ class Dem:
def kerb_height(s, base, half_width, blend):
"""Full kerb, except flushed near a crossing so people can step off."""
"""Full kerb, except flushed near a crossing so people can step off.
`blend` is the horizontal run over which the kerb drops from `base` to the
0.02 m flush height. It is derived from a target maximum grade (see
`ramp_blend`) so the crossing ramps stay within a real ramp's slope instead
of the ~8.7 % the old fixed 1.5 m run produced.
"""
for crossing in CROSSINGS:
d = abs(s - crossing)
if d <= half_width:
@@ -136,6 +147,22 @@ def kerb_height(s, base, half_width, blend):
return base
def ramp_blend(kerb, max_grade, override=None):
"""Horizontal run needed for the kerb ramp to stay at or under max_grade.
0.15 m kerb at an 8 % grade needs 1.63 m of run; the previous fixed 1.5 m
gave 8.7 %, and the walk pre-check measured the transition as a 10-13 cm
step over 1.5 m. When `override` is given it wins (kept for reproducing old
meshes), otherwise the run is computed from the live kerb height so it stays
correct if the kerb height assumption changes.
"""
if override is not None:
return max(0.0, override)
if max_grade <= 0.0:
return 0.0
return max(0.0, (kerb - 0.02)) / max_grade
def strip(rows, offsets, z_at, name, material):
"""Build a quad grid from per-row sampled vertices."""
verts, faces = [], []
@@ -218,13 +245,31 @@ def main():
z = dem_z(x, y)
return (z if z is not None else 0.0) + 0.02
def south_z(r, off, x, y):
def sidewalk_z(r, off, inner, outer, x, y):
"""Sidewalk surface: full kerb upstand at the road-side (inner) edge,
tapering to zero at the building-side (outer) edge so the walk surface
meets the terrain flush.
Before this taper the outer lip stood the full kerb height above the DEM,
which the walkability pre-check measured as a 15.8 cm step where a sample
crosses from Terrain_DEM onto Sidewalk_South (with a matching lip on the
north side). A 0.15 m lip over a 4.0 m / 5.3 m strip is a ~3-4 % ramp:
walkable and visually negligible, and it removes the ledge entirely.
"""
z = dem_z(x, y)
base = z if z is not None else 0.0
return base + kerb_height(rows[r][0], args.kerb, args.ramp_half_width, args.ramp_blend)
h = kerb_height(rows[r][0], args.kerb, args.ramp_half_width, args.ramp_blend)
span = inner - outer
if abs(span) < 1e-9:
return base + h
t = min(1.0, max(0.0, (off - outer) / span))
return base + h * t
def south_z(r, off, x, y):
return sidewalk_z(r, off, SOUTH_GUTTER, SOUTH_SIDEWALK, x, y)
def north_z(r, off, x, y):
return south_z(r, off, x, y)
return sidewalk_z(r, off, NORTH_GUTTER, NORTH_SIDEWALK, x, y)
road = strip(rows, road_offsets, road_z, "Road_Hollywood", asphalt)
sw_s = strip(rows, south_offsets, south_z, "Sidewalk_South", concrete)