Three iterations of the Daikoku PA ramps never read as a real
interchange. Measured why, rather than tuning further:
* the entry and exit decks INTERSECTED -- 2547 sample pairs within
30 studs horizontally at <12 studs dY, worst case 0.5 studs apart
at dY=0.0. On screen that is the bowtie tangle of white rails.
* both real Daikoku ramps turn the SAME way (clockwise; the guide
video says "right, right"). This build mirrored them into each
other.
* the real arc wraps OUTSIDE the lot and forms its north/east
boundary. This build cut across the apron interior.
Gated behind BUILD_PA_RAMPS=false rather than deleted -- the radius
was already correct (470 studs vs an OSM-measured mean of 469) and
the merge lands tangent at 0.1-0.2 deg divergence. Both worth keeping.
Also removed two leftovers that would have outlived the ramps:
* rampOpen punched holes in the mainline's outer parapet, railing
and kill wall for ramps that no longer exist -- a barrier gap with
a drop behind it. Sealed via RAMP_EDGE_OPENINGS=false. Verified:
Barrier/EdgeRail/KillWall = 1646 each = 823 samples x 2 edges,
0 road samples without a guard within 90 studs.
* gore zebra hatching painted 1024 bars + 64 boundary lines marking
a junction that is not there. Sealed via RAMP_GORE_MARKINGS=false.
Verified after a cache-busting fresh rebuild (Studio's require() cache
otherwise serves the previous map and reports a false pass):
parts 22628 -> 15988
all ramp part classes = 0
Road = 823 (unchanged), spawn pad + PASpawn intact
clip probe TRUSTWORTHY (control +11/-0), CLIPPING=0
Research and rebuild spec kept for when this is revisited:
reference/ramps/BUILD_SPEC.md, INDEX.md (10 CC-licensed Commons
photos), docs/daikoku-reference-spec.md (OSM circle fits).
104 lines
3.3 KiB
Luau
104 lines
3.3 KiB
Luau
-- Geometry facts needed to re-cut the ramps so they (a) merge at the highway
|
|
-- EDGE instead of landing on top of it, (b) run a corridor that misses the PA
|
|
-- shops, and (c) get pole/beam supports instead of whatever's there now.
|
|
--
|
|
-- Outputs:
|
|
-- 1. Mainline cross-section at the PA: per-carriageway edge X/Z + width, so
|
|
-- a merge can target the OUTER edge rather than the deck centreline.
|
|
-- 2. Plan-footprint of every shop model near the PA, as an X/Z box, so a
|
|
-- clear corridor can be computed instead of guessed.
|
|
-- 3. Current support part sizes/shapes, to see what needs replacing.
|
|
|
|
local ws = game:GetService("Workspace")
|
|
local map = ws:FindFirstChild("Map")
|
|
|
|
local out = {}
|
|
|
|
-- 1. mainline cross-section near the PA (sample by Z band)
|
|
local band = {}
|
|
for _, d in ipairs(map:GetDescendants()) do
|
|
if d:IsA("BasePart") and d.Name:match("^Road_") then
|
|
local p = d.Position
|
|
if p.Z > -7250 and p.Z < -7050 and p.X > -4700 and p.X < -3400 then
|
|
table.insert(band, d)
|
|
end
|
|
end
|
|
end
|
|
table.sort(band, function(a, b)
|
|
return a.Position.Z < b.Position.Z
|
|
end)
|
|
table.insert(out, ("mainline sample parts in band=%d"):format(#band))
|
|
-- cluster by Z to find the two carriageways
|
|
local zmin, zmax = 1e9, -1e9
|
|
for _, d in ipairs(band) do
|
|
zmin = math.min(zmin, d.Position.Z - d.Size.Z / 2)
|
|
zmax = math.max(zmax, d.Position.Z + d.Size.Z / 2)
|
|
end
|
|
table.insert(out, ("mainline Z extent %.1f .. %.1f (total %.1f)"):format(zmin, zmax, zmax - zmin))
|
|
for i = 1, math.min(#band, 6) do
|
|
local d = band[i]
|
|
table.insert(
|
|
out,
|
|
(" %s pos %.0f,%.1f,%.1f size %.1fx%.1f rotY=%.0f"):format(
|
|
d.Name,
|
|
d.Position.X,
|
|
d.Position.Y,
|
|
d.Position.Z,
|
|
d.Size.X,
|
|
d.Size.Z,
|
|
math.deg(select(2, d.CFrame:ToEulerAnglesYXZ()))
|
|
)
|
|
)
|
|
end
|
|
|
|
-- 2. shop model footprints near the PA
|
|
table.insert(out, "--- shop model footprints (plan boxes) ---")
|
|
for _, top in ipairs(ws:GetChildren()) do
|
|
if top ~= map and not top:IsA("Terrain") and not top:IsA("Camera") and not top:IsA("BasePart") then
|
|
local x0, x1, z0, z1, y0, y1, n = 1e9, -1e9, 1e9, -1e9, 1e9, -1e9, 0
|
|
for _, d in ipairs(top:GetDescendants()) do
|
|
if d:IsA("BasePart") then
|
|
n += 1
|
|
x0 = math.min(x0, d.Position.X - d.Size.X / 2)
|
|
x1 = math.max(x1, d.Position.X + d.Size.X / 2)
|
|
z0 = math.min(z0, d.Position.Z - d.Size.Z / 2)
|
|
z1 = math.max(z1, d.Position.Z + d.Size.Z / 2)
|
|
y0 = math.min(y0, d.Position.Y - d.Size.Y / 2)
|
|
y1 = math.max(y1, d.Position.Y + d.Size.Y / 2)
|
|
end
|
|
end
|
|
if n > 0 and x0 > -6000 and x1 < -2500 and z0 > -9000 and z1 < -6000 then
|
|
table.insert(
|
|
out,
|
|
(" %-42s n=%-5d X[%.0f..%.0f] Z[%.0f..%.0f] Y[%.0f..%.0f]"):format(top.Name, n, x0, x1, z0, z1, y0, y1)
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- 3. what the current supports look like
|
|
table.insert(out, "--- current ramp supports ---")
|
|
local sup = {}
|
|
for _, d in ipairs(ws:GetDescendants()) do
|
|
if d:IsA("BasePart") and (d.Name:find("RampPillar") or d.Name:find("PierColumn") or d.Name:find("PierCap")) then
|
|
local k = ("%s shape=%s size=%.1fx%.1fx%.1f"):format(
|
|
d.Name,
|
|
d.Shape and d.Shape.Name or "n/a",
|
|
d.Size.X,
|
|
d.Size.Y,
|
|
d.Size.Z
|
|
)
|
|
sup[k] = (sup[k] or 0) + 1
|
|
end
|
|
end
|
|
local sl = {}
|
|
for k, v in pairs(sup) do
|
|
table.insert(sl, ("%s x%d"):format(k, v))
|
|
end
|
|
table.sort(sl)
|
|
for i = 1, math.min(#sl, 12) do
|
|
table.insert(out, " " .. sl[i])
|
|
end
|
|
|
|
return table.concat(out, "\n")
|