Track plans read as outlines, not hatching
Nearly every track is one model repeated: cn3, a wall bar whose gauge image is two closed 25x5 rectangles. Five metres of wall thickness is finer than the plan can resolve, so each bar was landing as two parallel lines plus two end caps - several hundred times over, which is the hatching that swamped the arenas. Collapse a thin closed quad to the centreline between its short edges: one stroke for one wall. Walls stacked to build height coincide seen from above, so draw each distinct wall once. And drop placements standing alone more than 200 units from any other - fourteen tracks park a single bar at (1200,0,0) well off the course, and that one placement stretched the frame to twelve times the width of the track. The four tracks without it are exactly the four that always framed correctly. A real branch keeps its neighbours and stays: Paingod's second canyon is sixty bars out at x=-400.
This commit is contained in:
+74
-63
File diff suppressed because one or more lines are too long
@@ -86,7 +86,7 @@ def era(key):
|
||||
GAUGE_IDS = {rid for rid, r in TABLE.items() if r['desc'].endswith(': GaugeImage')}
|
||||
|
||||
|
||||
def plan_view(segs, box=900):
|
||||
def plan_view(segs, box=520):
|
||||
"""The track's own outlines, straight down, as an alpha mask."""
|
||||
pts = [p for seg in segs for p in seg]
|
||||
xs = [p[0] for p in pts]
|
||||
@@ -171,9 +171,9 @@ for key in ORDER:
|
||||
kin = [k for k in twins.get(sig, []) if k != key]
|
||||
twin = ('<p class="twin">Same footprint and instance count as '
|
||||
+ ', '.join(tracks[k]['name'] for k in kin) + '</p>') if kin else ''
|
||||
short = ('<p class="twin">%d of %d placements carry map art; the rest the '
|
||||
'map screen does not draw either</p>' % (t['drawn'], t['instances'])
|
||||
) if t['skipped'] else ''
|
||||
short = ('<p class="twin">%d of %d placements are drawn; the rest carry no '
|
||||
'map art or stand alone off the course</p>'
|
||||
% (t['drawn'], t['instances'])) if t['skipped'] else ''
|
||||
art = ('<span class="plan__img" role="img" aria-label="%s plan view" '
|
||||
'style="--art:url(%s);--ar:%d/%d"></span>'
|
||||
% (html.escape(t['name']), t['art'], t['aw'], t['ah'])) if t['art'] else ''
|
||||
|
||||
+54
-3
@@ -100,6 +100,29 @@ def instances(res, addr, size, count, gauge_ids):
|
||||
return out
|
||||
|
||||
|
||||
def spine(seg):
|
||||
"""A track is built almost entirely from one model, cn3: a wall bar
|
||||
drawn as two closed 25x5 rectangles. Five metres of wall thickness is
|
||||
below the map's own resolution, so drawing the rectangle puts two
|
||||
parallel lines and two end caps where the wall is one line - and a few
|
||||
hundred bars of that is the hatching that swamps the plan. Collapse a
|
||||
thin closed quad to the centreline joining its two short edges: the
|
||||
same wall, drawn as the single stroke it reads as."""
|
||||
p = seg[:-1] if len(seg) >= 5 and seg[0] == seg[-1] else None
|
||||
if not p or len(p) != 4:
|
||||
return [seg]
|
||||
import math
|
||||
edge = [math.hypot(p[(i + 1) % 4][0] - p[i][0],
|
||||
p[(i + 1) % 4][2] - p[i][2]) for i in range(4)]
|
||||
if max(edge) == 0 or min(edge) / max(edge) > 0.5:
|
||||
return [seg] # not a bar - leave it alone
|
||||
lo = min(range(4), key=lambda i: edge[i])
|
||||
a, b = p[lo], p[(lo + 1) % 4]
|
||||
c, d = p[(lo + 2) % 4], p[(lo + 3) % 4]
|
||||
mid = lambda u, v: tuple((u[k] + v[k]) / 2 for k in range(3))
|
||||
return [[mid(a, b), mid(c, d)]]
|
||||
|
||||
|
||||
def rotate(q, p):
|
||||
"""Quaternion (x,y,z,w) applied to a point."""
|
||||
qx, qy, qz, qw = q
|
||||
@@ -120,10 +143,29 @@ def track_lines(res, table, map_addr, map_size, map_count):
|
||||
gauge[rid] = r
|
||||
segs = []
|
||||
drawn = skipped = 0
|
||||
for gid, pos, q in instances(res, map_addr, map_size, map_count, set(gauge)):
|
||||
placed = instances(res, map_addr, map_size, map_count, set(gauge))
|
||||
|
||||
# Fourteen of the eighteen tracks carry a single bar parked at exactly
|
||||
# (1200, 0, 0), well off the course; the four that don't are the four
|
||||
# that always framed correctly. One stray placement drags the bounding
|
||||
# box out to twelve times the width of the course and squeezes the
|
||||
# track into a sliver, so drop placements that stand alone. A real
|
||||
# branch - Paingod's second canyon is sixty bars out at x=-400 - has
|
||||
# neighbours and stays.
|
||||
def isolated(i):
|
||||
x, _, z = placed[i][1]
|
||||
for j, (g, p, _) in enumerate(placed):
|
||||
if j != i and g is not None and (p[0] - x) ** 2 + (p[2] - z) ** 2 < 200 ** 2:
|
||||
return False
|
||||
return True
|
||||
|
||||
for i, (gid, pos, q) in enumerate(placed):
|
||||
if gid is None:
|
||||
skipped += 1
|
||||
continue
|
||||
if isolated(i):
|
||||
skipped += 1
|
||||
continue
|
||||
r = gauge[gid]
|
||||
img = read_gauge_image(res, r['addr'], r['size'])
|
||||
if img is None:
|
||||
@@ -138,5 +180,14 @@ def track_lines(res, table, map_addr, map_size, map_count):
|
||||
wx, wy, wz = rotate(q, verts[k])
|
||||
pts.append((wx + pos[0], wy + pos[1], wz + pos[2]))
|
||||
if len(pts) > 1:
|
||||
segs.append(pts)
|
||||
return segs, drawn, skipped
|
||||
segs.extend(spine(pts))
|
||||
|
||||
# Walls are stacked to build height. Seen from above those copies land
|
||||
# on each other exactly, so draw each distinct wall once.
|
||||
seen, out = set(), []
|
||||
for s in segs:
|
||||
key = tuple(round(v, 1) for p in s for v in (p[0], p[2]))
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
out.append(s)
|
||||
return out, drawn, skipped
|
||||
|
||||
@@ -50,6 +50,17 @@
|
||||
rotated and positioned. A placement whose model has no gauge image is skipped here
|
||||
exactly as the map screen skips it, which is why a card can say fewer placements
|
||||
carry map art than the track contains.</p>
|
||||
<p class="foot">Two things are drawn plainer than the pod draws them, so the plan reads
|
||||
as an outline. Nearly every track is one model repeated — <code>cn3</code>, a
|
||||
wall bar whose gauge image is two closed 25×5 rectangles. Five metres of wall
|
||||
thickness is finer than the plan can resolve, so each bar is collapsed to the
|
||||
centreline between its short edges: one stroke for one wall, instead of two parallel
|
||||
lines and two end caps several hundred times over. Walls stacked to build height land
|
||||
on each other seen from above, and are drawn once. Placements standing alone more than
|
||||
200 units from any other are dropped — fourteen tracks park a single bar at
|
||||
(1200, 0, 0) far off the course, and one stray placement stretches the frame
|
||||
to twelve times the width of the track. A real branch keeps its neighbours and stays:
|
||||
Paingod's second canyon is sixty bars out at x = -400.</p>
|
||||
<p class="foot">So the walls are the canyon walls and the chambers are real chambers,
|
||||
but there is still no driving surface: the map draws what lines the route, never the
|
||||
tarmac. Extent and relief are the span of the drawn outlines in world units, so a
|
||||
|
||||
Reference in New Issue
Block a user