diff --git a/src/core/controller/concrete/ControllerCutting.tsx b/src/core/controller/concrete/ControllerCutting.tsx index 3fbdc201..44a046cf 100644 --- a/src/core/controller/concrete/ControllerCutting.tsx +++ b/src/core/controller/concrete/ControllerCutting.tsx @@ -10,6 +10,7 @@ import { Controller } from "../Controller"; import { ControllerClass } from "../ControllerClass"; import { Line } from "../../dataStruct/shape/Line"; import { EdgeRenderer } from "../../render/canvas2d/entityRenderer/edge/EdgeRenderer"; +import { Section } from "../../stageObject/entity/Section"; /** * 关于斩断线的控制器 @@ -52,13 +53,16 @@ ControllerCutting.mousemove = (event: MouseEvent) => { ControllerCutting.lastMoveLocation, ); - Stage.warningNodes = []; - for (const node of StageManager.getTextNodes()) { - const collidePoints = node.rectangle.getCollidePointsWithLine( - Stage.cuttingLine, - ); + Stage.warningEntity = []; + for (const entity of StageManager.getEntities()) { + if (entity instanceof Section) { + continue; // Section的碰撞箱比较特殊 + } + const collidePoints = entity.collisionBox + .getRectangle() + .getCollidePointsWithLine(Stage.cuttingLine); if (collidePoints.length > 0) { - Stage.warningNodes.push(node); + Stage.warningEntity.push(entity); for (const collidePoint of collidePoints) { Stage.effects.push( new CircleFlameEffect( @@ -104,8 +108,8 @@ ControllerCutting.mouseup = (event: MouseEvent) => { } Stage.isCutting = false; - StageManager.deleteEntities(Stage.warningNodes); - Stage.warningNodes = []; + StageManager.deleteEntities(Stage.warningEntity); + Stage.warningEntity = []; for (const edge of Stage.warningEdges) { StageManager.deleteEdge(edge); @@ -116,9 +120,9 @@ ControllerCutting.mouseup = (event: MouseEvent) => { for (const section of Stage.warningSections) { StageManager.deleteSection(section); } - + Stage.warningSections = []; - + StageManager.updateReferences(); Stage.warningEdges = []; diff --git a/src/core/controller/concrete/ControllerNodeConnection.tsx b/src/core/controller/concrete/ControllerNodeConnection.tsx index 72bd5bcc..9bd23c70 100644 --- a/src/core/controller/concrete/ControllerNodeConnection.tsx +++ b/src/core/controller/concrete/ControllerNodeConnection.tsx @@ -9,6 +9,7 @@ import { ControllerClass } from "../ControllerClass"; import { Controller } from "../Controller"; import { RectangleNoteEffect } from "../../effect/concrete/RectangleNoteEffect"; import { EdgeRenderer } from "../../render/canvas2d/entityRenderer/edge/EdgeRenderer"; +import { ConnectableEntity } from "../../stageObject/StageObject"; /** * 右键连线功能 的控制器 @@ -29,25 +30,33 @@ ControllerNodeConnection.mousedown = (event: MouseEvent) => { new Vector(event.clientX, event.clientY), ); const clickedNode = StageManager.findTextNodeByLocation(pressWorldLocation); - + const clickedSection = StageManager.findSectionByLocation(pressWorldLocation); + let clickedConnectableEntity: ConnectableEntity | null = null; + if (clickedNode) { + clickedConnectableEntity = clickedNode; + } + if (clickedSection) { + clickedConnectableEntity = clickedSection; + } + + if (clickedConnectableEntity) { // 右键点击了某个节点 - // Stage.isCutting = false; - Stage.connectFromNodes = []; - for (const node of StageManager.getTextNodes()) { + Stage.connectFromEntities = []; + for (const node of StageManager.getConnectableEntity()) { if (node.isSelected) { - Stage.connectFromNodes.push(node); + Stage.connectFromEntities.push(node); } } - if (Stage.connectFromNodes.includes(clickedNode)) { + if (Stage.connectFromEntities.includes(clickedConnectableEntity)) { // 多重连接 - for (const node of StageManager.getTextNodes()) { + for (const node of StageManager.getConnectableEntity()) { if (node.isSelected) { // 特效 Stage.effects.push( new RectangleNoteEffect( new ProgressNumber(0, 15), - node.rectangle.clone(), + node.collisionBox.getRectangle().clone(), new Color(0, 255, 0, 1), ), ); @@ -55,12 +64,12 @@ ControllerNodeConnection.mousedown = (event: MouseEvent) => { } } else { // 不触发多重连接 - Stage.connectFromNodes = [clickedNode]; + Stage.connectFromEntities = [clickedConnectableEntity]; // 特效 Stage.effects.push( new RectangleNoteEffect( new ProgressNumber(0, 15), - clickedNode.rectangle.clone(), + clickedConnectableEntity.collisionBox.getRectangle().clone(), new Color(0, 255, 0, 1), ), ); @@ -81,9 +90,9 @@ ControllerNodeConnection.mousemove = (event: MouseEvent) => { ); // 连接线 let isFindConnectToNode = false; - for (const node of StageManager.getTextNodes()) { - if (node.collisionBox.isPointInCollisionBox(worldLocation)) { - if (Stage.connectToNode === null) { + for (const entity of StageManager.getConnectableEntity()) { + if (entity.collisionBox.isPointInCollisionBox(worldLocation)) { + if (Stage.connectToEntity === null) { // 特效 // Stage.effects.push( // new RectangleNoteEffect( @@ -93,14 +102,14 @@ ControllerNodeConnection.mousemove = (event: MouseEvent) => { // ), // ); } - Stage.connectToNode = node; + Stage.connectToEntity = entity; isFindConnectToNode = true; break; } } if (!isFindConnectToNode) { - Stage.connectToNode = null; + Stage.connectToEntity = null; } // 由于连接线要被渲染器绘制,所以需要更新总控制里的lastMoveLocation Controller.lastMoveLocation = worldLocation.clone(); @@ -111,16 +120,18 @@ ControllerNodeConnection.mouseup = (event: MouseEvent) => { return; } // 结束连线 - if (Stage.connectFromNodes.length > 0 && Stage.connectToNode !== null) { + if (Stage.connectFromEntities.length > 0 && Stage.connectToEntity !== null) { let isHaveConnectResult = false; // 在多重链接的情况下,是否有连接成功 - for (const node of Stage.connectFromNodes) { - const connectResult = StageManager.connectNode(node, Stage.connectToNode); + for (const entity of Stage.connectFromEntities) { + const connectResult = StageManager.connectNode(entity, Stage.connectToEntity); if (connectResult) { // 连接成功,特效 isHaveConnectResult = true; - for (const effect of EdgeRenderer.getConnectedEffects(node, Stage.connectToNode)) { + for (const effect of EdgeRenderer.getConnectedEffects(entity, Stage.connectToEntity)) { Stage.effects.push(effect); } + } else { + console.warn("连接失败!", entity, Stage.connectToEntity); } } if (isHaveConnectResult) { @@ -128,13 +139,13 @@ ControllerNodeConnection.mouseup = (event: MouseEvent) => { Stage.effects.push( new CircleFlameEffect( new ProgressNumber(0, 15), - Stage.connectToNode.rectangle.center, + Stage.connectToEntity.collisionBox.getRectangle().center, 80, new Color(0, 255, 0, 1), ), ); } } - Stage.connectFromNodes = []; - Stage.connectToNode = null; + Stage.connectFromEntities = []; + Stage.connectToEntity = null; }; diff --git a/src/core/controller/concrete/ControllerRectangleSelect.tsx b/src/core/controller/concrete/ControllerRectangleSelect.tsx index 4e755a77..9fad312a 100644 --- a/src/core/controller/concrete/ControllerRectangleSelect.tsx +++ b/src/core/controller/concrete/ControllerRectangleSelect.tsx @@ -145,11 +145,7 @@ ControllerRectangleSelect.mousemove = (event: MouseEvent) => { if ( edge.collisionBox.isRectangleInCollisionBox(Stage.selectingRectangle) ) { - if ( - Controller.lastSelectedEdge.has( - edge.target.uuid + "&" + edge.source.uuid, - ) - ) { + if (Controller.lastSelectedEdge.has(edge.uuid)) { edge.isSelected = false; } else { edge.isSelected = true; @@ -207,12 +203,9 @@ ControllerRectangleSelect.mouseup = (event: MouseEvent) => { } Controller.lastSelectedEdge = new Set(); for (const edge of StageManager.getEdges()) { - // TODO: 该改了,edge已经有uuid了 if (edge.isSelected) { - Controller.lastSelectedEdge.add( - edge.target.uuid + "&" + edge.source.uuid, - ); + Controller.lastSelectedEdge.add(edge.uuid); } } }; diff --git a/src/core/render/canvas2d/entityRenderer/EntityRenderer.tsx b/src/core/render/canvas2d/entityRenderer/EntityRenderer.tsx index ff18b764..c877aeaf 100644 --- a/src/core/render/canvas2d/entityRenderer/EntityRenderer.tsx +++ b/src/core/render/canvas2d/entityRenderer/EntityRenderer.tsx @@ -40,6 +40,13 @@ export namespace EntityRenderer { new Color(0, 255, 0, 0.5), ); } + // debug + RenderUtils.renderRect( + section.collisionBox.getRectangle().transformWorld2View(), + section.color, + new Color(0, 2, 255, 1), + 0.5 * Camera.currentScale + ) } export function renderNode(node: TextNode) { diff --git a/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx b/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx index b334f26a..4545edf4 100644 --- a/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx +++ b/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx @@ -2,7 +2,6 @@ import { Color } from "../../../../dataStruct/Color"; import { Vector } from "../../../../dataStruct/Vector"; import { Edge } from "../../../../stageObject/association/Edge"; -import { TextNode } from "../../../../stageObject/entity/TextNode"; import { Settings } from "../../../../Settings"; import { Renderer } from "../../renderer"; @@ -11,6 +10,7 @@ import { StraightEdgeRenderer } from "./concrete/StraightEdgeRenderer"; import { SymmetryCurveEdgeRenderer } from "./concrete/SymmetryCurveEdgeRenderer"; import { VerticalPolyEdgeRenderer } from "./concrete/VerticalPolyEdgeRenderer"; import { CollisionBoxRenderer } from "../CollisionBoxRenderer"; +import { ConnectableEntity } from "../../../../stageObject/StageObject"; /** * 边的总渲染器单例 @@ -73,14 +73,14 @@ export namespace EdgeRenderer { } export function renderVirtualEdge( - startNode: TextNode, + startNode: ConnectableEntity, mouseLocation: Vector, ) { currentRenderer.renderVirtualEdge(startNode, mouseLocation); } export function renderVirtualConfirmedEdge( - startNode: TextNode, - endNode: TextNode, + startNode: ConnectableEntity, + endNode: ConnectableEntity, ) { currentRenderer.renderVirtualConfirmedEdge(startNode, endNode); } @@ -88,7 +88,7 @@ export namespace EdgeRenderer { export function getCuttingEffects(edge: Edge) { return currentRenderer.getCuttingEffects(edge); } - export function getConnectedEffects(startNode: TextNode, toNode: TextNode) { + export function getConnectedEffects(startNode: ConnectableEntity, toNode: ConnectableEntity) { return currentRenderer.getConnectedEffects(startNode, toNode); } diff --git a/src/core/render/canvas2d/entityRenderer/edge/EdgeRendererClass.tsx b/src/core/render/canvas2d/entityRenderer/edge/EdgeRendererClass.tsx index eb1a8112..4196ca61 100644 --- a/src/core/render/canvas2d/entityRenderer/edge/EdgeRendererClass.tsx +++ b/src/core/render/canvas2d/entityRenderer/edge/EdgeRendererClass.tsx @@ -1,7 +1,7 @@ import { Vector } from "../../../../dataStruct/Vector"; import { Edge } from "../../../../stageObject/association/Edge"; import { Effect } from "../../../../effect/effect"; -import { TextNode } from "../../../../stageObject/entity/TextNode"; +import { ConnectableEntity } from "../../../../stageObject/StageObject"; /** * 不同类型的边的渲染器 基类 @@ -49,14 +49,14 @@ export abstract class EdgeRendererClass { * @param startNode * @param mouseLocation 世界坐标系 */ - public abstract renderVirtualEdge(startNode: TextNode, mouseLocation: Vector): void; + public abstract renderVirtualEdge(startNode: ConnectableEntity, mouseLocation: Vector): void; /** * 绘制鼠标连线移动到目标节点上吸附住 时候虚拟连线效果 * @param startNode * @param endNode */ - public abstract renderVirtualConfirmedEdge(startNode: TextNode, endNode: TextNode): void; + public abstract renderVirtualConfirmedEdge(startNode: ConnectableEntity, endNode: ConnectableEntity): void; /** * 获取这个线在切断时的特效 * 外层将在切断时根据此函数来获取特效并自动加入到渲染器中 @@ -66,5 +66,5 @@ export abstract class EdgeRendererClass { /** * 获取这个线在连接成功时的特效 */ - abstract getConnectedEffects(startNode: TextNode, toNode: TextNode): Effect[]; + abstract getConnectedEffects(startNode: ConnectableEntity, toNode: ConnectableEntity): Effect[]; } diff --git a/src/core/render/canvas2d/entityRenderer/edge/concrete/StraightEdgeRenderer.tsx b/src/core/render/canvas2d/entityRenderer/edge/concrete/StraightEdgeRenderer.tsx index fe359a72..89b07483 100644 --- a/src/core/render/canvas2d/entityRenderer/edge/concrete/StraightEdgeRenderer.tsx +++ b/src/core/render/canvas2d/entityRenderer/edge/concrete/StraightEdgeRenderer.tsx @@ -6,12 +6,12 @@ import { Edge } from "../../../../../stageObject/association/Edge"; import { CircleFlameEffect } from "../../../../../effect/concrete/CircleFlameEffect"; import { LineCuttingEffect } from "../../../../../effect/concrete/LineCuttingEffect"; import { Effect } from "../../../../../effect/effect"; -import { TextNode } from "../../../../../stageObject/entity/TextNode"; import { Camera } from "../../../../../stage/Camera"; import { Renderer } from "../../../renderer"; import { RenderUtils } from "../../../RenderUtils"; import { EdgeRenderer } from "../EdgeRenderer"; import { EdgeRendererClass } from "../EdgeRendererClass"; +import { ConnectableEntity } from "../../../../../stageObject/StageObject"; /** * 直线渲染器 @@ -45,18 +45,18 @@ export class StraightEdgeRenderer extends EdgeRendererClass { ]; } - getConnectedEffects(startNode: TextNode, toNode: TextNode): Effect[] { + getConnectedEffects(startNode: ConnectableEntity, toNode: ConnectableEntity): Effect[] { return [ new CircleFlameEffect( new ProgressNumber(0, 15), - startNode.rectangle.center, + startNode.collisionBox.getRectangle().center, 80, new Color(83, 175, 29, 1), ), new LineCuttingEffect( new ProgressNumber(0, 30), - startNode.rectangle.center, - toNode.rectangle.center, + startNode.collisionBox.getRectangle().center, + toNode.collisionBox.getRectangle().center, new Color(78, 201, 176, 1), new Color(83, 175, 29, 1), 20, @@ -106,9 +106,9 @@ export class StraightEdgeRenderer extends EdgeRendererClass { // 画箭头 { const size = 15; - const direction = edge.target.rectangle + const direction = edge.target.collisionBox.getRectangle() .getCenter() - .subtract(edge.source.rectangle.getCenter()) + .subtract(edge.source.collisionBox.getRectangle().getCenter()) .normalize(); const endPoint = edge.bodyLine.end.clone(); EdgeRenderer.renderArrowHead(endPoint, direction, size); @@ -118,8 +118,8 @@ export class StraightEdgeRenderer extends EdgeRendererClass { public renderCycleState(edge: Edge): void { // 自环 RenderUtils.renderArc( - Renderer.transformWorld2View(edge.target.rectangle.location), - (edge.target.rectangle.size.y / 2) * Camera.currentScale, + Renderer.transformWorld2View(edge.target.collisionBox.getRectangle().location), + (edge.target.collisionBox.getRectangle().size.y / 2) * Camera.currentScale, Math.PI / 2, 0, new Color(204, 204, 204), @@ -129,14 +129,14 @@ export class StraightEdgeRenderer extends EdgeRendererClass { { const size = 15; const direction = new Vector(1, 0).rotateDegrees(15); - const endPoint = edge.target.rectangle.leftCenter; + const endPoint = edge.target.collisionBox.getRectangle().leftCenter; EdgeRenderer.renderArrowHead(endPoint, direction, size); } } - public renderVirtualEdge(startNode: TextNode, mouseLocation: Vector): void { + public renderVirtualEdge(startNode: ConnectableEntity, mouseLocation: Vector): void { RenderUtils.renderGradientLine( - Renderer.transformWorld2View(startNode.rectangle.getCenter()), + Renderer.transformWorld2View(startNode.collisionBox.getRectangle().getCenter()), Renderer.transformWorld2View(mouseLocation), new Color(255, 255, 255, 0), new Color(255, 255, 255, 0.5), @@ -144,10 +144,10 @@ export class StraightEdgeRenderer extends EdgeRendererClass { ); } - public renderVirtualConfirmedEdge(startNode: TextNode, endNode: TextNode): void { + public renderVirtualConfirmedEdge(startNode: ConnectableEntity, endNode: ConnectableEntity): void { RenderUtils.renderGradientLine( - Renderer.transformWorld2View(startNode.rectangle.getCenter()), - Renderer.transformWorld2View(endNode.rectangle.getCenter()), + Renderer.transformWorld2View(startNode.collisionBox.getRectangle().getCenter()), + Renderer.transformWorld2View(endNode.collisionBox.getRectangle().getCenter()), new Color(0, 255, 0, 0), new Color(0, 255, 0, 0.5), 2, diff --git a/src/core/render/canvas2d/entityRenderer/edge/concrete/SymmetryCurveEdgeRenderer.tsx b/src/core/render/canvas2d/entityRenderer/edge/concrete/SymmetryCurveEdgeRenderer.tsx index 722aa912..ac34fc14 100644 --- a/src/core/render/canvas2d/entityRenderer/edge/concrete/SymmetryCurveEdgeRenderer.tsx +++ b/src/core/render/canvas2d/entityRenderer/edge/concrete/SymmetryCurveEdgeRenderer.tsx @@ -6,13 +6,13 @@ import { Edge } from "../../../../../stageObject/association/Edge"; import { CircleFlameEffect } from "../../../../../effect/concrete/CircleFlameEffect"; import { LineCuttingEffect } from "../../../../../effect/concrete/LineCuttingEffect"; import { Effect } from "../../../../../effect/effect"; -import { TextNode } from "../../../../../stageObject/entity/TextNode"; import { Camera } from "../../../../../stage/Camera"; import { Renderer } from "../../../renderer"; import { RenderUtils } from "../../../RenderUtils"; import { EdgeRenderer } from "../EdgeRenderer"; import { EdgeRendererClass } from "../EdgeRendererClass"; import { WorldRenderUtils } from "../../../WorldRenderUtils"; +import { ConnectableEntity } from "../../../../../stageObject/StageObject"; export class SymmetryCurveEdgeRenderer extends EdgeRendererClass { getCuttingEffects(edge: Edge): Effect[] { @@ -42,18 +42,18 @@ export class SymmetryCurveEdgeRenderer extends EdgeRendererClass { ), ]; } - getConnectedEffects(startNode: TextNode, toNode: TextNode): Effect[] { + getConnectedEffects(startNode: ConnectableEntity, toNode: ConnectableEntity): Effect[] { return [ new CircleFlameEffect( new ProgressNumber(0, 15), - startNode.rectangle.center, + startNode.collisionBox.getRectangle().center, 80, new Color(83, 175, 29, 1), ), new LineCuttingEffect( new ProgressNumber(0, 30), - startNode.rectangle.center, - toNode.rectangle.center, + startNode.collisionBox.getRectangle().center, + toNode.collisionBox.getRectangle().center, new Color(78, 201, 176, 1), new Color(83, 175, 29, 1), 20, @@ -99,8 +99,8 @@ export class SymmetryCurveEdgeRenderer extends EdgeRendererClass { public renderCycleState(edge: Edge): void { // 自环 RenderUtils.renderArc( - Renderer.transformWorld2View(edge.target.rectangle.location), - (edge.target.rectangle.size.y / 2) * Camera.currentScale, + Renderer.transformWorld2View(edge.target.collisionBox.getRectangle().location), + (edge.target.collisionBox.getRectangle().size.y / 2) * Camera.currentScale, Math.PI / 2, 0, new Color(204, 204, 204), @@ -110,14 +110,14 @@ export class SymmetryCurveEdgeRenderer extends EdgeRendererClass { { const size = 15; const direction = new Vector(1, 0).rotateDegrees(15); - const endPoint = edge.target.rectangle.leftCenter; + const endPoint = edge.target.collisionBox.getRectangle().leftCenter; EdgeRenderer.renderArrowHead(endPoint, direction, size); } } - public renderVirtualEdge(startNode: TextNode, mouseLocation: Vector): void { + public renderVirtualEdge(startNode: ConnectableEntity, mouseLocation: Vector): void { // 绘制曲线本体 - const start = Renderer.transformWorld2View(startNode.rectangle.center); + const start = Renderer.transformWorld2View(startNode.collisionBox.getRectangle().center); const end = Renderer.transformWorld2View(mouseLocation); const direction = end.subtract(start); const startDirection = new Vector( @@ -139,9 +139,9 @@ export class SymmetryCurveEdgeRenderer extends EdgeRendererClass { ); } - public renderVirtualConfirmedEdge(startNode: TextNode, endNode: TextNode): void { - const start = Renderer.transformWorld2View(startNode.rectangle.center); - const end = Renderer.transformWorld2View(endNode.rectangle.center); + public renderVirtualConfirmedEdge(startNode: ConnectableEntity, endNode: ConnectableEntity): void { + const start = Renderer.transformWorld2View(startNode.collisionBox.getRectangle().center); + const end = Renderer.transformWorld2View(endNode.collisionBox.getRectangle().center); const direction = end.subtract(start); const startDirection = new Vector( Math.abs(direction.x) >= Math.abs(direction.y) ? direction.x : 0, diff --git a/src/core/render/canvas2d/entityRenderer/edge/concrete/VerticalPolyEdgeRenderer.tsx b/src/core/render/canvas2d/entityRenderer/edge/concrete/VerticalPolyEdgeRenderer.tsx index 2554961f..a40434f2 100644 --- a/src/core/render/canvas2d/entityRenderer/edge/concrete/VerticalPolyEdgeRenderer.tsx +++ b/src/core/render/canvas2d/entityRenderer/edge/concrete/VerticalPolyEdgeRenderer.tsx @@ -6,12 +6,12 @@ import { Edge } from "../../../../../stageObject/association/Edge"; import { CircleFlameEffect } from "../../../../../effect/concrete/CircleFlameEffect"; import { LineCuttingEffect } from "../../../../../effect/concrete/LineCuttingEffect"; import { Effect } from "../../../../../effect/effect"; -import { TextNode } from "../../../../../stageObject/entity/TextNode"; import { Camera } from "../../../../../stage/Camera"; import { Renderer } from "../../../renderer"; import { RenderUtils } from "../../../RenderUtils"; import { EdgeRenderer } from "../EdgeRenderer"; import { EdgeRendererClass } from "../EdgeRendererClass"; +import { ConnectableEntity } from "../../../../../stageObject/StageObject"; /** * 折线渲染器 @@ -45,18 +45,18 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { ]; } - getConnectedEffects(startNode: TextNode, toNode: TextNode): Effect[] { + getConnectedEffects(startNode: ConnectableEntity, toNode: ConnectableEntity): Effect[] { return [ new CircleFlameEffect( new ProgressNumber(0, 15), - startNode.rectangle.center, + startNode.collisionBox.getRectangle().center, 80, new Color(83, 175, 29, 1), ), new LineCuttingEffect( new ProgressNumber(0, 30), - startNode.rectangle.center, - toNode.rectangle.center, + startNode.collisionBox.getRectangle().center, + toNode.collisionBox.getRectangle().center, new Color(78, 201, 176, 1), new Color(83, 175, 29, 1), 20, @@ -74,8 +74,8 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { * @returns */ getVerticalDirection(edge: Edge): Vector { - const startLocation = edge.source.rectangle.center; - const endLocation = edge.target.rectangle.center; + const startLocation = edge.source.collisionBox.getRectangle().center; + const endLocation = edge.target.collisionBox.getRectangle().center; const startToEnd = endLocation.subtract(startLocation); if (startLocation.x < endLocation.x) { // |左侧 @@ -131,11 +131,13 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { renderTest(edge: Edge) { for (let i = 0; i < 4; i++) { RenderUtils.renderSolidLine( - Renderer.transformWorld2View(edge.target.rectangle.center), Renderer.transformWorld2View( - edge.target.rectangle.center.add( - new Vector(100, 0).rotateDegrees(45 + 90 * i), - ), + edge.target.collisionBox.getRectangle().center, + ), + Renderer.transformWorld2View( + edge.target.collisionBox + .getRectangle() + .center.add(new Vector(100, 0).rotateDegrees(45 + 90 * i)), ), Color.Green, 1, @@ -158,32 +160,36 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { const rate = 1 - this.gaussianFunction( - edge.target.rectangle.center.x - edge.source.rectangle.center.x, + edge.target.collisionBox.getRectangle().center.x - + edge.source.collisionBox.getRectangle().center.x, ); // 左右偏离距离 恒正 - const distance = (rate * edge.target.rectangle.size.x) / 2; + const distance = + (rate * edge.target.collisionBox.getRectangle().size.x) / 2; // 根据偏移距离计算附加高度 恒正 - const h = (edge.target.rectangle.size.x / 2) * (1 - rate); + const h = + (edge.target.collisionBox.getRectangle().size.x / 2) * (1 - rate); // 终点 const p1 = new Vector( - edge.target.rectangle.center.x + + edge.target.collisionBox.getRectangle().center.x + distance * - (edge.source.rectangle.center.x > edge.target.rectangle.center.x + (edge.source.collisionBox.getRectangle().center.x > + edge.target.collisionBox.getRectangle().center.x ? 1 : -1), verticalDirection.y > 0 - ? edge.target.rectangle.top - : edge.target.rectangle.bottom, + ? edge.target.collisionBox.getRectangle().top + : edge.target.collisionBox.getRectangle().bottom, ); const length = (this.fixedLength + h) * (verticalDirection.y > 0 ? -1 : 1); const p2 = p1.add(new Vector(0, length)); const p4 = new Vector( - edge.source.rectangle.center.x, + edge.source.collisionBox.getRectangle().center.x, verticalDirection.y > 0 - ? edge.source.rectangle.bottom - : edge.source.rectangle.top, + ? edge.source.collisionBox.getRectangle().bottom + : edge.source.collisionBox.getRectangle().top, ); const p3 = new Vector(p4.x, p2.y); @@ -203,20 +209,24 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { const rate = 1 - this.gaussianFunction( - edge.target.rectangle.center.y - edge.source.rectangle.center.y, + edge.target.collisionBox.getRectangle().center.y - + edge.source.collisionBox.getRectangle().center.y, ); // 偏离距离 恒正 - const distance = (rate * edge.target.rectangle.size.y) / 2; + const distance = + (rate * edge.target.collisionBox.getRectangle().size.y) / 2; // 根据偏移距离计算附加高度 - const h = (edge.target.rectangle.size.y / 2) * (1 - rate); + const h = + (edge.target.collisionBox.getRectangle().size.y / 2) * (1 - rate); // 终点 const p1 = new Vector( verticalDirection.x > 0 - ? edge.target.rectangle.left - : edge.target.rectangle.right, - edge.target.rectangle.center.y + + ? edge.target.collisionBox.getRectangle().left + : edge.target.collisionBox.getRectangle().right, + edge.target.collisionBox.getRectangle().center.y + distance * - (edge.source.rectangle.center.y > edge.target.rectangle.center.y + (edge.source.collisionBox.getRectangle().center.y > + edge.target.collisionBox.getRectangle().center.y ? 1 : -1), ); @@ -227,9 +237,9 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { const p4 = new Vector( verticalDirection.x > 0 - ? edge.source.rectangle.right - : edge.source.rectangle.left, - edge.source.rectangle.center.y, + ? edge.source.collisionBox.getRectangle().right + : edge.source.collisionBox.getRectangle().left, + edge.source.collisionBox.getRectangle().center.y, ); const p3 = new Vector(p2.x, p4.y); @@ -287,9 +297,10 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { // 画箭头 { const size = 15; - const direction = edge.target.rectangle + const direction = edge.target.collisionBox + .getRectangle() .getCenter() - .subtract(edge.source.rectangle.getCenter()) + .subtract(edge.source.collisionBox.getRectangle().getCenter()) .normalize(); const endPoint = edge.bodyLine.end.clone(); EdgeRenderer.renderArrowHead(endPoint, direction, size); @@ -300,8 +311,11 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { public renderCycleState(edge: Edge): void { // 自环 RenderUtils.renderArc( - Renderer.transformWorld2View(edge.target.rectangle.location), - (edge.target.rectangle.size.y / 2) * Camera.currentScale, + Renderer.transformWorld2View( + edge.target.collisionBox.getRectangle().location, + ), + (edge.target.collisionBox.getRectangle().size.y / 2) * + Camera.currentScale, Math.PI / 2, 0, new Color(204, 204, 204), @@ -311,14 +325,16 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { { const size = 15; const direction = new Vector(1, 0).rotateDegrees(15); - const endPoint = edge.target.rectangle.leftCenter; + const endPoint = edge.target.collisionBox.getRectangle().leftCenter; EdgeRenderer.renderArrowHead(endPoint, direction, size); } } - public renderVirtualEdge(startNode: TextNode, mouseLocation: Vector): void { + public renderVirtualEdge(startNode: ConnectableEntity, mouseLocation: Vector): void { RenderUtils.renderGradientLine( - Renderer.transformWorld2View(startNode.rectangle.getCenter()), + Renderer.transformWorld2View( + startNode.collisionBox.getRectangle().getCenter(), + ), Renderer.transformWorld2View(mouseLocation), new Color(255, 255, 255, 0), new Color(255, 255, 255, 0.5), @@ -326,10 +342,17 @@ export class VerticalPolyEdgeRenderer extends EdgeRendererClass { ); } - public renderVirtualConfirmedEdge(startNode: TextNode, endNode: TextNode): void { + public renderVirtualConfirmedEdge( + startNode: ConnectableEntity, + endNode: ConnectableEntity, + ): void { RenderUtils.renderGradientLine( - Renderer.transformWorld2View(startNode.rectangle.getCenter()), - Renderer.transformWorld2View(endNode.rectangle.getCenter()), + Renderer.transformWorld2View( + startNode.collisionBox.getRectangle().getCenter(), + ), + Renderer.transformWorld2View( + endNode.collisionBox.getRectangle().getCenter(), + ), new Color(0, 255, 0, 0), new Color(0, 255, 0, 0.5), 2, diff --git a/src/core/render/canvas2d/renderer.tsx b/src/core/render/canvas2d/renderer.tsx index f737fbe6..ef76fd34 100644 --- a/src/core/render/canvas2d/renderer.tsx +++ b/src/core/render/canvas2d/renderer.tsx @@ -150,7 +150,7 @@ export namespace Renderer { ); } // 手动连接线 - if (Stage.connectFromNodes.length > 0 && Controller.lastMoveLocation) { + if (Stage.connectFromEntities.length > 0 && Controller.lastMoveLocation) { // 如果鼠标位置没有和任何节点相交 let connectTargetNode = null; for (const node of StageManager.getTextNodes()) { @@ -160,12 +160,12 @@ export namespace Renderer { } } if (connectTargetNode === null) { - for (const node of Stage.connectFromNodes) { + for (const node of Stage.connectFromEntities) { EdgeRenderer.renderVirtualEdge(node, Controller.lastMoveLocation); } } else { // 画一条像吸住了的线 - for (const node of Stage.connectFromNodes) { + for (const node of Stage.connectFromEntities) { EdgeRenderer.renderVirtualConfirmedEdge(node, connectTargetNode); } } @@ -204,7 +204,7 @@ export namespace Renderer { } function renderWarningEntities() { // 待删除的节点 - for (const node of Stage.warningNodes) { + for (const node of Stage.warningEntity) { CollisionBoxRenderer.render(node.collisionBox, new Color(255, 0, 0, 0.5)); } // 待删除的边 @@ -423,9 +423,9 @@ export namespace Renderer { `框选框: ${Stage.selectingRectangle}`, `正在移动节点: ${Controller.isMovingEntity}`, `正在切割: ${Stage.isCutting}`, - `Stage.warningNodes: ${Stage.warningNodes.length}`, + `Stage.warningNodes: ${Stage.warningEntity.length}`, `Stage.warningEdges: ${Stage.warningEdges.length}`, - `ConnectFromNodes: ${Stage.connectFromNodes}`, + `ConnectFromNodes: ${Stage.connectFromEntities}`, `lastSelectedNode: ${Controller.lastSelectedEntity.size}`, `粘贴板: ${JSON.stringify(Stage.copyBoardData)}`, `历史: ${StageHistoryManager.statusText()}`, diff --git a/src/core/stage/Stage.tsx b/src/core/stage/Stage.tsx index b030bfcb..e21e546c 100644 --- a/src/core/stage/Stage.tsx +++ b/src/core/stage/Stage.tsx @@ -7,6 +7,7 @@ import { Serialized } from "../../types/node"; import { StageDumper } from "./StageDumper"; import { Line } from "../dataStruct/shape/Line"; import { Section } from "../stageObject/entity/Section"; +import { ConnectableEntity, Entity } from "../stageObject/StageObject"; /** * 舞台对象 @@ -48,7 +49,7 @@ export namespace Stage { /** * 正在准备要删除的节点 */ - export let warningNodes: TextNode[] = []; + export let warningEntity: Entity[] = []; /** * 正在准备要删除的连线 */ @@ -57,8 +58,8 @@ export namespace Stage { /** * 用于多重连接 */ - export let connectFromNodes: TextNode[] = []; - export let connectToNode: TextNode | null = null; + export let connectFromEntities: ConnectableEntity[] = []; + export let connectToEntity: ConnectableEntity | null = null; /** * 鼠标悬浮的边 diff --git a/src/core/stage/stageManager/StageManager.tsx b/src/core/stage/stageManager/StageManager.tsx index 537125b9..1fd3f379 100644 --- a/src/core/stage/stageManager/StageManager.tsx +++ b/src/core/stage/stageManager/StageManager.tsx @@ -16,7 +16,11 @@ import { Stage } from "../Stage"; import { StageDumper } from "../StageDumper"; import { Rectangle } from "../../dataStruct/shape/Rectangle"; import { StringDict } from "../../dataStruct/StringDict"; -import { Association, Entity } from "../../stageObject/StageObject"; +import { + Association, + ConnectableEntity, + Entity, +} from "../../stageObject/StageObject"; import { Section } from "../../stageObject/entity/Section"; import { StageSectionInOutManager } from "./concreteMethods/StageSectionInOutManager"; @@ -36,6 +40,14 @@ export namespace StageManager { export function getTextNodes(): TextNode[] { return entities.valuesToArray().filter((node) => node instanceof TextNode); } + export function getConnectableEntity(): ConnectableEntity[] { + return entities + .valuesToArray() + .filter((node) => node instanceof ConnectableEntity); + } + export function isEntityExists(uuid: string): boolean { + return entities.hasId(uuid); + } export function getSections(): Section[] { return entities.valuesToArray().filter((node) => node instanceof Section); } @@ -98,17 +110,19 @@ export namespace StageManager { export let selectedEdgeCount = 0; /** 获取节点连接的子节点数组 */ - export function nodeChildrenArray(node: TextNode): TextNode[] { - const res: TextNode[] = []; + export function nodeChildrenArray( + node: ConnectableEntity, + ): ConnectableEntity[] { + const res: ConnectableEntity[] = []; for (const edge of getEdges()) { - if (edge.source === node) { + if (edge.source.uuid === node.uuid) { res.push(edge.target); } } return res; } - function isConnected(node: TextNode, target: TextNode): boolean { + function isConnected(node: ConnectableEntity, target: ConnectableEntity): boolean { for (const edge of getEdges()) { if (edge.source === node && edge.target === target) { return true; @@ -122,11 +136,11 @@ export namespace StageManager { * 节点什么情况下会是unknown的? * * 包含了对Section框的更新 - * + * */ export function updateReferences() { for (const entity of getEntities()) { - if (entity instanceof TextNode) { + if (entity instanceof ConnectableEntity) { for (const edge of getEdges()) { if (edge.source.unknown && edge.source.uuid === entity.uuid) { edge.source = entity; @@ -142,7 +156,7 @@ export namespace StageManager { for (const child of entity.children) { if (entities.hasId(child.uuid)) { - const childObject = entities.getById(child.uuid) + const childObject = entities.getById(child.uuid); if (childObject) { newChildList.push(childObject); } @@ -152,7 +166,6 @@ export namespace StageManager { entity.adjustLocationAndSize(); } } - } export function getTextNodeByUUID(uuid: string): TextNode | null { @@ -163,6 +176,16 @@ export namespace StageManager { } return null; } + export function getConnectableEntityByUUID( + uuid: string, + ): ConnectableEntity | null { + for (const node of getConnectableEntity()) { + if (node.uuid === uuid) { + return node; + } + } + return null; + } export function isSectionByUUID(uuid: string): boolean { return entities.getById(uuid) instanceof Section; } @@ -382,7 +405,7 @@ export namespace StageManager { StageHistoryManager.recordStep(); } - export function connectNode(fromNode: TextNode, toNode: TextNode) { + export function connectNode(fromNode: ConnectableEntity, toNode: ConnectableEntity) { StageNodeConnector.connectNode(fromNode, toNode); StageHistoryManager.recordStep(); return isConnected(fromNode, toNode); @@ -425,7 +448,7 @@ export namespace StageManager { StageSectionInOutManager.goInSection(entities, section); StageHistoryManager.recordStep(); } - + export function goOutSection(entities: Entity[], section: Section) { StageSectionInOutManager.goOutSection(entities, section); StageHistoryManager.recordStep(); diff --git a/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx b/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx index 17737cdd..177a0f6a 100644 --- a/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx +++ b/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx @@ -82,8 +82,8 @@ export namespace StageDeleteManager { const toNode = deleteEdge.target; // 先判断这两个节点是否在nodes里 if ( - StageManager.getTextNodes().includes(fromNode) && - StageManager.getTextNodes().includes(toNode) + StageManager.isEntityExists(fromNode.uuid) && + StageManager.isEntityExists(toNode.uuid) ) { // 删除边 StageManager.deleteOneEdge(deleteEdge); diff --git a/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx b/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx index 9c233e09..c4a56efe 100644 --- a/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx +++ b/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx @@ -1,5 +1,5 @@ import { Edge } from "../../../stageObject/association/Edge"; -import { TextNode } from "../../../stageObject/entity/TextNode"; +import { ConnectableEntity } from "../../../stageObject/StageObject"; import { StageManager } from "../StageManager"; import { StageDeleteManager } from "./StageDeleteManager"; import { v4 as uuidv4 } from "uuid"; @@ -10,13 +10,13 @@ import { v4 as uuidv4 } from "uuid"; export namespace StageNodeConnector { // 连接两两节点 export function connectNode( - fromNode: TextNode, - toNode: TextNode, + fromNode: ConnectableEntity, + toNode: ConnectableEntity, text: string = "", ): void { if ( - StageManager.getTextNodes().includes(fromNode) && - StageManager.getTextNodes().includes(toNode) + StageManager.isEntityExists(fromNode.uuid) && + StageManager.isEntityExists(toNode.uuid) ) { // const addResult = fromNode.addChild(toNode); diff --git a/src/core/stage/stageManager/concreteMethods/StageNodeMoveManager.tsx b/src/core/stage/stageManager/concreteMethods/StageNodeMoveManager.tsx index 6b82761a..c77f8b96 100644 --- a/src/core/stage/stageManager/concreteMethods/StageNodeMoveManager.tsx +++ b/src/core/stage/stageManager/concreteMethods/StageNodeMoveManager.tsx @@ -1,6 +1,5 @@ import { Vector } from "../../../dataStruct/Vector"; -import { TextNode } from "../../../stageObject/entity/TextNode"; -import { Entity } from "../../../stageObject/StageObject"; +import { ConnectableEntity, Entity } from "../../../stageObject/StageObject"; import { StageManager } from "../StageManager"; /** @@ -67,13 +66,18 @@ export namespace StageNodeTextMoveManager { moveWithChildren(node, delta); } } + for (const section of StageManager.getSections()) { + if (section.isSelected) { + moveWithChildren(section, delta); + } + } } - function moveWithChildren(node: TextNode, delta: Vector) { + function moveWithChildren(node: ConnectableEntity, delta: Vector) { moveWithChildrenDfs(node, delta, [node.uuid]); } function moveWithChildrenDfs( - node: TextNode, + node: ConnectableEntity, delta: Vector, visitedUUIDs: string[], ) { diff --git a/src/core/stage/stageManager/concreteMethods/stageNodeRotate.tsx b/src/core/stage/stageManager/concreteMethods/stageNodeRotate.tsx index fc84450f..6882ad02 100644 --- a/src/core/stage/stageManager/concreteMethods/stageNodeRotate.tsx +++ b/src/core/stage/stageManager/concreteMethods/stageNodeRotate.tsx @@ -1,11 +1,11 @@ import { Vector } from "../../../dataStruct/Vector"; import { StageManager } from "../StageManager"; -import { TextNode } from "../../../stageObject/entity/TextNode"; import { Stage } from "../../Stage"; import { LineEffect } from "../../../effect/concrete/LineEffect"; import { ProgressNumber } from "../../../dataStruct/ProgressNumber"; import { Color } from "../../../dataStruct/Color"; import { StageNodeTextMoveManager } from "./StageNodeMoveManager"; +import { ConnectableEntity } from "../../../stageObject/StageObject"; /** * 所有和旋转相关的操作 @@ -33,8 +33,8 @@ export namespace StageNodeRotate { degrees = 0; } rotateNodeDfs( - StageManager.getTextNodeByUUID(edge.source.uuid)!, - StageManager.getTextNodeByUUID(edge.target.uuid)!, + StageManager.getConnectableEntityByUUID(edge.source.uuid)!, + StageManager.getConnectableEntityByUUID(edge.target.uuid)!, degrees, [edge.source.uuid], ); @@ -50,8 +50,8 @@ export namespace StageNodeRotate { * @param visitedUUIDs 已经访问过的节点的uuid列表,用于避免死循环 */ export function rotateNodeDfs( - rotateCenterNode: TextNode, // 待改成ConnectedAbleEntity - currentNode: TextNode, + rotateCenterNode: ConnectableEntity, + currentNode: ConnectableEntity, degrees: number, visitedUUIDs: string[], ): void { diff --git a/src/core/stageObject/StageObject.tsx b/src/core/stageObject/StageObject.tsx index 4951efc5..9ed71a9d 100644 --- a/src/core/stageObject/StageObject.tsx +++ b/src/core/stageObject/StageObject.tsx @@ -50,6 +50,12 @@ export abstract class ConnectableEntity extends Entity { * 用于联动旋转等算法 */ abstract geometryCenter: Vector; + + /** + * 当该实体被连线识别时,会改成false + */ + public unknown = true; + } /** diff --git a/src/core/stageObject/association/Edge.tsx b/src/core/stageObject/association/Edge.tsx index 8ab0d934..803d7792 100644 --- a/src/core/stageObject/association/Edge.tsx +++ b/src/core/stageObject/association/Edge.tsx @@ -7,7 +7,7 @@ import { Renderer } from "../../render/canvas2d/renderer"; import { Vector } from "../../dataStruct/Vector"; import { Circle } from "../../dataStruct/shape/Circle"; import { StageManager } from "../../stage/stageManager/StageManager"; -import { ConnectableAssociation } from "../StageObject"; +import { ConnectableAssociation, ConnectableEntity } from "../StageObject"; import { CollisionBox } from "../collisionBox/collisionBox"; import { v4 as uuidv4 } from "uuid"; @@ -41,17 +41,17 @@ export class Edge extends ConnectableAssociation { } } - get source(): TextNode { + get source(): ConnectableEntity { return this._source; } - set source(value: TextNode) { + set source(value: ConnectableEntity) { this._source = value; } - get target(): TextNode { + get target(): ConnectableEntity { return this._target; } - set target(value: TextNode) { + set target(value: ConnectableEntity) { this._target = value; } @@ -69,8 +69,8 @@ export class Edge extends ConnectableAssociation { } } - private _source: TextNode; - private _target: TextNode; + private _source: ConnectableEntity; + private _target: ConnectableEntity; constructor( { source, target, text, uuid }: Serialized.Edge, @@ -105,13 +105,13 @@ export class Edge extends ConnectableAssociation { */ get bodyLine(): Line { const edgeCenterLine = new Line( - this.source.rectangle.center, - this.target.rectangle.center, + this.source.collisionBox.getRectangle().center, + this.target.collisionBox.getRectangle().center, ); const startPoint = - this.source.rectangle.getLineIntersectionPoint(edgeCenterLine); + this.source.collisionBox.getRectangle().getLineIntersectionPoint(edgeCenterLine); const endPoint = - this.target.rectangle.getLineIntersectionPoint(edgeCenterLine); + this.target.collisionBox.getRectangle().getLineIntersectionPoint(edgeCenterLine); return new Line(startPoint, endPoint); } diff --git a/src/core/stageObject/entity/Section.tsx b/src/core/stageObject/entity/Section.tsx index 00bd9c74..2f34948d 100644 --- a/src/core/stageObject/entity/Section.tsx +++ b/src/core/stageObject/entity/Section.tsx @@ -26,16 +26,19 @@ export class Section extends ConnectableEntity { /** 是否是隐藏状态 */ isHidden: boolean; - constructor({ - uuid, - text = "", - location = [0, 0], - size = [0, 0], - color = [0, 0, 0, 0], - isHidden = false, - isCollapsed = false, - children = [], - }: Partial & { uuid: string }) { + constructor( + { + uuid, + text = "", + location = [0, 0], + size = [0, 0], + color = [0, 0, 0, 0], + isHidden = false, + isCollapsed = false, + children = [], + }: Partial & { uuid: string }, + public unknown = false, + ) { super(); this.uuid = uuid; this.collisionBox = new CollisionBox( diff --git a/src/pages/_app_menu.tsx b/src/pages/_app_menu.tsx index 41fe542a..dc583379 100644 --- a/src/pages/_app_menu.tsx +++ b/src/pages/_app_menu.tsx @@ -135,7 +135,6 @@ export default function AppMenu({ }; const onSaveNew = async () => { - // TODO: const path = await saveFileDialog({ title: "另存为", defaultPath: "新文件.json", // 提供一个默认的文件名