(() => { // src/ui/panel.test.js var root = window; var panelNamespace = root.Panel; if (!panelNamespace || typeof panelNamespace !== "object") { throw new Error("加载 panel.test.js 前必须先准备 window.Panel 命名空间"); } var page = panelNamespace.ui; if (!page) throw new Error("加载 panel.test.js 前必须先加载 Panel.ui"); var noop = () => { }; var BatchAppendOptions = Object.freeze({ insertMode: "append" }); var createCarCode = (index) => (index + 1).toString().padStart(4, "0"); var createCarName = (code) => `第[${code}]号车`; var createCarShape = ({ type, code, name = createCarName(code), point = { x: 0, y: 0 }, rotate = 0, lock = false }) => { const car = { type, code: { text: code }, name: { text: name }, base: { point: { x: point.x, y: point.y }, rotate } }; if (lock) car.lock = { enable: true }; return car; }; var setCarTransform = (car, x, y, rotate) => { car.base.point.x = x; car.base.point.y = y; car.base.rotate = rotate; return car; }; var StaticCarDefs = Object.freeze([ { type: "CarForklift", code: "2000", point: { x: 300, y: 100 }, rotate: 0, lock: true }, { type: "CarForklift", code: "2001", point: { x: 400, y: 100 }, rotate: 45 }, { type: "CarForklift", code: "2002", point: { x: 500, y: 100 }, rotate: 90 }, { type: "CarForklift", code: "2003", point: { x: 600, y: 100 }, rotate: 135 }, { type: "CarConveyor", code: "2010", point: { x: 700, y: 100 }, rotate: 180 }, { type: "CarConveyor", code: "2011", point: { x: 800, y: 100 }, rotate: 225, lock: true }, { type: "CarConveyor", code: "2012", point: { x: 900, y: 100 }, rotate: 270 }, { type: "CarConveyor", code: "2013", point: { x: 1e3, y: 100 }, rotate: 315 }, { type: "CarTugger", code: "2020", point: { x: 300, y: 300 }, rotate: 360 }, { type: "CarTugger", code: "2021", point: { x: 400, y: 300 }, rotate: 405 }, { type: "CarTugger", code: "2022", point: { x: 500, y: 300 }, rotate: 450, lock: true }, { type: "CarTugger", code: "2023", point: { x: 600, y: 300 }, rotate: 495 }, { type: "CarCarrier", code: "2030", point: { x: 700, y: 300 }, rotate: 540 }, { type: "CarCarrier", code: "2031", point: { x: 800, y: 300 }, rotate: 585 }, { type: "CarCarrier", code: "2032", point: { x: 900, y: 300 }, rotate: 630 }, { type: "CarCarrier", code: "2033", point: { x: 1e3, y: 300 }, rotate: 675, lock: true } ]); var createStaticCars = () => StaticCarDefs.map((def) => createCarShape(def)); var testConfig = { seed: 20240214, debug: false, shapeCount: 4e3, grid: { rows: 4, cols: 4, gap: 100 }, padding: 60, car: { types: ["CarForklift", "CarConveyor", "CarTugger", "CarCarrier"], pointX: 0, pointY: 0, rotate: 0, count: 900, row: 30, space: 50 } }; var testState = { carFrame: [], staticCars: null, carLoop: { frameId: 0, interval: 20, lastTick: 0, callback: noop } }; var testUtil = { log: (...args) => { if (page.testConfig.debug) { console.log(...args); } }, rand: () => { page.testConfig.seed = page.testConfig.seed * 1664525 + 1013904223 >>> 0; return page.testConfig.seed / 4294967296; }, randInt: (min, max) => { if (max <= min) return min; return Math.floor(page.testUtil.rand() * (max - min + 1)) + min; }, measure: (label, fn = noop) => { const start = performance.now(); const result = fn(); const cost = performance.now() - start; page.testUtil.log(`[测试] ${label}: ${cost.toFixed(2)}毫秒`); return result; }, scatterPoints: (count, options = {}) => { const size = page.panel.map.base.size; const safeCount = Math.max(1, Number.isFinite(count) ? Math.floor(count) : 1); const padding = Number.isFinite(options.padding) ? options.padding : page.testConfig.padding; const usableW = Math.max(1, size.w - padding * 2); const usableH = Math.max(1, size.h - padding * 2); const cols = Math.max(1, Math.ceil(Math.sqrt(safeCount * usableW / usableH))); const rows = Math.max(1, Math.ceil(safeCount / cols)); const gapX = usableW / cols; const gapY = usableH / rows; const jitterRatio = Number.isFinite(options.jitter) ? options.jitter : 0.35; const jitterX = Math.max(2, Math.floor(gapX * jitterRatio)); const jitterY = Math.max(2, Math.floor(gapY * jitterRatio)); const cells = []; for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { cells.push({ r, c }); } } for (let i = cells.length - 1; i > 0; i--) { const j = page.testUtil.randInt(0, i); const tmp = cells[i]; cells[i] = cells[j]; cells[j] = tmp; } const points = []; for (let i = 0; i < safeCount; i++) { const cell = cells[i]; const x = padding + (cell.c + 0.5) * gapX + page.testUtil.randInt(-jitterX, jitterX); const y = padding + (cell.r + 0.5) * gapY + page.testUtil.randInt(-jitterY, jitterY); points.push({ x: Math.min(size.w - padding, Math.max(padding, Math.round(x))), y: Math.min(size.h - padding, Math.max(padding, Math.round(y))) }); } return { points, gapX, gapY }; }, shuffleInPlace: (list = []) => { for (let i = list.length - 1; i > 0; i--) { const j = page.testUtil.randInt(0, i); const tmp = list[i]; list[i] = list[j]; list[j] = tmp; } return list; }, polyPoints: (x, y, size = 80) => [ { x, y }, { x: x + size, y }, { x: x + size * 1.5, y: y + size * 0.7 }, { x: x + size, y: y + size * 1.4 }, { x, y: y + size * 1.4 } ], pathPoints: (x, y, size = 80) => [ { x, y }, { x: x + size, y }, { x: x + size * 1.5, y: y + size * 0.7 }, { x: x + size, y: y + size * 1.4 }, { x, y: y + size * 0.7 } ], createRotateAdder: (panel, method, prefix, options = {}) => { const formatCode = typeof options.formatCode === "function" ? options.formatCode : (i) => `${prefix}-${i}`; return (point, i) => { panel[method]({ code: { text: formatCode(i) }, base: { point, rotate: page.testUtil.randInt(0, 359) } }, BatchAppendOptions); }; }, addEdgeByIndex: (panel, codePrefix, i, startNode, endNode, options = BatchAppendOptions) => { if (i % 4 === 0) { panel.addEdgeBeeline({ code: { text: `${codePrefix}-EL-${i}` }, base: { startNode, endNode } }, options); return; } if (i % 4 === 1) { panel.addEdgeArc({ code: { text: `${codePrefix}-EA-${i}` }, base: { startNode, endNode, p1: { x: startNode.base.point.x, y: endNode.base.point.y } } }, options); return; } if (i % 4 === 2) { panel.addEdgeQuadratic({ code: { text: `${codePrefix}-EQ-${i}` }, base: { startNode, endNode, p1: { x: endNode.base.point.x, y: startNode.base.point.y } } }, options); return; } panel.addEdgeBezier({ code: { text: `${codePrefix}-EB-${i}` }, base: { startNode, endNode, p1: { x: startNode.base.point.x + 40, y: startNode.base.point.y - 40 }, p2: { x: endNode.base.point.x - 40, y: endNode.base.point.y + 40 } } }, options); }, addRandomEdges: (panel, nodes, edgeCount, codePrefix, options = BatchAppendOptions) => { if (!Array.isArray(nodes) || nodes.length < 2) return; const max = nodes.length - 1; for (let i = 0; i < edgeCount; i++) { const startIndex = page.testUtil.randInt(0, max); let endIndex = page.testUtil.randInt(0, max); if (endIndex === startIndex) { endIndex = (startIndex + 1) % nodes.length; } const startNode = nodes[startIndex]; const endNode = nodes[endIndex]; page.testUtil.addEdgeByIndex(panel, codePrefix, i, startNode, endNode, options); } }, withPerfBatch: (panel, action) => panel.withBatch(action), measureBatchAdd: (panel, points, adders, label) => { page.testUtil.measure(label, () => { page.testUtil.withPerfBatch(panel, () => { for (let i = 0; i < points.length; i++) { adders[i % adders.length](points[i], i); } }); }); }, measureBatchEdges: (panel, edgeCount, codePrefix, label) => { const nodes = panel.nodeShapes; const safeEdgeCount = Math.min(nodes.length * 2, edgeCount); if (nodes.length < 2 || safeEdgeCount <= 0) return; page.testUtil.measure(label, () => { page.testUtil.withPerfBatch(panel, () => { page.testUtil.addRandomEdges(panel, nodes, safeEdgeCount, codePrefix); }); }); }, createSizedAdder: (panel, method, prefix, sizeFactory, rotateFactory, options = BatchAppendOptions) => (point, i) => { panel[method]({ code: { text: `${prefix}-${i}` }, base: { point, size: sizeFactory(), rotate: rotateFactory() } }, options); }, createPolyAdder: (panel, method, prefix, pointsBuilder, sizeFactory, rotateFactory, options = BatchAppendOptions) => (point, i) => { const size = sizeFactory(); panel[method]({ code: { text: `${prefix}-${i}` }, base: { point, points: pointsBuilder(point.x, point.y, size), closed: true, rotate: rotateFactory() } }, options); }, ensureStaticCars: () => page.testState.staticCars ?? (page.testState.staticCars = createStaticCars()), ensureCarFrame: () => { const carConfig = page.testConfig.car; const staticCars = page.testUtil.ensureStaticCars(); const frame = page.testState.carFrame; frame.length = carConfig.count + staticCars.length; for (let i = 0; i < carConfig.count; i++) { const code = createCarCode(i); const type = carConfig.types[i % carConfig.types.length]; let car = frame[i]; if (!car || car.code?.text !== code) { car = createCarShape({ type, code }); frame[i] = car; } else { car.type = type; } } for (let i = 0; i < staticCars.length; i++) { frame[carConfig.count + i] = staticCars[i]; } return frame; }, buildCarFrame: (panel) => { const size = panel.map.base.size; const carConfig = page.testConfig.car; const cars = page.testUtil.ensureCarFrame(); const { row, space } = carConfig; carConfig.pointX = carConfig.pointX + 1 > size.w ? 0 : carConfig.pointX + 1; carConfig.pointY = carConfig.pointY + 1 > size.h ? 0 : carConfig.pointY + 1; carConfig.rotate = carConfig.rotate + 1 > 359 ? 0 : carConfig.rotate + 1; for (let i = 0; i < carConfig.count; i++) { const col = i % row; const rowIndex = Math.floor(i / row); const type = carConfig.types[i % carConfig.types.length]; const car = cars[i]; car.type = type; setCarTransform(car, carConfig.pointX - col * space, carConfig.pointY - rowIndex * space, carConfig.rotate); } return cars; }, resetSeed: (seed) => { if (Number.isFinite(seed)) { page.testConfig.seed = seed; } }, resetPanel: (options = {}) => { page.testUtil.carStop(); if (options.clear !== false) { page.panel.clearShapes(); } page.panel.clearActiveShape(); page.panel.clearHoverShape(); page.panel.clearMarkerShape(); }, carStart: (callback, interval = 20) => { page.testUtil.carStop(); const loop = page.testState.carLoop; loop.callback = typeof callback === "function" ? callback : noop; loop.interval = Number.isFinite(interval) && interval > 0 ? interval : 20; loop.lastTick = 0; const tick = (time) => { if (!loop.frameId) return; if (loop.lastTick === 0 || time - loop.lastTick >= loop.interval) { loop.lastTick = time; loop.callback(); if (!loop.frameId) return; } loop.frameId = window.requestAnimationFrame(tick); }; loop.frameId = window.requestAnimationFrame(tick); }, carStop: () => { const loop = page.testState.carLoop; if (loop.frameId) window.cancelAnimationFrame(loop.frameId); loop.frameId = 0; loop.lastTick = 0; loop.callback = noop; } }; Object.assign(page, { testConfig, testState, testUtil }); var testActions = { test1: () => { page.testUtil.log("测试1:图形"); page.testUtil.resetPanel(); const panel = page.panel; const startX = 200; const startY = 200; const gapX = 220; const gapY = 180; const polyPoints = (x, y) => [{ x, y }, { x: x + 120, y }, { x: x + 180, y: y + 90 }, { x: x + 120, y: y + 180 }, { x, y: y + 180 }]; const pathPoints = (x, y) => [{ x, y }, { x: x + 120, y }, { x: x + 180, y: y + 90 }, { x: x + 120, y: y + 180 }, { x, y: y + 90 }]; panel.addNodeRect({ code: { text: "T10-N-Rect" }, base: { point: { x: startX, y: startY }, size: { w: 120, h: 80 }, rotate: 5 } }); panel.addNodeEllipse({ code: { text: "T10-N-Ellipse" }, base: { point: { x: startX + gapX, y: startY }, size: { w: 120, h: 80 }, rotate: 10 } }); panel.addNodePoly({ code: { text: "T10-N-Poly" }, base: { point: { x: startX + gapX * 2, y: startY }, points: polyPoints(startX + gapX * 2, startY), closed: true, rotate: 15 } }); panel.addNodePath({ code: { text: "T10-N-Path" }, base: { point: { x: startX + gapX * 3, y: startY }, points: pathPoints(startX + gapX * 3, startY), closed: true, rotate: 20 } }); panel.addZoneRect({ code: { text: "T10-Z-Rect" }, base: { point: { x: startX, y: startY + gapY }, size: { w: 140, h: 90 }, rotate: 5 } }); panel.addZoneEllipse({ code: { text: "T10-Z-Ellipse" }, base: { point: { x: startX + gapX, y: startY + gapY }, size: { w: 140, h: 90 }, rotate: 10 } }); panel.addZonePoly({ code: { text: "T10-Z-Poly" }, base: { point: { x: startX + gapX * 2, y: startY + gapY }, points: polyPoints(startX + gapX * 2, startY + gapY), closed: true, rotate: 15 } }); panel.addZonePath({ code: { text: "T10-Z-Path" }, base: { point: { x: startX + gapX * 3, y: startY + gapY }, points: pathPoints(startX + gapX * 3, startY + gapY), closed: true, rotate: 20 } }); panel.addTagRect({ code: { text: "T10-T-Rect" }, base: { point: { x: startX, y: startY + gapY * 2 }, size: { w: 140, h: 90 }, rotate: 5 } }); panel.addTagEllipse({ code: { text: "T10-T-Ellipse" }, base: { point: { x: startX + gapX, y: startY + gapY * 2 }, size: { w: 140, h: 90 }, rotate: 10 } }); panel.addTagPoly({ code: { text: "T10-T-Poly" }, base: { point: { x: startX + gapX * 2, y: startY + gapY * 2 }, points: polyPoints(startX + gapX * 2, startY + gapY * 2), closed: true, rotate: 15 } }); panel.addTagPath({ code: { text: "T10-T-Path" }, base: { point: { x: startX + gapX * 3, y: startY + gapY * 2 }, points: pathPoints(startX + gapX * 3, startY + gapY * 2), closed: true, rotate: 20 } }); const nodeRect = panel.getNode({ code: { text: "T10-N-Rect" } }); const nodeEllipse = panel.getNode({ code: { text: "T10-N-Ellipse" } }); const nodePoly = panel.getNode({ code: { text: "T10-N-Poly" } }); const nodePath = panel.getNode({ code: { text: "T10-N-Path" } }); panel.addEdgeBeeline({ code: { text: "T10-E-Line" }, base: { lineWidth: 3, startNode: nodeRect, endNode: nodeEllipse } }); panel.addEdgeArc({ code: { text: "T10-E-Arc" }, base: { lineWidth: 3, startNode: nodeEllipse, endNode: nodePoly, p1: { x: startX + gapX * 1.5, y: startY - 100 } } }); panel.addEdgeQuadratic({ code: { text: "T10-E-Quadratic" }, base: { lineWidth: 3, startNode: nodePoly, endNode: nodePath, p1: { x: startX + gapX * 2.5, y: startY + 120 } } }); panel.addEdgeBezier({ code: { text: "T10-E-Bezier" }, base: { lineWidth: 3, startNode: nodePath, endNode: nodeRect, p1: { x: startX + gapX * 2, y: startY + 260 }, p2: { x: startX + gapX * 0.5, y: startY + 260 } } }); panel.addCarForklift({ code: { text: "T10-C-Forklift" }, name: { text: "Forklift" }, base: { point: { x: startX, y: startY + gapY * 3 }, rotate: 45 } }); panel.addCarConveyor({ code: { text: "T10-C-Conveyor" }, name: { text: "Conveyor" }, base: { point: { x: startX + gapX, y: startY + gapY * 3 }, rotate: 90 } }); panel.addCarTugger({ code: { text: "T10-C-Tugger" }, name: { text: "Tugger" }, base: { point: { x: startX + gapX * 2, y: startY + gapY * 3 }, rotate: 135 } }); panel.addCarCarrier({ code: { text: "T10-C-Carrier" }, name: { text: "Carrier" }, base: { point: { x: startX + gapX * 3, y: startY + gapY * 3 }, rotate: 180 } }); }, test2: () => { page.testUtil.log("测试2:车辆"); const panel = page.panel; const cars = page.testUtil.buildCarFrame(panel); panel.addOrUpdateOrDeleteCars(cars); }, test3: () => { page.testUtil.log("测试3:运动"); page.testUtil.carStart(testActions.test2, 20); }, test4: () => { page.testUtil.log("测试4:停止"); page.testUtil.carStop(); page.testConfig.car.pointX = 0; page.testConfig.car.pointY = 0; page.testConfig.car.rotate = 0; }, test5: () => { page.testUtil.log("测试5:轨迹"); page.testUtil.resetPanel(); const panel = page.panel; panel.addNodeRect({ code: { text: "1" }, base: { point: { x: 300, y: 200 } } }); panel.addNodeRect({ code: { text: "2" }, base: { point: { x: 400, y: 200 } } }); panel.addNodeRect({ code: { text: "3" }, base: { point: { x: 500, y: 200 } } }); panel.addNodeRect({ code: { text: "4" }, base: { point: { x: 600, y: 200 } } }); panel.addNodeEllipse({ code: { text: "5" }, base: { point: { x: 300, y: 300 } } }); panel.addNodeEllipse({ code: { text: "6" }, base: { point: { x: 400, y: 300 } } }); panel.addNodeEllipse({ code: { text: "7" }, base: { point: { x: 500, y: 300 } } }); panel.addNodeEllipse({ code: { text: "8" }, base: { point: { x: 600, y: 300 } } }); panel.addNodeRect({ code: { text: "9" }, base: { point: { x: 300, y: 400 } } }); panel.addNodeRect({ code: { text: "10" }, base: { point: { x: 400, y: 400 } } }); panel.addNodeRect({ code: { text: "11" }, base: { point: { x: 500, y: 400 } } }); panel.addNodeRect({ code: { text: "12" }, base: { point: { x: 600, y: 400 } } }); panel.addNodeEllipse({ code: { text: "13" }, base: { point: { x: 300, y: 500 } } }); panel.addNodeEllipse({ code: { text: "14" }, base: { point: { x: 400, y: 500 } } }); panel.addNodeEllipse({ code: { text: "15" }, base: { point: { x: 500, y: 500 } } }); panel.addNodeEllipse({ code: { text: "16" }, base: { point: { x: 600, y: 500 } } }); panel.addEdgeBeeline({ code: { text: "1-2" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "1" } }), endNode: panel.getNode({ code: { text: "2" } }) } }); panel.addEdgeBeeline({ code: { text: "3-4" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "3" } }), endNode: panel.getNode({ code: { text: "4" } }) } }); panel.addEdgeArc({ code: { text: "5-6" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "5" } }), endNode: panel.getNode({ code: { text: "6" } }) } }); panel.addEdgeArc({ code: { text: "7-8" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "7" } }), endNode: panel.getNode({ code: { text: "8" } }) } }); panel.addEdgeQuadratic({ code: { text: "9-10" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "9" } }), endNode: panel.getNode({ code: { text: "10" } }) } }); panel.addEdgeQuadratic({ code: { text: "11-12" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "11" } }), endNode: panel.getNode({ code: { text: "12" } }) } }); panel.addEdgeBezier({ code: { text: "13-14" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "13" } }), endNode: panel.getNode({ code: { text: "14" } }) } }); panel.addEdgeBezier({ code: { text: "15-16" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "15" } }), endNode: panel.getNode({ code: { text: "16" } }) } }); panel.addEdgeBeeline({ code: { text: "1-5" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "1" } }), endNode: panel.getNode({ code: { text: "5" } }) } }); panel.addEdgeBeeline({ code: { text: "5-9" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "5" } }), endNode: panel.getNode({ code: { text: "9" } }) } }); panel.addEdgeBeeline({ code: { text: "9-13" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "9" } }), endNode: panel.getNode({ code: { text: "13" } }) } }); panel.addEdgeArc({ code: { text: "2-6" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "2" } }), endNode: panel.getNode({ code: { text: "6" } }) } }); panel.addEdgeArc({ code: { text: "6-10" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "6" } }), endNode: panel.getNode({ code: { text: "10" } }) } }); panel.addEdgeArc({ code: { text: "10-14" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "10" } }), endNode: panel.getNode({ code: { text: "14" } }) } }); panel.addEdgeQuadratic({ code: { text: "3-7" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "3" } }), endNode: panel.getNode({ code: { text: "7" } }) } }); panel.addEdgeQuadratic({ code: { text: "7-11" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "7" } }), endNode: panel.getNode({ code: { text: "11" } }) } }); panel.addEdgeQuadratic({ code: { text: "11-15" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "11" } }), endNode: panel.getNode({ code: { text: "15" } }) } }); panel.addEdgeBezier({ code: { text: "4-8" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "4" } }), endNode: panel.getNode({ code: { text: "8" } }) } }); panel.addEdgeBezier({ code: { text: "8-12" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "8" } }), endNode: panel.getNode({ code: { text: "12" } }) } }); panel.addEdgeBezier({ code: { text: "12-16" }, base: { lineWidth: 4, strokeStyle: "#FFFFFF", startNode: panel.getNode({ code: { text: "12" } }), endNode: panel.getNode({ code: { text: "16" } }) } }); panel.addOrUpdateCar({ type: "CarForklift", code: { text: "9000", enable: true }, name: { text: "第[9000]号车", enable: true }, lock: { enable: true }, base: { point: panel.getNode({ code: { text: "5" } }).base.point, rotate: 45, carEdges: [ { code: { text: "1-2" }, type: "Prev", process: 1 }, { code: { text: "2-6" }, type: "Prev", process: 1 }, { code: { text: "5-6" }, type: "Prev", process: 1 }, { code: { text: "5-9" }, type: "Curr", process: 0.5 }, { code: { text: "9-10" }, type: "Curr", process: 0.5 }, { code: { text: "10-14" }, type: "Next", process: 0 }, { code: { text: "13-14" }, type: "Next", process: 0 } ], carNodes: [ { code: { text: "1" }, type: "Prev", process: 1 }, { code: { text: "2" }, type: "Prev", process: 1 }, { code: { text: "6" }, type: "Prev", process: 1 }, { code: { text: "5" }, type: "Curr", process: 0.5 }, { code: { text: "9" }, type: "Curr", process: 0.5 }, { code: { text: "10" }, type: "Curr", process: 0.5 }, { code: { text: "14" }, type: "Next", process: 0 }, { code: { text: "13" }, type: "Next", process: 0 } ] } }); panel.addOrUpdateCar({ type: "CarConveyor", code: { text: "9001", enable: true }, name: { text: "第[9001]号车", enable: true }, base: { point: panel.getNode({ code: { text: "7" } }).base.point, rotate: 135, carEdges: [ { code: { text: "3-4" }, type: "Prev", process: 1 }, { code: { text: "3-7" }, type: "Prev", process: 1 }, { code: { text: "7-8" }, type: "Curr", process: 0.5 }, { code: { text: "8-12" }, type: "Curr", process: 0.5 }, { code: { text: "11-12" }, type: "Next", process: 0 }, { code: { text: "11-15" }, type: "Next", process: 0 }, { code: { text: "15-16" }, type: "Next", process: 0 } ], carNodes: [ { code: { text: "4" }, type: "Prev", process: 1 }, { code: { text: "3" }, type: "Prev", process: 1 }, { code: { text: "7" }, type: "Curr", process: 0.5 }, { code: { text: "8" }, type: "Curr", process: 0.5 }, { code: { text: "12" }, type: "Curr", process: 0.5 }, { code: { text: "11" }, type: "Next", process: 0 }, { code: { text: "15" }, type: "Next", process: 0 }, { code: { text: "16" }, type: "Next", process: 0 } ] } }); }, test6: () => { page.testUtil.log("测试6:处理"); page.testUtil.resetPanel(); const panel = page.panel; panel.addNodeRect({ code: { text: "T11-N1" }, base: { point: { x: 200, y: 200 }, size: { w: 120, h: 80 } } }); panel.addNodeEllipse({ code: { text: "T11-N2" }, base: { point: { x: 420, y: 200 }, size: { w: 120, h: 80 } } }); panel.updateNode({ code: { text: "T11-N1", font: "16px Arial" }, name: { text: "Node-1", font: "14px Arial" }, base: { point: { x: 240, y: 220 }, size: { w: 140, h: 90 }, rotate: 12 } }); panel.updateNode({ code: { text: "T11-N2" }, base: { point: { x: 460, y: 220 }, rotate: 18 } }); panel.addEdgeBeeline({ code: { text: "T11-E1" }, base: { lineWidth: 4, startNode: panel.getNode({ code: { text: "T11-N1" } }), endNode: panel.getNode({ code: { text: "T11-N2" } }) } }); panel.updateEdge({ code: { text: "T11-E1" }, base: { lineWidth: 6 } }); panel.addZoneRect({ code: { text: "T11-Z1" }, base: { point: { x: 200, y: 380 }, size: { w: 160, h: 90 } } }); panel.updateZone({ code: { text: "T11-Z1" }, base: { point: { x: 220, y: 400 }, rotate: 15 } }); panel.addTagEllipse({ code: { text: "T11-T1" }, base: { point: { x: 420, y: 380 }, size: { w: 140, h: 90 } } }); panel.updateTag({ code: { text: "T11-T1" }, name: { text: "Tag-1" }, base: { rotate: 25 } }); panel.addCarForklift({ code: { text: "T11-C1" }, base: { point: { x: 640, y: 380 }, rotate: 0 } }); panel.updateCar({ code: { text: "T11-C1" }, base: { rotate: 45 } }); panel.deleteTag({ code: { text: "T11-T1" } }); panel.deleteZone({ code: { text: "T11-Z1" } }); panel.deleteEdge({ code: { text: "T11-E1" } }); panel.deleteCar({ code: { text: "T11-C1" } }); panel.deleteNode({ code: { text: "T11-N2" } }); panel.deleteNode({ code: { text: "T11-N1" } }); }, test7: () => { page.testUtil.log("测试7:流程"); page.testUtil.resetPanel(); const panel = page.panel; const baseX = 200; const baseY = 200; panel.addNodeRect({ code: { text: "T14-N1" }, base: { point: { x: baseX, y: baseY }, size: { w: 100, h: 70 } } }); panel.addNodeEllipse({ code: { text: "T14-N2" }, base: { point: { x: baseX + 200, y: baseY }, size: { w: 100, h: 70 } } }); panel.addEdgeBeeline({ code: { text: "T14-E1" }, base: { lineWidth: 4, startNode: panel.getNode({ code: { text: "T14-N1" } }), endNode: panel.getNode({ code: { text: "T14-N2" } }) } }); panel.addZoneRect({ code: { text: "T14-Z1" }, base: { point: { x: baseX, y: baseY + 200 }, size: { w: 140, h: 80 } } }); panel.addTagRect({ code: { text: "T14-T1" }, base: { point: { x: baseX + 200, y: baseY + 200 }, size: { w: 140, h: 80 } } }); panel.addCarForklift({ code: { text: "T14-C1" }, base: { point: { x: baseX + 400, y: baseY + 200 }, rotate: 30 } }); panel.addNodeRect({ code: { text: "T15-N1", pointRatio: { w: -0.5, h: -0.5 } }, name: { text: "T15-N2", pointRatio: { w: 0.5, h: 0.5 } }, base: { point: { x: baseX + 500, y: baseY - 100 }, size: { w: 100, h: 100 } } }); panel.addZoneRect({ code: { text: "T15-Z1", pointRatio: { w: 0, h: -0.5 } }, name: { text: "T15-Z2", pointRatio: { w: 0, h: 0.5 } }, base: { point: { x: baseX + 500, y: baseY }, size: { w: 150, h: 150 } } }); panel.addTagRect({ code: { text: "T15-T1", pointRatio: { w: -0.5, h: 0 } }, name: { text: "T15-T2", pointRatio: { w: 0.5, h: 0 } }, base: { point: { x: baseX + 500, y: baseY + 200 }, size: { w: 200, h: 200 } } }); panel.addNodeEllipse({ code: { text: "T16-N1", pointRatio: { w: 0, h: 0 } }, name: { text: "T16-N2", pointRatio: { w: 0, h: 0 } }, base: { point: { x: baseX, y: baseY + 400 }, size: { w: 100, h: 100 } } }); panel.addZoneEllipse({ code: { text: "T16-Z1", pointRatio: { w: 0.5, h: 0 } }, name: { text: "T16-Z2", pointRatio: { w: -0.5, h: 0 } }, base: { point: { x: baseX + 100, y: baseY + 400 }, size: { w: 150, h: 150 } } }); panel.addTagEllipse({ code: { text: "T16-T1", pointRatio: { w: 0.5, h: 0.5 } }, name: { text: "T16-T2", pointRatio: { w: -0.5, h: -0.5 } }, base: { point: { x: baseX + 300, y: baseY + 400 }, size: { w: 200, h: 200 } } }); panel.setModeEdit(); panel.setSwitchGrid(); panel.setSwitchInfo(); panel.setSwitchArea(); panel.setSwitchZoom(); panel.setSwitchCarRoute(); panel.setSwitchCarEdges(); panel.setSwitchCarNodes(); panel.setSwitchFixed(); panel.setSwitchFixedCode(); panel.setSwitchFixedName(); panel.setScalingAdapt(); panel.setScalingNormal(); panel.setSwitchInfo(); panel.setSwitchArea(); panel.setSwitchZoom(); panel.addActiveAction((e) => e.code && e.code.text === "T14-N1"); panel.switchActiveAction((e) => e.code && e.code.text === "T14-E1"); panel.addActiveAction((e) => e.code && e.code.text === "T14-Z1"); panel.addActiveAction((e) => e.code && e.code.text === "T14-T1"); panel.addActiveAction((e) => e.code && e.code.text === "T14-C1"); page.updateStatusBar(); }, test8: (count) => { page.testUtil.log("测试8:性能"); page.testUtil.resetSeed(Date.now()); const panel = page.panel; const shapeCount = Math.max(0, Math.floor(Number.isFinite(count) ? count : page.testConfig.shapeCount)); const scatter = page.testUtil.scatterPoints(shapeCount, { jitter: 0.25 }); const adders = page.testUtil.shuffleInPlace([ page.testUtil.createRotateAdder(panel, "addNodeRect", "P-NR", { formatCode: (i) => `P-NR-${i.toString().padStart(4, "0")}` }), page.testUtil.createRotateAdder(panel, "addNodeEllipse", "P-NE", { formatCode: (i) => `P-NE-${i.toString().padStart(4, "0")}` }), page.testUtil.createRotateAdder(panel, "addZoneRect", "P-ZR"), page.testUtil.createRotateAdder(panel, "addZoneEllipse", "P-ZE"), page.testUtil.createRotateAdder(panel, "addTagRect", "P-TR"), page.testUtil.createRotateAdder(panel, "addTagEllipse", "P-TE"), page.testUtil.createRotateAdder(panel, "addCarForklift", "P-CF"), page.testUtil.createRotateAdder(panel, "addCarConveyor", "P-CC"), page.testUtil.createRotateAdder(panel, "addCarTugger", "P-CT"), page.testUtil.createRotateAdder(panel, "addCarCarrier", "P-CR") ]); page.testUtil.measureBatchAdd(panel, scatter.points, adders, "perf:addShapes"); page.testUtil.measureBatchEdges(panel, Math.floor(shapeCount * 1.2), "P", "perf:addEdges"); }, test9: (count) => { page.testUtil.log("测试9:压力"); page.testUtil.resetSeed(Date.now()); const panel = page.panel; const shapeCount = Math.max(0, Math.floor(Number.isFinite(count) ? count : page.testConfig.shapeCount)); const scatter = page.testUtil.scatterPoints(shapeCount, { jitter: 0.25 }); const minGap = Math.min(scatter.gapX, scatter.gapY); const baseSize = Math.max(50, Math.floor(minGap * 0.5)); const sizeJitter = Math.max(6, Math.floor(baseSize * 0.25)); const rectSize = () => { const w = Math.max(36, baseSize + page.testUtil.randInt(-sizeJitter, sizeJitter)); const h = Math.max(28, Math.floor(w * 0.7)); return { w, h }; }; const polySize = () => Math.max(40, baseSize + page.testUtil.randInt(-sizeJitter, sizeJitter)); const rotate = () => page.testUtil.randInt(0, 359); const adders = page.testUtil.shuffleInPlace([ page.testUtil.createSizedAdder(panel, "addNodeRect", "S-NR", rectSize, rotate), page.testUtil.createSizedAdder(panel, "addNodeEllipse", "S-NE", rectSize, rotate), page.testUtil.createPolyAdder(panel, "addNodePoly", "S-NP", page.testUtil.polyPoints, polySize, rotate), page.testUtil.createPolyAdder(panel, "addNodePath", "S-NH", page.testUtil.pathPoints, polySize, rotate), page.testUtil.createSizedAdder(panel, "addZoneRect", "S-ZR", rectSize, rotate), page.testUtil.createSizedAdder(panel, "addZoneEllipse", "S-ZE", rectSize, rotate), page.testUtil.createPolyAdder(panel, "addZonePoly", "S-ZP", page.testUtil.polyPoints, polySize, rotate), page.testUtil.createPolyAdder(panel, "addZonePath", "S-ZH", page.testUtil.pathPoints, polySize, rotate), page.testUtil.createSizedAdder(panel, "addTagRect", "S-TR", rectSize, rotate), page.testUtil.createSizedAdder(panel, "addTagEllipse", "S-TE", rectSize, rotate), page.testUtil.createPolyAdder(panel, "addTagPoly", "S-TP", page.testUtil.polyPoints, polySize, rotate), page.testUtil.createPolyAdder(panel, "addTagPath", "S-TH", page.testUtil.pathPoints, polySize, rotate), page.testUtil.createRotateAdder(panel, "addCarForklift", "S-CF"), page.testUtil.createRotateAdder(panel, "addCarConveyor", "S-CC"), page.testUtil.createRotateAdder(panel, "addCarTugger", "S-CT"), page.testUtil.createRotateAdder(panel, "addCarCarrier", "S-CR") ]); page.testUtil.measureBatchAdd(panel, scatter.points, adders, "stress:addShapes"); page.testUtil.measureBatchEdges(panel, 2e3, "S", "stress:addEdges"); } }; panelNamespace.test = Object.freeze({ page, config: page.testConfig, util: page.testUtil, names: Object.freeze(Object.keys(testActions)), has(name) { return typeof testActions[name] === "function"; }, stop() { page.testUtil.carStop(); }, run(name, ...args) { const handler = testActions[name]; if (typeof handler !== "function") { throw new Error(`未找到测试方法:${name}`); } return handler(...args); } }); page.syncTestButtons?.(); })();