This commit is contained in:
joylink_zhaoerwei 2024-01-19 15:14:08 +08:00
parent 589b8d6952
commit b0dc4c7e1a
6 changed files with 1098 additions and 0 deletions

View File

@ -0,0 +1,55 @@
import { AbsorbablePosition, DraggablePoint, IGraphicApp, GraphicDrawAssistant, GraphicInteractionPlugin, GraphicTransformEvent, IDrawApp, JlGraphic, VectorText, GraphicEditPlugin, GraphicState } from 'jl-graphic';
import { JlTurnout, TurnoutSection } from './JlTurnout';
import { DisplayObject, FederatedMouseEvent, IHitArea, Point } from 'pixi.js';
import { ITurnoutData } from './TurnoutConfig';
import { TurnoutTemplate } from './TurnoutTemplate';
import { JlSection } from 'src/packages/Section/common/JlSection';
export declare class TurnoutDraw<S extends GraphicState> extends GraphicDrawAssistant<TurnoutTemplate<S>, ITurnoutData> {
turnout: JlTurnout;
constructor(app: IDrawApp, template: TurnoutTemplate<S>);
bind(): void;
onLeftUp(e: FederatedMouseEvent): void;
prepareData(data: ITurnoutData): boolean;
redraw(cp: Point): void;
}
export declare class ForkHitArea implements IHitArea {
turnout: JlTurnout;
constructor(turnout: JlTurnout);
contains(x: number, y: number): boolean;
}
export declare class TurnoutSectionHitArea implements IHitArea {
section: TurnoutSection;
constructor(section: TurnoutSection);
contains(x: number, y: number): boolean;
}
type dragType = JlTurnout | JlSection;
export declare function buildDragMoveAbsorbablePositions(target: dragType): AbsorbablePosition[];
export declare class TurnoutPointsInteractionPlugin extends GraphicInteractionPlugin<JlTurnout> {
static Name: string;
static init(app: IDrawApp): TurnoutPointsInteractionPlugin;
constructor(app: IGraphicApp);
onSectionContextMenu(e: FederatedMouseEvent, section: TurnoutSection): void;
bind(g: JlTurnout): void;
unbind(g: JlTurnout): void;
onSelected(g: DisplayObject): void;
onUnSelected(g: DisplayObject): void;
filter(...grahpics: JlGraphic[]): JlTurnout[] | undefined;
onDragMove(e: GraphicTransformEvent): void;
}
type onTurnoutEditPointCreate = (turnout: JlTurnout, dp: DraggablePoint) => void;
export interface ITurnoutEditOptions {
onEditPointCreate?: onTurnoutEditPointCreate;
}
export declare class TurnoutEditPlugin extends GraphicEditPlugin<JlTurnout> {
static Name: string;
options: ITurnoutEditOptions;
editPoints: DraggablePoint[][];
labels: VectorText[];
constructor(graphic: JlTurnout, options?: ITurnoutEditOptions);
reset(): void;
hideAll(): void;
initEditPoints(): void;
destoryEditPoints(): void;
updateEditedPointsPosition(): void;
}
export {};

View File

@ -0,0 +1,389 @@
import { ContextMenu, GraphicDrawAssistant, linePoint, polylinePoint, GraphicInteractionPlugin, getWaypointRangeIndex, GraphicEditPlugin, DraggablePoint, VectorText, AppConsts, AbsorbablePoint, distance, AbsorbableLine } from 'jl-graphic';
import { getForkPoint, JlTurnout } from './JlTurnout.js';
import { Point } from 'pixi.js';
import { DevicePort } from '../../../common/common.js';
import { JlSection } from '../../Section/common/JlSection.js';
const commonTurnoutConsts = {
lineWidth: 5,
forkLenth: 20,
};
class TurnoutDraw extends GraphicDrawAssistant {
turnout;
constructor(app, template) {
super(app, template, 'sym_o_ramp_left', '道岔Turnout');
this.turnout = this.graphicTemplate.new();
this.container.addChild(this.turnout);
TurnoutPointsInteractionPlugin.init(app);
}
bind() {
super.bind();
this.turnout.loadData(this.graphicTemplate.datas);
this.turnout.doRepaint();
}
onLeftUp(e) {
this.turnout.position.copyFrom(this.toCanvasCoordinates(e.global));
this.createAndStore(true);
}
prepareData(data) {
data.transform = this.turnout.saveTransform();
data.code = 'A000000';
return true;
}
redraw(cp) {
this.turnout.position.copyFrom(cp);
}
}
class ForkHitArea {
turnout;
constructor(turnout) {
this.turnout = turnout;
}
contains(x, y) {
const intersectPointB = getForkPoint(commonTurnoutConsts.forkLenth, this.turnout.datas.pointB[0]);
const intersectPointC = getForkPoint(commonTurnoutConsts.forkLenth, this.turnout.datas.pointC[0]);
return (linePoint(intersectPointB, { x: 0, y: 0 }, { x, y }, commonTurnoutConsts.lineWidth) ||
linePoint(intersectPointC, { x: 0, y: 0 }, { x, y }, commonTurnoutConsts.lineWidth));
}
}
class TurnoutSectionHitArea {
section;
constructor(section) {
this.section = section;
}
contains(x, y) {
let points;
let start;
switch (this.section.port) {
case DevicePort.A:
points = this.section.turnout.datas.pointA;
start = { x: 0, y: 0 };
break;
case DevicePort.B:
points = this.section.turnout.datas.pointB;
start = getForkPoint(commonTurnoutConsts.forkLenth, points[0]);
break;
case DevicePort.C:
points = this.section.turnout.datas.pointC;
start = getForkPoint(commonTurnoutConsts.forkLenth, points[0]);
break;
}
return polylinePoint([start, ...points], { x, y }, commonTurnoutConsts.lineWidth);
}
}
function buildAbsorbablePositions(turnout) {
const aps = [];
const sections = turnout.queryStore.queryByType(JlSection.Type);
sections.forEach((section) => {
const ps = new AbsorbablePoint(section.localToCanvasPoint(section.getStartPoint()));
const pe = new AbsorbablePoint(section.localToCanvasPoint(section.getEndPoint()));
aps.push(ps, pe); //区段端点
});
const turnouts = turnout.queryStore.queryByType(JlTurnout.Type);
turnouts.forEach((otherTurnout) => {
const { pointA: [A], pointB: [B], pointC: [C], } = otherTurnout.datas;
[A, B, C].forEach((p) => {
aps.push(new AbsorbablePoint(otherTurnout.localToCanvasPoint(p)), //道岔端点
new AbsorbableLine(otherTurnout.localToCanvasPoint({ x: -5 * p.x, y: -5 * p.y }), otherTurnout.localToCanvasPoint({ x: 5 * p.x, y: 5 * p.y })));
});
aps.push(new AbsorbableLine(otherTurnout.localToCanvasPoint({ x: 0, y: -500 }), otherTurnout.localToCanvasPoint({ x: 0, y: 500 })), //岔心垂直线
new AbsorbableLine(otherTurnout.localToCanvasPoint({ x: -500, y: 0 }), otherTurnout.localToCanvasPoint({ x: 500, y: 0 })), //岔心水平线
new AbsorbableLine(otherTurnout.localToCanvasPoint({ x: -500, y: 500 }), otherTurnout.localToCanvasPoint({ x: 500, y: -500 })), //岔心/
new AbsorbableLine(otherTurnout.localToCanvasPoint({ x: -500, y: -500 }), otherTurnout.localToCanvasPoint({ x: 500, y: 500 })));
});
return aps;
}
class DragMoveAbsorbablePoint extends AbsorbablePoint {
moveTarget;
constructor(point, absorbRange = 15) {
super(point, absorbRange);
}
tryAbsorb(...dragTargets) {
const dragTarget = dragTargets[0];
if (dragTarget instanceof JlTurnout) {
if (this.moveTarget == undefined) {
const { pointA: [A], pointB: [B], pointC: [C], } = dragTarget.datas;
this.moveTarget = {
position: dragTarget.getGlobalPosition(),
portPos: [
dragTarget.localToCanvasPoint(A),
dragTarget.localToCanvasPoint(B),
dragTarget.localToCanvasPoint(C),
],
};
}
const { pointA: [A], pointB: [B], pointC: [C], } = dragTarget.datas;
[A, B, C].forEach((p, i) => {
const changePos = dragTarget.localToCanvasPoint(p);
if (distance(this._point.x, this._point.y, changePos.x, changePos.y) <
this.absorbRange &&
this.moveTarget) {
dragTarget.updatePositionByCanvasPosition(new Point(this.moveTarget.position.x +
this._point.x -
this.moveTarget.portPos[i].x, this.moveTarget.position.y +
this._point.y -
this.moveTarget.portPos[i].y));
}
});
}
else {
if (this.moveTarget == undefined) {
this.moveTarget = {
position: dragTarget.getGlobalPosition(),
portPos: [
dragTarget.localToCanvasPoint(dragTarget.getStartPoint()),
dragTarget.localToCanvasPoint(dragTarget.getEndPoint()),
],
};
}
dragTarget
.localToCanvasPoints(...dragTarget.datas.points)
.forEach((p, i) => {
if (distance(this._point.x, this._point.y, p.x, p.y) <
this.absorbRange &&
this.moveTarget) {
dragTarget.updatePositionByCanvasPosition(new Point(this.moveTarget.position.x +
this._point.x -
this.moveTarget.portPos[i].x, this.moveTarget.position.y +
this._point.y -
this.moveTarget.portPos[i].y));
}
});
}
}
}
function buildDragMoveAbsorbablePositions(target) {
const aps = [];
const sections = target.queryStore.queryByType(JlSection.Type);
sections.forEach((section) => {
if (section.id !== target.id) {
section.localToCanvasPoints(...section.datas.points).forEach((p) => {
aps.push(new DragMoveAbsorbablePoint(p)); //区段端点
});
}
});
const turnouts = target.queryStore.queryByType(JlTurnout.Type);
turnouts.forEach((otherTurnout) => {
if (otherTurnout.id !== target.id) {
const { pointA: [A], pointB: [B], pointC: [C], } = otherTurnout.datas;
[A, B, C].forEach((p) => {
aps.push(new DragMoveAbsorbablePoint(otherTurnout.localToCanvasPoint(p)));
});
}
});
return aps;
}
function onEditPointCreate(turnout, dp) {
dp.on('transformstart', (e) => {
if (e.isShift()) {
turnout.getGraphicApp().setOptions({
absorbablePositions: buildAbsorbablePositions(turnout),
});
}
});
}
const addPointConfig = { name: '添加路径点' };
const clearPointConfig = { name: '清除路径点' };
const turnoutSectionEditMenu = ContextMenu.init({
name: '道岔区段路径编辑',
groups: [
{
items: [addPointConfig, clearPointConfig],
},
],
});
class TurnoutPointsInteractionPlugin extends GraphicInteractionPlugin {
static Name = 'TurnoutPointsDrag';
static init(app) {
return new TurnoutPointsInteractionPlugin(app);
}
constructor(app) {
super(TurnoutPointsInteractionPlugin.Name, app);
app.registerMenu(turnoutSectionEditMenu);
}
onSectionContextMenu(e, section) {
const p = section.turnout.screenToLocalPoint(e.global);
addPointConfig.handler = () => {
if (section.port === DevicePort.A) {
const { start } = getWaypointRangeIndex([{ x: 0, y: 0 }, ...section.turnout.datas.pointA], false, p, commonTurnoutConsts.lineWidth);
const points = section.turnout.datas.pointA;
const ps = points.slice(0, start);
ps.push(new Point(p.x, p.y));
ps.push(...points.slice(start));
section.turnout.datas.pointA = ps;
}
if (section.port === DevicePort.B) {
const { start } = getWaypointRangeIndex([{ x: 0, y: 0 }, ...section.turnout.datas.pointB], false, p, commonTurnoutConsts.lineWidth);
const points = section.turnout.datas.pointB;
const ps = points.slice(0, start);
ps.push(new Point(p.x, p.y));
ps.push(...points.slice(start));
section.turnout.datas.pointB = ps;
}
if (section.port === DevicePort.C) {
const { start } = getWaypointRangeIndex([{ x: 0, y: 0 }, ...section.turnout.datas.pointC], false, p, commonTurnoutConsts.lineWidth);
const points = section.turnout.datas.pointC;
const ps = points.slice(0, start);
ps.push(new Point(p.x, p.y));
ps.push(...points.slice(start));
section.turnout.datas.pointC = ps;
}
this.onSelected(section.turnout);
};
clearPointConfig.handler = () => {
if (section.port === DevicePort.A)
section.turnout.datas.pointA = [
section.turnout.datas.pointA[section.turnout.datas.pointA.length - 1],
];
if (section.port === DevicePort.B)
section.turnout.datas.pointB = [
section.turnout.datas.pointB[section.turnout.datas.pointB.length - 1],
];
if (section.port === DevicePort.C)
section.turnout.datas.pointC = [
section.turnout.datas.pointC[section.turnout.datas.pointC.length - 1],
];
const tep = section.turnout.getAssistantAppend(TurnoutEditPlugin.Name);
if (tep) {
tep.reset();
}
section.turnout.repaint();
};
turnoutSectionEditMenu.open(e.global);
}
bind(g) {
g.graphics.fork.eventMode = 'static';
g.graphics.fork.cursor = 'pointer';
g.graphics.fork.hitArea = new ForkHitArea(g);
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.eventMode = 'static';
sectionGraphic.cursor = 'pointer';
sectionGraphic.hitArea = new TurnoutSectionHitArea(sectionGraphic);
sectionGraphic.on('rightclick', (e) => this.onSectionContextMenu(e, sectionGraphic), sectionGraphic);
});
g.graphics.label.eventMode = 'static';
g.graphics.label.cursor = 'pointer';
g.graphics.label.draggable = true;
g.graphics.label.selectable = true;
g.graphics.label.name = 'label';
g.graphics.label.transformSave = true;
g.transformSave = true;
g.on('selected', this.onSelected, this);
g.on('unselected', this.onUnSelected, this);
g.on('transformstart', this.onDragMove, this);
}
unbind(g) {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnSelected, this);
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.off('rightclick');
});
g.off('transformstart', this.onDragMove, this);
}
onSelected(g) {
const turnout = g;
let tep = turnout.getAssistantAppend(TurnoutEditPlugin.Name);
if (!tep) {
tep = new TurnoutEditPlugin(turnout, { onEditPointCreate });
turnout.addAssistantAppend(tep);
}
tep.reset();
tep.showAll();
}
onUnSelected(g) {
const turnout = g;
const tep = turnout.getAssistantAppend(TurnoutEditPlugin.Name);
if (tep) {
tep.hideAll();
}
}
filter(...grahpics) {
return grahpics.filter((g) => g.type == JlTurnout.Type);
}
onDragMove(e) {
const turnout = e.target;
this.app.setOptions({
absorbablePositions: buildDragMoveAbsorbablePositions(turnout),
});
}
}
class TurnoutEditPlugin extends GraphicEditPlugin {
static Name = 'TurnoutEdit';
options;
editPoints = [[], [], []];
labels = [];
constructor(graphic, options) {
super(graphic);
this.name = TurnoutEditPlugin.Name;
this.options = Object.assign({}, options);
this.initEditPoints();
}
reset() {
this.destoryEditPoints();
this.removeChildren();
this.initEditPoints();
}
hideAll() {
super.hideAll();
}
initEditPoints() {
const cpA = this.graphic.localToCanvasPoints(...this.graphic.datas.pointA);
const cpB = this.graphic.localToCanvasPoints(...this.graphic.datas.pointB);
const cpC = this.graphic.localToCanvasPoints(...this.graphic.datas.pointC);
const cpMap = new Map([
[cpA, this.graphic.datas.pointA],
[cpB, this.graphic.datas.pointB],
[cpC, this.graphic.datas.pointC],
]);
Array.from(cpMap.entries()).forEach(([cpDatas, dataPoints], i) => {
cpDatas.forEach((cpData, j) => {
const dp = new DraggablePoint(cpData);
dp.on('transforming', () => {
const localPoint = this.graphic.canvasToLocalPoint(dp.position);
dataPoints[j].x = localPoint.x;
dataPoints[j].y = localPoint.y;
this.graphic.repaint();
});
if (this.options.onEditPointCreate) {
this.options.onEditPointCreate(this.graphic, dp);
}
this.editPoints[i].push(dp);
});
});
this.editPoints.forEach((cps) => {
this.addChild(...cps);
});
this.labels = ['A', 'B', 'C'].map((str) => {
const vc = new VectorText(str, { fill: AppConsts.assistantElementColor });
vc.setVectorFontSize(14);
vc.anchor.set(0.5);
return vc;
});
this.addChild(...this.labels);
}
destoryEditPoints() {
this.editPoints.forEach((dps) => {
dps.forEach((dp) => {
dp.off('transforming');
dp.destroy();
this.removeChild(dp);
});
});
this.editPoints = [[], [], []];
}
updateEditedPointsPosition() {
const cpA = this.graphic.localToCanvasPoints(...this.graphic.datas.pointA);
const cpB = this.graphic.localToCanvasPoints(...this.graphic.datas.pointB);
const cpC = this.graphic.localToCanvasPoints(...this.graphic.datas.pointC);
[cpA, cpB, cpC].forEach((cps, i) => {
cps.forEach((cp, j) => {
this.editPoints[i][j].position.copyFrom(cp);
if (j === cps.length - 1) {
this.labels[i].position.copyFrom({ x: cp.x, y: cp.y + 12 });
}
});
});
}
}
export { ForkHitArea, TurnoutDraw, TurnoutEditPlugin, TurnoutPointsInteractionPlugin, TurnoutSectionHitArea, buildDragMoveAbsorbablePositions };

View File

@ -0,0 +1,9 @@
import { GraphicState, JlGraphicTemplate } from 'jl-graphic';
import { JlTurnout } from './JlTurnout';
import { ITurnoutData } from './TurnoutConfig';
import { StyleType } from 'common/common';
export declare class TurnoutTemplate<S extends GraphicState> extends JlGraphicTemplate<JlTurnout> {
styleType: StyleType;
constructor(dataTemplate: ITurnoutData, stateTemplate: S, styleType: StyleType);
new(): JlTurnout;
}

View File

@ -0,0 +1,32 @@
import { JlGraphicTemplate } from 'jl-graphic';
import { JlTurnout } from './JlTurnout.js';
import { StyleType } from '../../../common/common.js';
import { GPTurnout } from '../GPTurnout.js';
import { THTurnout } from '../THTurnout.js';
class TurnoutTemplate extends JlGraphicTemplate {
styleType;
constructor(dataTemplate, stateTemplate, styleType) {
super(JlTurnout.Type, {
dataTemplate,
stateTemplate,
});
this.styleType = styleType;
}
new() {
let turnout;
switch (this.styleType) {
case StyleType.GP:
turnout = new GPTurnout();
break;
default:
turnout = new THTurnout();
break;
}
turnout.loadData(this.datas);
turnout.loadState(this.states);
return turnout;
}
}
export { TurnoutTemplate };

View File

@ -0,0 +1,575 @@
import {
AbsorbablePosition,
DraggablePoint,
IGraphicApp,
GraphicDrawAssistant,
GraphicInteractionPlugin,
GraphicTransformEvent,
IDrawApp,
JlGraphic,
VectorText,
linePoint,
polylinePoint,
AppConsts,
GraphicEditPlugin,
getWaypointRangeIndex,
ContextMenu,
MenuItemOptions,
AbsorbablePoint,
AbsorbableLine,
distance,
GraphicState,
} from 'jl-graphic';
import { JlTurnout, TurnoutSection, getForkPoint } from './JlTurnout';
import {
DisplayObject,
FederatedMouseEvent,
IHitArea,
IPointData,
Point,
} from 'pixi.js';
import { ITurnoutData } from './TurnoutConfig';
import { TurnoutTemplate } from './TurnoutTemplate';
import { DevicePort } from 'common/common';
import { JlSection } from 'src/packages/Section/common/JlSection';
const commonTurnoutConsts = {
lineWidth: 5,
forkLenth: 20,
};
export class TurnoutDraw<S extends GraphicState> extends GraphicDrawAssistant<
TurnoutTemplate<S>,
ITurnoutData
> {
turnout: JlTurnout;
constructor(app: IDrawApp, template: TurnoutTemplate<S>) {
super(app, template, 'sym_o_ramp_left', '道岔Turnout');
this.turnout = this.graphicTemplate.new();
this.container.addChild(this.turnout);
TurnoutPointsInteractionPlugin.init(app);
}
bind(): void {
super.bind();
this.turnout.loadData(this.graphicTemplate.datas);
this.turnout.doRepaint();
}
onLeftUp(e: FederatedMouseEvent): void {
this.turnout.position.copyFrom(this.toCanvasCoordinates(e.global));
this.createAndStore(true);
}
prepareData(data: ITurnoutData): boolean {
data.transform = this.turnout.saveTransform();
data.code = 'A000000';
return true;
}
redraw(cp: Point): void {
this.turnout.position.copyFrom(cp);
}
}
export class ForkHitArea implements IHitArea {
turnout: JlTurnout;
constructor(turnout: JlTurnout) {
this.turnout = turnout;
}
contains(x: number, y: number): boolean {
const intersectPointB = getForkPoint(
commonTurnoutConsts.forkLenth,
this.turnout.datas.pointB[0],
);
const intersectPointC = getForkPoint(
commonTurnoutConsts.forkLenth,
this.turnout.datas.pointC[0],
);
return (
linePoint(
intersectPointB,
{ x: 0, y: 0 },
{ x, y },
commonTurnoutConsts.lineWidth,
) ||
linePoint(
intersectPointC,
{ x: 0, y: 0 },
{ x, y },
commonTurnoutConsts.lineWidth,
)
);
}
}
export class TurnoutSectionHitArea implements IHitArea {
section: TurnoutSection;
constructor(section: TurnoutSection) {
this.section = section;
}
contains(x: number, y: number): boolean {
let points: IPointData[];
let start: IPointData;
switch (this.section.port) {
case DevicePort.A:
points = this.section.turnout.datas.pointA;
start = { x: 0, y: 0 };
break;
case DevicePort.B:
points = this.section.turnout.datas.pointB;
start = getForkPoint(commonTurnoutConsts.forkLenth, points[0]);
break;
case DevicePort.C:
points = this.section.turnout.datas.pointC;
start = getForkPoint(commonTurnoutConsts.forkLenth, points[0]);
break;
}
return polylinePoint(
[start, ...points],
{ x, y },
commonTurnoutConsts.lineWidth,
);
}
}
function buildAbsorbablePositions(turnout: JlTurnout): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const sections = turnout.queryStore.queryByType<JlSection>(JlSection.Type);
sections.forEach((section) => {
const ps = new AbsorbablePoint(
section.localToCanvasPoint(section.getStartPoint()),
);
const pe = new AbsorbablePoint(
section.localToCanvasPoint(section.getEndPoint()),
);
aps.push(ps, pe); //区段端点
});
const turnouts = turnout.queryStore.queryByType<JlTurnout>(JlTurnout.Type);
turnouts.forEach((otherTurnout) => {
const {
pointA: [A],
pointB: [B],
pointC: [C],
} = otherTurnout.datas;
[A, B, C].forEach((p) => {
aps.push(
new AbsorbablePoint(otherTurnout.localToCanvasPoint(p)), //道岔端点
new AbsorbableLine(
otherTurnout.localToCanvasPoint({ x: -5 * p.x, y: -5 * p.y }),
otherTurnout.localToCanvasPoint({ x: 5 * p.x, y: 5 * p.y }),
), //道岔延长线
);
});
aps.push(
new AbsorbableLine(
otherTurnout.localToCanvasPoint({ x: 0, y: -500 }),
otherTurnout.localToCanvasPoint({ x: 0, y: 500 }),
), //岔心垂直线
new AbsorbableLine(
otherTurnout.localToCanvasPoint({ x: -500, y: 0 }),
otherTurnout.localToCanvasPoint({ x: 500, y: 0 }),
), //岔心水平线
new AbsorbableLine(
otherTurnout.localToCanvasPoint({ x: -500, y: 500 }),
otherTurnout.localToCanvasPoint({ x: 500, y: -500 }),
), //岔心/
new AbsorbableLine(
otherTurnout.localToCanvasPoint({ x: -500, y: -500 }),
otherTurnout.localToCanvasPoint({ x: 500, y: 500 }),
), //岔心\
);
});
return aps;
}
type dragType = JlTurnout | JlSection;
class DragMoveAbsorbablePoint extends AbsorbablePoint {
moveTarget:
| {
position: IPointData;
portPos: IPointData[];
}
| undefined;
constructor(point: IPointData, absorbRange = 15) {
super(point, absorbRange);
}
tryAbsorb(...dragTargets: dragType[]): void {
const dragTarget = dragTargets[0];
if (dragTarget instanceof JlTurnout) {
if (this.moveTarget == undefined) {
const {
pointA: [A],
pointB: [B],
pointC: [C],
} = dragTarget.datas;
this.moveTarget = {
position: dragTarget.getGlobalPosition(),
portPos: [
dragTarget.localToCanvasPoint(A),
dragTarget.localToCanvasPoint(B),
dragTarget.localToCanvasPoint(C),
],
};
}
const {
pointA: [A],
pointB: [B],
pointC: [C],
} = dragTarget.datas;
[A, B, C].forEach((p, i) => {
const changePos = dragTarget.localToCanvasPoint(p);
if (
distance(this._point.x, this._point.y, changePos.x, changePos.y) <
this.absorbRange &&
this.moveTarget
) {
dragTarget.updatePositionByCanvasPosition(
new Point(
this.moveTarget.position.x +
this._point.x -
this.moveTarget.portPos[i].x,
this.moveTarget.position.y +
this._point.y -
this.moveTarget.portPos[i].y,
),
);
}
});
} else {
if (this.moveTarget == undefined) {
this.moveTarget = {
position: dragTarget.getGlobalPosition(),
portPos: [
dragTarget.localToCanvasPoint(dragTarget.getStartPoint()),
dragTarget.localToCanvasPoint(dragTarget.getEndPoint()),
],
};
}
dragTarget
.localToCanvasPoints(...dragTarget.datas.points)
.forEach((p, i) => {
if (
distance(this._point.x, this._point.y, p.x, p.y) <
this.absorbRange &&
this.moveTarget
) {
dragTarget.updatePositionByCanvasPosition(
new Point(
this.moveTarget.position.x +
this._point.x -
this.moveTarget.portPos[i].x,
this.moveTarget.position.y +
this._point.y -
this.moveTarget.portPos[i].y,
),
);
}
});
}
}
}
export function buildDragMoveAbsorbablePositions(
target: dragType,
): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const sections = target.queryStore.queryByType<JlSection>(JlSection.Type);
sections.forEach((section) => {
if (section.id !== target.id) {
section.localToCanvasPoints(...section.datas.points).forEach((p) => {
aps.push(new DragMoveAbsorbablePoint(p)); //区段端点
});
}
});
const turnouts = target.queryStore.queryByType<JlTurnout>(JlTurnout.Type);
turnouts.forEach((otherTurnout) => {
if (otherTurnout.id !== target.id) {
const {
pointA: [A],
pointB: [B],
pointC: [C],
} = otherTurnout.datas;
[A, B, C].forEach((p) => {
aps.push(
new DragMoveAbsorbablePoint(otherTurnout.localToCanvasPoint(p)), //道岔端点
);
});
}
});
return aps;
}
function onEditPointCreate(turnout: JlTurnout, dp: DraggablePoint) {
dp.on('transformstart', (e: GraphicTransformEvent) => {
if (e.isShift()) {
turnout.getGraphicApp().setOptions({
absorbablePositions: buildAbsorbablePositions(turnout),
});
}
});
}
const addPointConfig: MenuItemOptions = { name: '添加路径点' };
const clearPointConfig: MenuItemOptions = { name: '清除路径点' };
const turnoutSectionEditMenu: ContextMenu = ContextMenu.init({
name: '道岔区段路径编辑',
groups: [
{
items: [addPointConfig, clearPointConfig],
},
],
});
export class TurnoutPointsInteractionPlugin extends GraphicInteractionPlugin<JlTurnout> {
static Name = 'TurnoutPointsDrag';
static init(app: IDrawApp) {
return new TurnoutPointsInteractionPlugin(app);
}
constructor(app: IGraphicApp) {
super(TurnoutPointsInteractionPlugin.Name, app);
app.registerMenu(turnoutSectionEditMenu);
}
onSectionContextMenu(e: FederatedMouseEvent, section: TurnoutSection) {
const p = section.turnout.screenToLocalPoint(e.global);
addPointConfig.handler = () => {
if (section.port === DevicePort.A) {
const { start } = getWaypointRangeIndex(
[{ x: 0, y: 0 }, ...section.turnout.datas.pointA],
false,
p,
commonTurnoutConsts.lineWidth,
);
const points = section.turnout.datas.pointA;
const ps = points.slice(0, start);
ps.push(new Point(p.x, p.y));
ps.push(...points.slice(start));
section.turnout.datas.pointA = ps;
}
if (section.port === DevicePort.B) {
const { start } = getWaypointRangeIndex(
[{ x: 0, y: 0 }, ...section.turnout.datas.pointB],
false,
p,
commonTurnoutConsts.lineWidth,
);
const points = section.turnout.datas.pointB;
const ps = points.slice(0, start);
ps.push(new Point(p.x, p.y));
ps.push(...points.slice(start));
section.turnout.datas.pointB = ps;
}
if (section.port === DevicePort.C) {
const { start } = getWaypointRangeIndex(
[{ x: 0, y: 0 }, ...section.turnout.datas.pointC],
false,
p,
commonTurnoutConsts.lineWidth,
);
const points = section.turnout.datas.pointC;
const ps = points.slice(0, start);
ps.push(new Point(p.x, p.y));
ps.push(...points.slice(start));
section.turnout.datas.pointC = ps;
}
this.onSelected(section.turnout);
};
clearPointConfig.handler = () => {
if (section.port === DevicePort.A)
section.turnout.datas.pointA = [
section.turnout.datas.pointA[section.turnout.datas.pointA.length - 1],
];
if (section.port === DevicePort.B)
section.turnout.datas.pointB = [
section.turnout.datas.pointB[section.turnout.datas.pointB.length - 1],
];
if (section.port === DevicePort.C)
section.turnout.datas.pointC = [
section.turnout.datas.pointC[section.turnout.datas.pointC.length - 1],
];
const tep = section.turnout.getAssistantAppend<TurnoutEditPlugin>(
TurnoutEditPlugin.Name,
);
if (tep) {
tep.reset();
}
section.turnout.repaint();
};
turnoutSectionEditMenu.open(e.global);
}
bind(g: JlTurnout): void {
g.graphics.fork.eventMode = 'static';
g.graphics.fork.cursor = 'pointer';
g.graphics.fork.hitArea = new ForkHitArea(g);
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.eventMode = 'static';
sectionGraphic.cursor = 'pointer';
sectionGraphic.hitArea = new TurnoutSectionHitArea(sectionGraphic);
sectionGraphic.on(
'rightclick',
(e) => this.onSectionContextMenu(e, sectionGraphic),
sectionGraphic,
);
});
g.graphics.label.eventMode = 'static';
g.graphics.label.cursor = 'pointer';
g.graphics.label.draggable = true;
g.graphics.label.selectable = true;
g.graphics.label.name = 'label';
g.graphics.label.transformSave = true;
g.transformSave = true;
g.on('selected', this.onSelected, this);
g.on('unselected', this.onUnSelected, this);
g.on('transformstart', this.onDragMove, this);
}
unbind(g: JlTurnout): void {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnSelected, this);
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.off('rightclick');
});
g.off('transformstart', this.onDragMove, this);
}
onSelected(g: DisplayObject) {
const turnout = g as JlTurnout;
let tep = turnout.getAssistantAppend<TurnoutEditPlugin>(
TurnoutEditPlugin.Name,
);
if (!tep) {
tep = new TurnoutEditPlugin(turnout, { onEditPointCreate });
turnout.addAssistantAppend(tep);
}
tep.reset();
tep.showAll();
}
onUnSelected(g: DisplayObject) {
const turnout = g as JlTurnout;
const tep = turnout.getAssistantAppend<TurnoutEditPlugin>(
TurnoutEditPlugin.Name,
);
if (tep) {
tep.hideAll();
}
}
filter(...grahpics: JlGraphic[]): JlTurnout[] | undefined {
return grahpics.filter((g) => g.type == JlTurnout.Type) as JlTurnout[];
}
onDragMove(e: GraphicTransformEvent) {
const turnout = e.target as JlTurnout;
this.app.setOptions({
absorbablePositions: buildDragMoveAbsorbablePositions(turnout),
});
}
}
type onTurnoutEditPointCreate = (
turnout: JlTurnout,
dp: DraggablePoint,
) => void;
export interface ITurnoutEditOptions {
onEditPointCreate?: onTurnoutEditPointCreate;
}
export class TurnoutEditPlugin extends GraphicEditPlugin<JlTurnout> {
static Name = 'TurnoutEdit';
options: ITurnoutEditOptions;
editPoints: DraggablePoint[][] = [[], [], []];
labels: VectorText[] = [];
constructor(graphic: JlTurnout, options?: ITurnoutEditOptions) {
super(graphic);
this.name = TurnoutEditPlugin.Name;
this.options = Object.assign({}, options);
this.initEditPoints();
}
reset(): void {
this.destoryEditPoints();
this.removeChildren();
this.initEditPoints();
}
hideAll(): void {
super.hideAll();
}
initEditPoints() {
const cpA = this.graphic.localToCanvasPoints(...this.graphic.datas.pointA);
const cpB = this.graphic.localToCanvasPoints(...this.graphic.datas.pointB);
const cpC = this.graphic.localToCanvasPoints(...this.graphic.datas.pointC);
const cpMap: Map<Point[], IPointData[]> = new Map([
[cpA, this.graphic.datas.pointA],
[cpB, this.graphic.datas.pointB],
[cpC, this.graphic.datas.pointC],
]);
Array.from(cpMap.entries()).forEach(([cpDatas, dataPoints], i) => {
cpDatas.forEach((cpData, j) => {
const dp = new DraggablePoint(cpData);
dp.on('transforming', () => {
const localPoint = this.graphic.canvasToLocalPoint(dp.position);
dataPoints[j].x = localPoint.x;
dataPoints[j].y = localPoint.y;
this.graphic.repaint();
});
if (this.options.onEditPointCreate) {
this.options.onEditPointCreate(this.graphic, dp);
}
this.editPoints[i].push(dp);
});
});
this.editPoints.forEach((cps) => {
this.addChild(...cps);
});
this.labels = ['A', 'B', 'C'].map((str) => {
const vc = new VectorText(str, { fill: AppConsts.assistantElementColor });
vc.setVectorFontSize(14);
vc.anchor.set(0.5);
return vc;
});
this.addChild(...this.labels);
}
destoryEditPoints() {
this.editPoints.forEach((dps) => {
dps.forEach((dp) => {
dp.off('transforming');
dp.destroy();
this.removeChild(dp);
});
});
this.editPoints = [[], [], []];
}
updateEditedPointsPosition() {
const cpA = this.graphic.localToCanvasPoints(...this.graphic.datas.pointA);
const cpB = this.graphic.localToCanvasPoints(...this.graphic.datas.pointB);
const cpC = this.graphic.localToCanvasPoints(...this.graphic.datas.pointC);
[cpA, cpB, cpC].forEach((cps, i) => {
cps.forEach((cp, j) => {
this.editPoints[i][j].position.copyFrom(cp);
if (j === cps.length - 1) {
this.labels[i].position.copyFrom({ x: cp.x, y: cp.y + 12 });
}
});
});
}
}

View File

@ -0,0 +1,38 @@
import { GraphicState, JlGraphicTemplate } from 'jl-graphic';
import { JlTurnout } from './JlTurnout';
import { ITurnoutData } from './TurnoutConfig';
import { StyleType } from 'common/common';
import { GPTurnout } from '../GPTurnout';
import { THTurnout } from '../THTurnout';
export class TurnoutTemplate<
S extends GraphicState,
> extends JlGraphicTemplate<JlTurnout> {
styleType: StyleType;
constructor(
dataTemplate: ITurnoutData,
stateTemplate: S,
styleType: StyleType,
) {
super(JlTurnout.Type, {
dataTemplate,
stateTemplate,
});
this.styleType = styleType;
}
new() {
let turnout: JlTurnout;
switch (this.styleType) {
case StyleType.GP:
turnout = new GPTurnout();
break;
default:
turnout = new THTurnout();
break;
}
turnout.loadData(this.datas);
turnout.loadState(this.states);
return turnout;
}
}