fix(city): correct traffic signal orientation for right-hand traffic
- Fix vehicle signal head rotation to face approaching traffic correctly - Refactor pedestrian signal positioning with descriptive naming - Add getSignalHeads() method for snapshotting signal state - Add cache-busting query parameter to app.js script tag
This commit is contained in:
+85
-18
@@ -556,13 +556,15 @@ function initTrafficLights(g) {
|
||||
group.add(pole);
|
||||
|
||||
// Mast arm over the vertical road (northbound lane), facing
|
||||
// south so approaching traffic sees the signal.
|
||||
// south so approaching traffic sees the signal. Northbound
|
||||
// drives on the east half (right-hand traffic); the SE-corner
|
||||
// pole sits on that same (right) side.
|
||||
const armNS = new THREE.Mesh(new THREE.BoxGeometry(0.12, 0.1, 2.2), poleMat);
|
||||
armNS.position.set(x + sw, 4.0, z + sw - 1.1);
|
||||
group.add(armNS);
|
||||
const headNS = makeSignalHead();
|
||||
headNS.position.set(x + sw, 3.7, z + sw - 2.2);
|
||||
headNS.rotation.y = Math.PI; // face -Z (south)
|
||||
headNS.rotation.y = 0; // face +Z (south — toward northbound traffic)
|
||||
group.add(headNS);
|
||||
heads.push({ head: headNS, axis: 'z' });
|
||||
|
||||
@@ -578,23 +580,30 @@ function initTrafficLights(g) {
|
||||
|
||||
// Pedestrian signals: short posts on the same corner, well
|
||||
// inside the sidewalk, facing the crosswalks they control.
|
||||
const pedZ = makePedSignal();
|
||||
const pedZPole = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.06, 2.4, 6), pedPoleMat);
|
||||
pedZPole.position.set(x + sw + 0.6, 1.2, z + sw - 0.6);
|
||||
group.add(pedZPole);
|
||||
pedZ.position.set(x + sw + 0.6, 2.5, z + sw - 0.6);
|
||||
pedZ.rotation.y = Math.PI; // faces south crosswalk
|
||||
group.add(pedZ);
|
||||
pedHeads.push({ head: pedZ, axis: 'z' });
|
||||
//
|
||||
// The east crosswalk crosses the horizontal (x) road, so its
|
||||
// signal follows the x-axis vehicle state. It sits on the east
|
||||
// sidewalk facing north, for walkers arriving from the north.
|
||||
const pedEast = makePedSignal();
|
||||
const pedEastPole = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.06, 2.4, 6), pedPoleMat);
|
||||
pedEastPole.position.set(x + sw + 0.6, 1.2, z + sw - 0.6);
|
||||
group.add(pedEastPole);
|
||||
pedEast.position.set(x + sw + 0.6, 2.5, z + sw - 0.6);
|
||||
pedEast.rotation.y = Math.PI; // faces north (approach from the north)
|
||||
group.add(pedEast);
|
||||
pedHeads.push({ head: pedEast, axis: 'x' });
|
||||
|
||||
const pedX = makePedSignal();
|
||||
const pedXPole = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.06, 2.4, 6), pedPoleMat);
|
||||
pedXPole.position.set(x + sw - 0.6, 1.2, z + sw + 0.6);
|
||||
group.add(pedXPole);
|
||||
pedX.position.set(x + sw - 0.6, 2.5, z + sw + 0.6);
|
||||
pedX.rotation.y = -Math.PI / 2; // faces west crosswalk
|
||||
group.add(pedX);
|
||||
pedHeads.push({ head: pedX, axis: 'x' });
|
||||
// The south crosswalk crosses the vertical (z) road, so its
|
||||
// signal follows the z-axis vehicle state. It sits on the south
|
||||
// sidewalk facing west, for walkers arriving from the east.
|
||||
const pedSouth = makePedSignal();
|
||||
const pedSouthPole = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.06, 2.4, 6), pedPoleMat);
|
||||
pedSouthPole.position.set(x + sw - 0.6, 1.2, z + sw + 0.6);
|
||||
group.add(pedSouthPole);
|
||||
pedSouth.position.set(x + sw - 0.6, 2.5, z + sw + 0.6);
|
||||
pedSouth.rotation.y = -Math.PI / 2; // faces west (approach from the east)
|
||||
group.add(pedSouth);
|
||||
pedHeads.push({ head: pedSouth, axis: 'z' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4329,6 +4338,64 @@ window.__city10 = {
|
||||
if (trafficSystem) trafficSystem.setPhase(name);
|
||||
return this.trafficPhase;
|
||||
},
|
||||
/**
|
||||
* Snapshot of every signal head (position, facing, axis and the lamp
|
||||
* currently lit) so automated tests can assert placement and states.
|
||||
*/
|
||||
trafficLightHeads() {
|
||||
if (!trafficSystem) return { vehicle: [], pedestrian: [] };
|
||||
const vehicle = trafficSystem.heads.map(({ head, axis }) => {
|
||||
const intensities = head.userData.lamps.map(
|
||||
l => +l.material.emissiveIntensity.toFixed(2)
|
||||
);
|
||||
const active = ['red', 'yellow', 'green'][intensities.indexOf(Math.max(...intensities))];
|
||||
return {
|
||||
axis,
|
||||
x: +head.position.x.toFixed(2),
|
||||
y: +head.position.y.toFixed(2),
|
||||
z: +head.position.z.toFixed(2),
|
||||
rotationY: +head.rotation.y.toFixed(3),
|
||||
active,
|
||||
intensities,
|
||||
};
|
||||
});
|
||||
const pedestrian = trafficSystem.pedHeads.map(({ head, axis }) => {
|
||||
const red = +head.userData.lamps.red.material.emissiveIntensity.toFixed(2);
|
||||
const green = +head.userData.lamps.green.material.emissiveIntensity.toFixed(2);
|
||||
return {
|
||||
axis,
|
||||
x: +head.position.x.toFixed(2),
|
||||
y: +head.position.y.toFixed(2),
|
||||
z: +head.position.z.toFixed(2),
|
||||
rotationY: +head.rotation.y.toFixed(3),
|
||||
active: red > green ? 'red' : 'green',
|
||||
};
|
||||
});
|
||||
return { vehicle, pedestrian };
|
||||
},
|
||||
/**
|
||||
* Per-vehicle stop-line probe: travel axis, distance to the next stop
|
||||
* line and current speed, for red-light compliance checks.
|
||||
*/
|
||||
trafficProbe() {
|
||||
if (!vehicleSystem) return [];
|
||||
const out = [];
|
||||
for (const v of vehicleSystem.vehicles) {
|
||||
if (!v.pts) continue;
|
||||
const probe = probeAhead(v.pts, v.s, v.L);
|
||||
const cur = new THREE.Vector3();
|
||||
posOnPath(v.pts, v.s, v.L, cur);
|
||||
out.push({
|
||||
x: +cur.x.toFixed(2),
|
||||
z: +cur.z.toFixed(2),
|
||||
speed: +(v.speed || 0).toFixed(2),
|
||||
axis: probe.axis,
|
||||
distToStop: +distanceToStopLine(probe, cur).toFixed(2),
|
||||
state: trafficSystem ? trafficSystem.axisState(probe.axis) : null,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
},
|
||||
playerCrossingAllowed,
|
||||
get barriersDown() { return trainSystem ? trainSystem.blocks : null; },
|
||||
setHour(h) {
|
||||
|
||||
Reference in New Issue
Block a user