feat(preview): add vehicle incident cards
This commit is contained in:
@@ -190,6 +190,21 @@ let baseStatus = "";
|
|||||||
这会让用户误判材质和模型质量。若确实需要性能模式,应做成显式开关,而不是默认牺牲
|
这会让用户误判材质和模型质量。若确实需要性能模式,应做成显式开关,而不是默认牺牲
|
||||||
预览清晰度。
|
预览清晰度。
|
||||||
|
|
||||||
|
### 车辆事件仅属于预览会话
|
||||||
|
|
||||||
|
点击车辆的信息卡可在 `normal`、`breakdown`、`accident` 三态间切换。这是验证交互,
|
||||||
|
不得写入 `package/`、路线 JSON 或 OSM。状态必须附着在 `addCruiseVehicle()` 返回的记录上;
|
||||||
|
`createTrafficAwarePositions()` 只在状态为 `normal` 时推进已有的 route distance,恢复正常从
|
||||||
|
当前停点继续。
|
||||||
|
|
||||||
|
- `breakdown`:黄色扳手 label,路线保留原色。
|
||||||
|
- `accident`:红色警示 label,路线设为红色。
|
||||||
|
- `normal`:隐藏 label,恢复原路线颜色。
|
||||||
|
|
||||||
|
车辆实体以 `properties.vehicleId` 标识;点击拾取必须只处理此属性,不能把静态模型、路线或
|
||||||
|
信号灯当作车辆。Cesium InfoBox 在本预览中关闭,信息卡必须使用 HTML/CSS,并把新增 DOM 句柄
|
||||||
|
集中在 `cesium-preview.js` 顶部。
|
||||||
|
|
||||||
### 单资产 vs 多资产的开关
|
### 单资产 vs 多资产的开关
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|||||||
BIN
assets/preview/vehicle-accident.png
Normal file
BIN
assets/preview/vehicle-accident.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/preview/vehicle-breakdown.png
Normal file
BIN
assets/preview/vehicle-breakdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -4,8 +4,13 @@ const fs = require("fs");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
function writeCesiumPreviewSupportFiles(outDir) {
|
function writeCesiumPreviewSupportFiles(outDir) {
|
||||||
for (const file of ["cesium-preview.css", "cesium-preview.js"]) {
|
const files = [
|
||||||
const source = path.join(__dirname, file);
|
[path.join(__dirname, "cesium-preview.css"), "cesium-preview.css"],
|
||||||
|
[path.join(__dirname, "cesium-preview.js"), "cesium-preview.js"],
|
||||||
|
[path.join(__dirname, "..", "..", "assets", "preview", "vehicle-breakdown.png"), "vehicle-breakdown.png"],
|
||||||
|
[path.join(__dirname, "..", "..", "assets", "preview", "vehicle-accident.png"), "vehicle-accident.png"],
|
||||||
|
];
|
||||||
|
for (const [source, file] of files) {
|
||||||
if (!fs.existsSync(source)) {
|
if (!fs.existsSync(source)) {
|
||||||
throw new Error(`Cesium preview support file '${file}' not found: ${source}`);
|
throw new Error(`Cesium preview support file '${file}' not found: ${source}`);
|
||||||
}
|
}
|
||||||
@@ -69,6 +74,21 @@ function cesiumPreviewHtml(glbName, metadataName, routeName, vehicleModelName, a
|
|||||||
</div>
|
</div>
|
||||||
<div id="diagnostics">Loading diagnostics...</div>
|
<div id="diagnostics">Loading diagnostics...</div>
|
||||||
<div id="status">Loading ${escapeHtml(glbName)}...</div>
|
<div id="status">Loading ${escapeHtml(glbName)}...</div>
|
||||||
|
<aside id="vehicleInfoCard" class="hidden" aria-live="polite">
|
||||||
|
<div class="vehicle-card-heading">
|
||||||
|
<strong id="vehicleInfoTitle">Vehicle</strong>
|
||||||
|
<button id="closeVehicleInfo" type="button" aria-label="Close vehicle information">Close</button>
|
||||||
|
</div>
|
||||||
|
<dl id="vehicleInfoDetails"></dl>
|
||||||
|
<label class="vehicle-card-field">Event note
|
||||||
|
<textarea id="vehicleIncidentNote" rows="2" maxlength="160" placeholder="Optional note"></textarea>
|
||||||
|
</label>
|
||||||
|
<div class="vehicle-card-actions" role="group" aria-label="Vehicle status">
|
||||||
|
<button type="button" data-vehicle-status="normal">Normal</button>
|
||||||
|
<button type="button" data-vehicle-status="breakdown">Breakdown</button>
|
||||||
|
<button type="button" data-vehicle-status="accident">Accident</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
<div id="loadingOverlay">
|
<div id="loadingOverlay">
|
||||||
<div class="loading-card">
|
<div class="loading-card">
|
||||||
<div class="loading-spinner" aria-hidden="true"></div>
|
<div class="loading-spinner" aria-hidden="true"></div>
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ body.scene-ready #cesiumContainer {
|
|||||||
|
|
||||||
#controls,
|
#controls,
|
||||||
#diagnostics,
|
#diagnostics,
|
||||||
#status {
|
#status,
|
||||||
|
#vehicleInfoCard {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -160,6 +161,96 @@ body.scene-error .loading-bar span {
|
|||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#vehicleInfoCard {
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: min(300px, calc(100vw - 24px));
|
||||||
|
padding: 10px 12px;
|
||||||
|
transform: translate(-50%, calc(-100% - 18px));
|
||||||
|
transform-origin: bottom center;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vehicleInfoCard::after {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: -7px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
content: "";
|
||||||
|
transform: translateX(-50%) rotate(45deg);
|
||||||
|
background: rgba(20, 24, 28, 0.82);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card-heading,
|
||||||
|
.vehicle-card-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card-heading strong {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vehicleInfoCard button {
|
||||||
|
height: 27px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: #f2f5f7;
|
||||||
|
color: #111;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vehicleInfoDetails {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 88px 1fr;
|
||||||
|
gap: 4px 8px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vehicleInfoDetails dt {
|
||||||
|
color: rgba(255, 255, 255, 0.62);
|
||||||
|
}
|
||||||
|
|
||||||
|
#vehicleInfoDetails dd {
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card-field {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
color: rgba(255, 255, 255, 0.78);
|
||||||
|
}
|
||||||
|
|
||||||
|
#vehicleIncidentNote {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
resize: vertical;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.30);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px;
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
color: #fff;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card-actions {
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card-actions button[data-vehicle-status="breakdown"] {
|
||||||
|
background: #f3c84b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card-actions button[data-vehicle-status="accident"] {
|
||||||
|
background: #e95950;
|
||||||
|
}
|
||||||
|
|
||||||
#controls button,
|
#controls button,
|
||||||
#controls select {
|
#controls select {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
@@ -285,4 +376,5 @@ body.scene-error .loading-bar span {
|
|||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
const toggleFps = document.getElementById("toggleFps");
|
const toggleFps = document.getElementById("toggleFps");
|
||||||
const toggleDiagnostics = document.getElementById("toggleDiagnostics");
|
const toggleDiagnostics = document.getElementById("toggleDiagnostics");
|
||||||
const assetToggles = document.getElementById("assetToggles");
|
const assetToggles = document.getElementById("assetToggles");
|
||||||
|
const vehicleInfoCard = document.getElementById("vehicleInfoCard");
|
||||||
|
const vehicleInfoTitle = document.getElementById("vehicleInfoTitle");
|
||||||
|
const vehicleInfoDetails = document.getElementById("vehicleInfoDetails");
|
||||||
|
const vehicleIncidentNote = document.getElementById("vehicleIncidentNote");
|
||||||
|
const closeVehicleInfo = document.getElementById("closeVehicleInfo");
|
||||||
|
const vehicleStatusButtons = Array.from(document.querySelectorAll("[data-vehicle-status]"));
|
||||||
// The exported road surface sits at the 0.35m scene anchor plus 0.03m.
|
// The exported road surface sits at the 0.35m scene anchor plus 0.03m.
|
||||||
// Vehicle models have their wheels at local Y=0, so keep them just clear
|
// Vehicle models have their wheels at local Y=0, so keep them just clear
|
||||||
// of the asphalt instead of using the old visibly floating 1.15m height.
|
// of the asphalt instead of using the old visibly floating 1.15m height.
|
||||||
@@ -56,6 +62,7 @@
|
|||||||
buildAssetToggles(assets);
|
buildAssetToggles(assets);
|
||||||
buildSemanticToggles(viewer, assets, placement);
|
buildSemanticToggles(viewer, assets, placement);
|
||||||
bindRuntimeControls(viewer, assets, cruise, cameras, placement, trafficSignals);
|
bindRuntimeControls(viewer, assets, cruise, cameras, placement, trafficSignals);
|
||||||
|
bindVehicleInfoCard(viewer, cruise);
|
||||||
startDiagnostics(viewer, metadata, assets, cruise, placement, trafficSignals);
|
startDiagnostics(viewer, metadata, assets, cruise, placement, trafficSignals);
|
||||||
cameras.overview();
|
cameras.overview();
|
||||||
baseStatus = summaryText(metadata, assets, cruise);
|
baseStatus = summaryText(metadata, assets, cruise);
|
||||||
@@ -726,7 +733,8 @@
|
|||||||
|
|
||||||
function addCruiseVehicle(viewer, segment, index, start, speed, signalData, vehicleModelName) {
|
function addCruiseVehicle(viewer, segment, index, start, speed, signalData, vehicleModelName) {
|
||||||
const route = prepareRoute(segment, signalData);
|
const route = prepareRoute(segment, signalData);
|
||||||
const trafficMotion = createTrafficAwarePositions(viewer, route, start, speed);
|
let record = null;
|
||||||
|
const trafficMotion = createTrafficAwarePositions(viewer, route, start, speed, () => record?.status === "normal");
|
||||||
const positions = trafficMotion.positions;
|
const positions = trafficMotion.positions;
|
||||||
const flat = [];
|
const flat = [];
|
||||||
for (const coord of segment.coordinates) {
|
for (const coord of segment.coordinates) {
|
||||||
@@ -761,14 +769,41 @@
|
|||||||
minimumPixelSize: 0
|
minimumPixelSize: 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
record = {
|
||||||
|
id: "vehicle-" + (index + 1),
|
||||||
entity: vehicle,
|
entity: vehicle,
|
||||||
routeEntity,
|
routeEntity,
|
||||||
positions,
|
positions,
|
||||||
route,
|
route,
|
||||||
segment,
|
segment,
|
||||||
label: routeLabel(segment, index),
|
label: routeLabel(segment, index),
|
||||||
|
modelName: vehicleModelLabel(vehicleModelName),
|
||||||
|
status: "normal",
|
||||||
|
incidentNote: "",
|
||||||
|
motion: trafficMotion,
|
||||||
|
routeColor,
|
||||||
|
markerEntity: null,
|
||||||
};
|
};
|
||||||
|
vehicle.properties = new Cesium.PropertyBag({ vehicleId: record.id });
|
||||||
|
record.markerEntity = viewer.entities.add({
|
||||||
|
name: "Vehicle incident marker " + (index + 1),
|
||||||
|
position: positions,
|
||||||
|
billboard: {
|
||||||
|
image: "",
|
||||||
|
show: false,
|
||||||
|
width: 36,
|
||||||
|
height: 44,
|
||||||
|
pixelOffset: new Cesium.Cartesian2(0, -40),
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
function vehicleModelLabel(name) {
|
||||||
|
const file = String(name || "").split("/").pop().replace(/\.gltf$/i, "");
|
||||||
|
return file.replace(/^.*-vehicle-/, "").replaceAll("_", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
function routeLabel(route, index) {
|
function routeLabel(route, index) {
|
||||||
@@ -789,6 +824,96 @@
|
|||||||
return cruise.vehicles[cruise.state.selectedIndex] || cruise.vehicles[0];
|
return cruise.vehicles[cruise.state.selectedIndex] || cruise.vehicles[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function vehicleById(cruise, id) {
|
||||||
|
return cruise.vehicles.find((vehicle) => vehicle.id === id) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVehicleStatus(viewer, vehicle, status) {
|
||||||
|
if (!vehicle || !["normal", "breakdown", "accident"].includes(status)) return;
|
||||||
|
vehicle.status = status;
|
||||||
|
const incident = status !== "normal";
|
||||||
|
const accident = status === "accident";
|
||||||
|
vehicle.markerEntity.billboard.image = status === "breakdown"
|
||||||
|
? "vehicle-breakdown.png"
|
||||||
|
: accident ? "vehicle-accident.png" : "";
|
||||||
|
vehicle.markerEntity.billboard.show = incident;
|
||||||
|
vehicle.routeEntity.polyline.material = accident ? Cesium.Color.RED : vehicle.routeColor;
|
||||||
|
if (viewer.scene.requestRender) viewer.scene.requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindVehicleInfoCard(viewer, cruise) {
|
||||||
|
if (!vehicleInfoCard || !cruise.vehicles.length) return;
|
||||||
|
let activeVehicle = null;
|
||||||
|
const render = () => {
|
||||||
|
if (!activeVehicle) return;
|
||||||
|
vehicleInfoTitle.textContent = activeVehicle.label;
|
||||||
|
vehicleIncidentNote.value = activeVehicle.incidentNote;
|
||||||
|
const details = [
|
||||||
|
["Vehicle", activeVehicle.id],
|
||||||
|
["Model", activeVehicle.modelName],
|
||||||
|
["Route", activeVehicle.label],
|
||||||
|
["Speed", cruise.baseSpeed + " m/s"],
|
||||||
|
["Status", activeVehicle.status],
|
||||||
|
];
|
||||||
|
vehicleInfoDetails.replaceChildren(...details.flatMap(([term, value]) => {
|
||||||
|
const dt = document.createElement("dt");
|
||||||
|
const dd = document.createElement("dd");
|
||||||
|
dt.textContent = term;
|
||||||
|
dd.textContent = value;
|
||||||
|
return [dt, dd];
|
||||||
|
}));
|
||||||
|
for (const button of vehicleStatusButtons) {
|
||||||
|
button.disabled = button.dataset.vehicleStatus === activeVehicle.status;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const open = (vehicle) => {
|
||||||
|
activeVehicle = vehicle;
|
||||||
|
const index = cruise.vehicles.indexOf(vehicle);
|
||||||
|
cruise.state.selectedIndex = index;
|
||||||
|
vehicleSelect.value = String(index);
|
||||||
|
syncSelectedRouteVisibility(cruise);
|
||||||
|
vehicleInfoCard.classList.remove("hidden");
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
const positionCard = () => {
|
||||||
|
if (!activeVehicle || vehicleInfoCard.classList.contains("hidden")) return;
|
||||||
|
const position = activeVehicle.entity.position?.getValue(viewer.clock.currentTime);
|
||||||
|
const windowPosition = position && Cesium.SceneTransforms.worldToWindowCoordinates(viewer.scene, position);
|
||||||
|
if (!windowPosition) {
|
||||||
|
vehicleInfoCard.classList.add("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const margin = 12;
|
||||||
|
const width = vehicleInfoCard.offsetWidth || 300;
|
||||||
|
const x = Cesium.Math.clamp(windowPosition.x, margin + width / 2, window.innerWidth - margin - width / 2);
|
||||||
|
vehicleInfoCard.style.left = x + "px";
|
||||||
|
vehicleInfoCard.style.top = Math.max(windowPosition.y, margin + 20) + "px";
|
||||||
|
};
|
||||||
|
closeVehicleInfo.addEventListener("click", () => {
|
||||||
|
activeVehicle = null;
|
||||||
|
vehicleInfoCard.classList.add("hidden");
|
||||||
|
});
|
||||||
|
vehicleIncidentNote.addEventListener("input", () => {
|
||||||
|
if (activeVehicle) activeVehicle.incidentNote = vehicleIncidentNote.value.trim();
|
||||||
|
});
|
||||||
|
for (const button of vehicleStatusButtons) {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
if (!activeVehicle) return;
|
||||||
|
setVehicleStatus(viewer, activeVehicle, button.dataset.vehicleStatus);
|
||||||
|
render();
|
||||||
|
setStatus(activeVehicle.label + " " + activeVehicle.status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
||||||
|
handler.setInputAction((movement) => {
|
||||||
|
const picked = viewer.scene.pick(movement.position);
|
||||||
|
const id = picked?.id?.properties?.vehicleId?.getValue?.();
|
||||||
|
const vehicle = vehicleById(cruise, id);
|
||||||
|
if (vehicle) open(vehicle);
|
||||||
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
||||||
|
viewer.scene.postRender.addEventListener(positionCard);
|
||||||
|
}
|
||||||
|
|
||||||
function prepareRoute(segment, signalData) {
|
function prepareRoute(segment, signalData) {
|
||||||
const distances = [0.0];
|
const distances = [0.0];
|
||||||
for (let i = 1; i < segment.coordinates.length; i += 1) {
|
for (let i = 1; i < segment.coordinates.length; i += 1) {
|
||||||
@@ -820,12 +945,13 @@
|
|||||||
return [...found.values()].sort((a, b) => a.distance - b.distance);
|
return [...found.values()].sort((a, b) => a.distance - b.distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTrafficAwarePositions(viewer, route, start, speed) {
|
function createTrafficAwarePositions(viewer, route, start, speed, isMoving = () => true) {
|
||||||
const state = { distance: 0, lastTime: start.clone() };
|
const state = { distance: 0, lastTime: start.clone() };
|
||||||
viewer.clock.onTick.addEventListener((clock) => {
|
viewer.clock.onTick.addEventListener((clock) => {
|
||||||
const elapsed = Cesium.JulianDate.secondsDifference(clock.currentTime, state.lastTime);
|
const elapsed = Cesium.JulianDate.secondsDifference(clock.currentTime, state.lastTime);
|
||||||
Cesium.JulianDate.clone(clock.currentTime, state.lastTime);
|
Cesium.JulianDate.clone(clock.currentTime, state.lastTime);
|
||||||
if (elapsed <= 0) return;
|
if (elapsed <= 0) return;
|
||||||
|
if (!isMoving()) return;
|
||||||
const next = nextRouteStop(route, state.distance);
|
const next = nextRouteStop(route, state.distance);
|
||||||
const advance = elapsed * speed;
|
const advance = elapsed * speed;
|
||||||
if (next && signalPhase(next.signal.phaseGroup, clock.currentTime, start).active !== "green") {
|
if (next && signalPhase(next.signal.phaseGroup, clock.currentTime, start).active !== "green") {
|
||||||
|
|||||||
@@ -251,6 +251,10 @@ assert.match(html, /"glbName":"scene\\u003c\\u0026\\u003e\.glb"/);
|
|||||||
assert.match(html, /"vehicleModelNames":\["car-a\.gltf","truck-a\.gltf"\]/);
|
assert.match(html, /"vehicleModelNames":\["car-a\.gltf","truck-a\.gltf"\]/);
|
||||||
assert.match(html, /"trafficSignalsName":"traffic-signals\.json"/);
|
assert.match(html, /"trafficSignalsName":"traffic-signals\.json"/);
|
||||||
assert.match(html, /id="toggleSignals"/);
|
assert.match(html, /id="toggleSignals"/);
|
||||||
|
assert.match(html, /id="vehicleInfoCard" class="hidden"/);
|
||||||
|
assert.match(html, /id="vehicleIncidentNote"/);
|
||||||
|
assert.match(html, /data-vehicle-status="breakdown"/);
|
||||||
|
assert.match(html, /data-vehicle-status="accident"/);
|
||||||
assert.match(html, /id="toggleBuildingGhost"/);
|
assert.match(html, /id="toggleBuildingGhost"/);
|
||||||
assert.match(html, /id="viewMode"/);
|
assert.match(html, /id="viewMode"/);
|
||||||
assert.match(html, /data-view-mode="inspect"/);
|
assert.match(html, /data-view-mode="inspect"/);
|
||||||
@@ -264,6 +268,14 @@ assert.ok(fs.existsSync(countdownFont), "7LED countdown font must be versioned w
|
|||||||
assert.doesNotMatch(previewRuntime, /cylinder: \{ length: 6\.7/);
|
assert.doesNotMatch(previewRuntime, /cylinder: \{ length: 6\.7/);
|
||||||
assert.doesNotMatch(previewRuntime, /Traffic Signal Housing/);
|
assert.doesNotMatch(previewRuntime, /Traffic Signal Housing/);
|
||||||
assert.match(previewRuntime, /asset\.category === "dynamic"/);
|
assert.match(previewRuntime, /asset\.category === "dynamic"/);
|
||||||
|
assert.match(previewRuntime, /new Cesium\.ScreenSpaceEventHandler/);
|
||||||
|
assert.match(previewRuntime, /vehicleId: record\.id/);
|
||||||
|
assert.match(previewRuntime, /status === "breakdown"\s+\? "vehicle-breakdown\.png"/);
|
||||||
|
assert.match(previewRuntime, /accident \? "vehicle-accident\.png"/);
|
||||||
|
assert.match(previewRuntime, /disableDepthTestDistance: Number\.POSITIVE_INFINITY/);
|
||||||
|
assert.match(previewRuntime, /routeEntity\.polyline\.material = accident \? Cesium\.Color\.RED : vehicle\.routeColor/);
|
||||||
|
assert.match(previewRuntime, /\(\) => record\?\.status === "normal"/);
|
||||||
|
assert.match(previewRuntime, /Cesium\.SceneTransforms\.worldToWindowCoordinates/);
|
||||||
assert.match(previewRuntime, /TrafficSignalDynamic_/);
|
assert.match(previewRuntime, /TrafficSignalDynamic_/);
|
||||||
assert.match(previewRuntime, /TrafficSignalDynamic_\$\{nodeKey\}_countdown_\$\{String\(value\)\.padStart\(2, "0"\)\}/);
|
assert.match(previewRuntime, /TrafficSignalDynamic_\$\{nodeKey\}_countdown_\$\{String\(value\)\.padStart\(2, "0"\)\}/);
|
||||||
assert.match(previewRuntime, /ColorBlendMode\.REPLACE/);
|
assert.match(previewRuntime, /ColorBlendMode\.REPLACE/);
|
||||||
|
|||||||
Reference in New Issue
Block a user