Compare commits

..

9 Commits

Author SHA1 Message Date
joylink_zhaoerwei
f2c40f2a78 道岔 2024-01-19 15:39:33 +08:00
joylink_zhaoerwei
c8eef1fb1c 引入路径修改 2024-01-11 13:54:58 +08:00
joylink_zhaoerwei
b25fee816a 站台车站调整 2024-01-10 15:57:49 +08:00
joylink_zhaoerwei
c4cb584853 站台车站计轴 2024-01-05 17:14:01 +08:00
dong
224f8975f8 代码调整 2024-01-05 16:53:58 +08:00
dong
5488cbea12 列车调整 2024-01-05 15:54:57 +08:00
dong
bc0017057c 列车调整 2024-01-05 15:20:40 +08:00
dong
8076e0bd6d 列车、分隔符组件化替换 2024-01-05 14:57:04 +08:00
dong
e10f41ce32 引入组件库 2024-01-04 17:38:36 +08:00
20 changed files with 362 additions and 3023 deletions

View File

@ -23,7 +23,8 @@
"centrifuge": "^4.0.1", "centrifuge": "^4.0.1",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"google-protobuf": "^3.21.2", "google-protobuf": "^3.21.2",
"jl-graphic": "git+https://git.code.tencent.com/jl-framework/graphic-pixi.git#v0.1.3", "jl-graphic": "git+https://git.code.tencent.com/jl-framework/graphic-pixi.git#v0.1.8",
"rt-graphic-component": "git+https://git.code.tencent.com/jl-framework/rt-graphic-component.git#5eecd80",
"js-base64": "^3.7.5", "js-base64": "^3.7.5",
"pinia": "^2.0.11", "pinia": "^2.0.11",
"quasar": "^2.6.0", "quasar": "^2.6.0",

View File

@ -95,9 +95,9 @@ const stationName = computed(() => {
}); });
const sectionName = computed(() => { const sectionName = computed(() => {
const platform = drawStore.selectedGraphic as Platform; const platform = drawStore.selectedGraphic as Platform;
if (platformModel.refSectionId) { if (platformModel.refSection) {
const refSection = platform.queryStore.queryById<Section>( const refSection = platform.queryStore.queryById<Section>(
platformModel.refSectionId platformModel.refSection
); );
return refSection.datas.code; return refSection.datas.code;
} }

View File

@ -65,10 +65,10 @@ export class PlatformData extends GraphicDataBase implements IPlatformData {
set refStation(v: number) { set refStation(v: number) {
this.data.refStation = v; this.data.refStation = v;
} }
get refSectionId(): number { get refSection(): number {
return this.data.refSectionId; return this.data.refSectionId;
} }
set refSectionId(v: number) { set refSection(v: number) {
this.data.refSectionId = v; this.data.refSectionId = v;
} }
get centralizedStation(): number { get centralizedStation(): number {

View File

@ -33,12 +33,19 @@ import { ConcentrationDividingLineDraw } from 'src/graphics/concentrationDividin
import { Rect, RectTemplate } from 'src/graphics/rect/Rect'; import { Rect, RectTemplate } from 'src/graphics/rect/Rect';
import { RectDraw } from 'src/graphics/rect/RectDrawAssistant'; import { RectDraw } from 'src/graphics/rect/RectDrawAssistant';
import { RectData } from './graphics/RectInteraction'; import { RectData } from './graphics/RectInteraction';
import { Platform, PlatformTemplate } from 'src/graphics/platform/Platform'; import {
Platform,
PlatformDraw,
PlatformTemplate,
} from 'src/graphics/platform/Platform';
import { PlatformData, PlatformState } from './graphics/PlatformInteraction'; import { PlatformData, PlatformState } from './graphics/PlatformInteraction';
import { PlatformDraw } from 'src/graphics/platform/PlatformDrawAssistant'; import {
import { Station, StationTemplate } from 'src/graphics/station/Station'; Station,
import { StationDraw } from 'src/graphics/station/StationDrawAssistant'; StationDraw,
StationTemplate,
} from 'src/graphics/station/Station';
import { StationData, StationState } from './graphics/StationInteraction'; import { StationData, StationState } from './graphics/StationInteraction';
import { StyleType } from 'rt-graphic-component/components/common/common';
import { import {
StationLine, StationLine,
StationLineTemplate, StationLineTemplate,
@ -67,8 +74,11 @@ import {
} from 'src/graphics/axleCounting/AxleCounting'; } from 'src/graphics/axleCounting/AxleCounting';
import { AxleCountingDraw } from 'src/graphics/axleCounting/AxleCountingDrawAssistant'; import { AxleCountingDraw } from 'src/graphics/axleCounting/AxleCountingDrawAssistant';
import { AxleCountingData } from './graphics/AxleCountingInteraction'; import { AxleCountingData } from './graphics/AxleCountingInteraction';
import { Turnout, TurnoutTemplate } from 'src/graphics/turnout/Turnout'; import {
import { TurnoutDraw } from 'src/graphics/turnout/TurnoutDrawAssistant'; Turnout,
TurnoutTemplate,
TurnoutDraw,
} from 'src/graphics/turnout/Turnout';
import { TurnoutData, TurnoutStates } from './graphics/TurnoutInteraction'; import { TurnoutData, TurnoutStates } from './graphics/TurnoutInteraction';
import { RunLine, RunLineTemplate } from 'src/graphics/runLine/RunLine'; import { RunLine, RunLineTemplate } from 'src/graphics/runLine/RunLine';
import { RunLineDraw } from 'src/graphics/runLine/RunLineDrawAssistant'; import { RunLineDraw } from 'src/graphics/runLine/RunLineDrawAssistant';
@ -84,7 +94,8 @@ import { PathLineDraw } from 'src/graphics/pathLine/PathLineDrawAssistant';
import { PathLineData } from './graphics/PathLineInteraction'; import { PathLineData } from './graphics/PathLineInteraction';
import { toStorageTransform } from './graphics/GraphicDataBase'; import { toStorageTransform } from './graphics/GraphicDataBase';
import { SeparatorDraw } from 'src/graphics/separator/SeparatorDrawAssistant'; import { SeparatorDraw } from 'src/graphics/separator/SeparatorDrawAssistant';
import { Separator, SeparatorTemplate } from 'src/graphics/separator/Separator'; import { Separator } from 'src/graphics/separator/Separator';
import { SeparatorTemplate } from 'rt-graphic-component/components/packages/Separator/Separator';
import { SeparatorData } from './graphics/SeparatorInteraction'; import { SeparatorData } from './graphics/SeparatorInteraction';
import { LogicSectionDraw } from 'src/graphics/logicSection/LogicSectionDrawAssistant'; import { LogicSectionDraw } from 'src/graphics/logicSection/LogicSectionDrawAssistant';
import { import {
@ -188,7 +199,7 @@ function constructMenu(app: IDrawApp): (e: FederatedMouseEvent) => void {
const jumpStaitonItems: MenuItemOptions[] = []; const jumpStaitonItems: MenuItemOptions[] = [];
stations.forEach((station) => { stations.forEach((station) => {
const item: MenuItemOptions = { const item: MenuItemOptions = {
name: station.datas.name, name: station.datas.name as string,
handler: () => { handler: () => {
app.makeGraphicCenterShow(station); app.makeGraphicCenterShow(station);
}, },
@ -233,11 +244,21 @@ export function initDrawApp(): IDrawApp {
if (draftType === 'Line') { if (draftType === 'Line') {
new PlatformDraw( new PlatformDraw(
app, app,
new PlatformTemplate(new PlatformData(), new PlatformState()) new PlatformTemplate(
new PlatformData(),
new PlatformState(),
StyleType.TH
),
'svguse:../../drawIcon.svg#icon-platform'
), ),
new StationDraw( new StationDraw(
app, app,
new StationTemplate(new StationData(), new StationState()) new StationTemplate(
new StationData(),
new StationState(),
StyleType.TH
),
'svguse:../../drawIcon.svg#icon-station'
), ),
new SignalDraw( new SignalDraw(
app, app,
@ -254,7 +275,11 @@ export function initDrawApp(): IDrawApp {
), ),
new TurnoutDraw( new TurnoutDraw(
app, app,
new TurnoutTemplate(new TurnoutData(), new TurnoutStates()) new TurnoutTemplate(
new TurnoutData(),
new TurnoutStates(),
StyleType.TH
)
), ),
new TrainWindowDraw(app, new TrainWindowTemplate(new TrainWindowData())), new TrainWindowDraw(app, new TrainWindowTemplate(new TrainWindowData())),
new OneClickGenerateDraw(app, new OneClickGenerateTemplate()), new OneClickGenerateDraw(app, new OneClickGenerateTemplate()),
@ -262,7 +287,12 @@ export function initDrawApp(): IDrawApp {
app, app,
new AxleCountingTemplate(new AxleCountingData()) new AxleCountingTemplate(new AxleCountingData())
), ),
new SeparatorDraw(app, new SeparatorTemplate(new SeparatorData())), new SeparatorDraw(
app,
new SeparatorTemplate(new SeparatorData(), {
lineColor: '0x617799',
})
),
new ConcentrationDividingLineDraw( new ConcentrationDividingLineDraw(
app, app,
new ConcentrationDividingLineTemplate( new ConcentrationDividingLineTemplate(

View File

@ -23,7 +23,7 @@ import {
PlatformOperateInteraction, PlatformOperateInteraction,
PlatformState, PlatformState,
} from './graphics/PlatformInteraction'; } from './graphics/PlatformInteraction';
import { PlatformTemplate, Platform } from 'src/graphics/platform/Platform'; import { Platform, PlatformTemplate } from 'src/graphics/platform/Platform';
import { import {
StationData, StationData,
StationOperateInteraction, StationOperateInteraction,
@ -58,7 +58,7 @@ import {
TrainWindowTemplate, TrainWindowTemplate,
} from 'src/graphics/trainWindow/TrainWindow'; } from 'src/graphics/trainWindow/TrainWindow';
import { TrainWindowData } from './graphics/TrainWindowInteraction'; import { TrainWindowData } from './graphics/TrainWindowInteraction';
import { SeparatorTemplate } from 'src/graphics/separator/Separator'; import { SeparatorTemplate } from 'rt-graphic-component/components/packages/Separator/Separator';
import { SeparatorData } from './graphics/SeparatorInteraction'; import { SeparatorData } from './graphics/SeparatorInteraction';
import { ContextMenu, MenuItemOptions } from 'jl-graphic'; import { ContextMenu, MenuItemOptions } from 'jl-graphic';
import { import {
@ -73,6 +73,7 @@ import {
import { Notify, QNotifyUpdateOptions } from 'quasar'; import { Notify, QNotifyUpdateOptions } from 'quasar';
import { useLineNetStore } from 'src/stores/line-net-store'; import { useLineNetStore } from 'src/stores/line-net-store';
import { alert } from 'src/protos/alertInfo'; import { alert } from 'src/protos/alertInfo';
import { StyleType } from 'rt-graphic-component/components/common/common';
let lineApp: IGraphicApp | null = null; let lineApp: IGraphicApp | null = null;
@ -111,12 +112,14 @@ export function initLineApp(): IGraphicApp {
const graphicTemplate = [ const graphicTemplate = [
new TrainTemplate(new TrainData(), new TrainState()), new TrainTemplate(new TrainData(), new TrainState()),
new SignalTemplate(new SignalData(), new SignalState()), new SignalTemplate(new SignalData(), new SignalState()),
new PlatformTemplate(new PlatformData(), new PlatformState()), new PlatformTemplate(new PlatformData(), new PlatformState(), StyleType.TH),
new StationTemplate(new StationData(), new StationState()), new StationTemplate(new StationData(), new StationState(), StyleType.TH),
new TurnoutTemplate(new TurnoutData(), new TurnoutStates()), new TurnoutTemplate(new TurnoutData(), new TurnoutStates(), StyleType.TH),
new SectionTemplate(new SectionData()), new SectionTemplate(new SectionData()),
new LogicSectionTemplate(new LogicSectionData(), new LogicSectionState()), new LogicSectionTemplate(new LogicSectionData(), new LogicSectionState()),
new SeparatorTemplate(new SeparatorData()), new SeparatorTemplate(new SeparatorData(), {
lineColor: '0x617799',
}),
new AxleCountingTemplate(new AxleCountingData()), new AxleCountingTemplate(new AxleCountingData()),
new TrainWindowTemplate(new TrainWindowData()), new TrainWindowTemplate(new TrainWindowData()),
]; ];

View File

@ -19,7 +19,7 @@ import {
LogicSectionData, LogicSectionData,
LogicSectionState, LogicSectionState,
} from './graphics/LogicSectionInteraction'; } from './graphics/LogicSectionInteraction';
import { SeparatorTemplate } from 'src/graphics/separator/Separator'; import { SeparatorTemplate } from 'rt-graphic-component/components/packages/Separator/Separator';
import { AxleCountingTemplate } from 'src/graphics/axleCounting/AxleCounting'; import { AxleCountingTemplate } from 'src/graphics/axleCounting/AxleCounting';
import { SignalData, SignalState } from './graphics/SignalInteraction'; import { SignalData, SignalState } from './graphics/SignalInteraction';
import { PlatformData, PlatformState } from './graphics/PlatformInteraction'; import { PlatformData, PlatformState } from './graphics/PlatformInteraction';
@ -32,6 +32,7 @@ import { getPublishMapInfoByLineId } from 'src/api/PublishApi';
import { useRangeConfigStore } from 'src/stores/range-config-store'; import { useRangeConfigStore } from 'src/stores/range-config-store';
import { graphicData } from 'src/protos/stationLayoutGraphics'; import { graphicData } from 'src/protos/stationLayoutGraphics';
import { toUint8Array } from 'js-base64'; import { toUint8Array } from 'js-base64';
import { StyleType } from 'rt-graphic-component/components/common/common';
let rangeConfigApp: IGraphicApp; let rangeConfigApp: IGraphicApp;
@ -57,9 +58,9 @@ export function initRangeConfigApp(lineId: number) {
}); });
const graphicTemplate = [ const graphicTemplate = [
new SignalTemplate(new SignalData(), new SignalState()), new SignalTemplate(new SignalData(), new SignalState()),
new PlatformTemplate(new PlatformData(), new PlatformState()), new PlatformTemplate(new PlatformData(), new PlatformState(), StyleType.TH),
new StationTemplate(new StationData(), new StationState()), new StationTemplate(new StationData(), new StationState(), StyleType.TH),
new TurnoutTemplate(new TurnoutData(), new TurnoutStates()), new TurnoutTemplate(new TurnoutData(), new TurnoutStates(), StyleType.TH),
new SectionTemplate(new SectionData()), new SectionTemplate(new SectionData()),
new LogicSectionTemplate(new LogicSectionData(), new LogicSectionState()), new LogicSectionTemplate(new LogicSectionData(), new LogicSectionState()),
new SeparatorTemplate(new SeparatorData()), new SeparatorTemplate(new SeparatorData()),

View File

@ -1,116 +1,9 @@
import { Color, Container, Graphics } from 'pixi.js';
import { import {
GraphicData, AxleCounting,
GraphicRelationParam, IAxleCountingData,
JlGraphic, AxleCountingTemplate,
JlGraphicTemplate, AxleCountingConsts,
VectorText, } from 'rt-graphic-component/components/packages/AxleCounting/AxleCounting';
} from 'jl-graphic';
import { IRelatedRefData, protoPort2Data } from '../CommonGraphics';
import { KilometerSystem } from '../signal/Signal';
export interface IAxleCountingData extends GraphicData { export { AxleCounting, AxleCountingTemplate, AxleCountingConsts };
get code(): string; // 编号 export type { IAxleCountingData };
set code(v: string);
get kilometerSystem(): KilometerSystem;
set kilometerSystem(v: KilometerSystem);
get axleCountingRef(): IRelatedRefData[]; //关联的设备
set axleCountingRef(ref: IRelatedRefData[]);
clone(): IAxleCountingData;
copyFrom(data: IAxleCountingData): void;
eq(other: IAxleCountingData): boolean;
}
export const AxleCountingConsts = {
radius: 6,
borderWidth: 1,
circleColorBlue: '0x08F80D',
codeColor: '0xF48815',
codeFontSize: 22,
codeOffsetY: 30,
kilometerCodeColor: '0xFFFFFF',
kilometerCodeFontSize: 14,
kilometerCodeOffsetY: 95,
offsetSection: 50,
};
class TwoCircleGraphic extends Container {
circleA: Graphics = new Graphics();
circleB: Graphics = new Graphics();
line: Graphics = new Graphics();
constructor() {
super();
this.addChild(this.circleA);
this.addChild(this.circleB);
this.addChild(this.line);
}
draw(): void {
this.drawCircle(this.circleA);
this.drawCircle(this.circleB);
this.circleA.position.set(-12, 0);
this.circleB.position.set(12, 0);
this.line.clear();
this.line.lineStyle(1, new Color(AxleCountingConsts.circleColorBlue));
this.line.moveTo(-24, 0);
this.line.lineTo(24, 0);
}
drawCircle(circle: Graphics): void {
circle.clear();
circle.lineStyle(
AxleCountingConsts.borderWidth,
new Color(AxleCountingConsts.circleColorBlue)
);
circle.beginFill(AxleCountingConsts.circleColorBlue, 1);
circle.drawCircle(0, 0, AxleCountingConsts.radius);
circle.endFill;
}
clear(): void {
this.circleA.clear();
this.circleB.clear();
}
}
export class AxleCounting extends JlGraphic {
static Type = 'AxleCounting';
twoCircle: TwoCircleGraphic = new TwoCircleGraphic();
kilometerGraph: VectorText = new VectorText(''); //公里标
direction: number;
constructor(direction: number) {
super(AxleCounting.Type);
this.addChild(this.twoCircle);
this.addChild(this.kilometerGraph);
this.kilometerGraph.name = 'kilometer';
this.direction = direction;
}
get datas(): IAxleCountingData {
return this.getDatas<IAxleCountingData>();
}
doRepaint(): void {
this.twoCircle.draw();
}
loadRelations(): void {
if (this.datas.axleCountingRef.length) {
this.datas.axleCountingRef.forEach((device) => {
this.relationManage.addRelation(
new GraphicRelationParam(this, 'A'),
new GraphicRelationParam(
this.queryStore.queryById(device.id),
protoPort2Data(device.devicePort)
)
);
});
}
}
}
export class AxleCountingTemplate extends JlGraphicTemplate<AxleCounting> {
constructor(dataTemplate: IAxleCountingData) {
super(AxleCounting.Type, {
dataTemplate,
});
}
new(): AxleCounting {
const axleCounting = new AxleCounting(1);
axleCounting.loadData(this.datas);
return axleCounting;
}
}

View File

@ -112,14 +112,7 @@ export class AxleCountingDraw extends GraphicDrawAssistant<
const refData2 = createRelatedRefProto(graphic.type, graphic.id, port); const refData2 = createRelatedRefProto(graphic.type, graphic.id, port);
const axleCounting = new AxleCounting(direction); const axleCounting = new AxleCounting(direction);
axleCounting.loadData(this.graphicTemplate.datas); axleCounting.loadData(this.graphicTemplate.datas);
if (graphic.type == 'Turnout') { axleCounting.position.set(ps.x, ps.y);
axleCounting.position.set(ps.x, ps.y);
} else {
axleCounting.position.set(
ps.x,
ps.y - AxleCountingConsts.offsetSection * direction
);
}
axleCounting.id = GraphicIdGenerator.next(); axleCounting.id = GraphicIdGenerator.next();
axleCounting.datas.axleCountingRef = [refData2, refData1]; axleCounting.datas.axleCountingRef = [refData2, refData1];
axleCounting.datas.code = `${graphic.datas.code}-${port}+${refGraphic.datas.code}-${refPort}`; axleCounting.datas.code = `${graphic.datas.code}-${port}+${refGraphic.datas.code}-${refPort}`;
@ -139,14 +132,7 @@ export class AxleCountingDraw extends GraphicDrawAssistant<
const refData = createRelatedRefProto(graphic.type, graphic.id, port); const refData = createRelatedRefProto(graphic.type, graphic.id, port);
const axleCounting = new AxleCounting(direction); const axleCounting = new AxleCounting(direction);
axleCounting.loadData(this.graphicTemplate.datas); axleCounting.loadData(this.graphicTemplate.datas);
if (graphic.type == 'Turnout') { axleCounting.position.set(ps.x, ps.y);
axleCounting.position.set(ps.x, ps.y);
} else {
axleCounting.position.set(
ps.x,
ps.y - AxleCountingConsts.offsetSection * direction
);
}
axleCounting.id = GraphicIdGenerator.next(); axleCounting.id = GraphicIdGenerator.next();
axleCounting.datas.axleCountingRef = [refData]; axleCounting.datas.axleCountingRef = [refData];
axleCounting.datas.code = `${graphic.datas.code}-${port}`; axleCounting.datas.code = `${graphic.datas.code}-${port}`;
@ -362,21 +348,12 @@ export class AxleCountingInteraction extends GraphicInteractionPlugin<AxleCounti
g.cursor = 'pointer'; g.cursor = 'pointer';
g.scalable = true; g.scalable = true;
g.rotatable = true; g.rotatable = true;
g.kilometerGraph.eventMode = 'static';
g.kilometerGraph.cursor = 'pointer';
g.kilometerGraph.draggable = true;
g.kilometerGraph.selectable = true;
g.kilometerGraph.transformSave = true;
g.on('selected', this.onSelected, this); g.on('selected', this.onSelected, this);
} }
unbind(g: AxleCounting): void { unbind(g: AxleCounting): void {
g.eventMode = 'none'; g.eventMode = 'none';
g.scalable = false; g.scalable = false;
g.rotatable = false; g.rotatable = false;
g.kilometerGraph.eventMode = 'none';
g.kilometerGraph.draggable = false;
g.kilometerGraph.selectable = false;
g.kilometerGraph.transformSave = false;
g.off('selected', this.onSelected, this); g.off('selected', this.onSelected, this);
} }
onSelected(): void { onSelected(): void {

View File

@ -1,487 +1,8 @@
import { Color, Container, Graphics, Point, Rectangle } from 'pixi.js'; import { THPlatform as Platform } from 'rt-graphic-component/components/packages/Platform/THPlatform';
import { import { IPlatformData } from 'rt-graphic-component/components/packages/Platform/common/PlatformConfig';
GraphicData, import { PlatformTemplate } from 'rt-graphic-component/components/packages/Platform/common/PlatformTemplate';
GraphicState, import { ITHPlatformState as IPlatformState } from 'rt-graphic-component/components/packages/Platform/THPlatform';
JlGraphic, import { PlatformDraw } from 'rt-graphic-component/components/packages/Platform/common/PlatformDrawAssistant';
JlGraphicTemplate,
VectorText,
calculateMirrorPoint,
distance2,
getRectangleCenter,
} from 'jl-graphic';
import { Station } from '../station/Station';
import { Section } from '../section/Section';
export interface IPlatformData extends GraphicData { export { Platform, PlatformTemplate, PlatformDraw };
get code(): string; // 编号 export type { IPlatformState, IPlatformData };
set code(v: string);
get hasdoor(): boolean; // 是否有屏蔽门
set hasdoor(v: boolean);
get direction(): string; // 屏蔽门上下
set direction(v: string);
get up(): boolean; // 站台上下行
set up(v: boolean);
get refStation(): number; // 关联的车站
set refStation(v: number);
get refSectionId(): number; // 关联的物理区段
set refSectionId(v: number);
get centralizedStation(): number; //所属集中站
set centralizedStation(v: number);
clone(): IPlatformData;
copyFrom(data: IPlatformData): void;
eq(other: IPlatformData): boolean;
}
export interface IPlatformState extends GraphicState {
get emergstop(): boolean; //紧急关闭
set emergstop(v: boolean);
get trainberth(): boolean; //列车停站
set trainberth(v: boolean);
get close(): boolean; //站台关闭,清客
set close(v: boolean);
get upHold(): boolean; //上行方向车站扣车
set upHold(v: boolean);
get downHold(): boolean; //下行方向车站扣车
set downHold(v: boolean);
get upOccHold(): boolean; //上行方向中心扣车
set upOccHold(v: boolean);
get downOccHold(): boolean; //下行方向中心扣车
set downOccHold(v: boolean);
get psdOpen(): boolean; //屏蔽门开
set psdOpen(v: boolean);
get psdCut(): boolean; //屏蔽门切除
set psdCut(v: boolean);
get upSkipstop(): boolean; //上行方向跳停
set upSkipstop(v: boolean);
get downSkipstop(): boolean; //下行方向跳停
set downSkipstop(v: boolean);
get upTrainSkipstop(): boolean; //上行方向指定列车跳停
set upTrainSkipstop(v: boolean);
get downTrainSkipstop(): boolean; //下行方向指定列车跳停
set downTrainSkipstop(v: boolean);
get nextSectionRunTime(): number; //下一区间运行时间
set nextSectionRunTime(v: number);
get nextSectionRunLevel(): number; //下一区间运行等级
set nextSectionRunLevel(v: number);
get stopTime(): number; //停站时间
set stopTime(v: number);
get rtuId(): number; // 集中站站号
set rtuId(v: number);
}
//站台颜色
export enum PlatformColorEnum {
grey = '0x7F7F7F', //站台没有列车停站
yellow = '0xfbff00', //列车在站台停站
blue = '0xC0C0FE', //列车在站台跳停
lozengeRed = '0xff0000', //站台旁的菱形图标
whiteNumbers = '0xffffff', //站台旁白色数字
whiteCircle = '0xffffff', //H字符旁的圆圈
HCharYellow = '0xfbff00', //站台旁的H字符
HCharWhite = '0xffffff',
HCharRed = '0xff0000',
doorGreen = '0x00FF00', //屏蔽门的颜色
doorRed = '0xff0000',
doorBlue = '0x4048C4',
blueShowColor = '0x3149c3',
}
const platformConsts = {
width: 90,
height: 20,
lineWidth: 3,
besideFontSize: 12,
doorOpenSpacing: 15,
doorPlatformSpacing: 10,
besideSpacing: 10,
circleRadius: 1,
};
//子元素--矩形
export class rectGraphic extends Container {
static Type = 'RectPlatForm';
rectGraphic: Graphics;
constructor() {
super();
this.rectGraphic = new Graphics();
this.addChild(this.rectGraphic);
}
draw(state: IPlatformState): void {
const rectGraphic = this.rectGraphic;
rectGraphic.clear();
let fillColor = PlatformColorEnum.grey;
if (state.trainberth) {
fillColor = PlatformColorEnum.yellow;
}
if (state.upSkipstop || state.downSkipstop) {
fillColor = PlatformColorEnum.blue;
}
rectGraphic.lineStyle(platformConsts.lineWidth, new Color(fillColor));
rectGraphic.beginFill(fillColor, 1);
rectGraphic.drawRect(0, 0, platformConsts.width, platformConsts.height);
rectGraphic.endFill;
const rectP = new Rectangle(
0,
0,
platformConsts.width,
platformConsts.height
);
rectGraphic.pivot = getRectangleCenter(rectP);
}
clear(): void {
this.rectGraphic.clear();
}
}
//子元素--门
export class doorGraphic extends Container {
static Type = 'Door';
doorGraphic: Graphics;
doorCloseGraphic: Graphics;
constructor() {
super();
this.doorGraphic = new Graphics();
this.doorCloseGraphic = new Graphics();
this.addChild(this.doorGraphic);
this.addChild(this.doorCloseGraphic);
}
draw(stateData: IPlatformState, ipRtuStusDown: boolean): void {
const doorGraphic = this.doorGraphic;
const doorCloseGraphic = this.doorCloseGraphic;
doorGraphic.clear();
doorCloseGraphic.clear();
let lineColor = PlatformColorEnum.doorGreen;
if (ipRtuStusDown) {
lineColor = PlatformColorEnum.blueShowColor;
} else if (stateData.psdCut) {
lineColor = PlatformColorEnum.doorRed;
}
doorGraphic.lineStyle(platformConsts.lineWidth, new Color(lineColor));
doorGraphic.moveTo(
-platformConsts.width / 2 - platformConsts.lineWidth / 2,
0
);
doorGraphic.lineTo(-platformConsts.doorOpenSpacing, 0);
doorGraphic.moveTo(platformConsts.doorOpenSpacing, 0);
doorGraphic.lineTo(
platformConsts.width / 2 + platformConsts.lineWidth / 2,
0
);
//屏蔽门闭合
doorCloseGraphic.lineStyle(platformConsts.lineWidth, new Color(lineColor));
doorCloseGraphic.moveTo(-platformConsts.doorOpenSpacing, 0);
doorCloseGraphic.lineTo(platformConsts.doorOpenSpacing, 0);
}
clear(): void {
this.doorGraphic.clear();
this.doorCloseGraphic.clear();
}
changeState(stateData: IPlatformState): void {
if (stateData.psdOpen) {
this.doorCloseGraphic.visible = false;
} else {
this.doorCloseGraphic.visible = true;
}
}
}
//子元素--字符
class codeGraph extends Container {
static Type = 'Code';
character: VectorText = new VectorText(''); //扣车H
runLevel: VectorText = new VectorText(''); //运行等级
runTime: VectorText = new VectorText(''); //运行时间
stopTime: VectorText = new VectorText(''); //停站时间
circle: Graphics = new Graphics();
constructor() {
super();
this.addChild(this.character);
this.addChild(this.runLevel);
this.addChild(this.circle);
this.addChild(this.stopTime);
this.addChild(this.runTime);
this.character.setVectorFontSize(platformConsts.besideFontSize);
this.runLevel.setVectorFontSize(platformConsts.besideFontSize);
this.stopTime.setVectorFontSize(platformConsts.besideFontSize);
this.runTime.setVectorFontSize(platformConsts.besideFontSize);
}
draw(): void {
//扣车
const character = this.character;
character.text = 'H';
character.anchor.set(0.5);
character.position.set(
-platformConsts.width / 2 -
platformConsts.lineWidth / 2 -
(platformConsts.besideSpacing * 2) / 3,
(platformConsts.height * 3) / 4
);
character.style.fill = PlatformColorEnum.whiteNumbers;
const circle = this.circle;
circle.clear();
circle.lineStyle(0.5, PlatformColorEnum.whiteCircle);
circle.drawCircle(0, 0, platformConsts.circleRadius);
circle.position.set(
-platformConsts.width / 2 -
platformConsts.lineWidth / 2 -
(platformConsts.besideSpacing * 4) / 3,
(platformConsts.height * 3) / 5
);
//区间运行等级状态
const runLevel = this.runLevel;
runLevel.anchor.set(0.5);
runLevel.position.set(
platformConsts.width / 2 +
platformConsts.lineWidth / 2 +
3 * platformConsts.besideSpacing,
-platformConsts.besideSpacing
);
runLevel.style.fill = PlatformColorEnum.whiteNumbers;
//区间运行时间
const runTime = this.runTime;
runTime.anchor.set(0.5);
runTime.position.set(
platformConsts.width / 2 +
platformConsts.lineWidth / 2 +
platformConsts.besideSpacing,
-platformConsts.besideSpacing
);
runTime.style.fill = PlatformColorEnum.whiteNumbers;
//停站时间
const stopTime = this.stopTime;
stopTime.anchor.set(0.5);
stopTime.position.set(
platformConsts.width / 2 +
platformConsts.lineWidth / 2 +
platformConsts.besideSpacing,
platformConsts.besideSpacing
);
stopTime.style.fill = PlatformColorEnum.whiteNumbers;
character.visible = false;
circle.visible = false;
runLevel.visible = false;
stopTime.visible = false;
runTime.visible = false;
}
clear(): void {
this.character.destroy();
}
changeState(stateData: IPlatformState): void {
if (
stateData.upHold ||
stateData.upOccHold ||
stateData.downHold ||
stateData.downOccHold
) {
this.character.text = 'H';
this.character.visible = true;
this.circle.visible = true;
//上行扣车
if (stateData.upHold) {
this.character.style.fill = PlatformColorEnum.HCharYellow;
}
if (stateData.upOccHold) {
this.character.style.fill = PlatformColorEnum.HCharWhite;
}
if (stateData.upHold && stateData.upOccHold) {
this.character.style.fill = PlatformColorEnum.HCharRed;
}
//下行扣车
if (stateData.downHold) {
this.character.style.fill = PlatformColorEnum.HCharYellow;
}
if (stateData.downOccHold) {
this.character.style.fill = PlatformColorEnum.HCharWhite;
}
if (stateData.downHold && stateData.downOccHold) {
this.character.style.fill = PlatformColorEnum.HCharRed;
}
}
//运行等级
if (stateData.nextSectionRunLevel) {
this.runLevel.visible = false;
this.runLevel.text = stateData.nextSectionRunLevel;
}
//运行时间
if (stateData.nextSectionRunTime) {
this.runTime.visible = true;
this.runTime.text = stateData.nextSectionRunTime;
}
//停站时间
if (stateData.stopTime) {
this.stopTime.visible = true;
this.stopTime.text = stateData.stopTime;
}
}
}
//子元素--站台旁菱形图标
class besideGraphic extends Container {
static Type = 'BesideGraphic';
besideGraphic: Graphics;
constructor() {
super();
this.besideGraphic = new Graphics();
this.addChild(this.besideGraphic);
}
draw(): void {
const besideGraphic = this.besideGraphic;
besideGraphic.clear();
besideGraphic.lineStyle(1, new Color(PlatformColorEnum.lozengeRed));
besideGraphic.beginFill(PlatformColorEnum.lozengeRed, 1);
besideGraphic.drawRect(
0,
0,
platformConsts.height / 4,
platformConsts.height / 4
);
besideGraphic.endFill();
const rect = new Rectangle(
0,
0,
platformConsts.height / 4,
platformConsts.height / 4
);
besideGraphic.pivot = getRectangleCenter(rect);
besideGraphic.rotation = Math.PI / 4;
besideGraphic.visible = false;
}
clear(): void {
this.besideGraphic.clear();
}
changeState(stateData: IPlatformState): void {
if (stateData.emergstop) {
this.besideGraphic.visible = true;
} else {
this.besideGraphic.visible = false;
}
}
}
export class Platform extends JlGraphic {
static Type = 'Platform';
platformGraphic: rectGraphic = new rectGraphic();
doorGraphic: doorGraphic = new doorGraphic();
besideGraphic: besideGraphic = new besideGraphic();
codeGraph: codeGraph = new codeGraph();
constructor() {
super(Platform.Type);
this.addChild(this.platformGraphic);
this.addChild(this.doorGraphic);
this.addChild(this.besideGraphic);
this.addChild(this.codeGraph);
}
get datas(): IPlatformData {
return this.getDatas<IPlatformData>();
}
get states(): IPlatformState {
return this.getStates<IPlatformState>();
}
get code(): string {
return this.datas.code;
}
doRepaint(): void {
const station = this.getGraphicApp().queryStore.queryByCodeAndType<Station>(
this.states.rtuId > 9 ? '' + this.states.rtuId : '0' + this.states.rtuId,
Station.Type
);
this.doorGraphic.clear();
if (this.datas.hasdoor) {
this.doorGraphic.draw(this.states, !!station?.states.ipRtuStusDown);
}
this.platformGraphic.draw(this.states);
this.besideGraphic.draw();
this.codeGraph.draw();
this.doorGraphic.position.set(
0,
-platformConsts.height / 2 - platformConsts.doorPlatformSpacing
);
this.besideGraphic.position.set(
0,
-platformConsts.height / 2 -
platformConsts.doorPlatformSpacing -
platformConsts.height / 3
);
this.codeGraph.position.set(0, 0);
//站台方向
if (this.datas.direction == 'down') {
const psChange = [
this.doorGraphic,
this.besideGraphic,
this.codeGraph.children[0],
this.codeGraph.children[1],
this.codeGraph.children[3],
this.codeGraph.children[4],
];
psChange.forEach((g) => {
g.position.copyFrom(calculateMirrorPoint(new Point(0, 0), g.position));
});
this.codeGraph.children[2].position.set(
platformConsts.width / 2 +
platformConsts.lineWidth / 2 +
(platformConsts.besideSpacing * 4) / 3,
(-platformConsts.height * 10) / 11
);
}
if (station?.states.ipRtuStusDown) {
return;
}
this.changeState();
}
changeState(): void {
this.doorGraphic.changeState(this.states);
this.besideGraphic.changeState(this.states);
this.codeGraph.changeState(this.states);
}
buildRelation() {
const stationas = this.queryStore.queryByType<Station>(Station.Type);
for (let i = 0; i < stationas.length; i++) {
const sP = stationas[i].localBoundsToCanvasPoints();
if (this.x > sP[0].x && this.x < sP[1].x) {
this.datas.refStation = stationas[i].id;
break;
}
}
const sections = this.queryStore.queryByType<Section>(Section.Type);
const minDistanceRefSections: Section[] = [];
sections.forEach((section) => {
const sP = section.localBoundsToCanvasPoints();
if (this.x > sP[0].x && this.x < sP[1].x) {
minDistanceRefSections.push(section);
}
});
if (minDistanceRefSections) {
const refSection = minDistanceRefSections.reduce((prev, cur) => {
return distance2(
prev.localToCanvasPoint(getRectangleCenter(prev.getLocalBounds())),
this.position
) >
distance2(
cur.localToCanvasPoint(getRectangleCenter(cur.getLocalBounds())),
this.position
)
? cur
: prev;
});
this.datas.refSectionId = refSection.id;
}
}
}
export class PlatformTemplate extends JlGraphicTemplate<Platform> {
hasdoor: boolean;
direction: string;
constructor(dataTemplate: IPlatformData, stateTemplate: IPlatformState) {
super(Platform.Type, {
dataTemplate,
stateTemplate,
});
this.hasdoor = true;
this.direction = 'up';
}
new(): Platform {
const platform = new Platform();
platform.loadData(this.datas);
platform.loadState(this.states);
return platform;
}
}

View File

@ -1,109 +0,0 @@
import { FederatedPointerEvent, Point } from 'pixi.js';
import {
AbsorbableLine,
AbsorbablePosition,
GraphicDrawAssistant,
GraphicInteractionPlugin,
IDrawApp,
JlGraphic,
} from 'jl-graphic';
import { IPlatformData, Platform, PlatformTemplate } from './Platform';
export interface IPlatformDrawOptions {
newData: () => IPlatformData;
}
export class PlatformDraw extends GraphicDrawAssistant<
PlatformTemplate,
IPlatformData
> {
platformGraphic: Platform;
constructor(app: IDrawApp, template: PlatformTemplate) {
super(
app,
template,
'svguse:../../drawIcon.svg#icon-platform',
'站台Platform'
);
this.platformGraphic = this.graphicTemplate.new();
this.container.addChild(this.platformGraphic);
platformInteraction.init(app);
}
bind(): void {
super.bind();
this.platformGraphic.loadData(this.graphicTemplate.datas);
this.platformGraphic.doRepaint();
}
clearCache(): void {
//this.platformGraphic.clear();
}
onLeftDown(e: FederatedPointerEvent): void {
this.container.position.copyFrom(this.toCanvasCoordinates(e.global));
this.createAndStore(true);
}
redraw(p: Point): void {
this.container.position.copyFrom(p);
}
prepareData(data: IPlatformData): boolean {
const template = this.graphicTemplate;
data.hasdoor = template.hasdoor;
data.direction = template.direction;
data.transform = this.container.saveTransform();
return true;
}
}
function buildAbsorbablePositions(platform: Platform): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const platforms = platform.queryStore.queryByType<Platform>(Platform.Type);
const { width, height } = platform.getGraphicApp().canvas;
platforms.forEach((other) => {
if (other.id == platform.id) {
return;
}
const ps = other.datas.transform.position;
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 });
aps.push(xs, ys);
});
return aps;
}
export class platformInteraction extends GraphicInteractionPlugin<Platform> {
static Name = 'platform_transform';
constructor(app: IDrawApp) {
super(platformInteraction.Name, app);
}
static init(app: IDrawApp) {
return new platformInteraction(app);
}
filter(...grahpics: JlGraphic[]): Platform[] | undefined {
return grahpics
.filter((g) => g.type === Platform.Type)
.map((g) => g as Platform);
}
bind(g: Platform): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.scalable = true;
g.rotatable = true;
g.on('selected', this.onSelected, this);
}
unbind(g: Platform): void {
g.eventMode = 'none';
g.scalable = false;
g.rotatable = false;
g.off('selected', this.onSelected, this);
}
onSelected(): void {
const platform = this.app.selectedGraphics[0] as Platform;
this.app.setOptions({
absorbablePositions: buildAbsorbablePositions(platform),
});
}
}

View File

@ -244,25 +244,18 @@ export class SectionGraphicHitArea implements IHitArea {
function buildAbsorbablePositions(section: Section): AbsorbablePosition[] { function buildAbsorbablePositions(section: Section): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = []; const aps: AbsorbablePosition[] = [];
const [ps, pe] = [ const sections = section.queryStore.queryByType<Section>(Section.Type);
section.localToCanvasPoint(section.getStartPoint()), sections.forEach((other) => {
section.localToCanvasPoint(section.getEndPoint()), const [ps, pe] = [
]; other.localToCanvasPoint(other.getStartPoint()),
const { width, height } = section.getGraphicApp().canvas; other.localToCanvasPoint(other.getEndPoint()),
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 { width, height } = section.getGraphicApp().canvas;
const xe = new AbsorbableLine({ x: 0, y: pe.y }, { x: width, y: pe.y }); const xs = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y });
const ye = new AbsorbableLine({ x: pe.x, y: 0 }, { x: pe.x, y: height }); const ys = new AbsorbableLine({ x: ps.x, y: 0 }, { x: ps.x, y: height });
aps.push(xs, ys, xe, ye); const xe = new AbsorbableLine({ x: 0, y: pe.y }, { x: width, y: pe.y });
const sections = section.queryStore const ye = new AbsorbableLine({ x: pe.x, y: 0 }, { x: pe.x, y: height });
.queryByType<Section>(Section.Type) aps.push(xs, ys, xe, ye);
.filter((g) => g.datas.sectionType == SectionType.Physical);
sections.forEach((item) => {
if (item.id !== section.id) {
item.localToCanvasPoints(...item.datas.points).forEach((p) => {
aps.push(new AbsorbablePoint(p));
});
}
}); });
const turnouts = section.queryStore.queryByType<Turnout>(Turnout.Type); const turnouts = section.queryStore.queryByType<Turnout>(Turnout.Type);

View File

@ -1,94 +1,8 @@
import { Color, Graphics } from 'pixi.js'; import { Separator } from 'rt-graphic-component/components/packages/Separator/Separator';
import { GraphicData, JlGraphic, JlGraphicTemplate } from 'jl-graphic'; import {
ISeparatorData,
separatorTypeEnum,
} from 'rt-graphic-component/components/packages/Separator/SeparatorConfig';
export interface ISeparatorData extends GraphicData { export { Separator, separatorTypeEnum };
get code(): string; // 编号 export type { ISeparatorData };
set code(v: string);
get separatorType(): string; // 类型
set separatorType(v: string);
clone(): ISeparatorData;
copyFrom(data: ISeparatorData): void;
eq(other: ISeparatorData): boolean;
}
export enum separatorTypeEnum {
turnout = 'turnout', // 道岔分隔符
endA = 'endA', // A端尽头分隔符
endB = 'endB', // B端尽头分隔符
section = 'section', // 区段分隔符
}
export const SeparatorConsts = {
height: 12,
lineWidth: 2,
lineColor: '0x617799',
circleColor: '0xEF0200',
radius: 5,
};
export class Separator extends JlGraphic {
static Type = 'Separator';
rectGraphic: Graphics = new Graphics();
circleGraphic: Graphics = new Graphics();
constructor() {
super(Separator.Type);
this.addChild(this.rectGraphic);
this.addChild(this.circleGraphic);
}
get datas(): ISeparatorData {
return this.getDatas<ISeparatorData>();
}
clear() {
this.rectGraphic.clear();
this.circleGraphic.clear();
}
doRepaint(): void {
this.clear();
const rectGraphic = this.rectGraphic;
if (!this.datas.separatorType) {
this.datas.separatorType = separatorTypeEnum.endA;
}
const typeArr = ['section', 'turnout'];
if (typeArr.includes(this.datas.separatorType)) {
rectGraphic.lineStyle(
SeparatorConsts.lineWidth,
new Color(SeparatorConsts.lineColor)
);
rectGraphic.moveTo(0, -SeparatorConsts.height / 2);
rectGraphic.lineTo(0, SeparatorConsts.height / 2);
if (this.datas.separatorType == 'turnout') {
this.circleGraphic.lineStyle(1, SeparatorConsts.circleColor);
this.circleGraphic.drawCircle(0, 0, SeparatorConsts.radius);
}
}
const endTypeArr = ['endA', 'endB'];
if (endTypeArr.includes(this.datas.separatorType)) {
let d = SeparatorConsts.radius;
if (this.datas.separatorType == 'endB') {
d = -d;
}
rectGraphic.lineStyle(
SeparatorConsts.lineWidth,
new Color(SeparatorConsts.lineColor)
);
rectGraphic.moveTo(0, 0);
rectGraphic.lineTo(-d, 0);
rectGraphic.lineTo(-d, -d);
rectGraphic.lineTo(-d * 3, -d);
rectGraphic.moveTo(-d, 0);
rectGraphic.lineTo(-d, d);
rectGraphic.lineTo(-d * 3, d);
}
}
}
export class SeparatorTemplate extends JlGraphicTemplate<Separator> {
constructor(dataTemplate: ISeparatorData) {
super(Separator.Type, {
dataTemplate,
});
}
new(): Separator {
return new Separator();
}
}

View File

@ -1,56 +1,18 @@
import { FederatedPointerEvent, IHitArea, Point } from 'pixi.js'; import { Point } from 'pixi.js';
import { import { GraphicIdGenerator, GraphicRelationParam, IDrawApp } from 'jl-graphic';
GraphicDrawAssistant,
GraphicIdGenerator,
GraphicInteractionPlugin,
GraphicRelationParam,
IDrawApp,
JlGraphic,
linePoint,
} from 'jl-graphic';
import { Section, SectionType } from '../section/Section'; import { Section, SectionType } from '../section/Section';
import { import { Separator, separatorTypeEnum } from './Separator';
ISeparatorData,
Separator,
SeparatorConsts,
SeparatorTemplate,
separatorTypeEnum,
} from './Separator';
import { SeparatorData } from 'src/drawApp/graphics/SeparatorInteraction'; import { SeparatorData } from 'src/drawApp/graphics/SeparatorInteraction';
import { Turnout } from '../turnout/Turnout'; import { Turnout } from '../turnout/Turnout';
import { LogicSection } from '../logicSection/LogicSection'; import { LogicSection } from '../logicSection/LogicSection';
import { SeparatorTemplate } from 'rt-graphic-component/components/packages/Separator/Separator';
import { SeparatorDraw as XaSeparatorDraw } from 'rt-graphic-component/components/packages/Separator/SeparatorDrawAssistant';
export class SeparatorDraw extends GraphicDrawAssistant< export class SeparatorDraw extends XaSeparatorDraw {
SeparatorTemplate,
ISeparatorData
> {
SeparatorGraph: Separator;
constructor(app: IDrawApp, template: SeparatorTemplate) { constructor(app: IDrawApp, template: SeparatorTemplate) {
super(app, template, 'sym_o_square', '分隔符Separator'); super(app, template);
this.SeparatorGraph = this.graphicTemplate.new();
this.container.addChild(this.SeparatorGraph);
SeparatorInteraction.init(app);
} }
bind(): void {
super.bind();
this.SeparatorGraph.loadData(this.graphicTemplate.datas);
this.SeparatorGraph.doRepaint();
}
onLeftDown(e: FederatedPointerEvent): void {
this.container.position.copyFrom(this.toCanvasCoordinates(e.global));
this.createAndStore(true);
}
redraw(p: Point): void {
this.container.position.copyFrom(p);
}
prepareData(data: ISeparatorData): boolean {
data.transform = this.container.saveTransform();
return true;
}
oneGenerates() { oneGenerates() {
const SeparatorAll = this.app.queryStore.queryByType<Separator>( const SeparatorAll = this.app.queryStore.queryByType<Separator>(
Separator.Type Separator.Type
@ -76,7 +38,7 @@ export class SeparatorDraw extends GraphicDrawAssistant<
allR.forEach((relation, index) => { allR.forEach((relation, index) => {
const r = relation.getRelationParam(section); const r = relation.getRelationParam(section);
const other = relation.getOtherRelationParam(section); const other = relation.getOtherRelationParam(section);
if (!section.datas.children.includes(other.g.id + '')) { if (!section.datas.children.includes(other.g.id)) {
// 排除物理区段和自身逻辑区段的关联关系 // 排除物理区段和自身逻辑区段的关联关系
port.push(r.param); port.push(r.param);
} }
@ -216,69 +178,3 @@ export class SeparatorDraw extends GraphicDrawAssistant<
this.storeGraphic(separator); this.storeGraphic(separator);
} }
} }
//碰撞检测
export class SeparatorGraphicHitArea implements IHitArea {
separator: Separator;
constructor(separator: Separator) {
this.separator = separator;
}
contains(x: number, y: number): boolean {
let contains = false;
const p = new Point(x, y);
const typeArr = ['section', 'turnout'];
const endTypeArr = ['endA', 'endB'];
let d = SeparatorConsts.radius;
if (typeArr.includes(this.separator.datas.separatorType)) {
const tolerance = SeparatorConsts.lineWidth;
const p1 = new Point(0, -SeparatorConsts.height / 2);
const p2 = new Point(0, SeparatorConsts.height / 2);
contains = contains || linePoint(p1, p2, p, tolerance);
return contains;
} else if (endTypeArr.includes(this.separator.datas.separatorType)) {
if (this.separator.datas.separatorType == 'endB') {
d = -d;
}
const tolerance = SeparatorConsts.lineWidth;
const p1 = new Point(0, 0);
const p2 = new Point(-d, 0);
const p3 = new Point(-d, -d);
const p4 = new Point(-d * 3, -d);
const p5 = new Point(-d, d);
const p6 = new Point(-d * 3, d);
contains = contains || linePoint(p1, p2, p, tolerance);
contains = contains || linePoint(p2, p3, p, tolerance);
contains = contains || linePoint(p3, p4, p, tolerance);
contains = contains || linePoint(p2, p5, p, tolerance);
contains = contains || linePoint(p5, p6, p, tolerance);
}
return contains;
}
}
export class SeparatorInteraction extends GraphicInteractionPlugin<Separator> {
static Name = 'Separator_transform';
constructor(app: IDrawApp) {
super(SeparatorInteraction.Name, app);
}
static init(app: IDrawApp) {
return new SeparatorInteraction(app);
}
filter(...grahpics: JlGraphic[]): Separator[] | undefined {
return grahpics
.filter((g) => g.type === Separator.Type)
.map((g) => g as Separator);
}
bind(g: Separator): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.scalable = true;
g.rotatable = true;
g.rectGraphic.hitArea = new SeparatorGraphicHitArea(g);
}
unbind(g: Separator): void {
g.eventMode = 'none';
g.scalable = false;
g.rotatable = false;
}
}

101
src/graphics/signal/Lamp.ts Normal file
View File

@ -0,0 +1,101 @@
import { Container } from '@pixi/display';
import { Graphics } from 'pixi.js';
const lampConsts = {
lampRadius: 8,
logicModeLineWidth: 2,
logicModeDistance: 5,
logicModeColor: '0x000000',
lampLineWidth: 1,
lampLineColor: '0x3149c3',
lampBadColor: '0xFF0000',
badStart: 10,
badEnd: 15,
};
export class Lamp extends Container {
circleLamp: Graphics = new Graphics();
logicMode: Graphics = new Graphics();
lampBad: Graphics = new Graphics();
radiusX = 0;
radiusY = 0;
constructor() {
super();
this.addChild(this.circleLamp);
this.addChild(this.logicMode);
this.addChild(this.lampBad);
}
paint(radiusX: number, radiusY: number) {
this.radiusX = radiusX;
this.radiusY = radiusY;
this.createLamp();
}
createLampBad() {
this.lampBad.clear();
this.lampBad.lineStyle(lampConsts.lampLineWidth, lampConsts.lampBadColor);
this.lampBad.moveTo(this.radiusX + lampConsts.badStart, this.radiusY);
this.lampBad.lineTo(this.radiusX + lampConsts.badEnd, this.radiusY);
this.lampBad.moveTo(this.radiusX - lampConsts.badStart, this.radiusY);
this.lampBad.lineTo(this.radiusX - lampConsts.badEnd, this.radiusY);
this.lampBad.moveTo(this.radiusX, this.radiusY + lampConsts.badStart);
this.lampBad.lineTo(this.radiusX, this.radiusY + lampConsts.badEnd);
this.lampBad.moveTo(this.radiusX, this.radiusY - lampConsts.badStart);
this.lampBad.lineTo(this.radiusX, this.radiusY - lampConsts.badEnd);
const xieStart = Math.sin(Math.PI / 4) * lampConsts.badStart;
const xieEnd = Math.sin(Math.PI / 4) * lampConsts.badEnd;
this.lampBad.moveTo(this.radiusX + xieStart, this.radiusY + xieStart);
this.lampBad.lineTo(this.radiusX + xieEnd, this.radiusY + xieEnd);
this.lampBad.moveTo(this.radiusX + xieStart, this.radiusY - xieStart);
this.lampBad.lineTo(this.radiusX + xieEnd, this.radiusY - xieEnd);
this.lampBad.moveTo(this.radiusX - xieStart, this.radiusY - xieStart);
this.lampBad.lineTo(this.radiusX - xieEnd, this.radiusY - xieEnd);
this.lampBad.moveTo(this.radiusX - xieStart, this.radiusY + xieStart);
this.lampBad.lineTo(this.radiusX - xieEnd, this.radiusY + xieEnd);
}
createLamp(color?: string) {
this.circleLamp.clear();
this.circleLamp.lineStyle(
lampConsts.lampLineWidth,
lampConsts.lampLineColor
);
if (!color) {
color = '0X' + this.getCanvas().backgroundColor.substring(1);
}
this.circleLamp.beginFill(color, 1);
this.circleLamp.drawCircle(
this.radiusX,
this.radiusY,
lampConsts.lampRadius
);
this.circleLamp.endFill();
}
createLogicMode() {
this.logicMode
.clear()
.lineStyle(lampConsts.logicModeLineWidth, lampConsts.logicModeColor)
.moveTo(
this.radiusX - lampConsts.logicModeDistance,
this.radiusY + lampConsts.logicModeDistance
)
.lineTo(
this.radiusX + lampConsts.logicModeDistance,
this.radiusY - lampConsts.logicModeDistance
)
.moveTo(
this.radiusX - lampConsts.logicModeDistance,
this.radiusY - lampConsts.logicModeDistance
)
.lineTo(
this.radiusX + lampConsts.logicModeDistance,
this.radiusY + lampConsts.logicModeDistance
);
}
logicModeClear() {
this.logicMode.clear();
}
lampClear() {
this.circleLamp.clear();
}
}

View File

@ -1,5 +1,6 @@
import { Graphics, Point } from 'pixi.js'; import { Graphics, Point } from 'pixi.js';
import { calculateMirrorPoint, GraphicAnimation, JlGraphic } from 'jl-graphic'; import { calculateMirrorPoint, GraphicAnimation, JlGraphic } from 'jl-graphic';
import { Lamp } from './Lamp';
import { ISignalState } from './Signal'; import { ISignalState } from './Signal';
export enum LampEnum { export enum LampEnum {
@ -9,7 +10,6 @@ export enum LampEnum {
yellowLamp = '0XFFFF00', yellowLamp = '0XFFFF00',
whiteLamp = '0XFFFFFF', whiteLamp = '0XFFFFFF',
blueLamp = '0x3149c3', blueLamp = '0x3149c3',
closeLamp = '0x000000',
} }
const lampConsts = { const lampConsts = {
@ -17,14 +17,6 @@ const lampConsts = {
levelLampPostLength: 4, levelLampPostLength: 4,
postLineWidth: 3, postLineWidth: 3,
lampRadius: 8, lampRadius: 8,
logicModeLineWidth: 2,
logicModeDistance: 5,
logicModeColor: '0x000000',
lampLineWidth: 1,
lampLineColor: '0x3149c3',
lampBadColor: '0xFF0000',
badStart: 10,
badEnd: 15,
}; };
const anmiationNameConst = { const anmiationNameConst = {
@ -39,31 +31,23 @@ export class LampMainBody extends JlGraphic {
static Type = 'LampMainBody'; static Type = 'LampMainBody';
lampNum = 1; lampNum = 1;
lampPost: Graphics = new Graphics(); lampPost: Graphics = new Graphics();
// lamps: Lamp[] = []; lamps: Lamp[] = [];
lamps: Graphics = new Graphics();
logicMode: Graphics = new Graphics();
mirror = false; mirror = false;
deltaTime = 0; deltaTime = 0;
states: ISignalState | null = null; states: ISignalState | null = null;
constructor() { constructor() {
super(LampMainBody.Type); super(LampMainBody.Type);
this.addChild(this.lampPost);
this.addChild(this.lamps);
this.addChild(this.logicMode);
} }
paint(lampNum: number, mirror: boolean, states: ISignalState) { paint(lampNum: number, mirror: boolean, states: ISignalState) {
this.lampPost.clear();
this.logicMode.clear();
this.mirror = mirror; this.mirror = mirror;
this.states = states; this.states = states;
if (lampNum < 1) { if (lampNum < 1) {
throw new Error('信号机灯数量异常'); throw new Error('信号机灯数量异常');
} }
this.lampNum = lampNum; this.lampNum = lampNum;
// this.removeChildren(0); this.removeChildren(0);
// this.lampPost = new Graphics(); this.lampPost = new Graphics();
let lpp = new Point(lampConsts.levelLampPostLength, 0); let lpp = new Point(lampConsts.levelLampPostLength, 0);
if (mirror) { if (mirror) {
lpp = calculateMirrorPoint(new Point(0, 0), lpp); lpp = calculateMirrorPoint(new Point(0, 0), lpp);
@ -74,81 +58,39 @@ export class LampMainBody extends JlGraphic {
.lineTo(0, lampConsts.verticalLampPostLength / 2) .lineTo(0, lampConsts.verticalLampPostLength / 2)
.moveTo(0, 0) .moveTo(0, 0)
.lineTo(lpp.x, lpp.y); .lineTo(lpp.x, lpp.y);
// this.addChild(this.lampPost); this.addChild(this.lampPost);
// this.lamps = []; this.lamps = [];
// for (let i = 0; i < this.lampNum; i++) { for (let i = 0; i < this.lampNum; i++) {
// const lamp = new Lamp(); const lamp = new Lamp();
// // this.addChild(lamp); this.addChild(lamp);
// const radiusX =
// (1 + i * 2) * lampConsts.lampRadius + lampConsts.levelLampPostLength;
// let lrp = new Point(radiusX, 0);
// if (mirror) {
// lrp = calculateMirrorPoint(new Point(0, 0), lrp);
// }
// lamp.paint(lrp.x, lrp.y);
// // this.lamps.push(lamp);
// }
this.chagneState(this.states);
}
paintLamp(colors: string[], lampNum?: number) {
this.lamps.clear();
this.lamps.lineStyle(
lampConsts.lampLineWidth,
lampConsts.lampLineColor
);
const number = lampNum || this.lampNum
for (let i = 0; i < number; i++) {
const radiusX = const radiusX =
(1 + i * 2) * lampConsts.lampRadius + (1 + i * 2) * lampConsts.lampRadius + lampConsts.levelLampPostLength;
lampConsts.levelLampPostLength;
let lrp = new Point(radiusX, 0); let lrp = new Point(radiusX, 0);
if (this.mirror) { if (mirror) {
lrp = calculateMirrorPoint(new Point(0, 0), lrp); lrp = calculateMirrorPoint(new Point(0, 0), lrp);
} }
const color = colors[i] ? colors[i] : LampEnum.closeLamp; lamp.paint(lrp.x, lrp.y);
this.lamps.beginFill(color, 1); this.lamps.push(lamp);
this.lamps.drawCircle(
lrp.x,
lrp.y,
lampConsts.lampRadius
);
this.lamps.endFill();
if (this.states?.extinguish) {
this.logicMode
.lineStyle(lampConsts.logicModeLineWidth, lampConsts.logicModeColor)
.moveTo(
lrp.x - lampConsts.logicModeDistance,
lrp.y + lampConsts.logicModeDistance
)
.lineTo(
lrp.x + lampConsts.logicModeDistance,
lrp.y - lampConsts.logicModeDistance
)
.moveTo(
lrp.x - lampConsts.logicModeDistance,
lrp.y - lampConsts.logicModeDistance
)
.lineTo(
lrp.x + lampConsts.logicModeDistance,
lrp.y + lampConsts.logicModeDistance
);
}
} }
this.chagneState(this.states);
} }
setBlueShow() { setBlueShow() {
this.stopAnmiation(); this.stopAnmiation();
this.paintLamp([LampEnum.blueLamp, LampEnum.blueLamp, LampEnum.blueLamp]); this.lamps.forEach((lamp) => {
lamp.createLamp(LampEnum.blueLamp);
lamp.logicModeClear();
});
} }
doRepaint() { doRepaint() {
// this.paint(this.lampNum, this.mirror, this.states); // this.paint(this.lampNum, this.mirror, this.states);
} }
stopAnmiation() { stopAnmiation() {
const redFlashA = this.animation(anmiationNameConst.signaRedFlash + this.states?.code); const redFlashA = this.animation(anmiationNameConst.signaRedFlash);
const greenFlashA = this.animation(anmiationNameConst.signalGreenFlash + this.states?.code); const greenFlashA = this.animation(anmiationNameConst.signalGreenFlash);
const blueFlashA = this.animation(anmiationNameConst.signalBlueFlash + this.states?.code); const blueFlashA = this.animation(anmiationNameConst.signalBlueFlash);
const yellowFlashA = this.animation(anmiationNameConst.signalYellowFlash + this.states?.code); const yellowFlashA = this.animation(anmiationNameConst.signalYellowFlash);
const whiteFlashA = this.animation(anmiationNameConst.signalWhiteFlash + this.states?.code); const whiteFlashA = this.animation(anmiationNameConst.signalWhiteFlash);
if (redFlashA) { if (redFlashA) {
redFlashA.pause(); redFlashA.pause();
} }
@ -167,13 +109,18 @@ export class LampMainBody extends JlGraphic {
} }
chagneState(states: ISignalState) { chagneState(states: ISignalState) {
this.stopAnmiation(); this.stopAnmiation();
if (states.extinguish) {
this.lamps.forEach((lamp) => lamp.createLogicMode());
} else {
this.lamps.forEach((lamp) => lamp.logicModeClear());
}
if (states.redOpen) { if (states.redOpen) {
this.paintLamp([LampEnum.redLamp]); this.lamps[0].createLamp(LampEnum.redLamp);
} else if (states.redFlash) { } else if (states.redFlash) {
let redFlashA = this.animation(anmiationNameConst.signaRedFlash + states.code); let redFlashA = this.animation(anmiationNameConst.signaRedFlash);
if (!redFlashA) { if (!redFlashA) {
redFlashA = this.createFlashAnmiation( redFlashA = this.createFlashAnmiation(
anmiationNameConst.signaRedFlash + states.code, anmiationNameConst.signaRedFlash,
LampEnum.redLamp, LampEnum.redLamp,
0 0
); );
@ -181,12 +128,12 @@ export class LampMainBody extends JlGraphic {
this.addAnimation(redFlashA); this.addAnimation(redFlashA);
redFlashA.resume(); redFlashA.resume();
} else if (states.greenOpen) { } else if (states.greenOpen) {
this.paintLamp([LampEnum.closeLamp, LampEnum.greenLamp]); this.lamps[1].createLamp(LampEnum.greenLamp);
} else if (states.greenFlash) { } else if (states.greenFlash) {
let greenFlashA = this.animation(anmiationNameConst.signalGreenFlash + states.code); let greenFlashA = this.animation(anmiationNameConst.signalGreenFlash);
if (!greenFlashA) { if (!greenFlashA) {
greenFlashA = this.createFlashAnmiation( greenFlashA = this.createFlashAnmiation(
anmiationNameConst.signalGreenFlash + states.code, anmiationNameConst.signalGreenFlash,
LampEnum.greenLamp, LampEnum.greenLamp,
1 1
); );
@ -194,12 +141,12 @@ export class LampMainBody extends JlGraphic {
this.addAnimation(greenFlashA); this.addAnimation(greenFlashA);
greenFlashA.resume(); greenFlashA.resume();
} else if (states.yellowOpen) { } else if (states.yellowOpen) {
this.paintLamp([LampEnum.closeLamp, LampEnum.yellowLamp]); this.lamps[1].createLamp(LampEnum.yellowLamp);
} else if (states.yellowFlash) { } else if (states.yellowFlash) {
let yellowFlashA = this.animation(anmiationNameConst.signalYellowFlash + states.code); let yellowFlashA = this.animation(anmiationNameConst.signalYellowFlash);
if (!yellowFlashA) { if (!yellowFlashA) {
yellowFlashA = this.createFlashAnmiation( yellowFlashA = this.createFlashAnmiation(
anmiationNameConst.signalYellowFlash + states.code, anmiationNameConst.signalYellowFlash,
LampEnum.yellowLamp, LampEnum.yellowLamp,
1 1
); );
@ -207,12 +154,12 @@ export class LampMainBody extends JlGraphic {
this.addAnimation(yellowFlashA); this.addAnimation(yellowFlashA);
yellowFlashA.resume(); yellowFlashA.resume();
} else if (states.blueOpen) { } else if (states.blueOpen) {
this.paintLamp([LampEnum.blueLamp, LampEnum.blueLamp, LampEnum.blueLamp]); this.lamps.forEach((lamp) => lamp.createLamp(LampEnum.blueLamp));
} else if (states.blueFlash) { } else if (states.blueFlash) {
let blueFlashA = this.animation(anmiationNameConst.signalBlueFlash + states.code); let blueFlashA = this.animation(anmiationNameConst.signalBlueFlash);
if (!blueFlashA) { if (!blueFlashA) {
blueFlashA = this.createFlashAnmiation( blueFlashA = this.createFlashAnmiation(
anmiationNameConst.signalBlueFlash + states.code, anmiationNameConst.signalBlueFlash,
LampEnum.blueLamp, LampEnum.blueLamp,
0 0
); );
@ -220,12 +167,13 @@ export class LampMainBody extends JlGraphic {
this.addAnimation(blueFlashA); this.addAnimation(blueFlashA);
blueFlashA.resume(); blueFlashA.resume();
} else if (states.whiteOpen) { } else if (states.whiteOpen) {
this.paintLamp([LampEnum.whiteLamp], 1); this.lamps[0].createLamp(LampEnum.whiteLamp);
this.lamps[1].visible = false;
} else if (states.whiteFlash) { } else if (states.whiteFlash) {
let whiteFlashA = this.animation(anmiationNameConst.signalWhiteFlash + states.code); let whiteFlashA = this.animation(anmiationNameConst.signalWhiteFlash);
if (!whiteFlashA) { if (!whiteFlashA) {
whiteFlashA = this.createFlashAnmiation( whiteFlashA = this.createFlashAnmiation(
anmiationNameConst.signalWhiteFlash + states.code, anmiationNameConst.signalWhiteFlash,
LampEnum.whiteLamp, LampEnum.whiteLamp,
0 0
); );
@ -233,15 +181,16 @@ export class LampMainBody extends JlGraphic {
this.addAnimation(whiteFlashA); this.addAnimation(whiteFlashA);
whiteFlashA.resume(); whiteFlashA.resume();
} else if (states.callon) { } else if (states.callon) {
this.paintLamp([LampEnum.redLamp, LampEnum.yellowLamp]); this.lamps[0].createLamp(LampEnum.redLamp);
this.lamps[1].createLamp(LampEnum.yellowLamp);
} else if (states.yellowYellow) { } else if (states.yellowYellow) {
this.paintLamp([LampEnum.yellowLamp, LampEnum.yellowLamp]); this.lamps[0].createLamp(LampEnum.yellowLamp);
this.lamps[1].createLamp(LampEnum.yellowLamp);
} else if (states.yellowGreen) { } else if (states.yellowGreen) {
this.paintLamp([LampEnum.yellowLamp, LampEnum.greenLamp]); this.lamps[0].createLamp(LampEnum.yellowLamp);
this.lamps[1].createLamp(LampEnum.greenLamp);
} else if (states.lampFailure) { } else if (states.lampFailure) {
this.paintLamp([LampEnum.redLamp]); this.lamps[0].createLamp(LampEnum.redLamp);
} else {
this.paintLamp([]);
} }
} }
createFlashAnmiation( createFlashAnmiation(
@ -249,17 +198,18 @@ export class LampMainBody extends JlGraphic {
color: string, color: string,
lampIndex: number lampIndex: number
): GraphicAnimation { ): GraphicAnimation {
const bgColor = '0X' + this.getCanvas().backgroundColor.substring(1);
const flashAnmiation = GraphicAnimation.init({ const flashAnmiation = GraphicAnimation.init({
name: name, name: name,
run: (dt: number) => { run: (dt: number) => {
this.deltaTime += dt; this.deltaTime += dt;
if (this.deltaTime > 60) { if (this.deltaTime > 60) {
const colors = ['', '', ''];
this.deltaTime = 0; this.deltaTime = 0;
colors[lampIndex] = color; this.lamps[lampIndex].createLamp(color);
this.paintLamp(colors);
} else if (this.deltaTime > 30) { } else if (this.deltaTime > 30) {
this.paintLamp([]); this.lamps.forEach((lamp) => {
lamp.createLamp(bgColor);
});
} }
}, },
}); });

View File

@ -1,280 +1,8 @@
import { Color, Container, Graphics } from 'pixi.js'; import { THStation as Station } from 'rt-graphic-component/components/packages/Station/THStation';
import { import { IStationData } from 'rt-graphic-component/components/packages/Station/common/StationConfig';
GraphicData, import { ITHStationState as IStationState } from 'rt-graphic-component/components/packages/Station/THStation';
GraphicState, import { StationTemplate } from 'rt-graphic-component/components/packages/Station/common/StationTemplate';
JlGraphic, import { StationDraw } from 'rt-graphic-component/components/packages/Station/common/StationDrawAssistant';
JlGraphicTemplate,
VectorText,
} from 'jl-graphic';
import { LogicSection } from '../logicSection/LogicSection';
import { Platform } from '../platform/Platform';
import { KilometerSystem, Signal } from '../signal/Signal';
import { Train } from '../train/Train';
import { Turnout } from '../turnout/Turnout';
export interface IStationData extends GraphicData { export { Station, StationTemplate, StationDraw };
get code(): string; // 车站索引 export type { IStationState, IStationData };
set code(v: string);
get kilometerSystem(): KilometerSystem;
set kilometerSystem(v: KilometerSystem);
get hasControl(): boolean; //是否有控制
set hasControl(v: boolean);
get concentrationStations(): boolean; //是否集中站
set concentrationStations(v: boolean);
get name(): string; //车站名称
set name(v: string);
get manageStations(): number[]; //集中站管理的车站
set manageStations(v: number[]);
get depots(): boolean; //是否车辆段
set depots(v: boolean);
clone(): IStationData;
copyFrom(data: IStationData): void;
eq(other: IStationData): boolean;
}
export interface IStationState extends GraphicState {
get ipRtuStusDown(): boolean; //通信终端---是否允许转到中控
set ipRtuStusDown(v: boolean);
get ipRtuStusInLocalCtrl(): boolean; //站控
set ipRtuStusInLocalCtrl(v: boolean);
get ipRtuStusInCentralCtrl(): boolean; //遥控
set ipRtuStusInCentralCtrl(v: boolean);
get ipRtuStusInEmergencyCtrl(): boolean; //紧急站控
set ipRtuStusInEmergencyCtrl(v: boolean);
get rtuId(): number; // 集中站站号
set rtuId(v: number);
}
const stationConsts = {
radius: 3,
borderWidth: 1,
circleColorGrey: '0x808080',
circleColorBlue: '0x08F80D',
circleColorYellow: '0xFFFA0C',
codeColor: '0xF48815',
codeFontSize: 22,
codeControlFontSize: 12,
codeOffsetY: 30,
circleOffsetY: 20,
circleBetweenOffset: 40,
kilometerCodeColor: '0xFFFFFF',
kilometerCodeFontSize: 8,
kilometerCodeOffsetY: -25,
};
class constrolGraphic extends Container {
circleA: Graphics = new Graphics();
codeGraphA: VectorText = new VectorText(''); //控制名--站控
circleB: Graphics = new Graphics();
codeGraphB: VectorText = new VectorText(''); //控制名--中控
arrow: Graphics = new Graphics();
inArrow: Graphics = new Graphics();
constructor() {
super();
this.addChild(this.circleA);
this.addChild(this.codeGraphA);
this.addChild(this.circleB);
this.addChild(this.codeGraphB);
this.addChild(this.arrow);
this.addChild(this.inArrow);
this.codeGraphA.setVectorFontSize(stationConsts.codeFontSize);
this.codeGraphB.setVectorFontSize(stationConsts.codeFontSize);
}
draw(states: IStationState): void {
let StationControlFillColor = stationConsts.circleColorGrey;
let centralControlFillColor = stationConsts.circleColorBlue;
let inArrowFillColor = stationConsts.circleColorGrey;
if (states.ipRtuStusInLocalCtrl) {
StationControlFillColor = stationConsts.circleColorYellow;
centralControlFillColor = stationConsts.circleColorGrey;
if (!states.ipRtuStusDown) {
inArrowFillColor = stationConsts.circleColorBlue;
}
}
this.drawCircleCode(
this.circleA,
this.codeGraphA,
'站控',
StationControlFillColor
);
this.circleA.position.set(
stationConsts.circleBetweenOffset / 2,
stationConsts.circleOffsetY
);
this.codeGraphA.position.set(
stationConsts.circleBetweenOffset / 2,
stationConsts.codeOffsetY
);
this.drawCircleCode(
this.circleB,
this.codeGraphB,
'中控',
centralControlFillColor
);
this.circleB.position.set(
-stationConsts.circleBetweenOffset / 2,
stationConsts.circleOffsetY
);
this.codeGraphB.position.set(
-stationConsts.circleBetweenOffset / 2,
stationConsts.codeOffsetY
);
const arrow = this.arrow;
arrow.clear();
arrow.lineStyle(stationConsts.borderWidth, new Color('0xFFFFFF'));
const points = [0, 0, 2, 2, 2, 1, 14, 1, 14, -1, 2, -1, 2, -2];
arrow.beginFill('0xFFFFFF');
arrow.drawPolygon(points);
arrow.endFill();
arrow.scale.set(1.1, 1.1);
arrow.position.set(-7, stationConsts.circleOffsetY);
const inArrow = this.inArrow;
inArrow.beginFill(inArrowFillColor);
inArrow.drawPolygon(points);
inArrow.endFill();
inArrow.position.set(-6.5, stationConsts.circleOffsetY);
}
drawCircleCode(
circle: Graphics,
codeGraph: VectorText,
code: string,
fillcolor: string
): void {
circle.clear();
circle.lineStyle(stationConsts.borderWidth, new Color(fillcolor));
circle.beginFill(fillcolor, 1);
circle.drawCircle(0, 0, stationConsts.radius);
circle.endFill;
codeGraph.text = code;
codeGraph.style.fill = fillcolor;
codeGraph.setVectorFontSize(stationConsts.codeControlFontSize);
codeGraph.anchor.set(0.5);
}
clear(): void {
this.circleA.clear();
this.circleB.clear();
this.codeGraphA.text = '';
this.codeGraphB.text = '';
this.arrow.clear();
this.inArrow.clear();
}
}
export class Station extends JlGraphic {
static Type = 'station';
codeGraph: VectorText = new VectorText(''); //车站名
kilometerGraph: VectorText = new VectorText(''); //公里标
controlGraphic: constrolGraphic = new constrolGraphic();
_ipRtuStusDown = false;
constructor() {
super(Station.Type);
this.addChild(this.codeGraph);
this.addChild(this.kilometerGraph);
this.addChild(this.controlGraphic);
this.kilometerGraph.name = 'kilometer';
this.controlGraphic.name = 'trainControl';
}
get datas(): IStationData {
return this.getDatas<IStationData>();
}
get states(): IStationState {
return this.getStates<IStationState>();
}
get code(): string {
return this.datas.code;
}
doRepaint(): void {
const codeGraph = this.codeGraph;
const kilometerGraph = this.kilometerGraph;
const controlGraphic = this.controlGraphic;
controlGraphic.clear();
codeGraph.text = this.datas?.code
? `${this.datas?.name}(${this.datas?.code})`
: `${this.datas?.name}`;
codeGraph.style.fill = stationConsts.codeColor;
codeGraph.setVectorFontSize(stationConsts.codeFontSize);
codeGraph.anchor.set(0.5);
const kilometerCode = this.datas.kilometerSystem?.kilometer || 12345678;
if (Math.floor(kilometerCode * 1000).toString().length > 3) {
const kiloBit = Math.floor(Number(kilometerCode) / 1000000).toString();
kilometerGraph.text =
'K' +
kiloBit +
'+' +
(
Number(kilometerCode.toString().substring(kiloBit.length)) / 1000
).toFixed(3);
} else {
kilometerGraph.text = (kilometerCode * 1000).toFixed(3);
}
kilometerGraph.style.fill = stationConsts.kilometerCodeColor;
kilometerGraph.setVectorFontSize(stationConsts.kilometerCodeFontSize);
kilometerGraph.anchor.set(0.5);
kilometerGraph.position.set(0, stationConsts.kilometerCodeOffsetY);
if (this.datas.childTransforms?.length) {
this.datas.childTransforms.forEach((child) => {
if (child.name == 'kilometer') {
const pos = child.transform.position;
kilometerGraph.position.set(pos.x, pos.y);
}
});
}
if (this.datas.hasControl) {
controlGraphic.draw(this.states);
}
if (this.states.ipRtuStusDown !== this._ipRtuStusDown) {
this._ipRtuStusDown = this.states.ipRtuStusDown;
this.handleBlueShow();
}
}
handleBlueShow() {
const signals = this.queryStore.queryByType<Signal>(Signal.Type);
const logicSections = this.queryStore.queryByType<LogicSection>(
LogicSection.Type
);
const turnouts = this.queryStore.queryByType<Turnout>(Turnout.Type);
const platfroms = this.queryStore.queryByType<Platform>(Platform.Type);
const trains = this.queryStore.queryByType<Train>(Train.Type);
signals.forEach((signal) => {
if (signal.states.rtuId === this.states.rtuId) {
signal.doRepaint();
}
});
logicSections.forEach((logicSection) => {
if (logicSection.states.rtuId === this.states.rtuId) {
logicSection.doRepaint();
}
});
turnouts.forEach((turnout) => {
if (turnout.states.rtuId === this.states.rtuId) {
turnout.doRepaint();
}
});
platfroms.forEach((platform) => {
if (platform.states.rtuId === this.states.rtuId) {
platform.doRepaint();
}
});
trains.forEach((train) => {
if (train.states.rtuId === this.states.rtuId) {
train.doRepaint();
}
});
}
}
export class StationTemplate extends JlGraphicTemplate<Station> {
hasControl: boolean;
constructor(dataTemplate: IStationData, stateTemplate: IStationState) {
super(Station.Type, {
dataTemplate,
stateTemplate,
});
this.hasControl = true;
}
new(): Station {
const station = new Station();
station.loadData(this.datas);
station.loadState(this.states);
return station;
}
}

View File

@ -1,123 +0,0 @@
import { FederatedPointerEvent, Point } from 'pixi.js';
import {
AbsorbableLine,
AbsorbablePosition,
GraphicDrawAssistant,
GraphicInteractionPlugin,
IDrawApp,
JlGraphic,
} from 'jl-graphic';
import { IStationData, Station, StationTemplate } from './Station';
export interface IStationDrawOptions {
newData: () => IStationData;
}
export class StationDraw extends GraphicDrawAssistant<
StationTemplate,
IStationData
> {
codeGraph: Station;
constructor(app: IDrawApp, template: StationTemplate) {
super(
app,
template,
'svguse:../../drawIcon.svg#icon-station',
'车站Station'
);
this.codeGraph = this.graphicTemplate.new();
this.container.addChild(this.codeGraph);
stationInteraction.init(app);
}
bind(): void {
super.bind();
this.codeGraph.loadData(this.graphicTemplate.datas);
this.codeGraph.doRepaint();
}
clearCache(): void {
//this.codeGraph.destroy();
}
onLeftDown(e: FederatedPointerEvent): void {
this.container.position.copyFrom(this.toCanvasCoordinates(e.global));
this.createAndStore(true);
}
redraw(p: Point): void {
this.container.position.copyFrom(p);
}
prepareData(data: IStationData): boolean {
data.transform = this.container.saveTransform();
data.hasControl = this.graphicTemplate.hasControl;
return true;
}
}
function buildAbsorbablePositions(station: Station): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const stations = station.queryStore.queryByType<Station>(Station.Type);
const { width } = station.getGraphicApp().canvas;
stations.forEach((other) => {
if (other.id == station.id) {
return;
}
const ps = other.datas.transform.position;
const xs = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y });
aps.push(xs);
});
return aps;
}
export class stationInteraction extends GraphicInteractionPlugin<Station> {
static Name = 'station_transform';
constructor(app: IDrawApp) {
super(stationInteraction.Name, app);
}
static init(app: IDrawApp) {
return new stationInteraction(app);
}
filter(...grahpics: JlGraphic[]): Station[] | undefined {
return grahpics
.filter((g) => g.type === Station.Type)
.map((g) => g as Station);
}
bind(g: Station): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.scalable = true;
g.rotatable = true;
g.controlGraphic.eventMode = 'static';
g.controlGraphic.cursor = 'pointer';
g.controlGraphic.draggable = true;
g.controlGraphic.selectable = true;
g.controlGraphic.transformSave = true;
g.kilometerGraph.eventMode = 'static';
g.kilometerGraph.cursor = 'pointer';
g.kilometerGraph.draggable = true;
g.kilometerGraph.selectable = true;
g.kilometerGraph.transformSave = true;
g.on('selected', this.onSelected, this);
}
unbind(g: Station): void {
g.eventMode = 'none';
g.scalable = false;
g.rotatable = false;
g.controlGraphic.eventMode = 'none';
g.controlGraphic.draggable = false;
g.controlGraphic.selectable = false;
g.controlGraphic.transformSave = false;
g.kilometerGraph.eventMode = 'none';
g.kilometerGraph.draggable = false;
g.kilometerGraph.selectable = false;
g.kilometerGraph.transformSave = false;
g.off('selected', this.onSelected, this);
}
onSelected(): void {
const station = this.app.selectedGraphics[0] as Station;
this.app.setOptions({
absorbablePositions: buildAbsorbablePositions(station),
});
}
}

View File

@ -1,12 +1,8 @@
import { Graphics, Container, Point } from 'pixi.js';
import { import {
GraphicData, GraphicData,
GraphicIdGenerator, GraphicIdGenerator,
GraphicState, GraphicState,
JlGraphic,
JlGraphicTemplate, JlGraphicTemplate,
VectorText,
calculateMirrorPoint,
} from 'jl-graphic'; } from 'jl-graphic';
import { train } from 'src/protos/train'; import { train } from 'src/protos/train';
import { state } from 'src/protos/device_status'; import { state } from 'src/protos/device_status';
@ -14,6 +10,13 @@ import { TrainWindow } from '../trainWindow/TrainWindow';
import { LogicSection } from '../logicSection/LogicSection'; import { LogicSection } from '../logicSection/LogicSection';
import { Section } from '../section/Section'; import { Section } from '../section/Section';
import { Station } from '../station/Station'; import { Station } from '../station/Station';
import { Train as XaTrain } from 'rt-graphic-component/components/packages/Train/Train';
import {
EnumDiriveModel,
EnumStatusText,
EnumTrainType,
UpdateTrainConsts,
} from 'rt-graphic-component/components/packages/Train/TrainConfig';
export interface ITrainData extends GraphicData { export interface ITrainData extends GraphicData {
get code(): string; // 车号 get code(): string; // 车号
@ -72,285 +75,13 @@ export interface ITrainState extends GraphicState {
set record(v: train.TrainRecord); set record(v: train.TrainRecord);
} }
interface bodyWH {
width: number; // 宽
height: number; // 高
}
// 列车颜色
export enum TrainColorEnum {
headColor = '0xFFCE4D', // 箭头颜色
bodyColor = '0xA388B1', // 背景色
codeColor = '0xffffff', // 车号颜色
borderColor = '0xA3E198', // 边框的颜色
directionColor = '0x00FF00', // 方向箭头颜色
}
enum DiriveModelColorEnum { // 驾驶模式对应颜色
AM = '0x00FF00', // ATO自动驾驶
SM = '0xFFFF00', // ATP 监控下的人工驾驶模式
RM = '0xFFC837', // 限制人工驾驶模式
NRM = '0xA0522D', // 非限制人工驾驶模式
red = '0xF80103', // 红色表示通信中断
}
enum BBBColorEnum { // 识别号颜色
accuracy = '0xffffff', // 准点
early = '0x00FF00', // 早点
late = '0xFFFF00', // 晚点
schedule = '0xffffff', // 计划车
head = '0xE9FC01', // 头码车
manual = '0xE9FC01', // 人工车
special = '0xE9FC01', // 特殊车
}
enum statusTextColor {
H = '0xFFFF00', // H扣车
S = '0x6260F3', // S跳停
D = '0x00FF00', // D开门
A = '0xFF0000', // A报警
}
const deviceTypeString = new Map(); const deviceTypeString = new Map();
deviceTypeString.set(state.DeviceType.TRACK, LogicSection.Type); deviceTypeString.set(state.DeviceType.TRACK, LogicSection.Type);
deviceTypeString.set(state.DeviceType.SWITCH_TRACK, Section.Type); deviceTypeString.set(state.DeviceType.SWITCH_TRACK, Section.Type);
export const trainConsts = { export class Train extends XaTrain {
codeWidth: 120, constructor(data?: UpdateTrainConsts) {
codeHeight: 40, super(data);
codePadding: 5,
borderWidth: 1,
codeFontSize: 22,
textFontSize: 16, // 状态字母大小
textMarginY: 10, // 状态字母与列车距离
statusTextList: ['H', 'S', 'D', 'A'],
marginX: 4, // 图形x轴边距
pauseW: 4, // 停止框宽度
};
export class TrainHead extends Container {
arrow: Graphics; // 箭头
pause: Graphics; // 停止
constructor() {
super();
this.arrow = new Graphics();
this.pause = new Graphics();
this.addChild(this.arrow);
this.addChild(this.pause);
}
clear() {
this.arrow.clear();
this.pause.clear();
}
doRepaint(states: ITrainState, bodyWH?: bodyWH) {
let direction = '';
if (states.mode?.ipModeTrainDirDown) {
direction = 'left';
}
if (states.mode?.ipModeTrainDirUp) {
direction = 'right';
}
this.clear();
if (!direction) {
return;
}
const marginX = trainConsts.marginX;
const pauseW = trainConsts.pauseW;
const codeWidth = bodyWH ? bodyWH.width : trainConsts.codeWidth;
const codeHeight = bodyWH ? bodyWH.height : trainConsts.codeHeight;
let arrowPoint = [
-codeHeight * 0.4 - marginX - codeWidth / 2,
0,
-marginX - codeWidth / 2,
codeHeight / 2,
-marginX - codeWidth / 2,
-codeHeight / 2,
];
let pausePoint = [
-marginX - pauseW / 2 - codeWidth / 2,
-codeHeight / 2,
-marginX - pauseW / 2 - codeWidth / 2,
codeHeight / 2,
];
if (direction != 'left') {
const aP: Array<number> = [];
arrowPoint.forEach((item, index) => {
if (index % 2 == 1) {
const p = new Point(arrowPoint[index - 1], item);
const newP = calculateMirrorPoint(new Point(0, 0), p);
aP.push(newP.x, newP.y);
}
});
arrowPoint = aP;
const pP: Array<number> = [];
pausePoint.forEach((item, index) => {
if (index % 2 == 1) {
const p = new Point(pausePoint[index - 1], item);
const newP = calculateMirrorPoint(new Point(0, 0), p);
pP.push(newP.x, newP.y);
}
});
pausePoint = pP;
}
let aColor = DiriveModelColorEnum.AM;
let pColor = DiriveModelColorEnum.SM;
if (states.mode?.ipModeTrainDriveModeCm) {
aColor = DiriveModelColorEnum.SM;
pColor = DiriveModelColorEnum.SM;
} else if (
states.mode?.ipModeTrainDriveModeRmf ||
states.mode?.ipModeTrainDriveModeRmr
) {
aColor = DiriveModelColorEnum.RM;
pColor = DiriveModelColorEnum.RM;
}
if (states.mode?.ipModeTrainStoped) {
this.pause.lineStyle(pauseW, pColor, 1);
this.pause.moveTo(pausePoint[0], pausePoint[1]);
this.pause.lineTo(pausePoint[2], pausePoint[3]);
} else {
const arrow = this.arrow;
arrow.beginFill(aColor, 1);
arrow.drawPolygon(arrowPoint);
arrow.endFill();
}
}
}
export class TrainBody extends Container {
// codeRact: Graphics;
codeAGraph: VectorText = new VectorText(''); //识别号AA
codeBGraph: VectorText = new VectorText(''); //识别号BBB
constructor() {
super();
// this.codeRact = new Graphics();
// this.addChild(this.codeRact);
this.addChild(this.codeAGraph);
this.addChild(this.codeBGraph);
this.codeAGraph.setVectorFontSize(trainConsts.codeFontSize);
this.codeBGraph.setVectorFontSize(trainConsts.codeFontSize);
}
clear() {
// this.codeRact.clear();
this.codeAGraph.text = '';
this.codeBGraph.text = '';
}
getBodyWH(): bodyWH {
const bodyAWH = this.codeAGraph.localBoundsToCanvasPoints();
const bodyBWH = this.codeBGraph.localBoundsToCanvasPoints();
return {
width: bodyAWH[1].x - bodyAWH[0].x + bodyBWH[1].x - bodyBWH[0].x,
height: bodyAWH[2].y - bodyAWH[1].y,
};
}
doRepaint(states: ITrainState): void {
this.clear();
const codeAGraph = this.codeAGraph;
const codeBGraph = this.codeBGraph;
// const codeRact = this.codeRact;
let codeA = states.trainId;
// let codeA = states?.groupId;
// const firstChar = codeA.substring(0, 1); // 获取首字符
// if (+firstChar == states.lineId) {
// codeA = codeA.substring(1); // 删除首字符是线路号的字符
// }
const fillAColor = BBBColorEnum.schedule;
let fillBColor = BBBColorEnum.schedule;
if (states.mode?.ipModeTrainTypeSchedule) {
if (states.mode?.ipModeTrainSchdLate) {
fillBColor = BBBColorEnum.late;
} else if (states.mode?.ipModeTrainSchdEarly) {
fillBColor = BBBColorEnum.early;
}
} else if (states.mode?.ipModeTrainTypeHead) {
codeA = states?.destinationId + '';
fillBColor = BBBColorEnum.head;
} else if (states.mode?.ipModeTrainTypeManual) {
codeA = 'MM';
fillBColor = BBBColorEnum.manual;
} else if (states.mode?.ipModeTrainTypeSpecial) {
codeA = '';
}
const codeB = states?.globalId;
codeAGraph.text = codeA || '01';
codeBGraph.text = codeB || '222';
codeAGraph.anchor.set(0.5);
codeBGraph.anchor.set(0.5);
const styleA = {
fill: fillAColor,
fontSize: trainConsts.codeFontSize,
};
const styleB = {
fill: fillBColor,
fontSize: trainConsts.codeFontSize,
};
codeAGraph.style = styleA;
codeBGraph.style = styleB;
const bodyAWH = codeAGraph.getLocalBounds();
const bodyBWH = codeBGraph.getLocalBounds();
codeAGraph.position.set(-bodyBWH.width / 2, 0);
codeBGraph.position.set(bodyAWH.width / 2, 0);
codeAGraph.updateOnScaled();
codeBGraph.updateOnScaled();
// const { width: codeWidth, height: codeHeight } = this.getBodyWH();
// codeRact.beginFill(new Color(TrainColorEnum.bodyColor));
// codeRact.drawRect(-codeWidth / 2, -codeHeight / 2, codeWidth, codeHeight);
// codeRact.endFill();
}
}
class StatusText extends Container {
sText: VectorText = new VectorText('');
constructor() {
super();
this.addChild(this.sText);
}
doRepaint(text: string, bodyWH: bodyWH): void {
this.sText.text = text;
this.sText.anchor.set(0.5);
const c = (statusTextColor as never)[text] || statusTextColor.D;
const style = {
fill: c,
fontSize: trainConsts.textFontSize,
};
this.sText.style = style;
const { width: codeWidth, height: codeHeight } = bodyWH;
const { width: textHWidth, height: textHeight } =
this.sText.getLocalBounds();
const num = trainConsts.statusTextList.length;
let index = trainConsts.statusTextList.findIndex((item) => {
return item == text;
});
if (index < 0) {
index = (num - 1) / 2; // 中间
}
const textMargin = (codeWidth - textHWidth * num) / (num - 1);
this.sText.position.set(
-codeWidth / 2 + (textHWidth * (index * 2 + 1)) / 2 + textMargin * index,
-codeHeight / 2 - textHeight / 2 - trainConsts.textMarginY
);
}
clear(): void {
this.sText.text = '';
}
}
export class Train extends JlGraphic {
static Type = 'Train';
trainHead: TrainHead;
trainbody: TrainBody;
statusTextMap: Map<string, StatusText> = new Map();
constructor() {
super(Train.Type);
this.trainbody = new TrainBody();
this.trainHead = new TrainHead();
this.addChild(this.trainHead);
this.addChild(this.trainbody);
}
get datas(): ITrainData {
return this.getDatas<ITrainData>();
} }
get states(): ITrainState { get states(): ITrainState {
@ -371,52 +102,78 @@ export class Train extends JlGraphic {
this.trainbody.clear(); this.trainbody.clear();
return; return;
} }
this.trainbody.doRepaint(this.states); let direction = '';
const bodyWH = this.trainbody.getBodyWH(); if (this.states.mode?.ipModeTrainDirDown) {
this.trainHead.doRepaint(this.states, bodyWH); direction = 'left';
if (this.states.mode?.ipModeTrainHolded) { }
this.showStatus('H'); if (this.states.mode?.ipModeTrainDirUp) {
direction = 'right';
}
this.isRightRoTop = direction != 'left';
if (!direction) {
this.trainHead.clear();
}
let dModel = EnumDiriveModel.AM;
if (this.states.mode?.ipModeTrainDriveModeCm) {
dModel = EnumDiriveModel.SM;
} else if (
this.states.mode?.ipModeTrainDriveModeRmf ||
this.states.mode?.ipModeTrainDriveModeRmr
) {
dModel = EnumDiriveModel.RM;
}
this.setDiriveModelColor(dModel);
let codeA = this.states.trainId;
let tType = EnumTrainType.schedule;
if (this.states.mode?.ipModeTrainTypeSchedule) {
if (this.states.mode?.ipModeTrainSchdLate) {
tType = EnumTrainType.late;
} else if (this.states.mode?.ipModeTrainSchdEarly) {
tType = EnumTrainType.early;
}
} else if (this.states.mode?.ipModeTrainTypeHead) {
codeA = this.states?.destinationId + '';
tType = EnumTrainType.head;
} else if (this.states.mode?.ipModeTrainTypeManual) {
codeA = 'MM';
tType = EnumTrainType.manual;
} else if (this.states.mode?.ipModeTrainTypeSpecial) {
codeA = '';
}
this.setTrainTypeColor(tType);
const codeB = this.states?.globalId;
this.setCodeAText(codeA);
this.setCodeBText(codeB);
this.trainbody.doRepaint();
this.trainHead.doRepaint();
if (this.states.mode?.ipModeTrainStoped) {
this.stop();
} else { } else {
this.hideStatus('H'); this.run();
}
if (this.states.mode?.ipModeTrainHolded) {
this.showStatus(EnumStatusText.H);
} else {
this.hideStatus(EnumStatusText.H);
} }
if (this.states.mode?.ipModeTrainSkipstop) { if (this.states.mode?.ipModeTrainSkipstop) {
this.showStatus('S'); this.showStatus(EnumStatusText.S);
} else { } else {
this.hideStatus('S'); this.hideStatus(EnumStatusText.S);
} }
if (this.states.mode?.ipModeTrainDoorOpen) { if (this.states.mode?.ipModeTrainDoorOpen) {
this.showStatus('D'); this.showStatus(EnumStatusText.D);
} else { } else {
this.hideStatus('D'); this.hideStatus(EnumStatusText.D);
} }
if (this.states.mode?.ipModeTrainRsAlarm) { if (this.states.mode?.ipModeTrainRsAlarm) {
this.showStatus('A'); this.showStatus(EnumStatusText.A);
} else { } else {
this.hideStatus('A'); this.hideStatus(EnumStatusText.A);
} }
this.setPosition(); this.setPosition();
} }
showStatus(s: string) {
if (this.statusTextMap.has(s)) {
return;
}
const bodyWH = this.trainbody.getBodyWH();
const textD = new StatusText();
textD.doRepaint(s, bodyWH);
this.addChild(textD);
this.statusTextMap.set(s, textD);
}
hideStatus(s: string) {
if (!this.statusTextMap.has(s)) {
return;
}
const textD = this.statusTextMap.get(s);
if (textD) {
textD.clear();
this.statusTextMap.delete(s);
}
}
setPosition(): void { setPosition(): void {
if (deviceTypeString.get(this.states.devType)) { if (deviceTypeString.get(this.states.devType)) {
const dev = this.queryStore.queryByCodeAndType<LogicSection | Section>( const dev = this.queryStore.queryByCodeAndType<LogicSection | Section>(
@ -455,12 +212,22 @@ export class Train extends JlGraphic {
} }
} }
const updataConsts: UpdateTrainConsts = {
arrowPauseOnlyOne: true,
hasBodyRact: false,
textMarginY: 10, // 状态字母与列车距离
marginX: 4, // 图形x轴边距
borderColor: '0xA3E198', // 边框的颜色
arrowDefaultColor: '0x00FF00', // 箭头默认颜色
pauseDefaultColor: '0xFFCE4D', // 停止默认颜色
};
export class TrainTemplate extends JlGraphicTemplate<Train> { export class TrainTemplate extends JlGraphicTemplate<Train> {
constructor(dataTemplate: ITrainData, stateTemplate: ITrainState) { constructor(dataTemplate: ITrainData, stateTemplate: ITrainState) {
super(Train.Type, { dataTemplate, stateTemplate }); super(Train.Type, { dataTemplate, stateTemplate });
} }
new(): Train { new(): Train {
const train = new Train(); const train = new Train(updataConsts);
train.id = GraphicIdGenerator.next(); train.id = GraphicIdGenerator.next();
train.loadState(this.states); train.loadState(this.states);
return train; return train;

View File

@ -1,732 +1,15 @@
import { Graphics, IPointData } from 'pixi.js'; import { THTurnout as Turnout } from 'rt-graphic-component/components/packages/Turnout/THTurnout';
import {
GraphicAnimation,
GraphicData,
GraphicRelationParam,
GraphicState,
JlGraphic,
JlGraphicTemplate,
VectorText,
angleOfIncludedAngle,
distance2,
getParallelOfPolyline,
epsilon,
Vector2,
} from 'jl-graphic';
import { Section, SectionPort, SectionType } from '../section/Section';
import {
IRelatedRefData,
createRelatedRefProto,
protoPort2Data,
} from '../CommonGraphics';
import { KilometerSystem } from '../signal/Signal';
import { Station } from '../station/Station';
export interface ITurnoutData extends GraphicData { import { ITurnoutData } from 'rt-graphic-component/components/packages/Turnout/common/TurnoutConfig';
get code(): string; import { TurnoutTemplate } from 'rt-graphic-component/components/packages/Turnout/common/TurnoutTemplate';
set code(code: string); import { ITHTurnoutState as ITurnoutState } from 'rt-graphic-component/components/packages/Turnout/THTurnout';
get pointA(): IPointData[]; //A端点列表(从岔心向外) import { TurnoutDraw } from 'rt-graphic-component/components/packages/Turnout/common/TurnoutDrawAssistant';
set pointA(point: IPointData[]);
get pointB(): IPointData[];
set pointB(point: IPointData[]);
get pointC(): IPointData[];
set pointC(point: IPointData[]);
get paRef(): IRelatedRefData | undefined;
set paRef(ref: IRelatedRefData | undefined);
get pbRef(): IRelatedRefData | undefined;
set pbRef(ref: IRelatedRefData | undefined);
get pcRef(): IRelatedRefData | undefined;
set pcRef(ref: IRelatedRefData | undefined);
get kilometerSystem(): KilometerSystem[];
set kilometerSystem(v: KilometerSystem[]);
get centralizedStation(): number; //所属集中站
set centralizedStation(v: number);
clone(): ITurnoutData;
copyFrom(data: ITurnoutData): void;
eq(other: ITurnoutData): boolean;
}
export const TurnoutConsts = { export { Turnout, TurnoutTemplate, TurnoutDraw };
lineColor: '#5578b6', export type { ITurnoutState, ITurnoutData };
idleColor: '#888', //空闲
jammedLineColor: '#f00', //挤岔
ciOccupiedColor: '#f00', //非通信车占用
cbtcOccupiedColor: '#f49', //通信车占用
lockedColor: '#fff', //锁闭 && 故障锁闭
atcInvalidColor: '#954', //atc报告失效
overlapColor: '#ff0', //overlap
blueShowColor: '0x3149c3',
lineWidth: 5,
forkLenth: 20,
labelFontSize: 12,
};
const TurnoutLabelColor = {
GREEN: '#0f0',
YELLOW: '#ff0',
RED: '#f00',
WHITE: '#fff',
};
export enum TurnoutPosition {
NORMAL = 0,
REVERSE = 1,
}
export enum TurnoutPort { export enum TurnoutPort {
A = 'A', A = 0,
B = 'B', B = 1,
C = 'C', C = 2,
}
export interface ITurnoutState extends GraphicState {
ipSingleSwitchStusCiOccupied: boolean;
ipSingleSwitchStusCbtcOccupied: boolean;
ipSingleSwitchStusLocked: boolean;
ipSingleSwitchStusFailLocked: boolean;
ipSingleSwitchStusNormal: boolean;
ipSingleSwitchStusReverse: boolean;
ipSingleSwitchStusBlocked1: boolean;
ipSingleSwitchStusJammed: boolean;
ipSingleSwitchStusCut: boolean;
ipSingleSwitchStusAtcInvalid: boolean;
ipSingleSwitchStusOverlap: boolean;
ipSingleSwitchStusTsrCbtcMain: boolean;
ipSingleSwitchStusTsrCbtcNormal: boolean;
ipSingleSwitchStusTsrCbtcReverse: boolean;
ipSingleSwitchStusTsrBmMain: boolean;
ipSingleSwitchStusTsrBmNormal: boolean;
ipSingleSwitchStusTsrBmReverse: boolean;
ipSingleSwitchStusBlocked2: boolean;
ipSingleSwitchStusLostIndication: boolean;
id: string;
speedLimit: number;
rtuId: number;
}
export function getForkPoint(r: number, p: IPointData): IPointData {
if (r === 0) return { x: 0, y: 0 };
const len = Math.sqrt((-p.x) ** 2 + (-p.y) ** 2);
const scale = r / len;
return { x: scale * p.x, y: scale * p.y };
}
export class TurnoutSection extends Graphics {
turnout: Turnout;
port: TurnoutPort;
constructor(turnout: Turnout, port: TurnoutPort) {
super();
this.turnout = turnout;
this.port = port;
}
paint() {
let pList: IPointData[] = [];
let lineColor = this.turnout.lineColor;
switch (this.port) {
case TurnoutPort.A:
pList = this.turnout.datas.pointA;
break;
case TurnoutPort.B:
pList = this.turnout.datas.pointB;
if (!this.turnout.states.ipSingleSwitchStusNormal) {
lineColor = TurnoutConsts.idleColor;
}
break;
case TurnoutPort.C:
pList = this.turnout.datas.pointC;
if (!this.turnout.states.ipSingleSwitchStusReverse) {
lineColor = TurnoutConsts.idleColor;
}
break;
}
const station = this.turnout.queryStore.queryByCodeAndType<Station>(
this.turnout.states.rtuId > 9
? '' + this.turnout.states.rtuId
: '0' + this.turnout.states.rtuId,
Station.Type
);
if (station?.states.ipRtuStusDown) {
lineColor = TurnoutConsts.blueShowColor;
}
const gap = this.port === TurnoutPort.A ? 0 : TurnoutConsts.forkLenth;
const start = getForkPoint(gap, pList[0]);
this.clear()
.lineStyle(TurnoutConsts.lineWidth, lineColor)
.moveTo(start.x, start.y);
pList.forEach((p) => {
const { x, y } = p;
this.lineTo(x, y);
});
}
}
class ForkGraphic extends Graphics {
turnout: Turnout;
dt = 0;
lostIndicationSquare: Graphics = new Graphics();
constructor(turnout: Turnout) {
super();
this.turnout = turnout;
this.addChild(this.lostIndicationSquare);
}
paint() {
this.clear();
this.lostIndicationSquare.clear();
if (
this.turnout.states.ipSingleSwitchStusJammed /* ||
this.turnout.states.ipSingleSwitchStusLostIndication */
) {
let x: number, y: number;
const w = 24,
h = 24;
if (this.turnout.datas.pointA[0].x > this.turnout.datas.pointB[0].x) {
x = -22;
} else {
x = -2;
}
if (this.turnout.datas.pointC[0].y > 0) {
y = -2;
} else {
y = -20;
}
this.lostIndicationSquare.lineStyle(2, '#f00').drawRect(x, y, w, h);
const flashAnimation = this.bindFlashAnimation(this.lostIndicationSquare);
flashAnimation.resume();
return;
}
if (this.turnout.states.ipSingleSwitchStusNormal) {
this.paintForkLine('B');
this.turnout.removeAllAnimation();
} else if (this.turnout.states.ipSingleSwitchStusReverse) {
this.paintForkLine('C');
this.turnout.removeAllAnimation();
}
}
paintForkLine(position: 'B' | 'C' | 'BOTH') {
this.clear().lineStyle(TurnoutConsts.lineWidth, this.turnout.lineColor);
const targetB = getForkPoint(
TurnoutConsts.forkLenth,
this.turnout.datas.pointB[0]
);
const targetC = getForkPoint(
TurnoutConsts.forkLenth,
this.turnout.datas.pointC[0]
);
if (position === 'B') {
this.moveTo(0, 0).lineTo(targetB.x, targetB.y);
} else if (position === 'C') {
this.moveTo(0, 0).lineTo(targetC.x, targetC.y);
} else if (position === 'BOTH') {
this.moveTo(targetB.x, targetB.y)
.lineTo(0, 0)
.lineTo(targetC.x, targetC.y);
}
}
bindFlashAnimation(g: Graphics) {
const flashAnimation = GraphicAnimation.init({
name: 'lostIndicationFlash',
run: (dt: number) => {
this.dt += dt;
if (this.dt > 60) {
this.dt = 0;
g.visible = true;
} else if (this.dt > 30) {
g.visible = false;
}
},
});
this.turnout.addAnimation(flashAnimation);
return flashAnimation;
}
}
export class Turnout extends JlGraphic {
static Type = 'Turnout';
graphics: {
fork: ForkGraphic;
sections: [TurnoutSection, TurnoutSection, TurnoutSection];
label: VectorText;
labelRect: Graphics;
speedLimit: Graphics;
};
lineColor: string;
dt = 0;
constructor() {
super(Turnout.Type);
this.name = 'turnout';
this.lineColor = TurnoutConsts.lineColor;
this.graphics = {
fork: new ForkGraphic(this),
sections: [
new TurnoutSection(this, TurnoutPort.A),
new TurnoutSection(this, TurnoutPort.B),
new TurnoutSection(this, TurnoutPort.C),
],
label: new VectorText(),
labelRect: new Graphics(),
speedLimit: new Graphics(),
};
this.addChild(this.graphics.sections[0]);
this.addChild(this.graphics.sections[1]);
this.addChild(this.graphics.sections[2]);
this.addChild(this.graphics.fork);
this.graphics.label.anchor.set(0.5);
this.graphics.label.style.fill = '#0f0';
this.graphics.label.setVectorFontSize(TurnoutConsts.labelFontSize);
this.graphics.label.position.set(20, 20);
this.graphics.label.transformSave = true;
this.graphics.label.name = 'label';
this.addChild(this.graphics.label);
this.addChild(this.graphics.labelRect);
this.addChild(this.graphics.speedLimit);
}
get code(): string {
return this.datas.code;
}
set code(code: string) {
this.datas.code = code;
}
get datas(): ITurnoutData {
return this.getDatas<ITurnoutData>();
}
get states(): ITurnoutState {
return this.getStates<ITurnoutState>();
}
getSpeedLimitLinePoints(conf: 'normal' | 'reverse' | 'main'): IPointData[][] {
const [pa, pb, pc] = [
this.datas.pointA[0],
this.datas.pointB[0],
this.datas.pointC[0],
];
const [va, vb, vc] = [
new Vector2([pa.x, pa.y]),
new Vector2([pb.x, pb.y]),
new Vector2([pc.x, pc.y]),
];
const [vab, vbc] = [vb.subtract(va), vc.subtract(vb)];
const cpd = vab.x * vbc.y - vab.y * vbc.x;
const side = cpd > 0 ? 'L' : 'R'; //计算三个分支的手性
const offset = 10;
if (conf === 'main') {
return [
getParallelOfPolyline(
[
...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(),
{ x: 0, y: 0 },
],
offset,
'L'
),
getParallelOfPolyline(
[
...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(),
{ x: 0, y: 0 },
],
offset,
'R'
),
];
} else if (conf === 'normal') {
return [
getParallelOfPolyline(
[
...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(),
{ x: 0, y: 0 },
...this.datas.pointB.map((p) => ({ x: p.x, y: p.y })),
],
offset,
'L'
),
getParallelOfPolyline(
[
...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(),
{ x: 0, y: 0 },
...this.datas.pointB.map((p) => ({ x: p.x, y: p.y })),
],
offset,
'R'
),
];
} else {
return [
getParallelOfPolyline(
[
...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(),
{ x: 0, y: 0 },
...this.datas.pointC.map((p) => ({ x: p.x, y: p.y })),
],
offset,
'L'
),
getParallelOfPolyline(
[
...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(),
{ x: 0, y: 0 },
...this.datas.pointC.map((p) => ({ x: p.x, y: p.y })),
],
offset,
'R'
),
];
}
}
getPortPoints() {
return [this.datas.pointA, this.datas.pointB, this.datas.pointC];
}
doRepaint(): void {
//线条颜色
const station = this.queryStore.queryByCodeAndType<Station>(
this.states.rtuId > 9 ? '' + this.states.rtuId : '0' + this.states.rtuId,
Station.Type
);
if (station?.states.ipRtuStusDown) {
this.lineColor = TurnoutConsts.blueShowColor;
} else if (this.states.ipSingleSwitchStusCbtcOccupied) {
this.lineColor = TurnoutConsts.cbtcOccupiedColor;
} else if (this.states.ipSingleSwitchStusCiOccupied) {
this.lineColor = TurnoutConsts.ciOccupiedColor;
} else if (
this.states.ipSingleSwitchStusLocked ||
this.states.ipSingleSwitchStusFailLocked
) {
this.lineColor = TurnoutConsts.lockedColor;
} else if (this.states.ipSingleSwitchStusAtcInvalid) {
this.lineColor = TurnoutConsts.atcInvalidColor;
} else if (this.states.ipSingleSwitchStusOverlap) {
this.lineColor = TurnoutConsts.overlapColor;
} else {
this.lineColor = TurnoutConsts.idleColor;
}
this.graphics.sections.forEach((sectionGraphic) => sectionGraphic.paint());
this.graphics.fork.paint();
this.graphics.label.text = this.datas.code;
//文字颜色
if (this.states.ipSingleSwitchStusBlocked1) {
this.graphics.label.style.fill = TurnoutLabelColor.RED;
} else if (this.states.ipSingleSwitchStusNormal) {
this.graphics.label.style.fill = TurnoutLabelColor.GREEN;
} else if (this.states.ipSingleSwitchStusReverse) {
this.graphics.label.style.fill = TurnoutLabelColor.YELLOW;
} else if (
this.states.ipSingleSwitchStusJammed ||
this.states.ipSingleSwitchStusLostIndication
) {
this.graphics.label.style.fill = TurnoutLabelColor.WHITE;
}
this.graphics.labelRect.clear();
this.graphics.speedLimit.clear();
this.graphics.fork.visible = true;
this.graphics.sections.forEach((s) => (s.visible = true));
this.removeAnimation('flash');
//文字框
if (this.states.ipSingleSwitchStusBlocked2) {
this.graphics.labelRect.clear().lineStyle(1, '#f00');
const { width, height } = this.graphics.label.getLocalBounds();
const { x, y } = this.graphics.label.transform.position;
this.graphics.labelRect.drawRect(
x - width / 2,
y - height / 2,
width,
height
);
}
if (station?.states.ipRtuStusDown) {
return;
}
if (
(this.states.speedLimit && this.states.speedLimit > 0) ||
this.states.ipSingleSwitchStusTsrBmMain ||
this.states.ipSingleSwitchStusTsrBmNormal ||
this.states.ipSingleSwitchStusTsrBmReverse ||
this.states.ipSingleSwitchStusTsrCbtcMain ||
this.states.ipSingleSwitchStusTsrCbtcNormal ||
this.states.ipSingleSwitchStusTsrCbtcReverse
) {
let limitConf: Parameters<
typeof Turnout.prototype.getSpeedLimitLinePoints
>[0];
if (
this.states.ipSingleSwitchStusTsrBmReverse ||
this.states.ipSingleSwitchStusTsrCbtcReverse
) {
limitConf = 'reverse';
} else if (
this.states.ipSingleSwitchStusTsrBmNormal ||
this.states.ipSingleSwitchStusTsrCbtcNormal
) {
limitConf = 'normal';
} else {
limitConf = 'main';
}
const points = this.getSpeedLimitLinePoints(limitConf);
this.graphics.speedLimit.lineStyle(1, '#ff0');
points.forEach((ps) => {
this.graphics.speedLimit.moveTo(ps[0].x, ps[0].y);
for (let i = 1; i < ps.length; i++) {
this.graphics.speedLimit.lineTo(ps[i].x, ps[i].y);
}
});
}
if (this.states.ipSingleSwitchStusCut) {
if (this.states.ipSingleSwitchStusNormal) {
this.bindFlashAnimation([
this.graphics.fork,
this.graphics.sections[0],
this.graphics.sections[1],
]);
} else if (this.states.ipSingleSwitchStusReverse) {
this.bindFlashAnimation([
this.graphics.fork,
this.graphics.sections[0],
this.graphics.sections[2],
]);
}
this.animation('flash')?.resume();
}
}
bindFlashAnimation(gList: Graphics[]) {
const flashAnimation = GraphicAnimation.init({
name: 'flash',
run: (dt: number) => {
this.dt += dt;
if (this.dt > 60) {
this.dt = 0;
gList.forEach((g) => (g.visible = true));
} else if (this.dt > 30) {
gList.forEach((g) => (g.visible = false));
}
},
});
this.addAnimation(flashAnimation);
return flashAnimation;
}
getGraphicOfPort(port: TurnoutPort) {
return this.relationManage
.getRelationsOfGraphic(this)
.filter(
(relation) =>
relation.getRelationParam(this).getParam<TurnoutPort>() === port
)
.map((relation) => {
return relation.getOtherGraphic(this);
});
}
buildRelation(): void {
this.relationManage.deleteRelationOfGraphic(this);
/** 道岔和区段 */
this.queryStore.queryByType<Section>(Section.Type)
.forEach((section) => {
if (section.datas.sectionType === SectionType.TurnoutPhysical) {
if (section.datas.children.includes(this.datas.id)) {
this.relationManage.addRelation(this, section)
}
return
}
this.getPortPoints().forEach((port, i) => {
if (
distance2(
section.localToCanvasPoint(section.getStartPoint()),
this.localToCanvasPoint(port[port.length - 1])
) <= epsilon
) {
this.relationManage.addRelation(
new GraphicRelationParam(
this,
[TurnoutPort.A, TurnoutPort.B, TurnoutPort.C][i]
),
new GraphicRelationParam(section, SectionPort.A)
);
}
if (
distance2(
section.localToCanvasPoint(section.getEndPoint()),
this.localToCanvasPoint(port[port.length - 1])
) <= epsilon
) {
this.relationManage.addRelation(
new GraphicRelationParam(
this,
[TurnoutPort.A, TurnoutPort.B, TurnoutPort.C][i]
),
new GraphicRelationParam(section, SectionPort.B)
);
}
});
});
/** 道岔和道岔 */
this.getPortPoints().forEach((thisPort, i) => {
let params: GraphicRelationParam[] = [],
deflection = 180;
this.queryStore.queryByType<Turnout>(Turnout.Type).forEach((turnout) => {
if (turnout.id === this.id) return;
turnout.getPortPoints().forEach((otherPort, j) => {
if (
distance2(
this.localToCanvasPoint(thisPort[thisPort.length - 1]),
turnout.localToCanvasPoint(otherPort[otherPort.length - 1])
) <= epsilon
) {
const angle = angleOfIncludedAngle(
this.localToCanvasPoint(thisPort[thisPort.length - 1]) /* 交点 */,
thisPort[thisPort.length - 2]
? this.localToCanvasPoint(thisPort[thisPort.length - 2])
: this.position,
otherPort[otherPort.length - 2]
? turnout.localToCanvasPoint(otherPort[otherPort.length - 2])
: turnout.position
);
if (180 - Math.abs(angle) <= deflection) {
deflection = 180 - Math.abs(angle);
params = [
new GraphicRelationParam(
this,
[TurnoutPort.A, TurnoutPort.B, TurnoutPort.C][i]
),
new GraphicRelationParam(
turnout,
[TurnoutPort.A, TurnoutPort.B, TurnoutPort.C][j]
),
];
}
}
});
});
if (params.length === 2) {
this.relationManage.addRelation(params[0], params[1]);
}
});
}
saveRelations() {
const paRelation = this.relationManage
.getRelationsOfGraphic(this)
.find(
(relation) =>
relation.getRelationParam(this).param === TurnoutPort.A &&
(relation.getOtherGraphic(this) instanceof Section ||
relation.getOtherGraphic(this) instanceof Turnout)
);
const paDevice = paRelation?.getOtherGraphic<Section | Turnout>(this);
if (paDevice) {
this.datas.paRef = createRelatedRefProto(
paDevice.type,
paDevice.id,
paRelation?.getOtherRelationParam(this).param
);
} else {
this.datas.paRef = undefined;
}
const pbRelation = this.relationManage
.getRelationsOfGraphic(this)
.find(
(relation) =>
relation.getRelationParam(this).param === TurnoutPort.B &&
(relation.getOtherGraphic(this) instanceof Section ||
relation.getOtherGraphic(this) instanceof Turnout)
);
const pbDevice = pbRelation?.getOtherGraphic<Section | Turnout>(this);
if (pbDevice) {
this.datas.pbRef = createRelatedRefProto(
pbDevice.type,
pbDevice.id,
pbRelation?.getOtherRelationParam(this).param
);
} else {
this.datas.pbRef = undefined;
}
const pcRelation = this.relationManage
.getRelationsOfGraphic(this)
.find(
(relation) => relation.getRelationParam(this).param === TurnoutPort.C
&& (!(relation.getOtherGraphic(this) instanceof Section
&& relation.getOtherGraphic<Section>(this).datas.sectionType !== SectionType.TurnoutPhysical))
);
const pcDevice = pcRelation?.getOtherGraphic<Section | Turnout>(this);
if (pcDevice) {
this.datas.pcRef = createRelatedRefProto(
pcDevice.type,
pcDevice.id,
pcRelation?.getOtherRelationParam(this).param
);
} else {
this.datas.pcRef = undefined;
}
}
loadRelations() {
if (this.datas.paRef?.id) {
this.relationManage.addRelation(
new GraphicRelationParam(this, TurnoutPort.A),
new GraphicRelationParam(
this.queryStore.queryById(this.datas.paRef.id),
protoPort2Data(this.datas.paRef.devicePort)
)
);
}
if (this.datas.pbRef?.id) {
this.relationManage.addRelation(
new GraphicRelationParam(this, TurnoutPort.B),
new GraphicRelationParam(
this.queryStore.queryById(this.datas.pbRef.id),
protoPort2Data(this.datas.pbRef.devicePort)
)
);
}
if (this.datas.pcRef?.id) {
this.relationManage.addRelation(
new GraphicRelationParam(this, TurnoutPort.C),
new GraphicRelationParam(
this.queryStore.queryById(this.datas.pcRef.id),
protoPort2Data(this.datas.pcRef.devicePort)
)
);
}
}
}
export class TurnoutTemplate extends JlGraphicTemplate<Turnout> {
constructor(dataTemplate: ITurnoutData, stateTemplate?: ITurnoutState) {
super(Turnout.Type, {
dataTemplate,
stateTemplate,
});
}
new() {
const g = new Turnout();
g.loadData(this.datas);
g.loadState(this.states);
return g;
}
} }

View File

@ -1,493 +1,6 @@
import { import {
AbsorbablePosition, ForkHitArea,
DraggablePoint, TurnoutSectionHitArea,
IGraphicApp, } from 'rt-graphic-component/components/packages/Turnout/common/TurnoutDrawAssistant';
GraphicDrawAssistant,
GraphicInteractionPlugin,
GraphicRelation,
GraphicTransformEvent,
IDrawApp,
JlGraphic,
VectorText,
linePoint,
polylinePoint,
GraphicEditPlugin,
getWaypointRangeIndex,
AbsorbablePoint,
AbsorbableLine,
ContextMenu,
MenuItemOptions,
} from 'jl-graphic';
import {
ITurnoutData,
Turnout,
TurnoutConsts,
TurnoutPort,
TurnoutSection,
TurnoutTemplate,
getForkPoint,
} from './Turnout';
import {
DisplayObject,
FederatedMouseEvent,
IHitArea,
IPointData,
Point,
} from 'pixi.js';
import { Section, SectionPort } from '../section/Section';
export class TurnoutDraw extends GraphicDrawAssistant< export { ForkHitArea, TurnoutSectionHitArea };
TurnoutTemplate,
ITurnoutData
> {
turnout: Turnout;
constructor(app: IDrawApp, template: TurnoutTemplate) {
super(app, template, 'sym_o_ramp_left', '道岔Turnout');
this.turnout = this.graphicTemplate.new();
this.container.addChild(this.turnout);
TurnoutPointsInteractionPlugin.init(app);
}
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: Turnout;
constructor(turnout: Turnout) {
this.turnout = turnout;
}
contains(x: number, y: number): boolean {
const intersectPointB = getForkPoint(
TurnoutConsts.forkLenth,
this.turnout.datas.pointB[0]
);
const intersectPointC = getForkPoint(
TurnoutConsts.forkLenth,
this.turnout.datas.pointC[0]
);
return (
linePoint(
intersectPointB,
{ x: 0, y: 0 },
{ x, y },
TurnoutConsts.lineWidth
) ||
linePoint(
intersectPointC,
{ x: 0, y: 0 },
{ x, y },
TurnoutConsts.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 TurnoutPort.A:
points = this.section.turnout.datas.pointA;
start = { x: 0, y: 0 };
break;
case TurnoutPort.B:
points = this.section.turnout.datas.pointB;
start = getForkPoint(TurnoutConsts.forkLenth, points[0]);
break;
case TurnoutPort.C:
points = this.section.turnout.datas.pointC;
start = getForkPoint(TurnoutConsts.forkLenth, points[0]);
break;
}
return polylinePoint([start, ...points], { x, y }, TurnoutConsts.lineWidth);
}
}
function buildAbsorbablePositions(turnout: Turnout): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const sections = turnout.queryStore.queryByType<Section>(Section.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<Turnout>(Turnout.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;
}
function onEditPointCreate(turnout: Turnout, 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<Turnout> {
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) {
let points: IPointData[];
if (section.port === TurnoutPort.A) points = section.turnout.datas.pointA;
if (section.port === TurnoutPort.B) points = section.turnout.datas.pointB;
if (section.port === TurnoutPort.C) points = section.turnout.datas.pointC;
const p = section.turnout.screenToLocalPoint(e.global);
addPointConfig.handler = () => {
if (section.port === TurnoutPort.A) {
const { start } = getWaypointRangeIndex(
[{ x: 0, y: 0 }, ...section.turnout.datas.pointA],
false,
p,
TurnoutConsts.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 === TurnoutPort.B) {
const { start } = getWaypointRangeIndex(
[{ x: 0, y: 0 }, ...section.turnout.datas.pointB],
false,
p,
TurnoutConsts.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 === TurnoutPort.C) {
const { start } = getWaypointRangeIndex(
[{ x: 0, y: 0 }, ...section.turnout.datas.pointC],
false,
p,
TurnoutConsts.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 (points.length <= 1) return;
points.splice(0, points.length - 1);
section.turnout.repaint();
};
turnoutSectionEditMenu.open(e.global);
}
bind(g: Turnout): 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);
}
unbind(g: Turnout): void {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnSelected, this);
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.off('rightclick');
});
}
onSelected(g: DisplayObject) {
const turnout = g as Turnout;
let tep = turnout.getAssistantAppend<TurnoutEditPlugin>(
TurnoutEditPlugin.Name
);
if (!tep) {
tep = new TurnoutEditPlugin(turnout, { onEditPointCreate });
turnout.addAssistantAppend(tep);
}
// tep.reset();
tep.showAll();
tep.setRelatedDrag();
}
onUnSelected(g: DisplayObject) {
const turnout = g as Turnout;
const tep = turnout.getAssistantAppend<TurnoutEditPlugin>(
TurnoutEditPlugin.Name
);
if (tep) {
tep.hideAll();
}
}
filter(...grahpics: JlGraphic[]): Turnout[] | undefined {
return grahpics.filter((g) => g.type == Turnout.Type) as Turnout[];
}
}
type onTurnoutEditPointCreate = (turnout: Turnout, dp: DraggablePoint) => void;
export interface ITurnoutEditOptions {
onEditPointCreate?: onTurnoutEditPointCreate;
}
export class TurnoutEditPlugin extends GraphicEditPlugin<Turnout> {
static Name = 'TurnoutEdit';
options: ITurnoutEditOptions;
editPoints: DraggablePoint[][] = [[], [], []];
labels: VectorText[] = [];
constructor(graphic: Turnout, 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();
}
setRelatedDrag() {
this.editPoints.forEach((eps, i) => {
const ep = eps[eps.length - 1];
let relations: GraphicRelation[];
if (i === 0) {
relations = this.graphic.relationManage
.getRelationsOfGraphic(this.graphic)
.filter(
(relation) =>
relation.getRelationParam(this.graphic).param === TurnoutPort.A
);
} else if (i === 1) {
relations = this.graphic.relationManage
.getRelationsOfGraphic(this.graphic)
.filter(
(relation) =>
relation.getRelationParam(this.graphic).param === TurnoutPort.B
);
} else {
relations = this.graphic.relationManage
.getRelationsOfGraphic(this.graphic)
.filter(
(relation) =>
relation.getRelationParam(this.graphic).param === TurnoutPort.C
);
}
if (!relations.length) return;
const otherGraphics = relations.map((relation) =>
relation.getOtherGraphic(this.graphic)
);
console.log(otherGraphics);
const otherPorts = relations.map(
(relation) => relation.getOtherRelationParam(this.graphic).param
);
const point: IPointData[] = [];
otherGraphics.map((otherGraphic, i) => {
const otherPort = otherPorts[i];
if (otherGraphic instanceof Turnout) {
if (otherPort === TurnoutPort.A) {
point.push(
otherGraphic.datas.pointA[otherGraphic.datas.pointA.length - 1]
);
} else if (otherPort === TurnoutPort.B) {
point.push(
otherGraphic.datas.pointB[otherGraphic.datas.pointB.length - 1]
);
} else if (otherPort === TurnoutPort.C) {
point.push(
otherGraphic.datas.pointC[otherGraphic.datas.pointC.length - 1]
);
}
} else if (otherGraphic instanceof Section) {
if (otherPort === SectionPort.A) {
point.push(otherGraphic.datas.points[0]);
} else if (otherPort === SectionPort.B) {
point.push(
otherGraphic.datas.points[otherGraphic.datas.points.length - 1]
);
}
}
});
const transformingHandler = () => {
otherGraphics.forEach((otherGraphic, i) => {
const p = otherGraphic.canvasToLocalPoint(ep);
point[i].x = p.x;
point[i].y = p.y;
otherGraphic.repaint();
});
};
ep.on('transforming', transformingHandler);
});
}
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', (e: GraphicTransformEvent) => {
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);
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 });
}
});
});
}
}