优化仿真求解性能并修复流量闭合问题(初版)

This commit is contained in:
ljz committed 2026-08-16 17:46:05 +08:00
1 parent 57b459bc72
commit 5332a788f3
55 files changed
+8973 -549

No files matched your search

+231 -2
View File
@@ -6,9 +6,9 @@ import {
wideProject,
} from "./fixtures";
async function readModelingViewport(page: Page) {
async function readViewport(page: Page, canvasSelector: string) {
return page
.locator(".flow-canvas .react-flow__viewport")
.locator(`${canvasSelector} .react-flow__viewport`)
.evaluate((viewport) => {
const values =
getComputedStyle(viewport)
@@ -22,6 +22,10 @@ async function readModelingViewport(page: Page) {
});
}
async function readModelingViewport(page: Page) {
return readViewport(page, ".flow-canvas");
}
test("拖入组件不自动适配画布,手动适配按钮仍然生效", async ({ page }) => {
await prepareApp(page);
await page.goto("/");
@@ -257,10 +261,24 @@ test("切换到结果页时自动适配只读系统图", async ({ page }) => {
"system-simulation-flow:project:demo-system",
JSON.stringify(project),
);
window.localStorage.setItem(
"system-simulation-flow:canvas-grid-visibility",
JSON.stringify({ lines: true, dots: false }),
);
}, { project: wideProject, snapshot: resultSnapshot });
await page.goto("/");
await page.getByRole("button", { name: "加载工程" }).click();
await expect(
page.locator(
'.flow-canvas .react-flow__background pattern[id$="major-grid-lines"]',
),
).toHaveCount(1);
await expect(
page.locator(
'.flow-canvas .react-flow__background pattern[id$="minor-grid-dots"]',
),
).toHaveCount(0);
const freeModelingHandle = page.locator(
'.flow-canvas .react-flow__node[data-id="generic_sensor_1"] .port-handle[data-port-name="port_a"]',
);
@@ -271,6 +289,24 @@ test("切换到结果页时自动适配只读系统图", async ({ page }) => {
);
await page.getByRole("tab", { name: /^结果/ }).click();
await expectAllNodesInsideCanvas(page, ".results-system-canvas");
await expect(
page.locator(
'.results-system-canvas .react-flow__background pattern[id$="major-grid-lines"]',
),
).toHaveCount(1);
await expect(
page.locator(
'.results-system-canvas .react-flow__background pattern[id$="minor-grid-dots"]',
),
).toHaveCount(0);
await expect(
page.locator(".results-system-canvas .react-flow__controls"),
).toHaveCount(0);
const fitSystemViewButton = page.getByRole("button", {
name: "适应系统图窗口",
exact: true,
});
await expect(fitSystemViewButton).toBeVisible();
await expect(
page.locator(".results-system-canvas .react-flow__attribution"),
).toHaveCount(0);
@@ -278,6 +314,49 @@ test("切换到结果页时自动适配只读系统图", async ({ page }) => {
page.getByRole("complementary", { name: "仿真控制台", exact: true }),
).toHaveCount(0);
const resultPane = page.locator(".results-system-canvas .react-flow__pane");
const resultPaneBox = await resultPane.boundingBox();
expect(resultPaneBox).not.toBeNull();
const fittedResultViewport = await readViewport(
page,
".results-system-canvas",
);
await page.mouse.move(
resultPaneBox!.x + resultPaneBox!.width / 2,
resultPaneBox!.y + resultPaneBox!.height / 2,
);
await page.mouse.down();
await page.mouse.move(
resultPaneBox!.x + resultPaneBox!.width / 2 + 180,
resultPaneBox!.y + resultPaneBox!.height / 2 + 90,
{ steps: 5 },
);
await page.mouse.up();
await expect
.poll(async () => {
const viewport = await readViewport(page, ".results-system-canvas");
return Math.hypot(
viewport.x - fittedResultViewport.x,
viewport.y - fittedResultViewport.y,
);
})
.toBeGreaterThan(100);
const pannedResultViewport = await readViewport(
page,
".results-system-canvas",
);
await fitSystemViewButton.click();
await expect
.poll(async () => {
const viewport = await readViewport(page, ".results-system-canvas");
return Math.hypot(
viewport.x - pannedResultViewport.x,
viewport.y - pannedResultViewport.y,
);
})
.toBeGreaterThan(100);
await expectAllNodesInsideCanvas(page, ".results-system-canvas");
await page.getByRole("tab", { name: "建模" }).click();
await expectAllNodesInsideCanvas(page, ".flow-canvas");
await expect(freeModelingHandle).toHaveAttribute(
@@ -354,3 +433,153 @@ test("切换到结果页时自动适配只读系统图", async ({ page }) => {
expect(shape.circle).toEqual({ cx: 3, cy: 3, r: 2.5 });
}
});
test("结果页专用图标只按实际包络命中被相邻节点包围的元件", async ({
page,
}) => {
await prepareApp(page);
const pneumaticPort = (name: string, side: "left" | "right") => ({
name,
kind: "physical",
domain: "pneumatic",
nominalRole: "bidirectional",
positiveFlowDirection: "intoComponent",
side,
});
const pnplNode = (
id: string,
position: { x: number; y: number },
rotation: 0 | 90 | 180 | 270,
) => ({
id,
type: "simulationComponent",
position,
data: {
label: id,
componentType: "amesim_pnpl01",
modelType: "amesim_pnpl01",
modelVersion: "0.1.0",
ports: [pneumaticPort("port_1", "left")],
parameters: {},
parameterUnits: {},
parameterScientificNotation: {},
rotation,
mirrored: false,
},
});
const targetNodeId = "amesim_pnch012_2";
const overlapProject = {
...wideProject,
name: "overlapped-symbols",
nodes: [
{
id: "generic_sensor_0",
type: "simulationComponent",
position: { x: 360, y: 390 },
data: {
label: "generic_sensor_0",
componentType: "generic_sensor",
modelType: "generic_sensor",
modelVersion: "1.0.0",
symbol: "symbol-not-registered",
ports: [],
parameters: {},
parameterUnits: {},
parameterScientificNotation: {},
rotation: 0,
mirrored: false,
},
},
{
id: targetNodeId,
type: "simulationComponent",
position: { x: 569.2129060235693, y: 515.6448805401301 },
data: {
label: targetNodeId,
componentType: "amesim_pnch012",
modelType: "amesim_pnch012",
modelVersion: "0.1.0",
ports: [
pneumaticPort("port_1", "left"),
pneumaticPort("port_2", "right"),
pneumaticPort("port_3", "left"),
pneumaticPort("port_4", "right"),
],
parameters: {},
parameterUnits: {},
parameterScientificNotation: {},
rotation: 0,
mirrored: false,
},
},
pnplNode(
"amesim_pnpl01_7",
{ x: 535.6129060235693, y: 523.6448805401301 },
180,
),
pnplNode(
"amesim_pnpl01_8",
{ x: 587.2129060235693, y: 555.2448805401301 },
90,
),
pnplNode(
"amesim_pnpl01_9",
{ x: 618.8129060235693, y: 523.6448805401301 },
0,
),
],
edges: [],
};
const overlapSnapshot = {
...resultSnapshot,
id: "overlapped-symbols-result",
project: overlapProject,
};
await page.addInitScript((snapshot) => {
window.sessionStorage.setItem(
"system-simulation-flow:latest-result",
JSON.stringify(snapshot),
);
}, overlapSnapshot);
await page.goto("/");
await page.getByRole("tab", { name: /^结果/ }).click();
await expectAllNodesInsideCanvas(
page,
".results-system-canvas",
overlapProject.nodes.length,
);
const targetNode = page.locator(
`.results-system-canvas .react-flow__node[data-id="${targetNodeId}"]`,
);
const targetEnvelope = targetNode.locator('[data-symbol-envelope="true"]');
await expect(targetNode).toHaveClass(/component-icon-node/);
await expect(targetNode).toHaveCSS("pointer-events", "none");
await expect(targetEnvelope).toHaveCSS("pointer-events", "auto");
const pane = page.locator(".results-system-canvas .react-flow__pane");
const paneBox = await pane.boundingBox();
expect(paneBox).not.toBeNull();
await page.mouse.click(paneBox!.x + 6, paneBox!.y + 6);
await expect(targetNode).not.toHaveClass(/selected/);
const envelopeBox = await targetEnvelope.boundingBox();
expect(envelopeBox).not.toBeNull();
const targetPoint = {
x: envelopeBox!.x + envelopeBox!.width / 2,
y: envelopeBox!.y + envelopeBox!.height / 2,
};
const hitNodeId = await page.evaluate(({ x, y }) => {
const element = document.elementFromPoint(x, y);
return element?.closest<HTMLElement>(".react-flow__node")?.dataset.id ?? null;
}, targetPoint);
expect(hitNodeId).toBe(targetNodeId);
await page.mouse.click(targetPoint.x, targetPoint.y);
await expect(targetNode).toHaveClass(/selected/);
await expect(
page.locator(
'.results-system-canvas .react-flow__node[data-id^="amesim_pnpl01_"].selected',
),
).toHaveCount(0);
});