From aa584dd3bb4c18629b84792552b0f477a5637e1f Mon Sep 17 00:00:00 2001 From: Yuan Date: Mon, 19 Jun 2023 17:37:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A1=86=E6=9E=B6=E6=9B=B4=E6=96=B0,=20?= =?UTF-8?q?=E9=81=93=E5=B2=94=E5=92=8C=E5=8C=BA=E6=AE=B5=E5=90=B8=E9=99=84?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- graphic-pixi | 2 +- src/graphics/section/SectionDrawAssistant.ts | 6 +- src/jl-graphic/app/JlGraphicApp.ts | 9 +-- src/jl-graphic/graphic/AbsorbablePosition.ts | 72 ++++++++++++++++--- src/jl-graphic/message/WsMsgBroker.ts | 28 +++++--- src/jl-graphic/plugins/CommonMousePlugin.ts | 2 +- .../plugins/GraphicTransformPlugin.ts | 35 +++++++-- src/jl-graphic/utils/GraphicUtils.ts | 68 ++++++++++++++++++ 8 files changed, 189 insertions(+), 33 deletions(-) diff --git a/graphic-pixi b/graphic-pixi index 533ebdc..ecfe142 160000 --- a/graphic-pixi +++ b/graphic-pixi @@ -1 +1 @@ -Subproject commit 533ebdc9a89ac1e2a6e5d9f3fbdc98c9f7d416c8 +Subproject commit ecfe1421bb7ddf732790d6d2a709b4bf26eec91c diff --git a/src/graphics/section/SectionDrawAssistant.ts b/src/graphics/section/SectionDrawAssistant.ts index aaacde7..c5b5720 100644 --- a/src/graphics/section/SectionDrawAssistant.ts +++ b/src/graphics/section/SectionDrawAssistant.ts @@ -125,14 +125,14 @@ function buildAbsorbablePositions(section: Section): AbsorbablePosition[] { other.localToCanvasPoint(other.getStartPoint()), other.localToCanvasPoint(other.getEndPoint()), ]; - const apa = new AbsorbablePoint(ps); - const apb = new AbsorbablePoint(pe); + // const apa = new AbsorbablePoint(ps); + // const apb = new AbsorbablePoint(pe); const { width, height } = section.getGraphicApp().canvas; const xs = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y }); const ys = new AbsorbableLine({ x: ps.x, y: 0 }, { x: ps.x, y: height }); const xe = new AbsorbableLine({ x: 0, y: pe.y }, { x: width, y: pe.y }); const ye = new AbsorbableLine({ x: pe.x, y: 0 }, { x: pe.x, y: height }); - aps.push(apa, apb, xs, ys, xe, ye); + aps.push(/* apa, apb, */ xs, ys, xe, ye); }); const turnouts = section.queryStore.queryByType(Turnout.Type); diff --git a/src/jl-graphic/app/JlGraphicApp.ts b/src/jl-graphic/app/JlGraphicApp.ts index 529f67d..7b23002 100644 --- a/src/jl-graphic/app/JlGraphicApp.ts +++ b/src/jl-graphic/app/JlGraphicApp.ts @@ -288,6 +288,7 @@ export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents { drag_op_end: [event: AppDragEvent]; 'pre-menu-handle': [menu: MenuItemOptions]; 'post-menu-handle': [menu: MenuItemOptions]; + 'websocket-state-change': [app: GraphicApp, connected: boolean]; destroy: [app: GraphicApp]; } @@ -888,10 +889,10 @@ export class GraphicApp extends EventEmitter { this.emit('destroy', this); if (this.wsMsgBroker) { this.wsMsgBroker.close(); - if (!StompCli.hasAppMsgBroker()) { - // 如果没有其他消息代理,关闭websocket Stomp客户端 - StompCli.close(); - } + // if (!StompCli.hasAppMsgBroker()) { + // // 如果没有其他消息代理,关闭websocket Stomp客户端 + // StompCli.close(); + // } } this.interactionPluginMap.forEach((plugin) => { plugin.pause(); diff --git a/src/jl-graphic/graphic/AbsorbablePosition.ts b/src/jl-graphic/graphic/AbsorbablePosition.ts index ec379a1..0d5405f 100644 --- a/src/jl-graphic/graphic/AbsorbablePosition.ts +++ b/src/jl-graphic/graphic/AbsorbablePosition.ts @@ -11,6 +11,8 @@ import { calculateFootPointFromPointToLine, calculateIntersectionPointOfCircleAndPoint, distance, + distance2, + isLineContainOther, linePoint, } from '../utils'; import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic'; @@ -19,12 +21,25 @@ import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic'; * 抽象可吸附位置 */ export interface AbsorbablePosition extends Container { + /** + * 是否与另一个可吸附位置重叠(相似,但可能范围不同) + * @param other + */ + isOverlapping(other: AbsorbablePosition): boolean; + + /** + * 与另一个相似的吸附位置比较范围大小 + * @param other + * @returns >0此吸附范围大,<0另一个吸附范围大,=0两个吸附范围一样大 + */ + compareTo(other: AbsorbablePosition): number; + /** * 尝试吸附图形对象 * @param objs 图形对象列表 * @returns 如果吸附成功,返回true,否则false */ - tryAbsorb(...objs: DisplayObject[]): boolean; + tryAbsorb(...objs: DisplayObject[]): void; } /** @@ -65,7 +80,22 @@ export default class AbsorbablePoint this.interactive; VectorGraphicUtil.handle(this); } - tryAbsorb(...objs: DisplayObject[]): boolean { + compareTo(other: AbsorbablePosition): number { + if (other instanceof AbsorbablePoint) { + return this.absorbRange - other.absorbRange; + } + throw new Error('非可吸附点'); + } + isOverlapping(other: AbsorbablePosition): boolean { + if (other instanceof AbsorbablePoint) { + return ( + this._point.equals(other._point) && + this.absorbRange === other.absorbRange + ); + } + return false; + } + tryAbsorb(...objs: DisplayObject[]): void { for (let i = 0; i < objs.length; i++) { const obj = objs[i]; if ( @@ -73,10 +103,8 @@ export default class AbsorbablePoint this.absorbRange ) { obj.position.copyFrom(this._point); - return true; } } - return false; } updateOnScaled() { const scaled = this.getAllParentScaled(); @@ -101,6 +129,22 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition { this.absorbRange = absorbRange; this.redraw(); } + isOverlapping(other: AbsorbablePosition): boolean { + if (other instanceof AbsorbableLine) { + const contain = isLineContainOther( + { p1: this.p1, p2: this.p2 }, + { p1: other.p1, p2: other.p2 } + ); + return contain; + } + return false; + } + compareTo(other: AbsorbablePosition): number { + if (other instanceof AbsorbableLine) { + return distance2(this.p1, this.p2) - distance2(other.p1, other.p2); + } + throw new Error('非可吸附线'); + } redraw() { this.clear(); this.lineStyle(1, new Color(this._color)); @@ -108,17 +152,15 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition { this.lineTo(this.p2.x, this.p2.y); } - tryAbsorb(...objs: DisplayObject[]): boolean { + tryAbsorb(...objs: DisplayObject[]): void { for (let i = 0; i < objs.length; i++) { const obj = objs[i]; const p = obj.position.clone(); if (linePoint(this.p1, this.p2, p, this.absorbRange, true)) { const fp = calculateFootPointFromPointToLine(this.p1, this.p2, p); obj.position.copyFrom(fp); - return true; } } - return false; } } @@ -138,6 +180,18 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition { this.absorbRange = absorbRange; this.redraw(); } + isOverlapping(other: AbsorbablePosition): boolean { + if (other instanceof AbsorbableCircle) { + return this.p0.equals(other.p0) && this.radius === other.radius; + } + return false; + } + compareTo(other: AbsorbablePosition): number { + if (other instanceof AbsorbableCircle) { + return this.absorbRange - other.absorbRange; + } + throw new Error('非可吸附圆'); + } redraw() { this.clear(); @@ -145,7 +199,7 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition { this.drawCircle(this.p0.x, this.p0.y, this.radius); } - tryAbsorb(...objs: DisplayObject[]): boolean { + tryAbsorb(...objs: DisplayObject[]): void { for (let i = 0; i < objs.length; i++) { const obj = objs[i]; const len = distance( @@ -165,9 +219,7 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition { obj.position ); obj.position.copyFrom(p); - return true; } } - return false; } } diff --git a/src/jl-graphic/message/WsMsgBroker.ts b/src/jl-graphic/message/WsMsgBroker.ts index 8b8cd53..940b11a 100644 --- a/src/jl-graphic/message/WsMsgBroker.ts +++ b/src/jl-graphic/message/WsMsgBroker.ts @@ -22,7 +22,7 @@ export interface StompCliOption { * @returns */ onAuthenticationFailed?: () => void; - reconnectDelay?: number; // 重连延时,默认3秒 + reconnectDelay?: number; // 重连延时,默认3秒,设置为0不重连. heartbeatIncoming?: number; // 服务端过来的心跳间隔,默认30秒 heartbeatOutgoing?: number; // 到服务端的心跳间隔,默认30秒 } @@ -36,18 +36,22 @@ const DefaultStompOption: StompCliOption = { }; export class StompCli { - private static enabled = false; - private static options: StompCliOption; private static client: StompClient; + private static options: StompCliOption; private static appMsgBroker: AppWsMsgBroker[] = []; + /** + * key-订阅路径 + */ + subscriptions: Map = new Map< + string, + AppStateSubscription + >(); private static connected = false; static new(options: StompCliOption) { - if (StompCli.enabled) { - // 已经启用 + if (StompCli.client) { + // 已经创建 return; - // throw new Error('websocket 已连接,若确实需要重新连接,请先断开StompCli.close再重新StompCli.new') } - StompCli.enabled = true; StompCli.options = Object.assign({}, DefaultStompOption, options); StompCli.client = new StompClient({ brokerURL: StompCli.options.wsUrl, @@ -62,6 +66,7 @@ export class StompCli { StompCli.client.onConnect = () => { // console.log('websocket连接(重连),重新订阅', StompCli.appMsgBroker.length) StompCli.connected = true; + StompCli.emitConnectStateChangeEvent(); StompCli.appMsgBroker.forEach((broker) => { broker.resubscribe(); }); @@ -83,10 +88,12 @@ export class StompCli { StompCli.client.onDisconnect = (frame: Frame) => { console.log('Stomp 断开连接', frame); StompCli.connected = false; + StompCli.emitConnectStateChangeEvent(); }; StompCli.client.onWebSocketClose = (evt: CloseEvent) => { console.log('websocket 关闭', evt); StompCli.connected = false; + StompCli.emitConnectStateChangeEvent(); }; // websocket错误处理 StompCli.client.onWebSocketError = (err: Event) => { @@ -96,8 +103,10 @@ export class StompCli { StompCli.client.activate(); } - static isEnabled(): boolean { - return StompCli.enabled; + static emitConnectStateChangeEvent() { + StompCli.appMsgBroker.forEach((broker) => { + broker.app.emit('websocket-state-change', broker.app, StompCli.connected); + }); } static isConnected(): boolean { @@ -135,7 +144,6 @@ export class StompCli { * 关闭websocket连接 */ static close() { - StompCli.enabled = false; StompCli.connected = false; if (StompCli.client) { StompCli.client.deactivate(); diff --git a/src/jl-graphic/plugins/CommonMousePlugin.ts b/src/jl-graphic/plugins/CommonMousePlugin.ts index bdd8747..b7a33f1 100644 --- a/src/jl-graphic/plugins/CommonMousePlugin.ts +++ b/src/jl-graphic/plugins/CommonMousePlugin.ts @@ -194,7 +194,7 @@ export class CommonMouseTool extends AppInteractionPlugin { const graphic = this.leftDownTarget.getGraphic(); if (graphic) { const app = this.app; - console.log(this.leftDownTarget.isGraphic()); + // console.log(this.leftDownTarget.isGraphic()); // 图形选中 if (!e.ctrlKey && !graphic.selected && graphic.selectable) { app.updateSelected(graphic); diff --git a/src/jl-graphic/plugins/GraphicTransformPlugin.ts b/src/jl-graphic/plugins/GraphicTransformPlugin.ts index 369d55b..07fa18e 100644 --- a/src/jl-graphic/plugins/GraphicTransformPlugin.ts +++ b/src/jl-graphic/plugins/GraphicTransformPlugin.ts @@ -171,11 +171,40 @@ export class GraphicTransformPlugin extends InteractionPluginBase { this.app.canvas.addAssistantAppend(this.apContainer); app.on('options-update', (options) => { if (options.absorbablePositions) { - this.absorbablePositions = options.absorbablePositions; + this.absorbablePositions = this.filterAbsorbablePositions( + options.absorbablePositions + ); } }); } + /** + * 过滤重复的吸附位置 + * @param positions + * @returns + */ + filterAbsorbablePositions( + positions: AbsorbablePosition[] + ): AbsorbablePosition[] { + const aps: AbsorbablePosition[] = []; + for (let i = 0; i < positions.length; i++) { + const ap1 = positions[i]; + let ap: AbsorbablePosition | null = ap1; + for (let j = positions.length - 1; j > i; j--) { + const ap2 = positions[j]; + if (ap.isOverlapping(ap2) && ap.compareTo(ap2) <= 0) { + ap = null; + break; + } + } + if (ap != null) { + aps.push(ap); + } + } + // console.log(positions, aps); + return aps; + } + static new(app: GraphicApp) { return new GraphicTransformPlugin(app); } @@ -266,9 +295,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase { if (this.absorbablePositions) { for (let i = 0; i < this.absorbablePositions.length; i++) { const ap = this.absorbablePositions[i]; - if (ap.tryAbsorb(...targets)) { - break; - } + ap.tryAbsorb(...targets); } } // 事件发布 diff --git a/src/jl-graphic/utils/GraphicUtils.ts b/src/jl-graphic/utils/GraphicUtils.ts index 254e4bf..a710fde 100644 --- a/src/jl-graphic/utils/GraphicUtils.ts +++ b/src/jl-graphic/utils/GraphicUtils.ts @@ -607,3 +607,71 @@ export function getIntersectionPoint(line1: number[], line2: number[]) { -denominator; return new Point(x, y); } + +/** + * 是否平行线 + * @param p1 + * @param p2 + * @param pa + * @param pb + * @returns + */ +export function isParallelLines( + p1: IPointData, + p2: IPointData, + pa: IPointData, + pb: IPointData +): boolean { + const vle1 = Vector2.direction(Vector2.from(p1), Vector2.from(p2)); + const vle2 = Vector2.direction(Vector2.from(pa), Vector2.from(pb)); + if (vle2.equals(vle1)) { + return true; + } + return vle1.equals(Vector2.direction(Vector2.from(pb), Vector2.from(pa))); +} + +/** + * 点是否在线段上 + * @param p1 + * @param p2 + * @param p + * @returns + */ +export function isPointOnLine( + p1: IPointData, + p2: IPointData, + p: IPointData +): boolean { + const vp1 = Vector2.from(p1); + const vp2 = Vector2.from(p2); + const vp = Vector2.from(p); + if (vp1.equals(vp) || vp2.equals(vp)) { + return true; + } + const vle = Vector2.direction(vp1, Vector2.from(p2)); + const vpe = Vector2.direction(vp1, vp); + if (vle.equals(vpe)) { + return ( + Vector2.difference(vp1, vp2).squaredLength() >= + Vector2.difference(vp1, vp).squaredLength() + ); + } + return false; +} +/** + * 两条线段是否存在包含关系 + * @param line1 + * @param line2 + * @returns + */ +export function isLineContainOther( + line1: { p1: IPointData; p2: IPointData }, + line2: { p1: IPointData; p2: IPointData } +): boolean { + return ( + (isPointOnLine(line1.p1, line1.p2, line2.p1) && + isPointOnLine(line1.p1, line1.p2, line2.p2)) || + (isPointOnLine(line2.p1, line2.p2, line1.p1) && + isPointOnLine(line2.p1, line2.p2, line1.p2)) + ); +}