diff --git a/app/src/core/dataStruct/shape/CublicCatmullRomSpline.tsx b/app/src/core/dataStruct/shape/CubicCatmullRomSpline.tsx
similarity index 98%
rename from app/src/core/dataStruct/shape/CublicCatmullRomSpline.tsx
rename to app/src/core/dataStruct/shape/CubicCatmullRomSpline.tsx
index faad3182..c22f4857 100644
--- a/app/src/core/dataStruct/shape/CublicCatmullRomSpline.tsx
+++ b/app/src/core/dataStruct/shape/CubicCatmullRomSpline.tsx
@@ -7,7 +7,7 @@ import { Shape } from "./Shape";
/**
* CR曲线形状
*/
-export class CublicCatmullRomSpline extends Shape {
+export class CubicCatmullRomSpline extends Shape {
public controlPoints: Vector[];
public alpha: number;
public tension: number;
diff --git a/app/src/core/render/canvas2d/entityRenderer/CollisionBoxRenderer.tsx b/app/src/core/render/canvas2d/entityRenderer/CollisionBoxRenderer.tsx
index a9c83939..9deda26a 100644
--- a/app/src/core/render/canvas2d/entityRenderer/CollisionBoxRenderer.tsx
+++ b/app/src/core/render/canvas2d/entityRenderer/CollisionBoxRenderer.tsx
@@ -1,6 +1,6 @@
import { Color } from "../../../dataStruct/Color";
import { Circle } from "../../../dataStruct/shape/Circle";
-import { CublicCatmullRomSpline } from "../../../dataStruct/shape/CublicCatmullRomSpline";
+import { CubicCatmullRomSpline } from "../../../dataStruct/shape/CubicCatmullRomSpline";
import { SymmetryCurve } from "../../../dataStruct/shape/Curve";
import { Line } from "../../../dataStruct/shape/Line";
import { Rectangle } from "../../../dataStruct/shape/Rectangle";
@@ -50,8 +50,8 @@ export namespace CollisionBoxRenderer {
// const size = 15; // 箭头大小
// shape.end = shape.end.subtract(shape.endDirection.multiply(size / -2));
WorldRenderUtils.renderSymmetryCurve(shape, color, 10);
- } else if (shape instanceof CublicCatmullRomSpline) {
- WorldRenderUtils.renderCublicCatmullRomSpline(shape, color, 10);
+ } else if (shape instanceof CubicCatmullRomSpline) {
+ WorldRenderUtils.renderCubicCatmullRomSpline(shape, color, 10);
}
}
}
diff --git a/app/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx b/app/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx
index 8649c380..4776e4d9 100644
--- a/app/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx
+++ b/app/src/core/render/canvas2d/entityRenderer/edge/EdgeRenderer.tsx
@@ -3,7 +3,7 @@ import { Color } from "../../../../dataStruct/Color";
import { Vector } from "../../../../dataStruct/Vector";
import { Settings } from "../../../../service/Settings";
import { Camera } from "../../../../stage/Camera";
-import { CublicCatmullRomSplineEdge } from "../../../../stage/stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../../../../stage/stageObject/association/CubicCatmullRomSplineEdge";
import { LineEdge } from "../../../../stage/stageObject/association/LineEdge";
import { Section } from "../../../../stage/stageObject/entity/Section";
@@ -106,14 +106,14 @@ export namespace EdgeRenderer {
}
}
- export function renderCrEdge(edge: CublicCatmullRomSplineEdge) {
+ export function renderCrEdge(edge: CubicCatmullRomSplineEdge) {
if (edge.source.isHiddenBySectionCollapse && edge.target.isHiddenBySectionCollapse) {
return;
}
const crShape = edge.getShape();
const edgeColor = edge.color.a === 0 ? StageStyleManager.currentStyle.StageObjectBorder : edge.color;
// 画曲线
- WorldRenderUtils.renderCublicCatmullRomSpline(crShape, edgeColor, 2);
+ WorldRenderUtils.renderCubicCatmullRomSpline(crShape, edgeColor, 2);
if (edge.isSelected) {
CollisionBoxRenderer.render(edge.collisionBox, StageStyleManager.currentStyle.CollideBoxSelected);
}
diff --git a/app/src/core/render/canvas2d/renderer.tsx b/app/src/core/render/canvas2d/renderer.tsx
index c8ca84ee..133ab106 100644
--- a/app/src/core/render/canvas2d/renderer.tsx
+++ b/app/src/core/render/canvas2d/renderer.tsx
@@ -18,7 +18,7 @@ import { StageHistoryManager } from "../../stage/stageManager/StageHistoryManage
import { StageManager } from "../../stage/stageManager/StageManager";
import { StageObjectSelectCounter } from "../../stage/stageManager/concreteMethods/StageObjectSelectCounter";
import { StageObject } from "../../stage/stageObject/abstract/StageObject";
-import { CublicCatmullRomSplineEdge } from "../../stage/stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../../stage/stageObject/association/CubicCatmullRomSplineEdge";
import { LineEdge } from "../../stage/stageObject/association/LineEdge";
import { MultiTargetUndirectedEdge } from "../../stage/stageObject/association/MutiTargetUndirectedEdge";
import { CurveRenderer } from "./basicRenderer/curveRenderer";
@@ -635,7 +635,7 @@ export namespace Renderer {
if (association instanceof LineEdge) {
EdgeRenderer.renderLineEdge(association);
}
- if (association instanceof CublicCatmullRomSplineEdge) {
+ if (association instanceof CubicCatmullRomSplineEdge) {
EdgeRenderer.renderCrEdge(association);
}
renderedEdges++;
diff --git a/app/src/core/render/canvas2d/utilsRenderer/WorldRenderUtils.tsx b/app/src/core/render/canvas2d/utilsRenderer/WorldRenderUtils.tsx
index 2534c70b..422e7a64 100644
--- a/app/src/core/render/canvas2d/utilsRenderer/WorldRenderUtils.tsx
+++ b/app/src/core/render/canvas2d/utilsRenderer/WorldRenderUtils.tsx
@@ -1,5 +1,5 @@
import { Color } from "../../../dataStruct/Color";
-import { CublicCatmullRomSpline } from "../../../dataStruct/shape/CublicCatmullRomSpline";
+import { CubicCatmullRomSpline } from "../../../dataStruct/shape/CubicCatmullRomSpline";
import { CubicBezierCurve, SymmetryCurve } from "../../../dataStruct/shape/Curve";
import { Rectangle } from "../../../dataStruct/shape/Rectangle";
import { Vector } from "../../../dataStruct/Vector";
@@ -19,7 +19,7 @@ export namespace WorldRenderUtils {
* 绘制一条Catmull-Rom样条线
* @param curve
*/
- export function renderCublicCatmullRomSpline(spline: CublicCatmullRomSpline, color: Color, width: number): void {
+ export function renderCubicCatmullRomSpline(spline: CubicCatmullRomSpline, color: Color, width: number): void {
const points = spline.computePath().map(Renderer.transformWorld2View);
width *= Camera.currentScale;
const start = Renderer.transformWorld2View(spline.controlPoints[1]);
diff --git a/app/src/core/service/controlService/controller/ControllerClass.tsx b/app/src/core/service/controlService/controller/ControllerClass.tsx
index ae692648..e092182f 100644
--- a/app/src/core/service/controlService/controller/ControllerClass.tsx
+++ b/app/src/core/service/controlService/controller/ControllerClass.tsx
@@ -105,9 +105,10 @@ export class ControllerClass {
private _touchstart = (event: TouchEvent) => {
event.preventDefault();
- const touch = event.touches[event.touches.length - 1] as any as MouseEvent;
- // @ts-expect-error 必须给他来一个button属性
- touch.button = 0;
+ const touch = {
+ ...(event.touches[event.touches.length - 1] as unknown as MouseEvent),
+ button: 0, // 通过对象展开实现相对安全的属性合并
+ } as MouseEvent;
if (event.touches.length > 1) {
Stage.selectMachine.shutDown();
}
@@ -116,17 +117,19 @@ export class ControllerClass {
private _touchmove = (event: TouchEvent) => {
event.preventDefault();
- const touch = event.touches[event.touches.length - 1] as any as MouseEvent;
- // @ts-expect-error 必须给他来一个button属性
- touch.button = 0;
+ const touch = {
+ ...(event.touches[event.touches.length - 1] as unknown as MouseEvent),
+ button: 0, // 通过对象展开实现相对安全的属性合并
+ } as MouseEvent;
this.mousemove(touch);
};
private _touchend = (event: TouchEvent) => {
event.preventDefault();
- const touch = event.changedTouches[event.changedTouches.length - 1] as any as MouseEvent;
- // @ts-expect-error 必须给他来一个button属性
- touch.button = 0;
+ const touch = {
+ ...(event.touches[event.touches.length - 1] as unknown as MouseEvent),
+ button: 0, // 通过对象展开实现相对安全的属性合并
+ } as MouseEvent;
this._mouseup(touch);
};
diff --git a/app/src/core/service/controlService/controller/concrete/ControllerAssociationReshape.tsx b/app/src/core/service/controlService/controller/concrete/ControllerAssociationReshape.tsx
index 051794a9..d78dc99b 100644
--- a/app/src/core/service/controlService/controller/concrete/ControllerAssociationReshape.tsx
+++ b/app/src/core/service/controlService/controller/concrete/ControllerAssociationReshape.tsx
@@ -4,7 +4,7 @@ import { Renderer } from "../../../../render/canvas2d/renderer";
import { LeftMouseModeEnum, Stage } from "../../../../stage/Stage";
import { StageMultiTargetEdgeMove } from "../../../../stage/stageManager/concreteMethods/StageMultiTargetEdgeMove";
import { StageNodeConnector } from "../../../../stage/stageManager/concreteMethods/StageNodeConnector";
-import { StageNodeRotate } from "../../../../stage/stageManager/concreteMethods/stageNodeRotate";
+import { StageNodeRotate } from "../../../../stage/stageManager/concreteMethods/StageNodeRotate";
import { StageHistoryManager } from "../../../../stage/stageManager/StageHistoryManager";
import { StageManager } from "../../../../stage/stageManager/StageManager";
import { MultiTargetUndirectedEdge } from "../../../../stage/stageObject/association/MutiTargetUndirectedEdge";
diff --git a/app/src/core/service/controlService/controller/concrete/ControllerEntityClickSelectAndMove.tsx b/app/src/core/service/controlService/controller/concrete/ControllerEntityClickSelectAndMove.tsx
index 97b73b9e..63d02f80 100644
--- a/app/src/core/service/controlService/controller/concrete/ControllerEntityClickSelectAndMove.tsx
+++ b/app/src/core/service/controlService/controller/concrete/ControllerEntityClickSelectAndMove.tsx
@@ -50,6 +50,8 @@ class ControllerEntityClickSelectAndMoveClass extends ControllerClass {
} else if (Controller.pressingKeySet.has("shift")) {
// shift 按下,只选中节点
clickedStageObject.isSelected = true;
+ // 没有实体被选中则return
+ if (StageManager.getSelectedEntities().length === 0) return;
const rectangles = StageManager.getSelectedEntities().map((entity) => entity.collisionBox.getRectangle());
const boundingRectangle = Rectangle.getBoundingRectangle(rectangles);
Stage.effectMachine.addEffect(RectangleRenderEffect.fromShiftClickSelect(boundingRectangle));
diff --git a/app/src/core/service/controlService/controller/concrete/ControllerEntityCreate.tsx b/app/src/core/service/controlService/controller/concrete/ControllerEntityCreate.tsx
index e1d2cbb7..35c623b2 100644
--- a/app/src/core/service/controlService/controller/concrete/ControllerEntityCreate.tsx
+++ b/app/src/core/service/controlService/controller/concrete/ControllerEntityCreate.tsx
@@ -2,7 +2,7 @@ import { Vector } from "../../../../dataStruct/Vector";
import { Renderer } from "../../../../render/canvas2d/renderer";
import { LeftMouseModeEnum, Stage } from "../../../../stage/Stage";
import { SectionMethods } from "../../../../stage/stageManager/basicMethods/SectionMethods";
-import { StageNodeAdder } from "../../../../stage/stageManager/concreteMethods/stageNodeAdder";
+import { StageNodeAdder } from "../../../../stage/stageManager/concreteMethods/StageNodeAdder";
import { StageObjectSelectCounter } from "../../../../stage/stageManager/concreteMethods/StageObjectSelectCounter";
import { StageManager } from "../../../../stage/stageManager/StageManager";
import { Section } from "../../../../stage/stageObject/entity/Section";
diff --git a/app/src/core/service/controlService/controller/concrete/ControllerNodeConnection.tsx b/app/src/core/service/controlService/controller/concrete/ControllerNodeConnection.tsx
index 578aa53b..e4f5d3c7 100644
--- a/app/src/core/service/controlService/controller/concrete/ControllerNodeConnection.tsx
+++ b/app/src/core/service/controlService/controller/concrete/ControllerNodeConnection.tsx
@@ -14,7 +14,7 @@ import { ControllerClass } from "../ControllerClass";
import { addTextNodeByLocation } from "./utilsControl";
import { StageStyleManager } from "../../../feedbackService/stageStyle/StageStyleManager";
import { SectionMethods } from "../../../../stage/stageManager/basicMethods/SectionMethods";
-import { StageNodeAdder } from "../../../../stage/stageManager/concreteMethods/stageNodeAdder";
+import { StageNodeAdder } from "../../../../stage/stageManager/concreteMethods/StageNodeAdder";
import { Line } from "../../../../dataStruct/shape/Line";
import { Direction } from "../../../../../types/directions";
diff --git a/app/src/core/service/controlService/controller/concrete/utilsControl.tsx b/app/src/core/service/controlService/controller/concrete/utilsControl.tsx
index c8a255a7..d16afb78 100644
--- a/app/src/core/service/controlService/controller/concrete/utilsControl.tsx
+++ b/app/src/core/service/controlService/controller/concrete/utilsControl.tsx
@@ -7,7 +7,7 @@ import { InputElement } from "../../../../render/domElement/inputElement";
import { Camera } from "../../../../stage/Camera";
import { Stage } from "../../../../stage/Stage";
import { SectionMethods } from "../../../../stage/stageManager/basicMethods/SectionMethods";
-import { StageNodeAdder } from "../../../../stage/stageManager/concreteMethods/stageNodeAdder";
+import { StageNodeAdder } from "../../../../stage/stageManager/concreteMethods/StageNodeAdder";
import { StageObjectSelectCounter } from "../../../../stage/stageManager/concreteMethods/StageObjectSelectCounter";
import { StageHistoryManager } from "../../../../stage/stageManager/StageHistoryManager";
import { StageManager } from "../../../../stage/stageManager/StageManager";
@@ -126,6 +126,7 @@ export function editEdgeText(clickedLineEdge: Edge, selectAll = true) {
selectAll,
).then(() => {
// clickedLineEdge!.isEditing = false;
+ // 因为这里用的是不透明文本框,所以不需要停止节点上文字的渲染
Controller.isCameraLocked = false;
StageHistoryManager.recordStep();
});
@@ -160,6 +161,7 @@ export function editMultiTargetEdgeText(clickedEdge: MultiTargetUndirectedEdge,
selectAll,
).then(() => {
// clickedLineEdge!.isEditing = false;
+ // 因为这里用的是不透明文本框,所以不需要停止节点上文字的渲染
Controller.isCameraLocked = false;
StageHistoryManager.recordStep();
});
diff --git a/app/src/core/service/controlService/keyboardOnlyEngine/keyboardOnlyGraphEngine.tsx b/app/src/core/service/controlService/keyboardOnlyEngine/keyboardOnlyGraphEngine.tsx
index f4320378..3d72845a 100644
--- a/app/src/core/service/controlService/keyboardOnlyEngine/keyboardOnlyGraphEngine.tsx
+++ b/app/src/core/service/controlService/keyboardOnlyEngine/keyboardOnlyGraphEngine.tsx
@@ -2,7 +2,7 @@ import { Vector } from "../../../dataStruct/Vector";
import { EdgeRenderer } from "../../../render/canvas2d/entityRenderer/edge/EdgeRenderer";
// import { Camera } from "../../../stage/Camera";
import { Stage } from "../../../stage/Stage";
-import { StageNodeAdder } from "../../../stage/stageManager/concreteMethods/stageNodeAdder";
+import { StageNodeAdder } from "../../../stage/stageManager/concreteMethods/StageNodeAdder";
import { StageManager } from "../../../stage/stageManager/StageManager";
import { ConnectableEntity } from "../../../stage/stageObject/abstract/ConnectableEntity";
import { TextRiseEffect } from "../../feedbackService/effectEngine/concrete/TextRiseEffect";
diff --git a/app/src/core/service/controlService/secretKeysEngine/secretKeysEngine.tsx b/app/src/core/service/controlService/secretKeysEngine/secretKeysEngine.tsx
index eee6224d..5a58005f 100644
--- a/app/src/core/service/controlService/secretKeysEngine/secretKeysEngine.tsx
+++ b/app/src/core/service/controlService/secretKeysEngine/secretKeysEngine.tsx
@@ -13,7 +13,7 @@ import { StageManager } from "../../../stage/stageManager/StageManager";
import { SectionMethods } from "../../../stage/stageManager/basicMethods/SectionMethods";
import { LayoutManualAlignManager } from "../../../stage/stageManager/concreteMethods/layoutManager/layoutManualAlignManager";
import { ConnectableEntity } from "../../../stage/stageObject/abstract/ConnectableEntity";
-import { CublicCatmullRomSplineEdge } from "../../../stage/stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../../../stage/stageObject/association/CubicCatmullRomSplineEdge";
import { LineEdge } from "../../../stage/stageObject/association/LineEdge";
import { PortalNode } from "../../../stage/stageObject/entity/PortalNode";
import { Section } from "../../../stage/stageObject/entity/Section";
@@ -626,7 +626,7 @@ export class SecretKeysEngine {
isHidden: true,
func() {
const selectedCREdge = StageManager.getSelectedAssociations().filter(
- (edge) => edge instanceof CublicCatmullRomSplineEdge,
+ (edge) => edge instanceof CubicCatmullRomSplineEdge,
);
for (const edge of selectedCREdge) {
edge.addControlPoint();
diff --git a/app/src/core/service/dataFileService/fileLoader.tsx b/app/src/core/service/dataFileService/fileLoader.tsx
index 67d6b849..2f9f5fd5 100644
--- a/app/src/core/service/dataFileService/fileLoader.tsx
+++ b/app/src/core/service/dataFileService/fileLoader.tsx
@@ -15,7 +15,7 @@ import { PortalNode } from "../../stage/stageObject/entity/PortalNode";
import { PathString } from "../../../utils/pathString";
import { isWeb } from "../../../utils/platform";
import { Vector } from "../../dataStruct/Vector";
-import { CublicCatmullRomSplineEdge } from "../../stage/stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../../stage/stageObject/association/CubicCatmullRomSplineEdge";
import { MultiTargetUndirectedEdge } from "../../stage/stageObject/association/MutiTargetUndirectedEdge";
import { RecentFileManager } from "./RecentFileManager";
import { SvgNode } from "../../stage/stageObject/entity/SvgNode";
@@ -169,8 +169,8 @@ export namespace FileLoader {
let newAssociation = null;
if (Serialized.isLineEdge(edge)) {
newAssociation = new LineEdge(edge);
- } else if (Serialized.isCublicCatmullRomSplineEdge(edge)) {
- newAssociation = new CublicCatmullRomSplineEdge(edge);
+ } else if (Serialized.isCubicCatmullRomSplineEdge(edge)) {
+ newAssociation = new CubicCatmullRomSplineEdge(edge);
} else if (Serialized.isMultiTargetUndirectedEdge(edge)) {
newAssociation = new MultiTargetUndirectedEdge(edge);
}
diff --git a/app/src/core/service/dataGenerateService/autoComputeEngine/functions/nodeLogic.tsx b/app/src/core/service/dataGenerateService/autoComputeEngine/functions/nodeLogic.tsx
index 09306861..59b6356d 100644
--- a/app/src/core/service/dataGenerateService/autoComputeEngine/functions/nodeLogic.tsx
+++ b/app/src/core/service/dataGenerateService/autoComputeEngine/functions/nodeLogic.tsx
@@ -25,10 +25,10 @@ import { ConnectPoint } from "../../../../stage/stageObject/entity/ConnectPoint"
*/
export namespace NodeLogic {
export const delayStates: Map> = new Map();
- let step: number = 0;
+ /* eslint-disable prefer-const */
+ export let step: number = 0;
// step 是一个计数器,每当逻辑引擎实际执行一次时,step 就会加一
// TODO: 可以考虑把 step 放到逻辑引擎层面,甚至可以出一个节点获取当前步数,可以加一个每次只运行一步的快捷键
- // 现在的好处是只有在用延迟复制时才会有这个计数器,其他时候step不存在
/**
* 输入三个数字节点,并将所有的孩子节点更改为相应的颜色
* @param fatherNodes
@@ -686,13 +686,10 @@ export namespace NodeLogic {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_childNodes: ConnectableEntity[],
): string[] {
- step++;
if (fatherNodes.length < 4) {
// 多了一个逻辑节点本身,所以实际进来的节点比延迟复制需要的节点节点多一个
return ["Error: input node contains less than 3 nodes"];
}
- console.log("delayCopy", fatherNodes);
- console.log(delayStates);
if (
fatherNodes[0] instanceof TextNode &&
fatherNodes[1] instanceof TextNode &&
diff --git a/app/src/core/service/dataGenerateService/autoComputeEngine/mainTick.tsx b/app/src/core/service/dataGenerateService/autoComputeEngine/mainTick.tsx
index ded108bc..749a74da 100644
--- a/app/src/core/service/dataGenerateService/autoComputeEngine/mainTick.tsx
+++ b/app/src/core/service/dataGenerateService/autoComputeEngine/mainTick.tsx
@@ -201,6 +201,8 @@ export function autoComputeEngineTick(tickNumber: number) {
)) {
computeEdge(edge);
}
+ NodeLogic.step++;
+ // 逻辑引擎执行一步,计数器+1
}
export function isTextNodeLogic(node: TextNode): boolean {
diff --git a/app/src/core/stage/StageDumper.tsx b/app/src/core/stage/StageDumper.tsx
index 29b25cce..c136940c 100644
--- a/app/src/core/stage/StageDumper.tsx
+++ b/app/src/core/stage/StageDumper.tsx
@@ -3,7 +3,7 @@ import { SectionMethods } from "./stageManager/basicMethods/SectionMethods";
import { StageManager } from "./stageManager/StageManager";
import { Association } from "./stageObject/abstract/Association";
import { Entity } from "./stageObject/abstract/StageEntity";
-import { CublicCatmullRomSplineEdge } from "./stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "./stageObject/association/CubicCatmullRomSplineEdge";
import { LineEdge } from "./stageObject/association/LineEdge";
import { MultiTargetUndirectedEdge } from "./stageObject/association/MutiTargetUndirectedEdge";
import { ConnectPoint } from "./stageObject/entity/ConnectPoint";
@@ -49,13 +49,13 @@ export namespace StageDumper {
targetRectRate: [edge.targetRectangleRate.x, edge.targetRectangleRate.y],
};
}
- export function dumpCrEdge(edge: CublicCatmullRomSplineEdge): Serialized.CublicCatmullRomSplineEdge {
+ export function dumpCrEdge(edge: CubicCatmullRomSplineEdge): Serialized.CubicCatmullRomSplineEdge {
return {
source: edge.source.uuid,
target: edge.target.uuid,
text: edge.text,
uuid: edge.uuid,
- type: "core:cublic_catmull_rom_spline_edge",
+ type: "core:cubic_catmull_rom_spline_edge",
controlPoints: edge.getControlPoints().map((point) => [point.x, point.y]),
alpha: edge.alpha,
tension: edge.tension,
@@ -193,7 +193,7 @@ export namespace StageDumper {
export function dumpOneAssociation(association: Association): Serialized.CoreAssociation {
if (association instanceof LineEdge) {
return dumpEdge(association);
- } else if (association instanceof CublicCatmullRomSplineEdge) {
+ } else if (association instanceof CubicCatmullRomSplineEdge) {
return dumpCrEdge(association);
} else if (association instanceof MultiTargetUndirectedEdge) {
return dumpMTUEdge(association);
@@ -297,7 +297,7 @@ export namespace StageDumper {
if (entities.includes(edge.source) && entities.includes(edge.target)) {
result.push(dumpEdge(edge));
}
- } else if (edge instanceof CublicCatmullRomSplineEdge) {
+ } else if (edge instanceof CubicCatmullRomSplineEdge) {
if (entities.includes(edge.source) && entities.includes(edge.target)) {
result.push(dumpCrEdge(edge));
}
diff --git a/app/src/core/stage/stageManager/StageManager.tsx b/app/src/core/stage/stageManager/StageManager.tsx
index 49839f0c..6300bf23 100644
--- a/app/src/core/stage/stageManager/StageManager.tsx
+++ b/app/src/core/stage/stageManager/StageManager.tsx
@@ -16,7 +16,7 @@ import { Association } from "../stageObject/abstract/Association";
import { ConnectableEntity } from "../stageObject/abstract/ConnectableEntity";
import { Entity } from "../stageObject/abstract/StageEntity";
import { StageObject } from "../stageObject/abstract/StageObject";
-import { CublicCatmullRomSplineEdge } from "../stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../stageObject/association/CubicCatmullRomSplineEdge";
import { Edge } from "../stageObject/association/Edge";
import { LineEdge } from "../stageObject/association/LineEdge";
import { ConnectPoint } from "../stageObject/entity/ConnectPoint";
@@ -28,7 +28,7 @@ import { TextNode } from "../stageObject/entity/TextNode";
import { UrlNode } from "../stageObject/entity/UrlNode";
import { GraphMethods } from "./basicMethods/GraphMethods";
import { StageDeleteManager } from "./concreteMethods/StageDeleteManager";
-import { StageNodeAdder } from "./concreteMethods/stageNodeAdder";
+import { StageNodeAdder } from "./concreteMethods/StageNodeAdder";
import { StageNodeConnector } from "./concreteMethods/StageNodeConnector";
import { StageObjectSelectCounter } from "./concreteMethods/StageObjectSelectCounter";
import { StageSectionInOutManager } from "./concreteMethods/StageSectionInOutManager";
@@ -284,8 +284,8 @@ export namespace StageManager {
export function getLineEdges(): LineEdge[] {
return stageContent.associations.valuesToArray().filter((edge) => edge instanceof LineEdge);
}
- export function getCrEdges(): CublicCatmullRomSplineEdge[] {
- return stageContent.associations.valuesToArray().filter((edge) => edge instanceof CublicCatmullRomSplineEdge);
+ export function getCrEdges(): CubicCatmullRomSplineEdge[] {
+ return stageContent.associations.valuesToArray().filter((edge) => edge instanceof CubicCatmullRomSplineEdge);
}
/** 关于标签的相关操作 */
@@ -375,7 +375,7 @@ export namespace StageManager {
export function addLineEdge(edge: LineEdge) {
stageContent.associations.addValue(edge, edge.uuid);
}
- export function addCrEdge(edge: CublicCatmullRomSplineEdge) {
+ export function addCrEdge(edge: CubicCatmullRomSplineEdge) {
stageContent.associations.addValue(edge, edge.uuid);
}
export function addPenStroke(penStroke: PenStroke) {
@@ -1014,7 +1014,7 @@ export namespace StageManager {
export function addSelectedCREdgeControlPoint() {
const selectedCREdge = StageManager.getSelectedAssociations().filter(
- (edge) => edge instanceof CublicCatmullRomSplineEdge,
+ (edge) => edge instanceof CubicCatmullRomSplineEdge,
);
for (const edge of selectedCREdge) {
edge.addControlPoint();
@@ -1023,7 +1023,7 @@ export namespace StageManager {
export function addSelectedCREdgeTension() {
const selectedCREdge = StageManager.getSelectedAssociations().filter(
- (edge) => edge instanceof CublicCatmullRomSplineEdge,
+ (edge) => edge instanceof CubicCatmullRomSplineEdge,
);
for (const edge of selectedCREdge) {
edge.tension += 0.25;
@@ -1033,7 +1033,7 @@ export namespace StageManager {
export function reduceSelectedCREdgeTension() {
const selectedCREdge = StageManager.getSelectedAssociations().filter(
- (edge) => edge instanceof CublicCatmullRomSplineEdge,
+ (edge) => edge instanceof CubicCatmullRomSplineEdge,
);
for (const edge of selectedCREdge) {
edge.tension -= 0.25;
diff --git a/app/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx b/app/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx
index eaa8df3b..41af22c8 100644
--- a/app/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx
+++ b/app/src/core/stage/stageManager/concreteMethods/StageDeleteManager.tsx
@@ -23,28 +23,35 @@ import { NodeLogic } from "../../../service/dataGenerateService/autoComputeEngin
* 包含一切删除舞台上的元素的方法
*/
export namespace StageDeleteManager {
+ type DeleteHandler = (entity: T) => void;
+ type Constructor = { new (...args: any[]): T };
+ const _deleteHandlers = new Map, DeleteHandler>();
+ // 类型注册器,保证一个类型对应一个函数,绝对类型安全,同时可扩展
+ function registerHandler(constructor: Constructor, handler: DeleteHandler) {
+ _deleteHandlers.set(constructor, handler as DeleteHandler);
+ }
+
+ registerHandler(TextNode, deleteTextNode);
+ registerHandler(Section, deleteSection);
+ registerHandler(ConnectPoint, deleteConnectPoint);
+ registerHandler(ImageNode, deleteImageNode);
+ registerHandler(UrlNode, deleteUrlNode);
+ registerHandler(PortalNode, deletePortalNode);
+ registerHandler(PenStroke, deletePenStroke);
+ registerHandler(SvgNode, deleteSvgNode);
+
export function deleteEntities(deleteNodes: Entity[]) {
- // TODO: 这里应该优化,否则每次新加内容就得些一个类型判断
for (const entity of deleteNodes) {
- if (entity instanceof TextNode) {
- deleteTextNode(entity);
- } else if (entity instanceof Section) {
- deleteSection(entity);
- } else if (entity instanceof ConnectPoint) {
- deleteConnectPoint(entity);
- } else if (entity instanceof ImageNode) {
- deleteImageNode(entity);
- } else if (entity instanceof UrlNode) {
- deleteUrlNode(entity);
- } else if (entity instanceof PortalNode) {
- deletePortalNode(entity);
- } else if (entity instanceof PenStroke) {
- deletePenStroke(entity);
- } else if (entity instanceof SvgNode) {
- deleteSvgNode(entity);
- }
+ const handler = findDeleteHandler(entity);
+ handler?.(entity);
+ }
+ }
+
+ function findDeleteHandler(entity: Entity) {
+ for (const [ctor, handler] of _deleteHandlers) {
+ if (entity instanceof ctor) return handler;
}
- // StageManager.updateReferences();
+ console.warn(`No delete handler for ${entity.constructor.name}`);
}
function deleteSvgNode(entity: SvgNode) {
diff --git a/app/src/core/stage/stageManager/concreteMethods/StageNodeAdder.tsx b/app/src/core/stage/stageManager/concreteMethods/StageNodeAdder.tsx
new file mode 100644
index 00000000..500f59e8
--- /dev/null
+++ b/app/src/core/stage/stageManager/concreteMethods/StageNodeAdder.tsx
@@ -0,0 +1,398 @@
+import { v4 as uuidv4 } from "uuid";
+import { Direction } from "../../../../types/directions";
+import { MarkdownNode, parseMarkdownToJSON } from "../../../../utils/markdownParse";
+import { Color } from "../../../dataStruct/Color";
+import { MonoStack } from "../../../dataStruct/MonoStack";
+import { ProgressNumber } from "../../../dataStruct/ProgressNumber";
+import { Vector } from "../../../dataStruct/Vector";
+import { RectanglePushInEffect } from "../../../service/feedbackService/effectEngine/concrete/RectanglePushInEffect";
+import { Settings } from "../../../service/Settings";
+import { Stage } from "../../Stage";
+import { ConnectableEntity } from "../../stageObject/abstract/ConnectableEntity";
+import { ConnectPoint } from "../../stageObject/entity/ConnectPoint";
+import { Section } from "../../stageObject/entity/Section";
+import { TextNode } from "../../stageObject/entity/TextNode";
+import { SectionMethods } from "../basicMethods/SectionMethods";
+import { StageHistoryManager } from "../StageHistoryManager";
+import { StageManager } from "../StageManager";
+import { StageManagerUtils } from "./StageManagerUtils";
+import { GraphMethods } from "../basicMethods/GraphMethods";
+
+/**
+ * 包含增加节点的方法
+ * 有可能是用鼠标增加,涉及自动命名器
+ * 也有可能是用键盘增加,涉及快捷键和自动寻找空地
+ */
+export namespace StageNodeAdder {
+ /**
+ * 通过点击位置增加节点
+ * @param clickWorldLocation
+ * @returns
+ */
+ export async function addTextNodeByClick(
+ clickWorldLocation: Vector,
+ addToSections: Section[],
+ selectCurrent = false,
+ ): Promise {
+ const newUUID = uuidv4();
+ const node = new TextNode({
+ uuid: newUUID,
+ text: await getAutoName(),
+ details: "",
+ location: [clickWorldLocation.x, clickWorldLocation.y],
+ size: [100, 100],
+ });
+ node.color = await getAutoColor();
+ // 将node本身向左上角移动,使其居中
+ node.moveTo(node.rectangle.location.subtract(node.rectangle.size.divide(2)));
+ StageManager.addTextNode(node);
+
+ for (const section of addToSections) {
+ section.children.push(node);
+ section.childrenUUIDs.push(node.uuid); // 修复
+ section.adjustLocationAndSize();
+ Stage.effectMachine.addEffect(
+ new RectanglePushInEffect(node.rectangle.clone(), section.rectangle.clone(), new ProgressNumber(0, 100)),
+ );
+ }
+ // 处理选中问题
+ if (selectCurrent) {
+ for (const otherNode of StageManager.getTextNodes()) {
+ if (otherNode.isSelected) {
+ otherNode.isSelected = false;
+ }
+ }
+ node.isSelected = true;
+ }
+
+ StageHistoryManager.recordStep();
+ return newUUID;
+ }
+
+ /**
+ * 在当前已经选中的某个节点的情况下,增加节点
+ * 增加在某个选中的节点的上方,下方,左方,右方等位置
+ * ——快深频
+ * @param selectCurrent
+ * @returns 返回的是创建节点的uuid,如果当前没有选中节点,则返回空字符串
+ */
+ export async function addTextNodeFromCurrentSelectedNode(
+ direction: Direction,
+ addToSections: Section[],
+ selectCurrent = false,
+ ): Promise {
+ // 先检查当前是否有选中的唯一实体
+ const selectedEntities = StageManager.getSelectedEntities().filter((entity) => entity instanceof ConnectableEntity);
+ if (selectedEntities.length !== 1) {
+ // 未选中或选中多个
+ return "";
+ }
+ /**
+ * 当前选择的实体
+ */
+ const selectedEntity = selectedEntities[0];
+ const entityRectangle = selectedEntity.collisionBox.getRectangle();
+ let createLocation = new Vector(0, 0);
+ const distanceLength = 100;
+ if (direction === Direction.Up) {
+ createLocation = entityRectangle.topCenter.add(new Vector(0, -distanceLength));
+ } else if (direction === Direction.Down) {
+ createLocation = entityRectangle.bottomCenter.add(new Vector(0, distanceLength));
+ } else if (direction === Direction.Left) {
+ createLocation = entityRectangle.leftCenter.add(new Vector(-distanceLength, 0));
+ } else if (direction === Direction.Right) {
+ createLocation = entityRectangle.rightCenter.add(new Vector(distanceLength, 0));
+ }
+ addToSections = SectionMethods.getFatherSections(selectedEntity);
+ const uuid = await addTextNodeByClick(createLocation, addToSections, selectCurrent);
+ const newNode = StageManager.getTextNodeByUUID(uuid);
+ if (!newNode) {
+ throw new Error("Failed to add node");
+ }
+ // 如果是通过上下创建的节点,则需要左对齐
+ if (direction === Direction.Up || direction === Direction.Down) {
+ const distance = newNode.rectangle.left - entityRectangle.left;
+ newNode.moveTo(newNode.rectangle.location.add(new Vector(-distance, 0)));
+ }
+ if (direction === Direction.Left) {
+ // 顶对齐
+ const distance = newNode.rectangle.top - entityRectangle.top;
+ newNode.moveTo(newNode.rectangle.location.add(new Vector(0, -distance)));
+ }
+ if (direction === Direction.Right) {
+ // 顶对齐,+ 自己对齐到目标的右侧
+ const targetLocation = entityRectangle.rightTop;
+ newNode.moveTo(targetLocation);
+ }
+ if (direction === Direction.Up) {
+ const targetLocation = entityRectangle.leftTop.add(new Vector(0, newNode.collisionBox.getRectangle().height));
+ newNode.moveTo(targetLocation);
+ }
+ if (direction === Direction.Down) {
+ const targetLocation = entityRectangle.leftBottom;
+ newNode.moveTo(targetLocation);
+ }
+ StageHistoryManager.recordStep();
+ return uuid;
+ }
+
+ async function getAutoName(): Promise {
+ let template = await Settings.get("autoNamerTemplate");
+ template = StageManagerUtils.replaceAutoNameTemplate(template, StageManager.getTextNodes()[0]);
+ return template;
+ }
+
+ async function getAutoColor(): Promise {
+ const isEnable = await Settings.get("autoFillNodeColorEnable");
+ if (isEnable) {
+ const colorData = await Settings.get("autoFillNodeColor");
+ return new Color(...colorData);
+ } else {
+ return Color.Transparent;
+ }
+ }
+
+ export function addConnectPoint(clickWorldLocation: Vector, addToSections: Section[]): string {
+ const newUUID = uuidv4();
+ const connectPoint = new ConnectPoint({
+ uuid: newUUID,
+ location: [clickWorldLocation.x, clickWorldLocation.y],
+ });
+ StageManager.addConnectPoint(connectPoint);
+ for (const section of addToSections) {
+ section.children.push(connectPoint);
+ section.childrenUUIDs.push(connectPoint.uuid);
+ section.adjustLocationAndSize();
+ Stage.effectMachine.addEffect(
+ new RectanglePushInEffect(
+ connectPoint.collisionBox.getRectangle(),
+ section.rectangle.clone(),
+ new ProgressNumber(0, 100),
+ ),
+ );
+ }
+ StageHistoryManager.recordStep();
+ return newUUID;
+ }
+ /**
+ * 通过纯文本生成网状结构
+ *
+ * @param text 网状结构的格式文本
+ * @param diffLocation
+ */
+ export function addNodeGraphByText(text: string, diffLocation: Vector = Vector.getZero()) {
+ const lines = text.split("\n");
+
+ if (lines.length === 0) {
+ return;
+ }
+
+ const randomRadius = 40 * lines.length;
+
+ const nodeDict = new Map();
+
+ const createNodeByName = (name: string) => {
+ const newUUID = uuidv4();
+ const node = new TextNode({
+ uuid: newUUID,
+ text: name,
+ details: "",
+ location: [diffLocation.x + randomRadius * Math.random(), diffLocation.y + randomRadius * Math.random()],
+ size: [100, 100],
+ });
+ StageManager.addTextNode(node);
+ nodeDict.set(name, node);
+ return node;
+ };
+
+ for (const line of lines) {
+ if (line.trim() === "") {
+ continue;
+ }
+ if (line.includes("-->") || (line.includes("-") && line.includes("->"))) {
+ // 这一行是一个关系行
+ if (line.includes("-->")) {
+ // 连线上无文字
+ // 解析
+ const names = line.split("-->");
+ if (names.length !== 2) {
+ throw new Error(`解析时出现错误: "${line}",应该只有两个名称`);
+ }
+ const startName = names[0].trim();
+ const endName = names[1].trim();
+ if (startName === "" || endName === "") {
+ throw new Error(`解析时出现错误: "${line}",名称不能为空`);
+ }
+ let startNode = nodeDict.get(startName);
+ let endNode = nodeDict.get(endName);
+ if (!startNode) {
+ startNode = createNodeByName(startName);
+ }
+ if (!endNode) {
+ endNode = createNodeByName(endName);
+ }
+ StageManager.connectEntity(startNode, endNode);
+ } else {
+ // 连线上有文字
+ // 解析
+ // A -xx-> B
+ const names = line.split("->");
+ if (names.length !== 2) {
+ throw new Error(`解析时出现错误: "${line}",应该只有两个名称`);
+ }
+ const leftContent = names[0].trim();
+ const endName = names[1].trim();
+ if (leftContent === "" || endName === "") {
+ throw new Error(`解析时出现错误: "${line}",名称不能为空`);
+ }
+ let endNode = nodeDict.get(endName);
+ if (!endNode) {
+ // 没有endNode,临时创建一下
+ endNode = createNodeByName(endName);
+ }
+ const leftContentList = leftContent.split("-");
+ if (leftContentList.length !== 2) {
+ throw new Error(`解析时出现错误: "${line}",左侧内容应该只有两个名称`);
+ }
+ const startName = leftContentList[0].trim();
+ const edgeText = leftContentList[1].trim();
+ if (startName === "" || edgeText === "") {
+ throw new Error(`解析时出现错误: "${line}",名称不能为空`);
+ }
+ let startNode = nodeDict.get(startName);
+ if (!startNode) {
+ // 临时创建一下
+ startNode = createNodeByName(startName);
+ }
+ StageManager.connectEntity(startNode, endNode);
+ // 在线上填写文字
+ const edge = GraphMethods.getEdgeFromTwoEntity(startNode, endNode);
+ if (edge === null) {
+ throw new Error(`解析时出现错误: "${line}",找不到对应的连线`);
+ }
+ edge.rename(edgeText);
+ }
+ } else {
+ // 这一行是一个节点行
+ // 获取节点名称,创建节点
+ const nodeName = line.trim();
+ createNodeByName(nodeName);
+ }
+ }
+ }
+ /**
+ * 通过带有缩进格式的文本来增加节点
+ */
+ export function addNodeTreeByText(text: string, indention: number, diffLocation: Vector = Vector.getZero()) {
+ // 将本文转换成字符串数组,按换行符分割
+ const lines = text.split("\n");
+
+ const rootUUID = uuidv4();
+
+ // 准备好栈和根节点
+ const rootNode = new TextNode({
+ uuid: rootUUID,
+ text: "root",
+ details: "",
+ location: [diffLocation.x, diffLocation.y],
+ size: [100, 100],
+ });
+ const nodeStack = new MonoStack();
+ nodeStack.push(rootNode, -1);
+ StageManager.addTextNode(rootNode);
+ // 遍历每一行
+ for (let yIndex = 0; yIndex < lines.length; yIndex++) {
+ const line = lines[yIndex];
+ // 跳过空行
+ if (line.trim() === "") {
+ continue;
+ }
+ // 解析缩进格式
+ const indent = getIndentLevel(line, indention);
+ // 解析文本内容
+ const textContent = line.trim();
+
+ const newUUID = uuidv4();
+ const node = new TextNode({
+ uuid: newUUID,
+ text: textContent,
+ details: "",
+ location: [indent * 50 + diffLocation.x, yIndex * 100 + diffLocation.y],
+ size: [100, 100],
+ });
+ StageManager.addTextNode(node);
+
+ // 检查栈
+ // 保持一个严格单调栈
+ if (nodeStack.peek()) {
+ nodeStack.push(node, indent);
+ const fatherNode = nodeStack.unsafeGet(nodeStack.length - 2);
+ StageManager.connectEntity(fatherNode, node);
+ }
+ }
+ }
+
+ /***
+ * 'a' -> 0
+ * ' a' -> 1
+ * '\t\ta' -> 2
+ */
+ function getIndentLevel(line: string, indention: number): number {
+ let indent = 0;
+ for (let i = 0; i < line.length; i++) {
+ if (line[i] === " ") {
+ indent++;
+ } else if (line[i] === "\t") {
+ indent += indention;
+ } else {
+ break;
+ }
+ }
+ return Math.floor(indent / indention);
+ }
+
+ export function addNodeByMarkdown(markdownText: string, diffLocation: Vector = Vector.getZero()) {
+ const markdownJson = parseMarkdownToJSON(markdownText);
+ // 遍历markdownJson
+ const dfsMarkdownNode = (markdownNode: MarkdownNode, deepLevel: number) => {
+ // visit
+ visitFunction(markdownNode, deepLevel);
+ // visited
+ for (const child of markdownNode.children) {
+ dfsMarkdownNode(child, deepLevel + 1);
+ }
+ };
+ const monoStack = new MonoStack();
+ monoStack.push(
+ new TextNode({
+ uuid: uuidv4(),
+ text: "root",
+ details: "",
+ location: [diffLocation.x, diffLocation.y],
+ size: [100, 100],
+ }),
+ -1,
+ );
+
+ let visitedCount = 0;
+
+ const visitFunction = (markdownNode: MarkdownNode, deepLevel: number) => {
+ visitedCount++;
+ const newUUID = uuidv4();
+ const node = new TextNode({
+ uuid: newUUID,
+ text: markdownNode.title,
+ details: markdownNode.content,
+ location: [diffLocation.x + deepLevel * 50, diffLocation.y + visitedCount * 100],
+ size: [100, 100],
+ });
+ StageManager.addTextNode(node);
+ monoStack.push(node, deepLevel);
+ // 连接父节点
+ const fatherNode = monoStack.unsafeGet(monoStack.length - 2);
+ StageManager.connectEntity(fatherNode, node);
+ };
+
+ dfsMarkdownNode(markdownJson[0], 0);
+ }
+}
diff --git a/app/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx b/app/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx
index 6fc8af07..9410fe6d 100644
--- a/app/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx
+++ b/app/src/core/stage/stageManager/concreteMethods/StageNodeConnector.tsx
@@ -1,6 +1,6 @@
import { v4 as uuidv4 } from "uuid";
import { ConnectableEntity } from "../../stageObject/abstract/ConnectableEntity";
-import { CublicCatmullRomSplineEdge } from "../../stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../../stageObject/association/CubicCatmullRomSplineEdge";
import { LineEdge } from "../../stageObject/association/LineEdge";
import { ConnectPoint } from "../../stageObject/entity/ConnectPoint";
import { GraphMethods } from "../basicMethods/GraphMethods";
@@ -68,7 +68,7 @@ export namespace StageNodeConnector {
if (!isConnectable(fromNode, toNode)) {
return;
}
- const newEdge = CublicCatmullRomSplineEdge.fromTwoEntity(fromNode, toNode);
+ const newEdge = CubicCatmullRomSplineEdge.fromTwoEntity(fromNode, toNode);
StageManager.addCrEdge(newEdge);
StageManager.updateReferences();
}
diff --git a/app/src/core/stage/stageManager/concreteMethods/StageNodeRotate.tsx b/app/src/core/stage/stageManager/concreteMethods/StageNodeRotate.tsx
new file mode 100644
index 00000000..5e670474
--- /dev/null
+++ b/app/src/core/stage/stageManager/concreteMethods/StageNodeRotate.tsx
@@ -0,0 +1,117 @@
+import { Color } from "../../../dataStruct/Color";
+import { ProgressNumber } from "../../../dataStruct/ProgressNumber";
+import { Vector } from "../../../dataStruct/Vector";
+import { LineEffect } from "../../../service/feedbackService/effectEngine/concrete/LineEffect";
+import { Stage } from "../../Stage";
+import { ConnectableEntity } from "../../stageObject/abstract/ConnectableEntity";
+import { GraphMethods } from "../basicMethods/GraphMethods";
+import { StageManager } from "../StageManager";
+import { StageEntityMoveManager } from "./StageEntityMoveManager";
+
+/**
+ * 所有和旋转相关的操作
+ */
+export namespace StageNodeRotate {
+ /**
+ * 通过拖拽边的方式来旋转节点
+ * 会查找所有选中的边,但只能旋转一个边
+ * @param lastMoveLocation
+ * @param diffLocation
+ */
+ export function moveEdges(lastMoveLocation: Vector, diffLocation: Vector) {
+ for (const edge of StageManager.getLineEdges()) {
+ if (edge.isSelected) {
+ const startMouseDragLocation = lastMoveLocation.clone();
+ const endMouseDragLocation = startMouseDragLocation.add(diffLocation);
+ const vectorStart = startMouseDragLocation.subtract(edge.source.geometryCenter);
+ const vectorEnd = endMouseDragLocation.subtract(edge.source.geometryCenter);
+ let degrees = vectorStart.angleToSigned(vectorEnd);
+ // degrees一直是正数
+ if (Number.isNaN(degrees)) {
+ degrees = 0;
+ }
+ const sourceEntity = StageManager.getConnectableEntityByUUID(edge.source.uuid);
+ const targetEntity = StageManager.getConnectableEntityByUUID(edge.target.uuid);
+
+ if (sourceEntity && targetEntity) {
+ rotateNodeDfs(
+ StageManager.getConnectableEntityByUUID(edge.source.uuid)!,
+ StageManager.getConnectableEntityByUUID(edge.target.uuid)!,
+ degrees,
+ [edge.source.uuid],
+ );
+ } else {
+ console.error("source or target entity not found");
+ }
+ break; // 只旋转一个边
+ }
+ }
+ }
+
+ /**
+ *
+ * @param rotateCenterNode 递归开始的节点
+ * @param currentNode 当前递归遍历到的节点
+ * @param degrees 旋转角度
+ * @param visitedUUIDs 已经访问过的节点的uuid列表,用于避免死循环
+ */
+ export function rotateNodeDfs(
+ rotateCenterNode: ConnectableEntity,
+ currentNode: ConnectableEntity,
+ degrees: number,
+ visitedUUIDs: string[],
+ ): void {
+ const rotateCenterLocation = rotateCenterNode.geometryCenter;
+ // 先旋转自己
+
+ const centerToChildVector = currentNode.geometryCenter.subtract(rotateCenterLocation);
+
+ const centerToChildVectorRotated = centerToChildVector.rotateDegrees(degrees);
+
+ StageEntityMoveManager.moveEntityUtils(currentNode, centerToChildVectorRotated.subtract(centerToChildVector));
+ // 再旋转子节点
+ for (const child of GraphMethods.nodeChildrenArray(currentNode)) {
+ if (visitedUUIDs.includes(child.uuid)) {
+ continue;
+ }
+ visitedUUIDs.push(child.uuid);
+ const childNode = StageManager.getConnectableEntityByUUID(child.uuid);
+ if (!childNode) {
+ console.error("child node not found");
+ continue;
+ }
+ const midPoint = Vector.fromTwoPointsCenter(currentNode.geometryCenter, childNode.geometryCenter);
+
+ Stage.effectMachine.addEffect(
+ new LineEffect(
+ new ProgressNumber(0, 20),
+ currentNode.geometryCenter,
+ midPoint,
+ new Color(255, 255, 255, 0),
+ new Color(255, 255, 255, 0.5),
+ Math.abs(degrees),
+ ),
+ );
+ Stage.effectMachine.addEffect(
+ new LineEffect(
+ new ProgressNumber(0, 20),
+ midPoint,
+ childNode.geometryCenter,
+ new Color(255, 255, 255, 0.5),
+ new Color(255, 255, 255, 0),
+ Math.abs(degrees),
+ ),
+ );
+
+ rotateNodeDfs(
+ rotateCenterNode,
+ // 2024年10月6日:发现打开文件后,旋转节点无法带动子树,只能传递一层。
+ // child,
+ childNode,
+ degrees,
+ // visitedUUIDs.concat(currentNode.uuid),
+ visitedUUIDs,
+ );
+ }
+ }
+}
diff --git a/app/src/core/stage/stageManager/concreteMethods/StageObjectSelectCounter.tsx b/app/src/core/stage/stageManager/concreteMethods/StageObjectSelectCounter.tsx
index b55df824..111ec2be 100644
--- a/app/src/core/stage/stageManager/concreteMethods/StageObjectSelectCounter.tsx
+++ b/app/src/core/stage/stageManager/concreteMethods/StageObjectSelectCounter.tsx
@@ -1,6 +1,6 @@
import { Association } from "../../stageObject/abstract/Association";
import { Entity } from "../../stageObject/abstract/StageEntity";
-import { CublicCatmullRomSplineEdge } from "../../stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../../stageObject/association/CubicCatmullRomSplineEdge";
import { Edge } from "../../stageObject/association/Edge";
import { MultiTargetUndirectedEdge } from "../../stageObject/association/MutiTargetUndirectedEdge";
import { ImageNode } from "../../stageObject/entity/ImageNode";
@@ -72,7 +72,7 @@ export namespace StageObjectSelectCounter {
}
if (stageObject instanceof Edge) {
selectedEdgeCount++;
- if (stageObject instanceof CublicCatmullRomSplineEdge) {
+ if (stageObject instanceof CubicCatmullRomSplineEdge) {
selectedCREdgeCount++;
}
}
diff --git a/app/src/core/stage/stageManager/concreteMethods/StageSerializedAdder.tsx b/app/src/core/stage/stageManager/concreteMethods/StageSerializedAdder.tsx
index 9d41deac..58c6c44a 100644
--- a/app/src/core/stage/stageManager/concreteMethods/StageSerializedAdder.tsx
+++ b/app/src/core/stage/stageManager/concreteMethods/StageSerializedAdder.tsx
@@ -10,7 +10,7 @@ import { PortalNode } from "../../stageObject/entity/PortalNode";
import { PenStroke } from "../../stageObject/entity/PenStroke";
import { UrlNode } from "../../stageObject/entity/UrlNode";
import { Entity } from "../../stageObject/abstract/StageEntity";
-import { CublicCatmullRomSplineEdge } from "../../stageObject/association/CublicCatmullRomSplineEdge";
+import { CubicCatmullRomSplineEdge } from "../../stageObject/association/CubicCatmullRomSplineEdge";
import { ImageNode } from "../../stageObject/entity/ImageNode";
/**
* 直接向舞台中添加序列化数据
@@ -50,8 +50,8 @@ export namespace StageSerializedAdder {
for (const edge of updatedSerializedData.associations) {
if (Serialized.isLineEdge(edge)) {
StageManager.addLineEdge(new LineEdge(edge));
- } else if (Serialized.isCublicCatmullRomSplineEdge(edge)) {
- StageManager.addCrEdge(new CublicCatmullRomSplineEdge(edge));
+ } else if (Serialized.isCubicCatmullRomSplineEdge(edge)) {
+ StageManager.addCrEdge(new CubicCatmullRomSplineEdge(edge));
}
}
StageManager.updateReferences();
diff --git a/app/src/core/stage/stageObject/association/CublicCatmullRomSplineEdge.tsx b/app/src/core/stage/stageObject/association/CubicCatmullRomSplineEdge.tsx
similarity index 85%
rename from app/src/core/stage/stageObject/association/CublicCatmullRomSplineEdge.tsx
rename to app/src/core/stage/stageObject/association/CubicCatmullRomSplineEdge.tsx
index b4bdee23..9a78d887 100644
--- a/app/src/core/stage/stageObject/association/CublicCatmullRomSplineEdge.tsx
+++ b/app/src/core/stage/stageObject/association/CubicCatmullRomSplineEdge.tsx
@@ -1,6 +1,6 @@
import { v4 as uuidv4 } from "uuid";
import { Serialized } from "../../../../types/node";
-import { CublicCatmullRomSpline } from "../../../dataStruct/shape/CublicCatmullRomSpline";
+import { CubicCatmullRomSpline } from "../../../dataStruct/shape/CubicCatmullRomSpline";
import { Vector } from "../../../dataStruct/Vector";
import { ConnectableEntity } from "../abstract/ConnectableEntity";
import { CollisionBox } from "../collisionBox/collisionBox";
@@ -17,7 +17,7 @@ import { Color } from "../../../dataStruct/Color";
* alpha 不用自己修改了,这个是0.5固定值了,只会微微影响形状
* tension 控制曲线的弯曲程度,0是折线。
*/
-export class CublicCatmullRomSplineEdge extends Edge {
+export class CubicCatmullRomSplineEdge extends Edge {
public uuid: string;
public text: string;
protected _source: ConnectableEntity;
@@ -49,18 +49,18 @@ export class CublicCatmullRomSplineEdge extends Edge {
return this._collisionBox;
}
- static fromTwoEntity(source: ConnectableEntity, target: ConnectableEntity): CublicCatmullRomSplineEdge {
+ static fromTwoEntity(source: ConnectableEntity, target: ConnectableEntity): CubicCatmullRomSplineEdge {
// 处理控制点,控制点必须有四个,1 2 3 4,12可重叠,34可重叠
const startLocation = source.geometryCenter.clone();
const endLocation = target.geometryCenter.clone();
const line = Edge.getCenterLine(source, target);
- const result = new CublicCatmullRomSplineEdge({
+ const result = new CubicCatmullRomSplineEdge({
source: source.uuid,
target: target.uuid,
text: "",
uuid: uuidv4(),
- type: "core:cublic_catmull_rom_spline_edge",
+ type: "core:cubic_catmull_rom_spline_edge",
alpha: 0.5,
tension: 0,
color: [0, 0, 0, 0],
@@ -88,7 +88,7 @@ export class CublicCatmullRomSplineEdge extends Edge {
controlPoints,
sourceRectRate,
targetRectRate,
- }: Serialized.CublicCatmullRomSplineEdge /** true表示解析状态,false表示解析完毕 */,
+ }: Serialized.CubicCatmullRomSplineEdge /** true表示解析状态,false表示解析完毕 */,
public unknown = false,
) {
super();
@@ -104,12 +104,12 @@ export class CublicCatmullRomSplineEdge extends Edge {
this.controlPoints = controlPoints.map((item) => new Vector(item[0], item[1]));
this.setSourceRectangleRate(new Vector(...sourceRectRate));
this.setTargetRectangleRate(new Vector(...targetRectRate));
- this._collisionBox = new CollisionBox([new CublicCatmullRomSpline(this.controlPoints, this.alpha, this.tension)]);
+ this._collisionBox = new CollisionBox([new CubicCatmullRomSpline(this.controlPoints, this.alpha, this.tension)]);
}
- public getShape(): CublicCatmullRomSpline {
+ public getShape(): CubicCatmullRomSpline {
// 重新计算形状
- const crShape = this._collisionBox.shapeList[0] as CublicCatmullRomSpline;
+ const crShape = this._collisionBox.shapeList[0] as CubicCatmullRomSpline;
this.autoUpdateControlPoints(); // ?
return crShape;
}
@@ -135,14 +135,14 @@ export class CublicCatmullRomSplineEdge extends Edge {
this.controlPoints = [startLocation, line.start].concat(middleControlPoints).concat([line.end, endLocation]);
}
// 重新生成新的形状
- this._collisionBox.shapeList = [new CublicCatmullRomSpline(this.controlPoints, this.alpha, this.tension)];
+ this._collisionBox.shapeList = [new CubicCatmullRomSpline(this.controlPoints, this.alpha, this.tension)];
}
/**
* 获取箭头的位置和方向
*/
getArrowHead(): { location: Vector; direction: Vector } {
- const crShape = this._collisionBox.shapeList[0] as CublicCatmullRomSpline;
+ const crShape = this._collisionBox.shapeList[0] as CubicCatmullRomSpline;
const location = crShape.controlPoints[crShape.controlPoints.length - 2].clone();
const lines = crShape.computeFunction();
const funcs = lines[lines.length - 1];
diff --git a/app/src/types/node.tsx b/app/src/types/node.tsx
index 229a669d..bba5c962 100644
--- a/app/src/types/node.tsx
+++ b/app/src/types/node.tsx
@@ -159,11 +159,11 @@ export namespace Serialized {
export function isLineEdge(obj: StageObject): obj is LineEdge {
return obj.type === "core:line_edge";
}
- export function isCublicCatmullRomSplineEdge(obj: StageObject): obj is CublicCatmullRomSplineEdge {
- return obj.type === "core:cublic_catmull_rom_spline_edge";
+ export function isCubicCatmullRomSplineEdge(obj: StageObject): obj is CubicCatmullRomSplineEdge {
+ return obj.type === "core:cubic_catmull_rom_spline_edge";
}
- export type CublicCatmullRomSplineEdge = Edge & {
- type: "core:cublic_catmull_rom_spline_edge";
+ export type CubicCatmullRomSplineEdge = Edge & {
+ type: "core:cubic_catmull_rom_spline_edge";
text: string;
controlPoints: Vector[];
alpha: number;
@@ -174,7 +174,7 @@ export namespace Serialized {
export function isCoreEntity(obj: StageObject): obj is CoreEntity {
return obj.type.startsWith("core:");
}
- export type CoreAssociation = LineEdge | CublicCatmullRomSplineEdge | MultiTargetUndirectedEdge;
+ export type CoreAssociation = LineEdge | CubicCatmullRomSplineEdge | MultiTargetUndirectedEdge;
export type File = {
version: typeof StageDumper.latestVersion;