From f96207020962a07d8ee7d672fe902e8e65972548 Mon Sep 17 00:00:00 2001
From: joylink_fanyuhong <18706759286@163.com>
Date: Fri, 13 Sep 2024 16:57:58 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AE=AD=E5=A4=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/draw-app/DrawProperties.vue | 1 +
.../draw-app/properties/CanvasProperty.vue | 80 +++++++
src/drawApp/drawApp.ts | 40 ++--
src/drawApp/graphics/ArrowInteraction.ts | 48 +++++
src/graphics/arrow/Arrow.ts | 92 ++++++++
src/graphics/arrow/ArrowDrawAssistant.ts | 204 ++++++++++++++++++
src/layouts/DrawLayout.vue | 3 +-
src/stores/draw-store.ts | 7 +-
8 files changed, 455 insertions(+), 20 deletions(-)
create mode 100644 src/components/draw-app/properties/CanvasProperty.vue
create mode 100644 src/drawApp/graphics/ArrowInteraction.ts
create mode 100644 src/graphics/arrow/Arrow.ts
create mode 100644 src/graphics/arrow/ArrowDrawAssistant.ts
diff --git a/src/components/draw-app/DrawProperties.vue b/src/components/draw-app/DrawProperties.vue
index 7230a41..349ff48 100644
--- a/src/components/draw-app/DrawProperties.vue
+++ b/src/components/draw-app/DrawProperties.vue
@@ -33,6 +33,7 @@
diff --git a/src/components/draw-app/properties/CanvasProperty.vue b/src/components/draw-app/properties/CanvasProperty.vue
new file mode 100644
index 0000000..839ad02
--- /dev/null
+++ b/src/components/draw-app/properties/CanvasProperty.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+ {
+ canvas.backgroundColor = val;
+ onUpdate();
+ }
+ "
+ />
+
+
+
+
+
+
+
+
diff --git a/src/drawApp/drawApp.ts b/src/drawApp/drawApp.ts
index 8d74b20..065f0ce 100644
--- a/src/drawApp/drawApp.ts
+++ b/src/drawApp/drawApp.ts
@@ -13,6 +13,9 @@ import { useDrawStore } from 'src/stores/draw-store';
import { iscsGraphicData } from 'src/protos/iscs_graphic_data';
import { toStorageTransform } from './graphics/GraphicDataBase';
import { fromUint8Array } from 'js-base64';
+import { Arrow, ArrowTemplate } from 'src/graphics/arrow/Arrow';
+import { ArrowData } from './graphics/ArrowInteraction';
+import { ArrowDraw } from 'src/graphics/arrow/ArrowDrawAssistant';
// import { Notify } from 'quasar';
@@ -82,23 +85,24 @@ export function initDrawApp(): IDrawApp {
isSupportDeletion: isSupportDeletion,
});
const app = drawApp;
- // app.canvas.on('_rightclick', (e) => {
- // if (app.drawing) return;
- // UndoOptions.disabled = !app.opRecord.hasUndo;
- // RedoOptions.disabled = !app.opRecord.hasRedo;
+ new ArrowDraw(drawApp, new ArrowTemplate(new ArrowData()));
+ app.canvas.on('_rightclick', (e) => {
+ if (app.drawing) return;
+ UndoOptions.disabled = !app.opRecord.hasUndo;
+ RedoOptions.disabled = !app.opRecord.hasRedo;
- // UndoOptions.handler = () => {
- // app.opRecord.undo();
- // };
- // RedoOptions.handler = () => {
- // app.opRecord.redo();
- // };
- // SelectAllOptions.handler = () => {
- // app.selectAllGraphics();
- // };
- // DefaultCanvasMenu.open(e.global);
- // });
- // app.on('destroy', async () => {});
+ UndoOptions.handler = () => {
+ app.opRecord.undo();
+ };
+ RedoOptions.handler = () => {
+ app.opRecord.redo();
+ };
+ SelectAllOptions.handler = () => {
+ app.selectAllGraphics();
+ };
+ DefaultCanvasMenu.open(e.global);
+ });
+ app.on('destroy', async () => {});
app.addKeyboardListener(
new KeyListener({
value: 'KeyS',
@@ -136,6 +140,10 @@ export function saveDrawDatas(app: IDrawApp) {
viewportTransform: toStorageTransform(canvasData.viewportTransform),
});
const graphics = app.queryStore.getAllGraphics();
+ if (Arrow.Type === g.type) {
+ const arrowData = (g as Arrow).saveData();
+ storage.arrows.push((arrowData as ArrowData).data);
+ }
console.log(storage, '保存数据', graphics);
const base64 = fromUint8Array(storage.serialize());
return base64;
diff --git a/src/drawApp/graphics/ArrowInteraction.ts b/src/drawApp/graphics/ArrowInteraction.ts
new file mode 100644
index 0000000..bff1dbc
--- /dev/null
+++ b/src/drawApp/graphics/ArrowInteraction.ts
@@ -0,0 +1,48 @@
+import * as pb_1 from 'google-protobuf';
+// import { IArrowData, Arrow } from 'src/graphics/arrow/Arrow';
+import { iscsGraphicData } from 'src/protos/iscs_graphic_data';
+import { IArrowData, Arrow } from 'src/graphics/arrow/Arrow';
+import { GraphicDataBase } from './GraphicDataBase';
+import { IPointData } from 'pixi.js';
+
+export class ArrowData extends GraphicDataBase implements IArrowData {
+ constructor(data?: iscsGraphicData.Arrow) {
+ let arrow;
+ if (data) {
+ arrow = data;
+ } else {
+ arrow = new iscsGraphicData.Arrow({
+ common: GraphicDataBase.defaultCommonInfo(Arrow.Type),
+ });
+ }
+ super(arrow);
+ }
+
+ public get data(): iscsGraphicData.Arrow {
+ return this.getData();
+ }
+
+ get code(): string {
+ return this.data.code;
+ }
+ set code(v: string) {
+ this.data.code = v;
+ }
+ get points(): IPointData[] {
+ return this.data.points;
+ }
+ set points(points: IPointData[]) {
+ this.data.points = points.map(
+ (p) => new iscsGraphicData.Point({ x: p.x, y: p.y })
+ );
+ }
+ clone(): ArrowData {
+ return new ArrowData(this.data.cloneMessage());
+ }
+ copyFrom(data: ArrowData): void {
+ pb_1.Message.copyInto(data.data, this.data);
+ }
+ eq(other: ArrowData): boolean {
+ return pb_1.Message.equals(this.data, other.data);
+ }
+}
diff --git a/src/graphics/arrow/Arrow.ts b/src/graphics/arrow/Arrow.ts
new file mode 100644
index 0000000..155491b
--- /dev/null
+++ b/src/graphics/arrow/Arrow.ts
@@ -0,0 +1,92 @@
+import { Graphics, IPointData } from 'pixi.js';
+import {
+ GraphicData,
+ JlGraphic,
+ JlGraphicTemplate,
+ ILineGraphic,
+} from 'jl-graphic';
+
+export interface IArrowData extends GraphicData {
+ get code(): string; // 编号
+ set code(v: string);
+ get points(): IPointData[]; // 线坐标点
+ set points(points: IPointData[]);
+ clone(): IArrowData;
+ copyFrom(data: IArrowData): void;
+ eq(other: IArrowData): boolean;
+}
+
+export const ArrowConsts = {
+ lineColor: '#0000CD',
+ lineWidth: 5,
+};
+
+export class Arrow extends JlGraphic implements ILineGraphic {
+ static Type = 'Arrow';
+ lineGraphic: Graphics;
+ arrowGraphic: Graphics;
+ transformSave: boolean;
+
+ constructor() {
+ super(Arrow.Type);
+ this.lineGraphic = new Graphics();
+ this.arrowGraphic = new Graphics();
+ this.transformSave = true;
+ this.addChild(this.lineGraphic);
+ this.addChild(this.arrowGraphic);
+ }
+
+ doRepaint() {
+ if (this.datas.points.length < 2) {
+ throw new Error('Arrow坐标数据异常');
+ }
+
+ this.lineGraphic.clear();
+ this.lineGraphic.lineStyle(ArrowConsts.lineWidth, ArrowConsts.lineColor);
+ const p1 = this.datas.points[0];
+ const p2 = this.datas.points[1];
+ this.lineGraphic.moveTo(p1.x, p1.y);
+ this.lineGraphic.lineTo(p2.x, p2.y);
+ this.arrowGraphic.clear();
+ this.arrowGraphic.beginFill(ArrowConsts.lineColor, 1);
+ if (this.arrowGraphic.drawRegularPolygon) {
+ let roation = Math.PI / 2;
+ const angle = Math.atan2(p1.y - p2.y, p1.x - p2.x);
+ if (angle > Math.PI / 2) {
+ roation = angle - Math.PI / 2;
+ } else if (angle <= Math.PI / 2 && angle > 0) {
+ roation = Math.PI / 2 - angle;
+ } else {
+ roation = angle - Math.PI / 2;
+ }
+ this.arrowGraphic.drawRegularPolygon(p2.x, p2.y, 10, 3, roation);
+ }
+ this.arrowGraphic.endFill();
+ }
+
+ get code(): string {
+ return this.datas.code;
+ }
+
+ get datas(): IArrowData {
+ return this.getDatas();
+ }
+
+ get linePoints(): IPointData[] {
+ return this.datas.points;
+ }
+ set linePoints(points: IPointData[]) {
+ const old = this.datas.clone();
+ old.points = points;
+ this.updateData(old);
+ }
+}
+
+export class ArrowTemplate extends JlGraphicTemplate {
+ constructor(dataTemplate: IArrowData) {
+ super(Arrow.Type, { dataTemplate });
+ }
+ new() {
+ return new Arrow();
+ }
+}
diff --git a/src/graphics/arrow/ArrowDrawAssistant.ts b/src/graphics/arrow/ArrowDrawAssistant.ts
new file mode 100644
index 0000000..845d3a4
--- /dev/null
+++ b/src/graphics/arrow/ArrowDrawAssistant.ts
@@ -0,0 +1,204 @@
+import {
+ DraggablePoint,
+ IGraphicApp,
+ GraphicDrawAssistant,
+ GraphicInteractionPlugin,
+ GraphicTransformEvent,
+ IDrawApp,
+ JlGraphic,
+ linePoint,
+ AbsorbablePosition,
+ AbsorbableLine,
+ ILineGraphic,
+ PolylineEditPlugin,
+} from 'jl-graphic';
+import { IArrowData, Arrow, ArrowConsts, ArrowTemplate } from './Arrow';
+import {
+ DisplayObject,
+ FederatedMouseEvent,
+ Graphics,
+ IHitArea,
+ Point,
+} from 'pixi.js';
+
+export class ArrowDraw extends GraphicDrawAssistant {
+ points: Point[] = [];
+ lineGraphic = new Graphics();
+ arrowGraphic = new Graphics();
+
+ constructor(app: IDrawApp, template: ArrowTemplate) {
+ super(app, template, 'call_made', '箭头Arrow');
+ this.container.addChild(this.lineGraphic);
+ this.container.addChild(this.arrowGraphic);
+
+ ArrowPointEditPlugin.init(app, this);
+ }
+
+ bind(): void {
+ super.bind();
+ }
+ unbind(): void {
+ super.unbind();
+ }
+
+ onLeftDown(e: FederatedMouseEvent): void {
+ const { x, y } = this.toCanvasCoordinates(e.global);
+ const p = new Point(x, y);
+ this.points.push(p);
+ if (this.points.length === 2) {
+ this.createAndStore(true);
+ }
+ }
+
+ onRightClick(): void {
+ this.finish();
+ return;
+ }
+
+ onEsc(): void {
+ this.finish();
+ return;
+ }
+
+ redraw(p: Point): void {
+ if (this.points.length < 1) return;
+ const p1 = this.points[0];
+ this.lineGraphic.clear();
+ this.lineGraphic.lineStyle(ArrowConsts.lineWidth, ArrowConsts.lineColor);
+ this.lineGraphic.moveTo(p1.x, p1.y);
+ this.lineGraphic.lineTo(p.x, p.y);
+ this.arrowGraphic.clear();
+ this.arrowGraphic.beginFill(ArrowConsts.lineColor, 1);
+ if (this.arrowGraphic.drawRegularPolygon) {
+ let roation = Math.PI / 2;
+ const angle = Math.atan2(p1.y - p.y, p1.x - p.x);
+ if (angle > Math.PI / 2) {
+ roation = angle - Math.PI / 2;
+ } else if (angle <= Math.PI / 2 && angle >= 0) {
+ roation = Math.PI / 2 - angle;
+ } else {
+ roation = angle - Math.PI / 2;
+ }
+ this.arrowGraphic.drawRegularPolygon(p.x, p.y, 10, 3, roation);
+ }
+ this.arrowGraphic.endFill();
+ }
+
+ prepareData(data: IArrowData): boolean {
+ if (this.points.length < 2) {
+ console.log('Arrow绘制因点不够取消绘制');
+ return false;
+ }
+ data.points = this.points;
+ return true;
+ }
+
+ clearCache(): void {
+ this.points = [];
+ this.lineGraphic.clear();
+ this.arrowGraphic.clear();
+ }
+}
+
+export class ArrowGraphicHitArea implements IHitArea {
+ arrow: Arrow;
+ constructor(arrow: Arrow) {
+ this.arrow = arrow;
+ }
+ contains(x: number, y: number): boolean {
+ const p1 = this.arrow.datas.points[0];
+ const p2 = this.arrow.datas.points[1];
+ if (linePoint(p1, p2, { x, y }, ArrowConsts.lineWidth)) {
+ return true;
+ }
+ return false;
+ }
+}
+
+function buildAbsorbablePositions(arrow: Arrow): AbsorbablePosition[] {
+ const aps: AbsorbablePosition[] = [];
+
+ const arrows = arrow.queryStore.queryByType(Arrow.Type);
+ const canvas = arrow.getCanvas();
+ arrows.forEach((other) => {
+ if (other.id === arrow.id) {
+ return;
+ }
+ const [p1, p2] = [
+ other.localToCanvasPoint(other.datas.points[0]),
+ other.localToCanvasPoint(other.datas.points[1]),
+ ];
+ const ala = new AbsorbableLine(
+ new Point(p1.x, 0),
+ new Point(p1.x, canvas.height)
+ );
+ aps.push(ala);
+ const alb = new AbsorbableLine(
+ new Point(p2.x, 0),
+ new Point(p2.x, canvas.height)
+ );
+ aps.push(alb);
+ });
+ return aps;
+}
+
+function onEditPointCreate(g: ILineGraphic, dp: DraggablePoint): void {
+ const arrow = g as Arrow;
+ dp.on('transformstart', (e: GraphicTransformEvent) => {
+ if (e.isShift()) {
+ arrow.getGraphicApp().setOptions({
+ absorbablePositions: buildAbsorbablePositions(arrow),
+ });
+ }
+ });
+}
+
+export class ArrowPointEditPlugin extends GraphicInteractionPlugin {
+ static Name = 'ArrowPointDrag';
+ drawAssistant: ArrowDraw;
+
+ constructor(app: IGraphicApp, da: ArrowDraw) {
+ super(ArrowPointEditPlugin.Name, app);
+ this.drawAssistant = da;
+ }
+ static init(app: IGraphicApp, da: ArrowDraw) {
+ return new ArrowPointEditPlugin(app, da);
+ }
+ filter(...grahpics: JlGraphic[]): Arrow[] | undefined {
+ return grahpics.filter((g) => g.type == Arrow.Type) as Arrow[];
+ }
+ bind(g: Arrow): void {
+ g.eventMode = 'static';
+ g.cursor = 'pointer';
+ g.hitArea = new ArrowGraphicHitArea(g);
+ g.transformSave = true;
+ g.on('selected', this.onSelected, this);
+ g.on('unselected', this.onUnselected, this);
+ }
+ unbind(g: Arrow): void {
+ g.off('selected', this.onSelected, this);
+ g.off('unselected', this.onUnselected, this);
+ }
+
+ onContextMenu(e: FederatedMouseEvent) {
+ const target = e.target as DisplayObject;
+ const arrow = target.getGraphic() as Arrow;
+ this.app.updateSelected(arrow);
+ }
+
+ onSelected(g: DisplayObject): void {
+ const arrow = g as Arrow;
+ const lep = new PolylineEditPlugin(arrow, { onEditPointCreate });
+ arrow.addAssistantAppend(lep);
+ lep.showAll();
+ }
+ onUnselected(g: DisplayObject): void {
+ const arrow = g as Arrow;
+ const lep = arrow.getAssistantAppend(
+ PolylineEditPlugin.Name
+ );
+ if (lep) {
+ lep.hideAll();
+ }
+ }
+}
diff --git a/src/layouts/DrawLayout.vue b/src/layouts/DrawLayout.vue
index 27c6fdc..da6e055 100644
--- a/src/layouts/DrawLayout.vue
+++ b/src/layouts/DrawLayout.vue
@@ -111,6 +111,7 @@ import { useRoute, useRouter } from 'vue-router';
import { successNotify } from 'src/utils/CommonNotify';
import { useQuasar } from 'quasar';
import { useDrawStore } from 'src/stores/draw-store';
+import { Arrow } from 'src/graphics/arrow/Arrow';
const $q = useQuasar();
const route = useRoute();
@@ -195,7 +196,7 @@ onMounted(() => {
} else {
drawStore.setDraftId(null);
}
- const drawAssistantsTypes = [];
+ const drawAssistantsTypes = [Arrow.Type];
drawAssistantsTypes.forEach((type) => {
const drawAssistant = drawStore.getDrawApp().getDrawAssistant(type);
if (drawAssistant) {
diff --git a/src/stores/draw-store.ts b/src/stores/draw-store.ts
index c7fb00c..a24805d 100644
--- a/src/stores/draw-store.ts
+++ b/src/stores/draw-store.ts
@@ -1,6 +1,6 @@
import { defineStore } from 'pinia';
import { QTable } from 'quasar';
-import { initDrawApp, destroyDrawApp } from 'src/drawApp/drawApp';
+import { initDrawApp, destroyDrawApp, getDrawApp } from 'src/drawApp/drawApp';
import {
DrawAssistant,
GraphicData,
@@ -23,7 +23,9 @@ export const useDrawStore = defineStore('draw', {
drawGraphicType: (state) => state.drawAssistant?.type,
drawGraphicName: (state) => state.drawAssistant?.description,
drawGraphicTemplate: (state) => state.drawAssistant?.graphicTemplate,
-
+ getApp: () => {
+ return getDrawApp();
+ },
selectedGraphicType: (state) => {
if (state.selectedGraphics) {
if (state.selectedGraphics.length === 1) {
@@ -103,6 +105,5 @@ export const useDrawStore = defineStore('draw', {
setDraftId(id: number | null) {
this.draftId = id;
},
-
},
});