电子地图绘制备用

This commit is contained in:
joylink_zhaoerwei 2024-11-13 17:59:00 +08:00
parent c06acfd0b8
commit 5400acf7e3
43 changed files with 9509 additions and 450 deletions

@ -1 +1 @@
Subproject commit 1f53057b3f87790ef27c91399a5bb7e890f05549 Subproject commit 64e48a0441eedd0b7bc926ca922b2fb58075467b

View File

@ -7,7 +7,7 @@ const os = require('os');
const { exec } = require('child_process'); const { exec } = require('child_process');
const messageDir = resolve(__dirname, '../rtss-proto-msg'); const messageDir = resolve(__dirname, '../rtsa-proto-msg');
const protoDir = resolve(messageDir, 'src'); const protoDir = resolve(messageDir, 'src');
const destDir = resolve(__dirname, '../src/protos'); const destDir = resolve(__dirname, '../src/protos');

View File

@ -0,0 +1,49 @@
<template>
<!-- 绘制图形模板属性 -->
<div v-if="drawStore.drawMode">
<q-card flat>
<q-card-section>
<div class="text-h6">{{ drawStore.drawGraphicName + ' 模板' }}</div>
</q-card-section>
<q-separator inset></q-separator>
</q-card>
</div>
<!-- 画布或图形对象属性 -->
<div v-else-if="drawStore.selectedGraphics !== null">
<q-card flat>
<q-card-section>
<div class="text-h6">{{ drawStore.selectedObjName + ' 属性' }}</div>
</q-card-section>
<q-separator inset></q-separator>
<template v-if="drawStore.selectedGraphics.length === 0">
<q-card-section>
<canvas-property></canvas-property>
</q-card-section>
</template>
<template v-else-if="drawStore.selectedGraphics.length === 1">
<q-card-section>
<platform-property
v-if="drawStore.selectedGraphicType === Platform.Type"
></platform-property>
</q-card-section>
</template>
<!-- <template v-else-if="drawStore.selectedGraphics.length > 1">
<multiple-select-property></multiple-select-property>
</template> -->
</q-card>
</div>
</template>
<script setup lang="ts">
import { useDrawStore } from 'src/stores/electronicMap-draw-store';
import CanvasProperty from './properties/CanvasElectronicMapProperty.vue';
import PlatformProperty from './properties/electronicMap/PlatformProperty.vue';
import { Platform } from 'src/graphics/electronicMap/platform/Platform';
import { watch } from 'vue';
const drawStore = useDrawStore();
watch(
() => drawStore.selectedGraphics,
() => {}
);
</script>

View File

@ -0,0 +1,80 @@
<template>
<q-form>
<q-input
outlined
v-model.number="canvas.width"
@blur="onUpdate"
label="画布宽 *"
lazy-rules
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
/>
<q-input
outlined
type="number"
v-model.number="canvas.height"
@blur="onUpdate"
label="画布高 *"
lazy-rules
:rules="[(val) => val > 0 || '画布高必须大于0']"
/>
<q-input
outlined
v-model="canvas.backgroundColor"
@blur="onUpdate"
label="画布背景色 *"
lazy-rules
:rules="[(val) => (val && val.length > 0) || '画布背景色必须设置']"
>
<template v-slot:append>
<q-icon name="colorize" class="cursor-pointer">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-color
:model-value="canvas.backgroundColor"
@change="
(val) => {
canvas.backgroundColor = val;
onUpdate();
}
"
/>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</q-form>
</template>
<script setup lang="ts">
import { useDrawStore } from 'src/stores/electronicMap-draw-store';
import { onMounted, onUnmounted, reactive } from 'vue';
const drawStore = useDrawStore();
const canvas = reactive({
width: 1920,
height: 1080,
backgroundColor: '#ffffff',
});
onMounted(() => {
// console.log('mounted');
const jc = drawStore.getJlCanvas();
canvas.width = jc.properties.width;
canvas.height = jc.properties.height;
canvas.backgroundColor = jc.properties.backgroundColor;
});
onUnmounted(() => {
// console.log('unmounted');
});
function onUpdate() {
// console.log('');
const app = drawStore.getDrawApp();
app.updateCanvasAndRecord({
...canvas,
viewportTransform: app.canvas.properties.viewportTransform,
});
}
</script>

View File

@ -0,0 +1,188 @@
<template>
<q-card class="q-gutter-sm q-pa-sm">
<q-card-section>
<div class="text-h6">一键生成计轴配置</div>
</q-card-section>
<q-form ref="myForm">
<selectConfig-utils ref="selectConfigUtils" :drawStore="drawStore" />
<div class="q-mt-md q-gutter-md">
<q-btn label="确认修改" color="primary" @click="onSubmit" />
<q-btn label="返回" color="red" @click="goBack" />
<q-btn label="生成计轴" color="primary" @click="generateAxleCounting" />
</div>
<div class="q-mt-md q-gutter-md">
<q-btn
label="检查计轴"
color="primary"
@click="showErrorAxleCounting"
/>
<q-btn
v-if="showCheck"
label="上一个"
color="primary"
@click="clickSelectCenter(-1)"
/>
<q-btn
v-if="showCheck"
label="下一个"
color="primary"
@click="clickSelectCenter(1)"
/>
</div>
</q-form>
</q-card>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue';
import { QForm, useQuasar } from 'quasar';
import { JlGraphic } from 'jl-graphic';
import { useDrawStore } from 'src/stores/electronicMap-draw-store';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
import { Turnout } from 'src/graphics/electronicMap/turnout/Turnout';
import {
loadGenerateAxleCountingConfig,
setGenerateAxleCountingConfig,
} from 'src/drawApp/electronicMapApp';
import SelectConfigUtils from 'src/components/common/SelectConfigUtils.vue';
import { OneClickGenerate } from 'src/graphics/electronicMap/trainWindow/oneClickDrawAssistant';
import { AxleCounting } from 'src/graphics/electronicMap/axleCounting/AxleCounting';
//使
const filterSelect = (g: JlGraphic) => g instanceof Turnout;
const selectConfigUtils = ref<InstanceType<typeof SelectConfigUtils> | null>(
null
);
provide('filter', filterSelect);
const emit = defineEmits(['close']);
const drawStore = useDrawStore();
const $q = useQuasar();
onMounted(() => {
selectConfigUtils.value.selectConfig = [
{
code: 'bb连接的道岔',
refDevices: [],
refDevicesCode: [],
expanded: false,
},
{
code: '不生成计轴的道岔组',
refDevices: [],
refDevicesCode: [],
expanded: false,
},
];
const generateAxleCountingConfig = loadGenerateAxleCountingConfig();
if (generateAxleCountingConfig !== undefined) {
selectConfigUtils.value.selectConfig.forEach(
(generate: {
code: string;
refDevices: number[];
refDevicesCode: string[];
}) => {
if (generate.code == 'bb连接的道岔') {
generate.refDevices = generateAxleCountingConfig.bbConnect;
generateAxleCountingConfig.bbConnect.forEach((id) => {
const g = drawStore.getDrawApp().queryStore.queryById(id);
generate.refDevicesCode.push(g.code);
});
} else if (generate.code == '不生成计轴的道岔组') {
generate.refDevices = generateAxleCountingConfig.noGenerateGroup;
generateAxleCountingConfig.noGenerateGroup.forEach((id) => {
const g = drawStore.getDrawApp().queryStore.queryById(id);
generate.refDevicesCode.push(g.code);
});
}
}
);
}
});
const myForm = ref<QForm | null>(null);
async function onSubmit() {
myForm.value?.validate().then(async (res) => {
if (res) {
try {
const generateAxleCountingConfig =
new electronicMapGraphicData.GenerateAxleCountingConfig();
selectConfigUtils.value.selectConfig.forEach(
(generate: { code: string; refDevices: number[] }) => {
if (generate.code == 'bb连接的道岔') {
generateAxleCountingConfig.bbConnect = generate.refDevices;
} else if (generate.code == '不生成计轴的道岔组') {
generateAxleCountingConfig.noGenerateGroup = generate.refDevices;
}
}
);
setGenerateAxleCountingConfig(generateAxleCountingConfig);
$q.notify({
type: 'positive',
message: '更新成功',
});
} catch (err) {
$q.notify({
type: 'negative',
message: '更新失败',
});
}
}
});
}
function goBack() {
emit('close');
}
function generateAxleCounting() {
drawStore.oneClickType = 'AxleCounting';
drawStore.getDrawApp().interactionPlugin(OneClickGenerate.Type).resume();
}
const selectConfig = ref<
{
axleCountingId: number;
axleCountingCode: string;
}[]
>([]);
function showErrorAxleCounting() {
showCheck.value = true;
const axleCountings = drawStore
.getDrawApp()
.queryStore.queryByType<AxleCounting>(AxleCounting.Type);
const erroeAxleCountings = axleCountings
.filter(
(g) =>
g.datas.type ==
electronicMapGraphicData.AxleCounting.TypeDetectionPoint.AxleCounting &&
g.datas.axleCountingRef.length < 2
)
.sort((a, b) => a.position.x - b.position.x);
erroeAxleCountings.forEach((axleCounting) => {
selectConfig.value.push({
axleCountingId: axleCounting.id,
axleCountingCode: axleCounting.datas.code,
});
});
}
const currentIndex = ref(-1);
const showCheck = ref(false);
function clickSelectCenter(add: number) {
if (
currentIndex.value + add < 0 ||
currentIndex.value + add >= selectConfig.value.length
)
return;
currentIndex.value = currentIndex.value + add;
const target = drawStore
.getDrawApp()
.queryStore.queryById(
selectConfig.value[currentIndex.value].axleCountingId
);
drawStore.getDrawApp().makeGraphicCenterShow(target);
drawStore.getDrawApp().updateSelected(target);
}
</script>

View File

@ -0,0 +1,108 @@
<template>
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="platformModel.id" label="id" />
<q-input
outlined
label="站台编号"
type="textarea"
@blur="onUpdate"
v-model="platformModel.code"
lazy-rules
autogrow
/>
<q-input
outlined
label="紧急停车继电器的编号"
type="textarea"
@blur="onUpdate"
v-model="platformModel.refEsbRelayCode"
lazy-rules
autogrow
/>
<q-select
outlined
v-model="platformModel.type"
:options="platformTypeOptions"
:map-options="true"
:emit-value="true"
label="站台类型"
@update:model-value="onUpdate"
/>
<q-list bordered separator class="rounded-borders">
<q-item>
<q-item-section no-wrap class="q-gutter-y-sm column">
<q-item-label> 关联的车站 </q-item-label>
<div class="q-gutter-sm row">
<q-chip square color="primary" text-color="white">
{{ stationRelation }}
</q-chip>
</div>
</q-item-section>
</q-item>
<q-item>
<q-item-section no-wrap class="q-gutter-y-sm column">
<q-item-label> 关联的物理区段 </q-item-label>
<div class="q-gutter-sm row">
<q-chip square color="primary" text-color="white">
{{ sectionRelation }}
</q-chip>
</div>
</q-item-section>
</q-item>
</q-list>
</q-form>
</template>
<script setup lang="ts">
import { PlatformData } from 'src/drawApp/graphics/electronicMap/PlatformInteraction';
import { useFormData } from 'src/components/DrawAppFormUtils';
import { useDrawStore } from 'src/stores/electronicMap-draw-store';
import { computed } from 'vue';
import { Platform } from 'src/graphics/electronicMap/platform/Platform';
import { Station } from 'src/graphics/electronicMap/station/Station';
import { Section } from 'src/graphics/electronicMap/section/Section';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
const drawStore = useDrawStore();
const { data: platformModel, onUpdate } = useFormData(
new PlatformData(),
drawStore.getDrawApp()
);
const platformTypeOptions = [
{
label: '请选择',
value: electronicMapGraphicData.Platform.TypeOfPlatform.Unknown,
},
{
label: '上行站台',
value: electronicMapGraphicData.Platform.TypeOfPlatform.up,
},
{
label: '下行站台',
value: electronicMapGraphicData.Platform.TypeOfPlatform.down,
},
];
const stationRelation = computed(() => {
const platform = drawStore.selectedGraphic as Platform;
const refStations = platform?.relationManage
.getRelationsOfGraphicAndOtherType(platform, Station.Type)
.map((relation) => relation.getOtherGraphic<Station>(platform).datas.code);
let refStation;
if (refStations) {
refStation = refStations[0];
}
return refStation;
});
const sectionRelation = computed(() => {
const platform = drawStore.selectedGraphic as Platform;
const refSections = platform?.relationManage
.getRelationsOfGraphicAndOtherType(platform, Section.Type)
.map((relation) => relation.getOtherGraphic<Section>(platform).datas.code);
let refStation;
if (refSections) {
refStation = refSections[0];
}
return refStation;
});
</script>

View File

@ -0,0 +1,76 @@
import { toUint8Array } from 'js-base64';
import { GraphicData, IDrawApp, IGraphicStorage, newDrawApp } from 'jl-graphic';
import { getDraft } from 'src/api/DraftApi';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
import { useDrawStore } from 'src/stores/electronicMap-draw-store';
import { PlatformDraw } from 'src/graphics/electronicMap/platform/PlatformDrawAssistant';
import { PlatformTemplate } from 'src/graphics/electronicMap/platform/Platform';
import { PlatformData } from './graphics/electronicMap/PlatformInteraction';
let electronicMapDrawApp: IDrawApp | null = null;
export function getElectronicMapDrawApp(): IDrawApp | null {
return electronicMapDrawApp;
}
export function destroyElectronicMapDrawApp(): void {
if (electronicMapDrawApp) {
electronicMapDrawApp.destroy();
electronicMapDrawApp = null;
}
}
export function initElectronicMapDrawApp(): IDrawApp {
electronicMapDrawApp = newDrawApp({
dataLoader: loadThDrawDatas,
});
const app = electronicMapDrawApp;
new PlatformDraw(
app,
new PlatformTemplate(new PlatformData())
);
return electronicMapDrawApp;
}
export async function loadThDrawDatas(): Promise<IGraphicStorage> {
const drawStore = useDrawStore();
const id = drawStore.draftId;
if (!id) {
throw new Error('获取数据异常为获取到草稿地图ID');
}
const base64 = (await getDraft(id)).data;
if (base64) {
const storage =
electronicMapGraphicData.ElectronicMapGraphicStorage.deserialize(
toUint8Array(base64)
);
const datas: GraphicData[] = [];
generateAxleCountingConfig = storage.generateAxleCountingConfig;
console.log(storage, 'storage');
return Promise.resolve({
canvasProperty: storage.canvas,
datas: datas,
});
} else {
return Promise.resolve({
datas: [],
});
}
}
export function saveDrawDatas() {
console.log(111);
}
//一键生成计轴配置
let generateAxleCountingConfig =
new electronicMapGraphicData.GenerateAxleCountingConfig();
export function loadGenerateAxleCountingConfig() {
return generateAxleCountingConfig;
}
export function setGenerateAxleCountingConfig(
newScreenDoorConfig: electronicMapGraphicData.GenerateAxleCountingConfig
) {
generateAxleCountingConfig = newScreenDoorConfig;
}

View File

@ -0,0 +1,71 @@
import * as pb_1 from 'google-protobuf';
import {
AxleCounting,
IAxleCountingData,
} from 'src/graphics/axleCounting/AxleCounting';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase } from './GraphicDataBase';
import { KilometerSystem } from 'src/graphics/signal/Signal';
export class AxleCountingData
extends GraphicDataBase
implements IAxleCountingData
{
constructor(data?: graphicData.AxleCounting) {
let axleCounting;
if (!data) {
axleCounting = new graphicData.AxleCounting({
common: GraphicDataBase.defaultCommonInfo(AxleCounting.Type),
});
} else {
axleCounting = data;
}
super(axleCounting);
}
public get data(): graphicData.AxleCounting {
return this.getData<graphicData.AxleCounting>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get kilometerSystem(): KilometerSystem {
if (!this.data.kilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem();
}
return this.data.kilometerSystem;
}
set kilometerSystem(v: KilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem(v);
}
get axleCountingRef(): graphicData.RelatedRef[] {
return this.data.axleCountingRef;
}
set axleCountingRef(points: graphicData.RelatedRef[]) {
this.data.axleCountingRef = points;
}
get type(): graphicData.AxleCounting.TypeDetectionPoint {
return this.data.type;
}
set type(type: graphicData.AxleCounting.TypeDetectionPoint) {
this.data.type = type;
}
get centralizedStations(): number[] {
return this.data.centralizedStations;
}
set centralizedStations(v: number[]) {
this.data.centralizedStations = v;
}
clone(): AxleCountingData {
return new AxleCountingData(this.data.cloneMessage());
}
copyFrom(data: AxleCountingData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: AxleCountingData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}

View File

@ -0,0 +1,148 @@
import * as pb_1 from 'google-protobuf';
import {
IPlatformData,
Platform,
} from 'src/graphics/electronicMap/platform/Platform';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
import { GraphicDataBase } from '../GraphicDataBase';
import { IGraphicScene, GraphicInteractionPlugin, JlGraphic } from 'jl-graphic';
export class PlatformData extends GraphicDataBase implements IPlatformData {
constructor(data?: electronicMapGraphicData.Platform) {
let platform;
if (!data) {
platform = new electronicMapGraphicData.Platform({
common: GraphicDataBase.defaultCommonInfo(Platform.Type),
});
} else {
platform = data;
}
super(platform);
}
public get data(): electronicMapGraphicData.Platform {
return this.getData<electronicMapGraphicData.Platform>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get refStation(): number {
return this.data.refStationId;
}
set refStation(v: number) {
this.data.refStationId = v;
}
get refSection(): number {
return this.data.refSectionId;
}
set refSection(v: number) {
this.data.refSectionId = v;
}
get refEsbRelayCode(): string {
return this.data.refEsbRelayCode;
}
set refEsbRelayCode(v: string) {
this.data.refEsbRelayCode = v;
}
get type(): electronicMapGraphicData.Platform.TypeOfPlatform {
return this.data.type;
}
set type(v: electronicMapGraphicData.Platform.TypeOfPlatform) {
this.data.type = v;
}
clone(): PlatformData {
return new PlatformData(this.data.cloneMessage());
}
copyFrom(data: PlatformData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: PlatformData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}
/* export class PlatformState extends GraphicStateBase implements IPlatformState {
constructor(proto?: state.PlatformState) {
let states;
if (proto) {
states = proto;
} else {
states = new state.PlatformState();
}
super(states, Platform.Type);
}
get id(): number {
return this.states.id;
}
get code(): string {
return this.states.id + '';
}
get states(): state.PlatformState {
return this.getState<state.PlatformState>();
}
get empj(): boolean {
return this.states.empj;
}
get spksState(): state.ReplyState[] {
if (!this.states.spksState) {
this.states.spksState = [new state.ReplyState()];
}
return this.states.spksState;
}
set spksState(v: state.ReplyState[]) {
this.states.spksState = v;
}
get mkxJState(): state.MkxJState {
if (!this.states.mkxJState) {
this.states.mkxJState = new state.MkxJState();
}
return this.states.mkxJState;
}
set mkxJState(v: state.MkxJState) {
this.states.mkxJState = v;
}
clone(): PlatformState {
return new PlatformState(this.states.cloneMessage());
}
copyFrom(data: GraphicStateBase): void {
pb_1.Message.copyInto(data._state, this._state);
}
eq(data: GraphicStateBase): boolean {
return pb_1.Message.equals(this._state, data._state);
}
} */
export class PlatformOperateInteraction extends GraphicInteractionPlugin<Platform> {
static Name = 'platform_operate_menu';
constructor(app: IGraphicScene) {
super(PlatformOperateInteraction.Name, app);
}
static init(app: IGraphicScene) {
return new PlatformOperateInteraction(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.selectable = true;
g.on('_leftclick', this.onLeftClick, this);
}
unbind(g: Platform): void {
g.selectable = false;
g.eventMode = 'none';
g.off('_leftclick', this.onLeftClick, this);
}
onLeftClick() {
//useLineStore().stateProCountIncrease();
}
}

View File

@ -0,0 +1,192 @@
import * as pb_1 from 'google-protobuf';
import {
IScreenDoorData,
IScreenDoorState,
ScreenDoor,
} from 'src/graphics/screenDoor/ScreenDoor';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import { state } from 'src/protos/device_state';
import { useLineStore } from 'src/stores/line-store';
import {
ContextMenu,
GraphicInteractionPlugin,
IGraphicScene,
JlGraphic,
MenuItemOptions,
} from 'jl-graphic';
import { loadScreenDoorConfig } from '../commonApp';
import { Dialog } from 'quasar';
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
import ScreenDoorOperation from 'src/components/draw-app/dialogs/ScreenDoorOperation.vue';
import { request } from 'src/protos/request';
export class ScreenDoorData extends GraphicDataBase implements IScreenDoorData {
constructor(data?: graphicData.ScreenDoor) {
let screenDoor;
if (!data) {
screenDoor = new graphicData.ScreenDoor({
common: GraphicDataBase.defaultCommonInfo(ScreenDoor.Type),
});
} else {
screenDoor = data;
}
super(screenDoor);
}
public get data(): graphicData.ScreenDoor {
return this.getData<graphicData.ScreenDoor>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get refPlatform(): number {
return this.data.refPlatformId;
}
set refPlatform(v: number) {
this.data.refPlatformId = v;
}
get sonDoorAmount(): number {
return loadScreenDoorConfig()?.sonDoorAmount || 30;
}
clone(): ScreenDoorData {
return new ScreenDoorData(this.data.cloneMessage());
}
copyFrom(data: ScreenDoorData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: ScreenDoorData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}
export class ScreenDoorState
extends GraphicStateBase
implements IScreenDoorState
{
constructor(proto?: state.PsdState) {
let states;
if (proto) {
states = proto;
} else {
states = new state.PsdState();
}
super(states, ScreenDoor.Type);
}
get code(): string {
return this.states.id + '';
}
get id(): number {
return this.states.id;
}
set id(id: number) {
this.states.id = id;
}
get asdStates(): state.AsdState[] {
return this.states.asdStates;
}
set asdStates(v: state.AsdState[]) {
this.states.asdStates = v;
}
get mgj() {
return this.states.mgj;
}
set mgj(v: boolean) {
this.states.mgj = v;
}
get zaw() {
return this.states.zaw;
}
set zaw(v: boolean) {
this.states.zaw = v;
}
get param(): request.PsdParam {
return this.states.param;
}
set param(param: request.PsdParam) {
this.states.param = param;
}
get states(): state.PsdState {
return this.getState<state.PsdState>();
}
clone(): ScreenDoorState {
return new ScreenDoorState(this.states.cloneMessage());
}
copyFrom(data: GraphicStateBase): void {
pb_1.Message.copyInto(data._state, this._state);
}
eq(data: GraphicStateBase): boolean {
return pb_1.Message.equals(this._state, data._state);
}
}
const setSceenDoorParam: MenuItemOptions = { name: '设置参数' };
const sceenDoorOperateMenu: ContextMenu = ContextMenu.init({
name: '屏蔽门操作菜单',
groups: [
{
items: [setSceenDoorParam],
},
],
});
export class ScreenDoorOperateInteraction extends GraphicInteractionPlugin<ScreenDoor> {
static Name = 'screen_door_operate_menu';
constructor(app: IGraphicScene) {
super(ScreenDoorOperateInteraction.Name, app);
app.registerMenu(sceenDoorOperateMenu);
}
static init(app: IGraphicScene) {
return new ScreenDoorOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): ScreenDoor[] | undefined {
return grahpics
.filter((g) => g.type === ScreenDoor.Type)
.map((g) => g as ScreenDoor);
}
bind(g: ScreenDoor): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.selectable = true;
g.on('_leftclick', this.onLeftClick, this);
g.on('rightclick', this.onContextMenu, this);
}
unbind(g: ScreenDoor): void {
g.selectable = false;
g.eventMode = 'none';
g.off('_leftclick', this.onLeftClick, this);
g.off('rightclick', this.onContextMenu, this);
}
onLeftClick() {
useLineStore().stateProCountIncrease();
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const screenDoor = target.getGraphic<ScreenDoor>();
if (!screenDoor) return;
this.app.updateSelected(screenDoor);
const lineStore = useLineStore();
setSceenDoorParam.handler = async () => {
if (lineStore.deviceOpreratDialogInstance) return;
lineStore.deviceOpreratDialogInstance = Dialog.create({
component: ScreenDoorOperation,
componentProps: {
id: screenDoor.id,
code: screenDoor.datas.code,
sonDoorAmount: screenDoor.datas.sonDoorAmount,
asdCodesProp: screenDoor.states.param.asdCodes,
screenDoorForceProp: screenDoor.states.param.force,
screenDoorFaultProp: screenDoor.states.param.fault,
},
cancel: true,
persistent: true,
});
};
sceenDoorOperateMenu.open(e.global);
}
}

View File

@ -0,0 +1,317 @@
import * as pb_1 from 'google-protobuf';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import {
ISectionData,
ISectionState,
Section,
SectionType,
} from 'src/graphics/section/Section';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { DisplayObject, FederatedMouseEvent, IPointData } from 'pixi.js';
import {
GraphicInteractionPlugin,
IGraphicScene,
JlGraphic,
MenuItemOptions,
ContextMenu,
} from 'jl-graphic';
import { useLineStore } from 'src/stores/line-store';
import { SectionGraphicHitArea } from 'src/graphics/section/SectionDrawAssistant';
import { Dialog } from 'quasar';
import AddTrainDialog from '../../components/draw-app/dialogs/AddTrainDialog.vue';
import { AxleCounting } from 'src/graphics/axleCounting/AxleCounting';
import { state } from 'src/protos/device_state';
import { getKmDistance } from '../lineScene';
import SectionOperation from 'src/components/draw-app/dialogs/SectionOperation.vue';
import { request } from 'src/protos/request';
import { setAxleSectionState } from 'src/api/Simulation';
import { errorNotify } from 'src/utils/CommonNotify';
export class SectionData extends GraphicDataBase implements ISectionData {
constructor(data?: graphicData.Section) {
let section;
if (!data) {
section = new graphicData.Section({
common: GraphicDataBase.defaultCommonInfo(Section.Type),
});
} else {
section = data;
}
super(section);
}
public get data(): graphicData.Section {
return this.getData<graphicData.Section>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get points(): IPointData[] {
return this.data.points;
}
set points(points: IPointData[]) {
this.data.points = points.map(
(p) => new graphicData.Point({ x: p.x, y: p.y })
);
}
get paRef(): graphicData.RelatedRef {
return this.data.paRef;
}
set paRef(ref: graphicData.RelatedRef) {
this.data.paRef = ref;
}
get pbRef(): graphicData.RelatedRef {
return this.data.pbRef;
}
set pbRef(ref: graphicData.RelatedRef) {
this.data.pbRef = ref;
}
get sectionType(): graphicData.Section.SectionType {
return this.data.sectionType;
}
set sectionType(type: graphicData.Section.SectionType) {
this.data.sectionType = type;
}
get axleCountings(): number[] {
return this.data.axleCountings;
}
set axleCountings(axleCountings: number[]) {
this.data.axleCountings = axleCountings;
}
get trackSectionId(): number {
return this.data.trackSectionId;
}
set trackSectionId(v: number) {
this.data.trackSectionId = v;
}
get isCurve(): boolean {
return this.data.isCurve;
}
set isCurve(v: boolean) {
this.data.isCurve = v;
}
get segmentsCount(): number {
return this.data.segmentsCount;
}
set segmentsCount(v: number) {
this.data.segmentsCount = v;
}
get centralizedStations(): number[] {
return this.data.centralizedStations;
}
set centralizedStations(v: number[]) {
this.data.centralizedStations = v;
}
get normalRunningDirection(): graphicData.Section.RunningDirection {
return this.data.normalRunningDirection;
}
set normalRunningDirection(v: graphicData.Section.RunningDirection) {
this.data.normalRunningDirection = v;
}
get isTurnBackZone(): boolean {
return this.data.isTurnBackZone;
}
set isTurnBackZone(v: boolean) {
this.data.isTurnBackZone = v;
}
get direction(): graphicData.Direction {
return this.data.direction;
}
set direction(v: graphicData.Direction) {
this.data.direction = v;
}
clone(): SectionData {
return new SectionData(this.data.cloneMessage());
}
copyFrom(data: SectionData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: SectionData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}
export class SectionStates extends GraphicStateBase implements ISectionState {
constructor(proto?: state.SectionState) {
let states;
if (proto) {
states = proto;
} else {
states = new state.SectionState();
}
super(states, Section.Type);
}
get code(): string {
return this.states.id + '';
}
get id(): number {
return this.states.id;
}
set id(id: number) {
this.states.id = id;
}
get occupied(): boolean {
return this.states.occupied;
}
set occupied(occupied: boolean) {
this.states.occupied = occupied;
}
get axleFault(): boolean {
return this.states.axleFault;
}
set axleFault(axleFault: boolean) {
this.states.axleFault = axleFault;
}
get axleDrst(): boolean {
return this.states.axleDrst;
}
set axleDrst(axleDrst: boolean) {
this.states.axleDrst = axleDrst;
}
get axlePdrst(): boolean {
return this.states.axlePdrst;
}
set axlePdrst(axlePdrst: boolean) {
this.states.axlePdrst = axlePdrst;
}
get states(): state.SectionState {
return this.getState<state.SectionState>();
}
clone(): SectionStates {
return new SectionStates(this.states.cloneMessage());
}
copyFrom(data: GraphicStateBase): void {
pb_1.Message.copyInto(data._state, this._state);
}
eq(data: GraphicStateBase): boolean {
return pb_1.Message.equals(this._state, data._state);
}
}
const addTrainConfig: MenuItemOptions = {
name: '添加列车',
};
const setSectionParam: MenuItemOptions = { name: '设置参数' };
const setFaultOcc: MenuItemOptions = { name: '设置故障占用' };
const SectionOperateMenu: ContextMenu = ContextMenu.init({
name: '区段操作菜单',
groups: [
{
items: [setSectionParam, setFaultOcc, addTrainConfig],
},
],
});
export class SectionOperateInteraction extends GraphicInteractionPlugin<Section> {
static Name = 'section_operate_menu';
constructor(app: IGraphicScene) {
super(SectionOperateInteraction.Name, app);
app.registerMenu(SectionOperateMenu);
}
static init(app: IGraphicScene) {
return new SectionOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): Section[] | undefined {
return grahpics
.filter((g) => g.type === Section.Type)
.map((g) => g as Section);
}
bind(g: Section): void {
g.lineGraphic.eventMode = 'static';
g.lineGraphic.cursor = 'pointer';
g.lineGraphic.hitArea = new SectionGraphicHitArea(g);
g.lineGraphic.selectable = true;
g.selectable = true;
g.labelGraphic.eventMode = 'static';
g.labelGraphic.cursor = 'pointer';
g.labelGraphic.selectable = true;
g.on('_leftclick', this.onLeftClick, this);
g.on('rightclick', this.onContextMenu, this);
}
unbind(g: Section): void {
g.lineGraphic.eventMode = 'none';
g.lineGraphic.scalable = false;
g.lineGraphic.selectable = false;
g.selectable = false;
g.labelGraphic.eventMode = 'none';
g.labelGraphic.selectable = false;
g.off('_leftclick', this.onLeftClick, this);
g.off('rightclick', this.onContextMenu, this);
}
onLeftClick() {
useLineStore().stateProCountIncrease();
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const section = target.getGraphic<Section>();
if (!section || section.datas.sectionType != SectionType.Physical) return;
this.app.updateSelected(section);
const lineStore = useLineStore();
setSectionParam.handler = async () => {
if (lineStore.deviceOpreratDialogInstance) return;
lineStore.deviceOpreratDialogInstance = Dialog.create({
component: SectionOperation,
componentProps: {
id: section.id,
code: section.datas.code,
axleDrst: section.states.axleDrst,
axlePdrst: section.states.axlePdrst,
},
cancel: true,
persistent: true,
});
};
setFaultOcc.handler = async () => {
const obj = {
simulationId: lineStore?.simulationId || '',
mapId: lineStore.mapId as number,
deviceId: section.id,
operation: request.Section.Operation.SetFaultOcc,
};
setAxleSectionState(obj).catch((e) =>
errorNotify('区段操作失败:' + e.title, e)
);
};
addTrainConfig.disabled = !lineStore.trainConfigList;
addTrainConfig.handler = () => {
const relations =
section.relationManage.getRelationsOfGraphicAndOtherType(
section,
AxleCounting.Type
);
let AKm;
let BKm;
relations.forEach((item) => {
const rp = item.getRelationParam(section);
const other = item.getOtherGraphic(section) as AxleCounting;
if (
(other.datas.axleCountingRef.length > 1 &&
other.datas.type ==
graphicData.AxleCounting.TypeDetectionPoint.AxleCounting) ||
other.datas.axleCountingRef.length == 1
) {
if (rp.getParam() == 'A') {
AKm = other.datas.kilometerSystem;
}
if (rp.getParam() == 'B') {
BKm = other.datas.kilometerSystem;
}
}
});
const d = getKmDistance(BKm, AKm);
if (lineStore.deviceOpreratDialogInstance) return;
lineStore.deviceOpreratDialogInstance = Dialog.create({
title: '创建列车',
message: '',
component: AddTrainDialog,
componentProps: { dev: section, kmLength: d },
cancel: true,
persistent: true,
});
};
SectionOperateMenu.open(e.global);
}
}

View File

@ -0,0 +1,253 @@
import * as pb_1 from 'google-protobuf';
import {
ISignalData,
Signal,
ISignalState,
KilometerSystem,
} from 'src/graphics/signal/Signal';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import {
GraphicInteractionPlugin,
IGraphicScene,
JlGraphic,
MenuItemOptions,
ContextMenu,
} from 'jl-graphic';
import { FederatedMouseEvent, DisplayObject } from 'pixi.js';
import { state } from 'src/protos/device_state';
import { useLineStore } from 'src/stores/line-store';
import { SignalGraphicHitArea } from 'src/graphics/signal/SignalDrawAssistant';
import { request } from 'src/protos/request';
import { Dialog } from 'quasar';
import SignalOperation from 'src/components/draw-app/dialogs/SignalOperation.vue'
export class SignalData extends GraphicDataBase implements ISignalData {
constructor(data?: graphicData.Signal) {
let signal;
if (!data) {
signal = new graphicData.Signal({
common: GraphicDataBase.defaultCommonInfo(Signal.Type),
});
} else {
signal = data;
}
super(signal);
}
public get data(): graphicData.Signal {
return this.getData<graphicData.Signal>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get mirror(): boolean {
return this.data.mirror;
}
set mirror(v: boolean) {
this.data.mirror = v;
}
get kilometerSystem(): KilometerSystem {
if (!this.data.kilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem();
}
return this.data.kilometerSystem;
}
set kilometerSystem(v: KilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem(v);
}
get refDev(): graphicData.RelatedRef {
return this.data.refDev ?? new graphicData.RelatedRef();
}
set refDev(v: graphicData.RelatedRef) {
this.data.refDev = v;
}
get centralizedStations(): number[] {
return this.data.centralizedStations;
}
set centralizedStations(v: number[]) {
this.data.centralizedStations = v;
}
get mt(): graphicData.Signal.Model {
return this.data.mt;
}
set mt(v: graphicData.Signal.Model) {
this.data.mt = v;
}
get direction(): graphicData.Direction {
return this.data.direction;
}
set direction(v: graphicData.Direction) {
this.data.direction = v;
}
clone(): SignalData {
return new SignalData(this.data.cloneMessage());
}
copyFrom(data: SignalData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: SignalData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}
export class SignalState extends GraphicStateBase implements ISignalState {
constructor(proto?: state.SignalState) {
let states;
if (proto) {
states = proto;
} else {
states = new state.SignalState();
}
super(states, Signal.Type);
}
get code(): string {
return this.states.id + '';
}
get aspect(): number {
return this.states.aspect;
}
set aspect(v: number) {
this.states.aspect = v;
}
get param(): request.SignalParam {
return this.states.param;
}
set param(v: request.SignalParam) {
this.states.param = v;
}
get states(): state.SignalState {
return this.getState<state.SignalState>();
}
clone(): SignalState {
return new SignalState(this.states.cloneMessage());
}
copyFrom(data: GraphicStateBase): void {
pb_1.Message.copyInto(data._state, this._state);
}
eq(data: GraphicStateBase): boolean {
return pb_1.Message.equals(this._state, data._state);
}
}
const mirrorFlipConfig: MenuItemOptions = {
name: '镜像翻转',
};
const setSignalParam: MenuItemOptions = {
name: '设置参数'
}
const SignalEditMenu: ContextMenu = ContextMenu.init({
name: '信号机编辑菜单',
groups: [
{
items: [mirrorFlipConfig],
},
],
});
const SignalOperateMenu: ContextMenu = ContextMenu.init({
name: '信号机操作菜单',
groups: [
{
items: [
setSignalParam
// signalRedConfig,
// signalGreenConfig,
// signalYellowConfig,
// signalGuideConfig,
// signalCloseConfig,
],
},
],
});
export class DrawSignalInteraction extends GraphicInteractionPlugin<Signal> {
static Name = 'signal_draw_right_menu';
constructor(app: IGraphicScene) {
super(DrawSignalInteraction.Name, app);
app.registerMenu(SignalEditMenu);
}
static init(app: IGraphicScene) {
return new DrawSignalInteraction(app);
}
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
return grahpics
.filter((g) => g.type === Signal.Type)
.map((g) => g as Signal);
}
bind(g: Signal): void {
g.on('_rightclick', this.onContextMenu, this);
}
unbind(g: Signal): void {
g.off('_rightclick', this.onContextMenu, this);
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const signal = target.getGraphic() as Signal;
this.app.updateSelected(signal);
mirrorFlipConfig.handler = () => {
signal.mirror = !signal.mirror;
};
SignalEditMenu.open(e.global);
}
}
export class SignalOperateInteraction extends GraphicInteractionPlugin<Signal> {
static Name = 'signal_operate_menu';
constructor(app: IGraphicScene) {
super(SignalOperateInteraction.Name, app);
app.registerMenu(SignalOperateMenu);
}
static init(app: IGraphicScene) {
return new SignalOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
return grahpics
.filter((g) => g.type === Signal.Type)
.map((g) => g as Signal);
}
bind(g: Signal): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.selectable = true;
g.lampMainBody.hitArea = new SignalGraphicHitArea(g);
g.on('_leftclick', this.onLeftClick, this);
g.on('rightclick', this.onContextMenu, this);
}
unbind(g: Signal): void {
g.selectable = false;
g.eventMode = 'none';
g.off('_leftclick', this.onLeftClick, this);
g.off('rightclick', this.onContextMenu);
}
onLeftClick() {
useLineStore().stateProCountIncrease();
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const signal = target.getGraphic() as Signal;
this.app.updateSelected(signal);
const lineStore = useLineStore();
setSignalParam.handler = () => {
if (lineStore.deviceOpreratDialogInstance) return;
lineStore.deviceOpreratDialogInstance = Dialog.create({
title: '信号机设置参数',
message: '',
component: SignalOperation,
componentProps: {
id: signal.datas.id,
code: signal.datas.code,
mt: signal.datas.mt,
param: signal.states.param,
},
cancel: true,
persistent: true,
});
}
SignalOperateMenu.open(e.global);
}
}

View File

@ -0,0 +1,168 @@
import * as pb_1 from 'google-protobuf';
import {
IStationData,
IStationState,
Station,
} from 'src/graphics/station/Station';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import { state } from 'src/protos/device_state';
import { IGraphicScene, GraphicInteractionPlugin, JlGraphic } from 'jl-graphic';
import { KilometerSystem } from 'src/graphics/signal/Signal';
import { useLineStore } from 'src/stores/line-store';
export class StationData extends GraphicDataBase implements IStationData {
constructor(data?: graphicData.Station) {
let station;
if (!data) {
station = new graphicData.Station({
common: GraphicDataBase.defaultCommonInfo(Station.Type),
});
} else {
station = data;
}
super(station);
}
public get data(): graphicData.Station {
return this.getData<graphicData.Station>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get stationName(): string {
return this.data.stationName;
}
set stationName(v: string) {
this.data.stationName = v;
}
get stationNameAcronym(): string {
return this.data.stationNameAcronym;
}
set stationNameAcronym(v: string) {
this.data.stationNameAcronym = v;
}
get kilometerSystem(): KilometerSystem {
if (!this.data.kilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem();
}
return this.data.kilometerSystem;
}
set kilometerSystem(v: KilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem(v);
}
get concentrationStations(): boolean {
return this.data.concentrationStations;
}
set concentrationStations(v: boolean) {
this.data.concentrationStations = v;
}
get depots(): boolean {
return this.data.depots;
}
set depots(v: boolean) {
this.data.depots = v;
}
get manageStations(): number[] {
return this.data.manageStations;
}
set manageStations(v: number[]) {
this.data.manageStations = v;
}
clone(): StationData {
return new StationData(this.data.cloneMessage());
}
copyFrom(data: StationData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: StationData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}
export class StationState extends GraphicStateBase implements IStationState {
constructor(proto?: state.StationState) {
let states;
if (proto) {
states = proto;
} else {
states = new state.StationState();
}
super(states, Station.Type);
}
get id(): number {
return this.states.id;
}
get code(): string {
return this.states.id + '';
}
// get ipRtuStusDown(): boolean {
// return this.states.ipRtuStusDown;
// }
// set ipRtuStusDown(v: boolean) {
// this.states.ipRtuStusDown = v;
// }
// get ipRtuStusInLocalCtrl(): boolean {
// return this.states.ipRtuStusInLocalCtrl;
// }
// set ipRtuStusInLocalCtrl(v: boolean) {
// this.states.ipRtuStusInLocalCtrl = v;
// }
// get ipRtuStusInCentralCtrl(): boolean {
// return this.states.ipRtuStusInCentralCtrl;
// }
// set ipRtuStusInCentralCtrl(v: boolean) {
// this.states.ipRtuStusInCentralCtrl = v;
// }
// get ipRtuStusInEmergencyCtrl(): boolean {
// return this.states.ipRtuStusInEmergencyCtrl;
// }
// set ipRtuStusInEmergencyCtrl(v: boolean) {
// this.states.ipRtuStusInEmergencyCtrl = v;
// }
get states(): state.StationState {
return this.getState<state.StationState>();
}
clone(): StationState {
return new StationState(this.states.cloneMessage());
}
copyFrom(data: GraphicStateBase): void {
pb_1.Message.copyInto(data._state, this._state);
}
eq(data: GraphicStateBase): boolean {
return pb_1.Message.equals(this._state, data._state);
}
}
export class StationOperateInteraction extends GraphicInteractionPlugin<Station> {
static Name = 'station_operate_menu';
constructor(app: IGraphicScene) {
super(StationOperateInteraction.Name, app);
}
static init(app: IGraphicScene) {
return new StationOperateInteraction(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.selectable = true;
g.on('_leftclick', this.onLeftClick, this);
}
unbind(g: Station): void {
g.selectable = false;
g.eventMode = 'none';
g.off('_leftclick', this.onLeftClick, this);
}
onLeftClick() {
useLineStore().stateProCountIncrease();
}
}

View File

@ -0,0 +1,378 @@
import {
ITurnoutData,
ITurnoutState,
Turnout,
TurnoutSection,
} from 'src/graphics/turnout/Turnout';
import * as pb_1 from 'google-protobuf';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { DisplayObject, FederatedMouseEvent, IPointData } from 'pixi.js';
import { KilometerSystem } from 'src/graphics/signal/Signal';
import { state } from 'src/protos/device_state';
import {
IGraphicScene,
GraphicInteractionPlugin,
JlGraphic,
MenuItemOptions,
ContextMenu,
} from 'jl-graphic';
import {
ForkHitArea,
TurnoutSectionHitArea,
} from 'src/graphics/turnout/TurnoutDrawAssistant';
import { useLineStore } from 'src/stores/line-store';
import { Dialog } from 'quasar';
// import AddTrainDialog from '../../components/draw-app/dialogs/AddTrainDialog.vue';
// import { AxleCounting } from 'src/graphics/axleCounting/AxleCounting';
import TurnoutOperation from 'src/components/draw-app/dialogs/TurnoutOperation.vue';
// import { getKmDistance } from '../lineScene';
import { request } from 'src/protos/request';
function getDefaultEndPoint() {
return {
pointA: [new graphicData.Point([50, 0])],
pointB: [new graphicData.Point([-50, 0])],
pointC: [new graphicData.Point([-50, -50])],
};
}
const setTurnoutParam: MenuItemOptions = { name: '设置参数' };
// const addTrainConfig: MenuItemOptions = {
// name: '添加列车',
// };
const TurnoutOperationMenu: ContextMenu = ContextMenu.init({
name: 'Turnout操作',
groups: [{ items: [setTurnoutParam] }],
});
export class TurnoutOperationPlugin extends GraphicInteractionPlugin<Turnout> {
static Name = 'turnout_operate_menu';
constructor(app: IGraphicScene) {
super(TurnoutOperationPlugin.Name, app);
app.registerMenu(TurnoutOperationMenu);
}
static init(app: IGraphicScene) {
return new TurnoutOperationPlugin(app);
}
filter(...grahpics: JlGraphic[]): Turnout[] | undefined {
return grahpics.filter<Turnout>((g): g is Turnout => g instanceof Turnout);
}
bind(g: Turnout): void {
g.graphics.fork.eventMode = 'static';
g.graphics.fork.cursor = 'pointer';
g.selectable = true;
g.graphics.fork.hitArea = new ForkHitArea(g);
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.eventMode = 'static';
sectionGraphic.cursor = 'pointer';
sectionGraphic.hitArea = new TurnoutSectionHitArea(sectionGraphic);
});
g.on('rightclick', this.onContextMenu, this);
g.on('_leftclick', this.onLeftClick, this);
}
unbind(g: Turnout): void {
g.graphics.fork.eventMode = 'none';
g.selectable = false;
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.eventMode = 'none';
});
g.off('rightclick', this.onContextMenu);
g.off('_leftclick', this.onLeftClick, this);
}
onLeftClick() {
useLineStore().stateProCountIncrease();
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const turnout = target.getGraphic<Turnout>();
if (!turnout) return;
const lineStore = useLineStore();
this.app.updateSelected(turnout);
setTurnoutParam.handler = async () => {
if (lineStore.deviceOpreratDialogInstance) return;
lineStore.deviceOpreratDialogInstance = Dialog.create({
title: '道岔设置参数',
message: '',
component: TurnoutOperation,
componentProps: {
id: turnout.id,
code: turnout.datas.code,
force: turnout.states.param.forcePosition,
},
cancel: true,
persistent: true,
});
};
// const port = (target as TurnoutSection).port;
// addTrainConfig.disabled = !port || !lineStore.trainConfigList;
// addTrainConfig.handler = () => {
// const relations =
// turnout.relationManage.getRelationsOfGraphicAndOtherType(
// turnout,
// AxleCounting.Type
// );
// const findAc = relations.find((item) => {
// const rp = item.getRelationParam(turnout);
// const orp = item.getOtherRelationParam(turnout as Turnout);
// const ac = orp.g as AxleCounting;
// return (
// rp.getParam() == port &&
// ((ac.datas.axleCountingRef.length > 1 &&
// ac.datas.type ==
// graphicData.AxleCounting.TypeDetectionPoint.AxleCounting) ||
// ac.datas.axleCountingRef.length == 1)
// );
// });
// const oKm = turnout.datas.kilometerSystem;
// let pKm;
// if (findAc) {
// const other = findAc.getOtherGraphic(turnout) as AxleCounting;
// pKm = other.datas.kilometerSystem;
// } else {
// const relations =
// turnout.relationManage.getRelationsOfGraphicAndOtherType(
// turnout,
// Turnout.Type
// );
// const findT = relations.find((item) => {
// const rp = item.getRelationParam(turnout);
// return rp.getParam() == port;
// });
// if (findT) {
// const other = findT.getOtherGraphic(turnout) as Turnout;
// pKm = other.datas.kilometerSystem;
// }
// }
// const d = getKmDistance(pKm, oKm);
// if (lineStore.deviceOpreratDialogInstance) return;
// lineStore.deviceOpreratDialogInstance = Dialog.create({
// title: '创建列车',
// message: '',
// component: AddTrainDialog,
// componentProps: { dev: turnout, kmLength: d },
// cancel: true,
// persistent: true,
// });
// };
TurnoutOperationMenu.open(e.global);
}
}
export class TurnoutData extends GraphicDataBase implements ITurnoutData {
constructor(data?: graphicData.Turnout) {
let turnout = new graphicData.Turnout();
if (!data) {
turnout.common = GraphicDataBase.defaultCommonInfo(Turnout.Type);
const p = getDefaultEndPoint();
turnout.pointA = p.pointA;
turnout.pointB = p.pointB;
turnout.pointC = p.pointC;
} else {
turnout = data;
}
super(turnout);
}
get data(): graphicData.Turnout {
return this.getData<graphicData.Turnout>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get pointA(): IPointData[] {
return this.data.pointA;
}
set pointA(v: IPointData[]) {
this.data.pointA = v.map((p) => new graphicData.Point({ x: p.x, y: p.y }));
}
get pointB(): IPointData[] {
return this.data.pointB;
}
set pointB(v: IPointData[]) {
this.data.pointB = v.map((p) => new graphicData.Point({ x: p.x, y: p.y }));
}
get pointC(): IPointData[] {
return this.data.pointC;
}
set pointC(v: IPointData[]) {
this.data.pointC = v.map((p) => new graphicData.Point({ x: p.x, y: p.y }));
}
get paRef(): graphicData.RelatedRef {
return this.data.paRef;
}
set paRef(ref: graphicData.RelatedRef) {
this.data.paRef = ref;
}
get pbRef(): graphicData.RelatedRef {
return this.data.pbRef;
}
set pbRef(ref: graphicData.RelatedRef) {
this.data.pbRef = ref;
}
get pcRef(): graphicData.RelatedRef {
return this.data.pcRef;
}
set pcRef(ref: graphicData.RelatedRef) {
this.data.pcRef = ref;
}
get kilometerSystem(): KilometerSystem {
if (!this.data.kilometerSystem[0]) {
this.data.kilometerSystem = [new graphicData.KilometerSystem()];
}
return this.data.kilometerSystem[0];
}
set kilometerSystem(v: KilometerSystem) {
this.data.kilometerSystem = [new graphicData.KilometerSystem(v)];
}
get paTrackSectionId(): number {
return this.data.paTrackSectionId;
}
set paTrackSectionId(v: number) {
this.data.paTrackSectionId = v;
}
get pbTrackSectionId(): number {
return this.data.pbTrackSectionId;
}
set pbTrackSectionId(v: number) {
this.data.pbTrackSectionId = v;
}
get pcTrackSectionId(): number {
return this.data.pcTrackSectionId;
}
set pcTrackSectionId(v: number) {
this.data.pcTrackSectionId = v;
}
get switchMachineType(): graphicData.Turnout.SwitchMachineType {
return this.data.switchMachineType;
}
set switchMachineType(v: graphicData.Turnout.SwitchMachineType) {
this.data.switchMachineType = v;
}
get centralizedStations(): number[] {
return this.data.centralizedStations;
}
set centralizedStations(v: number[]) {
this.data.centralizedStations = v;
}
clone(): TurnoutData {
return new TurnoutData(this.data.cloneMessage());
}
copyFrom(data: TurnoutData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: TurnoutData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}
export class TurnoutStates extends GraphicStateBase implements ITurnoutState {
constructor(proto?: state.SwitchState) {
let states;
if (proto) {
states = proto;
} else {
states = new state.SwitchState();
}
super(states, Turnout.Type);
}
get code(): string {
return this.states.id + '';
}
get id(): number {
return this.states.id;
}
set id(id: number) {
this.states.id = id;
}
get normal(): boolean {
return this.states.normal;
}
set normal(normal: boolean) {
this.states.normal = normal;
}
get reverse(): boolean {
return this.states.reverse;
}
set reverse(reverse: boolean) {
this.states.reverse = reverse;
}
get dw(): boolean {
return this.states.dw;
}
set dw(dw: boolean) {
this.states.dw = dw;
}
get fw(): boolean {
return this.states.fw;
}
set fw(v: boolean) {
this.states.fw = v;
}
get param(): request.PointsParam {
return this.states.param;
}
set param(param: request.PointsParam) {
this.states.param = param;
}
get qdc(): boolean {
return this.states.qdc;
}
set qdc(v: boolean) {
this.states.qdc = v;
}
get qfc(): boolean {
return this.states.qfc;
}
set qfc(v: boolean) {
this.states.qfc = v;
}
get qyc(): boolean {
return this.states.qyc;
}
set qyc(v: boolean) {
this.states.qyc = v;
}
get dc(): boolean {
return this.states.dc;
}
set dc(v: boolean) {
this.states.dc = v;
}
get fc(): boolean {
return this.states.fc;
}
set fc(v: boolean) {
this.states.fc = v;
}
get yc(): boolean {
return this.states.yc;
}
set yc(v: boolean) {
this.states.yc = v;
}
get occupied(): boolean {
return this.states.occupied;
}
set occupied(v: boolean) {
this.states.occupied = v;
}
get states(): state.SwitchState {
return this.getState<state.SwitchState>();
}
clone(): TurnoutStates {
return new TurnoutStates(this.states.cloneMessage());
}
copyFrom(data: GraphicStateBase): void {
pb_1.Message.copyInto(data._state, this._state);
}
eq(data: GraphicStateBase): boolean {
return pb_1.Message.equals(this._state, data._state);
}
}

View File

@ -0,0 +1,128 @@
import { Graphics } from 'pixi.js';
import { calculateMirrorPoint } from 'jl-graphic';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
import { Turnout } from './turnout/Turnout';
import { Section, DevicePort } from './section/Section';
import { AxleCounting } from './axleCounting/AxleCounting';
import { Station } from './station/Station';
import { ScreenDoor } from './screenDoor/ScreenDoor';
/**
*
* @param polygon
* @param x x坐标
* @param y y坐标
* @param length
* @param radius
* @param lineWidth 线
* @param mirror ()
*/
export function drawArrow(
polygon: Graphics,
x: number,
y: number,
length: number,
radius: number,
lineWidth: number,
mirror: boolean
) {
const trianglAcme = { x, y };
let triangleP1 = {
x: x - radius - Math.sin(Math.PI / 6),
y: y + Math.cos(Math.PI / 6) * radius,
};
let triangleP2 = {
x: x - radius - Math.sin(Math.PI / 6),
y: y - Math.cos(Math.PI / 6) * radius,
};
let lineP1 = {
x: x - radius - Math.sin(Math.PI / 6),
y: y + lineWidth / 2,
};
let lineP2 = {
x: x - length,
y: y + lineWidth / 2,
};
let lineP3 = {
x: x - length,
y: y - lineWidth / 2,
};
let lineP4 = {
x: x - radius - Math.sin(Math.PI / 6),
y: y - lineWidth / 2,
};
if (mirror) {
triangleP1 = calculateMirrorPoint(trianglAcme, triangleP1);
triangleP2 = calculateMirrorPoint(trianglAcme, triangleP2);
lineP1 = calculateMirrorPoint(trianglAcme, lineP1);
lineP2 = calculateMirrorPoint(trianglAcme, lineP2);
lineP3 = calculateMirrorPoint(trianglAcme, lineP3);
lineP4 = calculateMirrorPoint(trianglAcme, lineP4);
}
polygon.drawPolygon(
trianglAcme.x,
trianglAcme.y,
triangleP1.x,
triangleP1.y,
lineP1.x,
lineP1.y,
lineP2.x,
lineP2.y,
lineP3.x,
lineP3.y,
lineP4.x,
lineP4.y,
triangleP2.x,
triangleP2.y
);
}
export function createRelatedRefProto(
type: string,
id: number,
port?: DevicePort
) {
const typeMap = new Map([
[Section.Type, electronicMapGraphicData.RelatedRef.DeviceType.Section],
[Turnout.Type, electronicMapGraphicData.RelatedRef.DeviceType.Turnout],
[AxleCounting.Type, electronicMapGraphicData.RelatedRef.DeviceType.AxleCounting],
[Station.Type, electronicMapGraphicData.RelatedRef.DeviceType.station],
[ScreenDoor.Type, electronicMapGraphicData.RelatedRef.DeviceType.ScreenDoor],
]);
const protoDeviceType = typeMap.get(type);
if (protoDeviceType === undefined) throw Error(`输入的type有误: ${type}`);
const protoData = new electronicMapGraphicData.RelatedRef({
deviceType: protoDeviceType,
id,
});
if (port) {
if (port === DevicePort.A)
protoData.devicePort = electronicMapGraphicData.RelatedRef.DevicePort.A;
if (port === DevicePort.B)
protoData.devicePort = electronicMapGraphicData.RelatedRef.DevicePort.B;
if (port === DevicePort.C)
protoData.devicePort = electronicMapGraphicData.RelatedRef.DevicePort.C;
}
return protoData;
}
export function protoPort2Data(port: electronicMapGraphicData.RelatedRef.DevicePort) {
if (port === electronicMapGraphicData.RelatedRef.DevicePort.A) return 'A';
if (port === electronicMapGraphicData.RelatedRef.DevicePort.B) return 'B';
if (port === electronicMapGraphicData.RelatedRef.DevicePort.C) return 'C';
}
export interface IRelatedRefData {
deviceType: electronicMapGraphicData.RelatedRef.DeviceType; //关联的设备类型
id: number; //关联的设备ID
devicePort: electronicMapGraphicData.RelatedRef.DevicePort; //关联的设备端口
}
export interface ISimpleRefData {
deviceType: electronicMapGraphicData.SimpleRef.DeviceType;
id: number;
}
export interface DevicePosition {
deviceId: number;
offset: number;
}

View File

@ -0,0 +1,135 @@
import { Color, Container, Graphics } from 'pixi.js';
import {
GraphicData,
GraphicRelationParam,
JlGraphic,
JlGraphicTemplate,
VectorText,
} from 'jl-graphic';
import { IRelatedRefData, protoPort2Data } from '../CommonGraphics';
import { KilometerSystem } from '../signal/Signal';
enum TypeDetectionPoint {
AxleCounting = 0,
SectionBoundary = 1,
}
export interface IAxleCountingData extends GraphicData {
get code(): string; // 编号
set code(v: string);
get kilometerSystem(): KilometerSystem;
set kilometerSystem(v: KilometerSystem);
get axleCountingRef(): IRelatedRefData[]; //关联的设备
set axleCountingRef(ref: IRelatedRefData[]);
get type(): TypeDetectionPoint; // 计轴、区段边界
set type(v: TypeDetectionPoint);
get centralizedStations(): number[];
set centralizedStations(v: number[]);
clone(): IAxleCountingData;
copyFrom(data: IAxleCountingData): void;
eq(other: IAxleCountingData): boolean;
}
export const AxleCountingConsts = {
radius: 6,
borderWidth: 1,
circleColorBlue: '0x08F80D',
circleColorRed: '0xff0000',
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(data: IAxleCountingData): void {
this.drawCircle(this.circleA, data);
this.drawCircle(this.circleB, data);
this.circleA.position.set(-12, 0);
this.circleB.position.set(12, 0);
this.line.clear();
let color = AxleCountingConsts.circleColorBlue;
if (data.type == 1) {
color = AxleCountingConsts.circleColorRed;
}
this.line.lineStyle(1, new Color(color));
this.line.moveTo(-24, 0);
this.line.lineTo(24, 0);
}
drawCircle(circle: Graphics, data: IAxleCountingData): void {
circle.clear();
let color = AxleCountingConsts.circleColorBlue;
if (data.type == 1) {
color = AxleCountingConsts.circleColorRed;
}
circle.lineStyle(AxleCountingConsts.borderWidth, new Color(color));
circle.beginFill(color, 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 code(): string {
return this.datas.code;
}
get datas(): IAxleCountingData {
return this.getDatas<IAxleCountingData>();
}
doRepaint(): void {
this.twoCircle.draw(this.datas);
}
buildRelation(): void {
this.loadRelations();
}
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

@ -0,0 +1,425 @@
import { FederatedPointerEvent, IPoint, Point } from 'pixi.js';
import {
AbsorbableLine,
AbsorbablePosition,
GraphicDrawAssistant,
GraphicIdGenerator,
GraphicInteractionPlugin,
IDrawApp,
JlGraphic,
distance2,
} from 'jl-graphic';
import {
IAxleCountingData,
AxleCounting,
AxleCountingTemplate,
} from './AxleCounting';
import { Section, DevicePort, SectionType } from '../section/Section';
import { Turnout } from '../turnout/Turnout';
import { IRelatedRefData, createRelatedRefProto } from '../CommonGraphics';
import { Signal } from '../signal/Signal';
import { loadGenerateAxleCountingConfig } from 'src/drawApp/commonApp';
import { graphicData } from 'src/protos/stationLayoutGraphics';
export interface IAxleCountingDrawOptions {
newData: () => IAxleCountingData;
}
export class AxleCountingDraw extends GraphicDrawAssistant<
AxleCountingTemplate,
IAxleCountingData
> {
codeGraph: AxleCounting;
constructor(app: IDrawApp, template: AxleCountingTemplate) {
super(app, template, 'sym_o_circle', '区段检测点');
this.codeGraph = this.graphicTemplate.new();
this.container.addChild(this.codeGraph);
AxleCountingInteraction.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: IAxleCountingData): boolean {
data.transform = this.container.saveTransform();
return true;
}
draw(
ps: IPoint,
direction: number,
graphic: Section | Turnout,
port: DevicePort,
map: Map<string, number>,
reftype: string,
refGraphic: Section | Turnout,
refPort: DevicePort
) {
const generateAxleCountingConfig = loadGenerateAxleCountingConfig();
if (generateAxleCountingConfig?.noGenerateGroup !== undefined) {
const noGenerateGroup = generateAxleCountingConfig.noGenerateGroup;
for (let i = 0; i < noGenerateGroup.length; i++) {
if (
noGenerateGroup[i] == graphic.id &&
((i % 2 == 0 && refGraphic.id == noGenerateGroup[i + 1]) ||
(i % 2 == 1 && refGraphic.id == noGenerateGroup[i - 1]))
) {
map.set(`${graphic.id}-${port}`, 1);
map.set(`${refGraphic.id}-${refPort}`, 1);
return;
}
}
}
if (
graphic.type == 'Turnout' &&
reftype == 'Turnout' &&
port == DevicePort.B &&
refPort == DevicePort.B
) {
//查看生成计轴bb配置
let hasBB = false;
if (generateAxleCountingConfig !== undefined) {
const bbConnect = generateAxleCountingConfig.bbConnect;
if (
bbConnect.includes(graphic.id) ||
bbConnect.includes(refGraphic.id)
) {
hasBB = true;
}
}
//bb连接处有信号机需要生成
const points = (graphic as Turnout).getPortPoints();
const portPs = graphic.localToCanvasPoints(points[1][0])[0];
let hasSingle = false;
const singles = this.app.queryStore.queryByType<Signal>(Signal.Type);
singles.forEach((single) => {
if (distance2(portPs, single.position) < 50) {
hasSingle = true;
}
});
if (!hasSingle && !hasBB) {
map.set(`${graphic.id}-${port}`, 1);
map.set(`${refGraphic.id}-${refPort}`, 1);
return;
}
}
if (
!map.has(`${refGraphic.id}-${refPort}`) &&
!map.has(`${graphic.id}-${port}`)
) {
map.set(`${graphic.id}-${port}`, 1);
map.set(`${refGraphic.id}-${refPort}`, 1);
const refData1 = createRelatedRefProto(reftype, refGraphic.id, refPort);
const refData2 = createRelatedRefProto(graphic.type, graphic.id, port);
const axleCounting = new AxleCounting(direction);
axleCounting.loadData(this.graphicTemplate.datas);
if (graphic.type == 'Turnout') {
axleCounting.position.set(ps.x, ps.y);
} else {
axleCounting.position.set(ps.x, ps.y);
}
axleCounting.id = GraphicIdGenerator.next();
axleCounting.datas.axleCountingRef = [refData2, refData1];
axleCounting.datas.code = `${graphic.datas.code}-${port}\\${refGraphic.datas.code}-${refPort}`;
this.storeGraphic(axleCounting);
axleCounting.loadRelations();
}
}
drawAdd(
ps: IPoint,
direction: number,
graphic: Section | Turnout,
port: DevicePort,
map: Map<string, number>
) {
if (!map.has(`${graphic.id}-${port}`)) {
map.set(`${graphic.id}-${port}`, 1);
const refData = createRelatedRefProto(graphic.type, graphic.id, port);
const axleCounting = new AxleCounting(direction);
axleCounting.loadData(this.graphicTemplate.datas);
if (graphic.type == 'Turnout') {
axleCounting.position.set(ps.x, ps.y);
} else {
axleCounting.position.set(ps.x, ps.y);
}
axleCounting.id = GraphicIdGenerator.next();
axleCounting.datas.axleCountingRef = [refData];
axleCounting.datas.code = `${graphic.datas.code}-${port}`;
this.storeGraphic(axleCounting);
axleCounting.loadRelations();
}
}
oneGenerates(height: Point) {
const map = new Map();
const needDelete: AxleCounting[] = [];
const axleCountings = this.app.queryStore
.queryByType<AxleCounting>(AxleCounting.Type)
.filter((axleCounting) => {
if (axleCounting.datas.axleCountingRef.length == 1) {
const refInfo = axleCounting.datas.axleCountingRef[0];
if (refInfo.deviceType == graphicData.RelatedRef.DeviceType.Section) {
const refSection = this.app.queryStore.queryById<Section>(
refInfo.id
);
if (
refSection.datas.paRef != undefined &&
refSection.datas.pbRef != undefined
) {
needDelete.push(axleCounting);
return false;
}
}
}
return true;
});
this.app.deleteGraphics(...needDelete);
const axleCountingRefs: IRelatedRefData[] = [];
axleCountings.forEach((axleCounting) => {
axleCountingRefs.push(...axleCounting.datas.axleCountingRef);
});
axleCountingRefs.forEach((axleCountingRef) => {
map.set(
`${axleCountingRef.id}-${
graphicData.RelatedRef.DevicePort[axleCountingRef.devicePort]
}`,
1
);
});
//由区段生成计轴--区段和区段或区段和道岔
const sections = this.app.queryStore
.queryByType<Section>(Section.Type)
.filter((section) => {
return (
section.relationManage.getRelationsOfGraphicAndOtherType(
section,
AxleCounting.Type
).length < 2 && section.datas.sectionType == SectionType.Physical
);
});
sections.forEach((section) => {
const sectionRelations =
section.relationManage.getRelationsOfGraphic(section);
const ps = section.localToCanvasPoint(section.getStartPoint());
const pe = section.localToCanvasPoint(section.getEndPoint());
sectionRelations.forEach((relation) => {
const port = relation.getRelationParam(section).param;
const refDevice = relation.getOtherGraphic<Section>(section);
const refDevicePort = relation.getOtherRelationParam(section).param;
let direction = 1;
let axleCountingPs = ps;
if (port == 'B') {
axleCountingPs = pe;
}
if (axleCountingPs.y > height.y) {
direction = -1;
}
if (refDevice.type == Section.Type || refDevice.type == Turnout.Type)
this.draw(
axleCountingPs,
direction,
section,
port,
map,
refDevice.type,
refDevice,
refDevicePort
);
});
});
//由区段生成计轴--单独区段
sections.forEach((section) => {
const axleCountingRelations =
section.relationManage.getRelationsOfGraphicAndOtherType(
section,
AxleCounting.Type
);
const ps = section.localToCanvasPoint(section.getStartPoint());
const pe = section.localToCanvasPoint(section.getEndPoint());
if (axleCountingRelations.length < 2) {
axleCountingRelations.forEach((relation) => {
const port = relation.getRelationParam(section).param;
let addPort = DevicePort.A;
let direction = 1;
let axleCountingPs = ps;
if (port == 'A') {
axleCountingPs = pe;
addPort = DevicePort.B;
}
if (axleCountingPs.y > height.y) {
direction = -1;
}
if (axleCountingPs.y > height.y) {
direction = -1;
}
this.drawAdd(axleCountingPs, direction, section, addPort, map);
});
}
});
//由道岔生成计轴--道岔和道岔
const turnouts = this.app.queryStore
.queryByType<Turnout>(Turnout.Type)
.filter((turnout) => {
return (
turnout.relationManage.getRelationsOfGraphicAndOtherType(
turnout,
AxleCounting.Type
).length < 3
);
});
turnouts.forEach((turnout) => {
const points = turnout.getPortPoints();
//道岔关联的道岔
const turnoutRelations =
turnout.relationManage.getRelationsOfGraphicAndOtherType(
turnout,
Turnout.Type
);
turnoutRelations.forEach((relation) => {
const port = relation.getRelationParam(turnout).getParam<DevicePort>();
const portIndex = Object.values(DevicePort).findIndex(
(p) => p === port
);
const refTurnout = relation.getOtherGraphic<Turnout>(turnout);
const refTurnoutPort = relation.getOtherRelationParam(turnout).param;
const portPs = turnout.localToCanvasPoints(...points[portIndex])[
points[portIndex].length - 1
];
this.draw(
portPs,
1,
turnout,
port,
map,
Turnout.Type,
refTurnout,
refTurnoutPort
);
});
});
//由道岔生成计轴--单独道岔
turnouts.forEach((turnout) => {
const axleCountingRelations =
turnout.relationManage.getRelationsOfGraphicAndOtherType(
turnout,
AxleCounting.Type
);
const points = turnout.getPortPoints();
const turnoutPort = [DevicePort.A, DevicePort.B, DevicePort.C];
if (axleCountingRelations.length == 1) {
const port = axleCountingRelations[0].getRelationParam(turnout).param;
const otherPort = turnoutPort.filter((p) => {
return p !== port && p;
});
otherPort.forEach((port) => {
const portIndex = Object.values(DevicePort).findIndex(
(p) => p === port
);
const axleCountingPs1 = turnout.localToCanvasPoints(
...points[portIndex]
)[points[portIndex].length - 1];
this.drawAdd(axleCountingPs1, 1, turnout, port, map);
});
} else if (axleCountingRelations.length == 2) {
const port = axleCountingRelations.map(
(item) => item.getRelationParam(turnout).param
);
const otherPort = turnoutPort.filter((p) => {
return p !== port[0] && p !== port[1] && p;
});
if (otherPort.length) {
const portIndex = Object.values(DevicePort).findIndex(
(p) => p === otherPort[0]
);
const axleCountingPs1 = turnout.localToCanvasPoints(
...points[portIndex]
)[points[portIndex].length - 1];
this.drawAdd(axleCountingPs1, 1, turnout, otherPort[0], map);
}
} else if (axleCountingRelations.length == 0) {
const axleCountingPsA = turnout.localToCanvasPoints(points[0][0])[0];
const axleCountingPsC = turnout.localToCanvasPoints(points[2][0])[0];
this.drawAdd(axleCountingPsA, 1, turnout, DevicePort.A, map);
this.drawAdd(axleCountingPsC, 1, turnout, DevicePort.C, map);
}
});
}
}
function buildAbsorbablePositions(
axleCounting: AxleCounting
): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const axleCountings = axleCounting.queryStore.queryByType<AxleCounting>(
AxleCounting.Type
);
const { width } = axleCounting.getGraphicApp().canvas;
axleCountings.forEach((other) => {
if (other.id == axleCounting.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 AxleCountingInteraction extends GraphicInteractionPlugin<AxleCounting> {
static Name = 'AxleCounting_transform';
constructor(app: IDrawApp) {
super(AxleCountingInteraction.Name, app);
}
static init(app: IDrawApp) {
return new AxleCountingInteraction(app);
}
filter(...grahpics: JlGraphic[]): AxleCounting[] | undefined {
return grahpics
.filter((g) => g.type === AxleCounting.Type)
.map((g) => g as AxleCounting);
}
bind(g: AxleCounting): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.scalable = 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);
}
unbind(g: AxleCounting): void {
g.eventMode = 'none';
g.scalable = 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);
}
onSelected(): void {
const AxleCounting = this.app.selectedGraphics[0] as AxleCounting;
this.app.setOptions({
absorbablePositions: buildAbsorbablePositions(AxleCounting),
});
}
}

View File

@ -0,0 +1,164 @@
import { Color, Container, Graphics, Rectangle } from 'pixi.js';
import {
GraphicData,
GraphicState,
JlGraphic,
JlGraphicTemplate,
distance2,
getRectangleCenter,
} from 'jl-graphic';
import { Station } from '../station/Station';
import { Section } from '../section/Section';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
export interface IPlatformData extends GraphicData {
get code(): string; // 编号
set code(v: string);
get refStation(): number; // 关联的车站
set refStation(v: number);
get refSection(): number; // 关联的物理区段
set refSection(v: number);
get refEsbRelayCode(): string; // 关联的紧急停车继电器的编号
set refEsbRelayCode(v: string);
get type(): electronicMapGraphicData.Platform.TypeOfPlatform; //站台上下行
set type(v: electronicMapGraphicData.Platform.TypeOfPlatform);
clone(): IPlatformData;
copyFrom(data: IPlatformData): void;
eq(other: IPlatformData): boolean;
}
export interface IPlatformState extends GraphicState {
id?: number;
empj?: boolean;
}
const platformConsts = {
width: 90,
height: 20,
lineWidth: 3,
white: '0xffffff', //站台颜色
};
//子元素--矩形
export class rectGraphic extends Container {
static Type = 'RectPlatForm';
rectGraphic: Graphics;
constructor() {
super();
this.rectGraphic = new Graphics();
this.addChild(this.rectGraphic);
}
draw(): void {
const rectGraphic = this.rectGraphic;
const fillColor = platformConsts.white;
rectGraphic
.clear()
.lineStyle(platformConsts.lineWidth, new Color(fillColor))
.beginFill(fillColor, 1)
.drawRect(0, 0, platformConsts.width, platformConsts.height)
.endFill();
rectGraphic.pivot = getRectangleCenter(
new Rectangle(0, 0, platformConsts.width, platformConsts.height)
);
}
clear(): void {
this.rectGraphic.clear();
}
}
export class Platform extends JlGraphic {
static Type = 'Platform';
rectGraphic: rectGraphic = new rectGraphic();
constructor() {
super(Platform.Type);
this.addChild(this.rectGraphic);
}
get code(): string {
return this.datas.code;
}
get datas(): IPlatformData {
return this.getDatas<IPlatformData>();
}
get states(): IPlatformState {
return this.getStates<IPlatformState>();
}
doRepaint(): void {
this.rectGraphic.draw();
}
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.relationManage.addRelation(this, stationas[i]);
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.relationManage.deleteRelationOfGraphicAndOtherType(
this,
Section.Type
);
this.relationManage.addRelation(this, refSection);
}
}
saveRelations() {
const refStation = this.relationManage
.getRelationsOfGraphicAndOtherType(this, Station.Type)
.map((relation) => relation.getOtherGraphic<Station>(this).datas.id);
if (refStation.length) {
this.datas.refStation = refStation[0];
}
const refSection = this.relationManage
.getRelationsOfGraphicAndOtherType(this, Section.Type)
.map((relation) => relation.getOtherGraphic<Section>(this).datas.id);
if (refSection.length) {
this.datas.refSection = refSection[0];
}
}
loadRelations() {
if (this.datas.refStation) {
this.relationManage.addRelation(
this,
this.queryStore.queryById<Platform>(this.datas.refStation)
);
}
if (this.datas.refSection) {
this.relationManage.addRelation(
this,
this.queryStore.queryById<Platform>(this.datas.refSection)
);
}
}
}
export class PlatformTemplate extends JlGraphicTemplate<Platform> {
constructor(dataTemplate: IPlatformData) {
super(Platform.Type, {
dataTemplate,
});
}
new(): Platform {
const platform = new Platform();
platform.loadData(this.datas);
return platform;
}
}

View File

@ -0,0 +1,103 @@
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();
}
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 {
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

@ -0,0 +1,219 @@
import { Color, Container, Graphics, Rectangle } from 'pixi.js';
import {
GraphicData,
GraphicState,
JlGraphic,
JlGraphicTemplate,
VectorText,
distance2,
getRectangleCenter,
} from 'jl-graphic';
import { Platform } from '../platform/Platform';
import { state } from 'src/protos/device_state';
import { request } from 'src/protos/request';
export interface IScreenDoorData extends GraphicData {
get code(): string; // 编号
set code(v: string);
get refPlatform(): number; // 关联的站台
set refPlatform(v: number);
get sonDoorAmount(): number; //子屏蔽门的数量
clone(): IScreenDoorData;
copyFrom(data: IScreenDoorData): void;
eq(other: IScreenDoorData): boolean;
}
export interface IScreenDoorState extends GraphicState {
get asdStates(): state.AsdState[]; //所有子门的状态
set asdStates(v: state.AsdState[]);
get mgj(): boolean; //屏蔽门整体的关闭(继电器)状态
set mgj(v: boolean);
get zaw(): boolean; //是否有障碍物
set zaw(v: boolean);
param: request.PsdParam;
}
const screenDoorConsts = {
lineWidth: 3,
smallDoorWidth: 10,
doorClose: '0x00FF00', //屏蔽门的颜色
doorOpen: '0xff0000',
zawWidth: 3,
zawHeight: 3,
zawlineWidth: 3,
zawColor: '0xff0000',
forceWidth: 11,
forceHeight: 12,
forcelineWidth: 1,
forceColor: '0xff0000',
};
class smallDoorGraphic extends Container {
smallDoorGraphic: Graphics;
labelGraphic: VectorText;
zawGraphic: Graphics;
forceGraphic: Graphics;
constructor() {
super();
this.smallDoorGraphic = new Graphics();
this.zawGraphic = new Graphics();
this.forceGraphic = new Graphics();
this.labelGraphic = new VectorText();
this.labelGraphic.setVectorFontSize(12);
this.labelGraphic.anchor.set(0.5);
this.addChild(this.smallDoorGraphic);
this.addChild(this.labelGraphic);
this.addChild(this.zawGraphic);
this.addChild(this.forceGraphic);
this.drawZaw();
this.drawForce();
}
draw(data: IScreenDoorData, i: number, state: state.AsdState): void {
const start =
(-screenDoorConsts.smallDoorWidth * data.sonDoorAmount) / 2 +
screenDoorConsts.smallDoorWidth * i;
const smallDoorGraphic = this.smallDoorGraphic;
const lineColor = state?.mgj
? screenDoorConsts.doorClose
: screenDoorConsts.doorOpen;
smallDoorGraphic
.lineStyle(screenDoorConsts.lineWidth, new Color(lineColor))
.moveTo(start, 0)
.lineTo(start + screenDoorConsts.smallDoorWidth - 3, 0);
this.labelGraphic.text = i + 1;
this.labelGraphic.style.fill = 'red';
if (i % 2 == 0) {
this.labelGraphic.position.set(start + 4, 9);
this.zawGraphic.position.set(start + 4, -9);
this.forceGraphic.position.set(start + 4, 9);
} else {
this.labelGraphic.position.set(start + 4, -9);
this.zawGraphic.position.set(start + 4, 9);
this.forceGraphic.position.set(start + 4, -9);
}
this.zawGraphic.visible = state?.zaw ? true : false;
this.forceGraphic.visible = state?.force ? true : false;
}
drawZaw() {
this.zawGraphic
.clear()
.lineStyle(
screenDoorConsts.zawlineWidth,
new Color(screenDoorConsts.zawColor)
)
.drawRect(0, 0, screenDoorConsts.zawWidth, screenDoorConsts.zawHeight);
this.zawGraphic.pivot = getRectangleCenter(
new Rectangle(0, 0, screenDoorConsts.zawWidth, screenDoorConsts.zawHeight)
);
}
drawForce() {
this.forceGraphic
.clear()
.lineStyle(
screenDoorConsts.forcelineWidth,
new Color(screenDoorConsts.forceColor)
)
.drawRect(
0,
0,
screenDoorConsts.forceWidth,
screenDoorConsts.forceHeight
);
this.forceGraphic.pivot = getRectangleCenter(
new Rectangle(
0,
0,
screenDoorConsts.forceWidth,
screenDoorConsts.forceHeight
)
);
}
}
export class ScreenDoor extends JlGraphic {
static Type = 'ScreenDoor';
doorGraphic = new Container();
constructor() {
super(ScreenDoor.Type);
this.addChild(this.doorGraphic);
}
get code(): string {
return this.datas.code;
}
get datas(): IScreenDoorData {
return this.getDatas<IScreenDoorData>();
}
get states(): IScreenDoorState {
return this.getStates<IScreenDoorState>();
}
doRepaint(): void {
const doorGraphic = this.doorGraphic;
doorGraphic.children.forEach((g) => {
(g as smallDoorGraphic).smallDoorGraphic.clear();
(g as smallDoorGraphic).zawGraphic.clear();
(g as smallDoorGraphic).forceGraphic.clear();
(g as smallDoorGraphic).labelGraphic.text = '';
});
for (let i = 0; i < this.datas.sonDoorAmount; i++) {
const smallDoor = new smallDoorGraphic();
const smallDoorState = this.states.asdStates.find(
(asdState) => +asdState.code == i + 1
);
smallDoor.draw(this.datas, i, smallDoorState as state.AsdState);
doorGraphic.addChild(smallDoor);
}
}
buildRelation() {
const platforms = this.queryStore.queryByType<Platform>(Platform.Type);
const minDistanceRefPlatform: Platform[] = [];
platforms.forEach((platform) => {
const sP = platform.localBoundsToCanvasPoints();
if (this.x > sP[0].x && this.x < sP[1].x) {
minDistanceRefPlatform.push(platform);
}
});
const refPlatform = minDistanceRefPlatform.reduce((prev, cur) => {
return distance2(prev.position, this.position) >
distance2(cur.position, this.position)
? cur
: prev;
});
if (refPlatform) {
this.relationManage.addRelation(this, refPlatform);
}
}
saveRelations() {
const refStation = this.relationManage
.getRelationsOfGraphicAndOtherType(this, Platform.Type)
.map((relation) => relation.getOtherGraphic<Platform>(this).datas.id);
if (refStation.length) {
this.datas.refPlatform = refStation[0];
}
}
loadRelations() {
if (this.datas.refPlatform) {
this.relationManage.addRelation(
this,
this.queryStore.queryById<Platform>(this.datas.refPlatform)
);
}
}
}
export class ScreenDoorTemplate extends JlGraphicTemplate<ScreenDoor> {
constructor(dataTemplate: IScreenDoorData, stateTemplate?: IScreenDoorState) {
super(ScreenDoor.Type, {
dataTemplate,
stateTemplate,
});
}
new(): ScreenDoor {
const screenDoor = new ScreenDoor();
screenDoor.loadData(this.datas);
screenDoor.loadState(this.states);
return screenDoor;
}
}

View File

@ -0,0 +1,97 @@
import { FederatedPointerEvent, Point } from 'pixi.js';
import {
AbsorbableLine,
AbsorbablePosition,
GraphicDrawAssistant,
GraphicInteractionPlugin,
IDrawApp,
JlGraphic,
} from 'jl-graphic';
import { IScreenDoorData, ScreenDoor, ScreenDoorTemplate } from './ScreenDoor';
import { Platform } from 'src/graphics/platform/Platform';
export interface IScreenDoorDrawOptions {
newData: () => IScreenDoorData;
}
export class ScreenDoorDraw extends GraphicDrawAssistant<
ScreenDoorTemplate,
IScreenDoorData
> {
screenDoorGraphic: ScreenDoor;
constructor(app: IDrawApp, template: ScreenDoorTemplate) {
super(app, template, 'door_sliding', '屏蔽门ScreenDoor');
this.screenDoorGraphic = this.graphicTemplate.new();
this.container.addChild(this.screenDoorGraphic);
screenDoorInteraction.init(app);
}
bind(): void {
super.bind();
this.screenDoorGraphic.loadData(this.graphicTemplate.datas);
this.screenDoorGraphic.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: IScreenDoorData): boolean {
data.transform = this.container.saveTransform();
return true;
}
}
function buildAbsorbablePositions(
screenDoor: ScreenDoor
): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const platforms = screenDoor.queryStore.queryByType<Platform>(Platform.Type);
const { height } = screenDoor.getGraphicApp().canvas;
platforms.forEach((platform) => {
const ps = platform.datas.transform.position;
const ys = new AbsorbableLine({ x: ps.x, y: 0 }, { x: ps.x, y: height });
aps.push(ys);
});
return aps;
}
export class screenDoorInteraction extends GraphicInteractionPlugin<ScreenDoor> {
static Name = 'screenDoor_transform';
constructor(app: IDrawApp) {
super(screenDoorInteraction.Name, app);
}
static init(app: IDrawApp) {
return new screenDoorInteraction(app);
}
filter(...grahpics: JlGraphic[]): ScreenDoor[] | undefined {
return grahpics
.filter((g) => g.type === ScreenDoor.Type)
.map((g) => g as ScreenDoor);
}
bind(g: ScreenDoor): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.scalable = true;
g.rotatable = true;
g.on('selected', this.onSelected, this);
}
unbind(g: ScreenDoor): void {
g.eventMode = 'none';
g.scalable = false;
g.rotatable = false;
g.off('selected', this.onSelected, this);
}
onSelected(): void {
const screenDoor = this.app.selectedGraphics[0] as ScreenDoor;
this.app.setOptions({
absorbablePositions: buildAbsorbablePositions(screenDoor),
});
}
}

View File

@ -0,0 +1,407 @@
import { IPointData } from 'pixi.js';
import {
GraphicData,
GraphicRelationParam,
GraphicState,
JlGraphic,
JlGraphicTemplate,
VectorText,
convertToBezierParams,
distance2,
splitLineEvenly,
ILineGraphic,
} from 'jl-graphic';
import { Vector2 } from 'jl-graphic';
import {
IRelatedRefData,
createRelatedRefProto,
protoPort2Data,
} from '../CommonGraphics';
import { Turnout } from '../turnout/Turnout';
import { AxleCounting } from '../axleCounting/AxleCounting';
import { SectionGraphic } from '../sectionGraphic/SectionGraphic';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
const tolerance = 0.01;
export enum SectionType {
Physical = 0,
Logic = 1,
TurnoutPhysical = 2,
}
export interface ISectionData extends GraphicData {
get code(): string; // 编号
set code(v: string);
get points(): IPointData[]; // 线坐标点
set points(points: IPointData[]);
get paRef(): IRelatedRefData | undefined;
set paRef(ref: IRelatedRefData | undefined);
get pbRef(): IRelatedRefData | undefined;
set pbRef(ref: IRelatedRefData | undefined);
get sectionType(): SectionType;
set sectionType(type: SectionType);
get axleCountings(): number[]; //计轴id列表
set axleCountings(axleCountings: number[]);
get trackSectionId(): number; // 轨道区段id
set trackSectionId(v: number);
get isCurve(): boolean; // 是否曲线
set isCurve(v: boolean);
get segmentsCount(): number; // 曲线分段数
set segmentsCount(v: number);
get centralizedStations(): number[];
set centralizedStations(v: number[]);
get normalRunningDirection(): electronicMapGraphicData.Section.RunningDirection;
set normalRunningDirection(v: electronicMapGraphicData.Section.RunningDirection);
get isTurnBackZone(): boolean;
set isTurnBackZone(v: boolean);
get direction(): electronicMapGraphicData.Direction;
set direction(v: electronicMapGraphicData.Direction);
clone(): ISectionData;
copyFrom(data: ISectionData): void;
eq(other: ISectionData): boolean;
}
export interface ISectionState extends GraphicState {
id: number;
occupied?: boolean; //区段占用
axleFault?: boolean; //计轴故障
axleDrst: boolean; // 计轴复位
axlePdrst: boolean; // 计轴预复位
}
export const SectionConsts = {
lineColor: '#5578b6',
occupiedColor: '#f00',
lineWidth: 5,
};
export enum DevicePort {
A = 'A',
B = 'B',
C = 'C',
}
export class Section extends JlGraphic implements ILineGraphic {
static Type = 'Section';
lineGraphic: SectionGraphic;
labelGraphic: VectorText;
childSections: Section[] = [];
constructor() {
super(Section.Type);
this.lineGraphic = new SectionGraphic();
this.labelGraphic = new VectorText();
this.labelGraphic.setVectorFontSize(14);
this.labelGraphic.anchor.set(0.5);
this.labelGraphic.style.fill = '#0f0';
this.labelGraphic.transformSave = true;
this.labelGraphic.name = 'label';
this.transformSave = true;
this.addChild(this.lineGraphic);
this.addChild(this.labelGraphic);
}
doRepaint() {
if (
this.datas.sectionType === SectionType.Physical &&
this.datas.points.length < 2
) {
throw new Error('Link坐标数据异常');
}
this.lineGraphic.clear();
if (this.datas.sectionType === SectionType.Physical) {
this.lineGraphic.isCurve = this.datas.isCurve;
if (this.lineGraphic.isCurve) {
this.lineGraphic.segmentsCount = this.datas.segmentsCount;
}
this.lineGraphic.points = this.datas.points;
this.lineGraphic.lineStyle(
SectionConsts.lineWidth,
this.states.occupied
? SectionConsts.occupiedColor
: SectionConsts.lineColor
);
//FIXME 依赖location.path不合适
if (location.pathname.includes('painting')) {
if (
this.datas.normalRunningDirection ===
electronicMapGraphicData.Section.RunningDirection.BtoA
) {
this.lineGraphic.lineStyle(SectionConsts.lineWidth, '#f00');
} else if (
this.datas.normalRunningDirection ===
electronicMapGraphicData.Section.RunningDirection.AtoB
) {
this.lineGraphic.lineStyle(SectionConsts.lineWidth, '#0f0');
}
}
this.lineGraphic.paint();
}
this.labelGraphic.text = this.datas.code;
const labelPosition = this.datas.childTransforms?.find(
(t) => t.name === this.labelGraphic.name
)?.transform.position;
if (labelPosition) {
this.labelGraphic.position.set(labelPosition.x, labelPosition.y);
} else {
this.labelGraphic.position.set(
this.datas.points[0].x,
this.datas.points[0].y + 20
);
}
}
getVerticesList(): IPointData[] {
if (this.datas.isCurve) {
return [
this.datas.points[0],
...convertToBezierParams(this.datas.points).map((param) => param.p2),
];
} else {
return this.datas.points;
}
}
getStartPoint() {
return this.datas.points[0];
}
getEndPoint(): IPointData {
return this.datas.points[this.datas.points.length - 1];
}
get code(): string {
return this.datas.code;
}
get datas(): ISectionData {
return this.getDatas<ISectionData>();
}
get states(): ISectionState {
return this.getStates<ISectionState>();
}
get linePoints(): IPointData[] {
return this.datas.points;
}
set linePoints(points: IPointData[]) {
const old = this.datas.clone();
old.points = points;
this.updateData(old);
}
getConnectElement(port: DevicePort) {
const relation = this.relationManage
.getRelationsOfGraphic(this)
.find(
(relation) =>
relation.getRelationParam(this).getParam<DevicePort>() === port &&
(relation.getOtherGraphic(this) instanceof Section ||
relation.getOtherGraphic(this) instanceof Turnout)
);
if (!relation) {
return;
}
return {
g: relation?.getOtherGraphic(this) as Section | Turnout,
port: relation?.getOtherRelationParam(this).getParam<DevicePort>(),
};
}
/** 获取拆分逻辑区段数据 */
getSplitPoints(count: number): IPointData[][] {
if (this.datas.points.length !== 2) {
let totalLen = 0;
const lengths: number[] = [];
for (let i = 1; i < this.datas.points.length; i++) {
const { x: x1, y: y1 } = this.datas.points[i - 1],
{ x: x2, y: y2 } = this.datas.points[i];
const len = new Vector2([x2 - x1, y2 - y1]).length();
totalLen += len;
lengths.push(len);
}
const counts = lengths.map((length) =>
Math.round((count * length) / totalLen)
);
if (counts.reduce((p, c) => p + c, 0) !== count) {
const intersection = counts.reduce((p, c) => p + c, 0) - count;
let maxCountIndex = 0,
maxCount = 0;
counts.forEach((c, i) => {
if (c > maxCount) {
maxCount = c;
maxCountIndex = i;
}
});
counts[maxCountIndex] + intersection;
}
return counts
.map((count, i) => {
return splitLineEvenly(
this.localToCanvasPoint(this.datas.points[i]),
this.localToCanvasPoint(this.datas.points[i + 1]),
count
);
})
.flat();
} else {
return splitLineEvenly(
this.localToCanvasPoint(this.datas.points[0]),
this.localToCanvasPoint(
this.datas.points[this.datas.points.length - 1]
),
count
);
}
}
buildRelation() {
this.relationManage.deleteRelationOfGraphicAndOtherType(this, Section.Type);
if (this.datas.sectionType === SectionType.Physical) {
this.queryStore.queryByType<Section>(Section.Type).forEach((section) => {
if (section.id === this.id) return;
let param: DevicePort[] = [];
if (
distance2(
this.localToCanvasPoint(this.getStartPoint()),
section.localToCanvasPoint(section.getStartPoint())
) <= tolerance
) {
param = [DevicePort.A, DevicePort.A];
}
if (
distance2(
this.localToCanvasPoint(this.getEndPoint()),
section.localToCanvasPoint(section.getStartPoint())
) <= tolerance
) {
param = [DevicePort.B, DevicePort.A];
}
if (
distance2(
this.localToCanvasPoint(this.getStartPoint()),
section.localToCanvasPoint(section.getEndPoint())
) <= tolerance
) {
param = [DevicePort.A, DevicePort.B];
}
if (
distance2(
this.localToCanvasPoint(this.getEndPoint()),
section.localToCanvasPoint(section.getEndPoint())
) <= tolerance
) {
param = [DevicePort.B, DevicePort.B];
}
if (param.length) {
this.relationManage.addRelation(
new GraphicRelationParam(this, param[0]),
new GraphicRelationParam(section, param[1])
);
}
});
}
}
saveRelations() {
const paRelation = this.relationManage
.getRelationsOfGraphic(this)
.find(
(relation) =>
relation.getRelationParam(this).param === DevicePort.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 === DevicePort.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;
}
this.datas.axleCountings = this.relationManage
.getRelationsOfGraphicAndOtherType(this, AxleCounting.Type)
.map((relation) => relation.getOtherGraphic<AxleCounting>(this).datas.id);
}
loadRelations() {
if (this.datas?.paRef?.id) {
this.relationManage.addRelation(
new GraphicRelationParam(this, DevicePort.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, DevicePort.B),
new GraphicRelationParam(
this.queryStore.queryById(this.datas.pbRef.id),
protoPort2Data(this.datas.pbRef.devicePort)
)
);
}
if (this.datas.sectionType === SectionType.TurnoutPhysical) {
if (this.datas.axleCountings) {
this.datas.axleCountings.forEach((id) => {
this.relationManage.addRelation(
this,
this.queryStore.queryById<AxleCounting>(id)
);
});
}
}
}
}
export interface ISectionTemplateProperty {
isCurve: boolean;
segmentsCount: number;
}
export class SectionTemplate
extends JlGraphicTemplate<Section>
implements ISectionTemplateProperty
{
isCurve = false;
segmentsCount = 10;
constructor(dataTemplate: ISectionData, stateTemplate?: ISectionState) {
super(Section.Type, { dataTemplate, stateTemplate });
}
new() {
const g = new Section();
g.loadData(this.datas);
g.loadState(this.states);
return g;
}
}

View File

@ -0,0 +1,671 @@
import {
DisplayObject,
FederatedMouseEvent,
Graphics,
IHitArea,
IPointData,
Point,
} from 'pixi.js';
import { Dialog } from 'quasar';
import SectionSplitDialog from 'src/components/draw-app/dialogs/SectionSplitDialog.vue';
import { LogicSectionData } from 'src/drawApp/graphics/LogicSectionInteraction';
import { SectionData } from 'src/drawApp/graphics/SectionInteraction';
import {
AppConsts,
ChildTransform,
DraggablePoint,
GraphicDrawAssistant,
GraphicInteractionPlugin,
GraphicTransform,
GraphicTransformEvent,
IDrawApp,
IGraphicApp,
JlGraphic,
KeyListener,
VectorText,
calculateLineMidpoint,
calculateMirrorPoint,
convertToBezierParams,
linePoint,
pointPolygon,
BezierCurveEditPlugin,
IEditPointOptions,
ILineGraphic,
PolylineEditPlugin,
addWayPoint,
clearWayPoint,
getWaypointRangeIndex,
ContextMenu,
MenuItemOptions,
AbsorbableLine,
AbsorbablePosition,
AbsorbablePoint,
} from 'jl-graphic';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { AxleCounting } from '../axleCounting/AxleCounting';
import { LogicSection } from '../logicSection/LogicSection';
import { LogicSectionDraw } from '../logicSection/LogicSectionDrawAssistant';
import { Turnout } from '../turnout/Turnout';
import {
ISectionData,
Section,
DevicePort,
SectionConsts,
SectionTemplate,
SectionType,
} from './Section';
import { ConcentrationDividingLine } from '../concentrationDividingLine/ConcentrationDividingLine';
import { buildDragMoveAbsorbablePositions } from '../turnout/TurnoutDrawAssistant';
export class SectionDraw extends GraphicDrawAssistant<
SectionTemplate,
ISectionData
> {
points: Point[] = [];
graphic = new Graphics();
keyQListener = new KeyListener({
value: 'KeyQ',
global: true,
onPress: () => {
if (this.points.length === 0) {
this.graphicTemplate.isCurve = true;
}
},
});
keyZListener = new KeyListener({
value: 'KeyZ',
global: true,
onPress: () => {
if (this.points.length === 0) {
this.graphicTemplate.isCurve = false;
}
},
});
constructor(app: IDrawApp, template: SectionTemplate) {
super(app, template, 'sym_o_timeline', '区段Section');
this.container.addChild(this.graphic);
SectionPointEditPlugin.init(app, this);
}
bind(): void {
super.bind();
this.app.addKeyboardListener(this.keyQListener, this.keyZListener);
}
unbind(): void {
super.unbind();
this.app.removeKeyboardListener(this.keyQListener, this.keyZListener);
}
onLeftDown(e: FederatedMouseEvent): void {
const { x, y } = this.toCanvasCoordinates(e.global);
const p = new Point(x, y);
if (this.graphicTemplate.isCurve) {
if (this.points.length == 0) {
this.points.push(p);
} else {
this.points.push(p, p.clone());
}
} else {
this.points.push(p);
}
}
onLeftUp(e: FederatedMouseEvent): void {
const template = this.graphicTemplate;
if (template.isCurve) {
const mp = this.toCanvasCoordinates(e.global);
if (this.points.length === 1) {
this.points.push(new Point(mp.x, mp.y));
} else if (this.points.length > 1) {
const cp2 = this.points[this.points.length - 2];
const p = this.points[this.points.length - 1];
cp2.copyFrom(calculateMirrorPoint(p, mp));
this.points.push(mp);
}
}
}
onRightClick(): void {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
onEsc(): void {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
redraw(p: Point): void {
if (this.points.length < 1) return;
const template = this.graphicTemplate;
this.graphic.clear();
this.graphic.lineStyle(SectionConsts.lineWidth, SectionConsts.lineColor);
const ps = [...this.points];
if (template.isCurve) {
if (ps.length === 1) {
this.graphic.moveTo(ps[0].x, ps[0].y);
this.graphic.lineTo(p.x, p.y);
} else {
if ((ps.length + 1) % 3 === 0) {
ps.push(p.clone(), p.clone());
} else {
const cp = ps[ps.length - 2];
const p1 = ps[ps.length - 1];
const mp = calculateMirrorPoint(p1, p);
cp.copyFrom(mp);
}
const bps = convertToBezierParams(ps);
bps.forEach((bp) => {
this.graphic.drawBezierCurve(
bp.p1,
bp.p2,
bp.cp1,
bp.cp2,
template.segmentsCount
);
});
}
} else {
ps.push(p);
ps.forEach((p, i) => {
if (i !== 0) {
this.graphic.lineTo(p.x, p.y);
} else {
this.graphic.moveTo(p.x, p.y);
}
});
}
}
prepareData(data: ISectionData): boolean {
const template = this.graphicTemplate;
if (
(!template.isCurve && this.points.length < 2) ||
(template.isCurve && this.points.length < 4)
) {
console.log('Section绘制因点不够取消绘制');
return false;
}
if (template.isCurve) {
this.points.pop();
}
data.isCurve = template.isCurve;
data.segmentsCount = template.segmentsCount;
data.points = this.points;
data.code = 'G000';
return true;
}
clearCache(): void {
this.points = [];
this.graphic.clear();
}
generateTurnoutSection() {
const turnoutIds: number[] = []; /* 已遍历的道岔id列表 */
const dfs = (turnout: Turnout) => {
const axleCountings: AxleCounting[] = [];
const turnouts: Turnout[] = [];
if (turnoutIds.includes(turnout.datas.id)) return;
turnoutIds.push(turnout.datas.id);
turnouts.push(turnout);
Object.values(DevicePort).forEach((port) => {
const currentPortRelated = turnout.getGraphicOfPort(port);
if (
currentPortRelated.some((graphic) => graphic instanceof AxleCounting)
) {
const axleCounting = currentPortRelated.find(
(graphic) => graphic instanceof AxleCounting
) as AxleCounting;
axleCountings.push(axleCounting);
} else {
const nextTurnout = currentPortRelated.find(
(graphic) => graphic instanceof Turnout
) as Turnout;
const result = dfs(nextTurnout);
if (result?.axleCountings) {
axleCountings.push(...result.axleCountings);
turnouts.push(...result.turnouts);
}
}
});
return { axleCountings, turnouts };
};
const graphics: Section[] = [];
this.app.queryStore
.queryByType<Turnout>(Turnout.Type)
.forEach((turnout) => {
const result = dfs(turnout);
if (!result || !result.axleCountings.length) {
return;
}
const turnoutSections = this.app.queryStore
.queryByType<Section>(Section.Type)
.filter((s) => s.datas.sectionType === SectionType.TurnoutPhysical);
//判重
const existed = turnoutSections.some((sec) =>
result.axleCountings.every((ac) =>
sec.datas.axleCountings.includes(ac.datas.id)
)
);
if (existed) return;
const turnoutPhysicalSectionData = new SectionData();
turnoutPhysicalSectionData.id = this.nextId();
turnoutPhysicalSectionData.sectionType =
graphicData.Section.SectionType.TurnoutPhysical;
turnoutPhysicalSectionData.points = result.axleCountings.map((ac) => {
return new Point(ac.position.x, ac.position.y);
});
turnoutPhysicalSectionData.axleCountings = result.axleCountings.map(
(ac) => ac.datas.id
);
turnoutPhysicalSectionData.code = result.turnouts
.map((t) => t.datas.code)
.join('-');
let labelPosition: IPointData;
if (result.turnouts.length === 2) {
labelPosition = calculateLineMidpoint(
result.turnouts[0].position,
result.turnouts[1].position
);
} else {
labelPosition = { x: result.turnouts[0].x, y: result.turnouts[0].y };
}
labelPosition.y += 20;
const labelTransform = GraphicTransform.default();
labelTransform.position = labelPosition;
turnoutPhysicalSectionData.childTransforms = [
new ChildTransform('label', labelTransform),
];
const g = this.graphicTemplate.new();
g.position.set(turnout.datas.pointC[0].x, turnout.datas.pointC[0].y);
g.loadData(turnoutPhysicalSectionData);
graphics.push(g);
});
this.storeGraphic(...graphics);
graphics.forEach((g) => {
g.loadRelations();
});
}
}
export class SectionGraphicHitArea implements IHitArea {
section: Section;
constructor(section: Section) {
this.section = section;
}
contains(x: number, y: number): boolean {
if (this.section.datas.sectionType === SectionType.TurnoutPhysical) {
return false;
}
if (this.section.datas.isCurve) {
const bps = convertToBezierParams(this.section.datas.points);
for (let i = 0; i < bps.length; i++) {
const bp = bps[i];
if (
pointPolygon(
{ x, y },
[bp.p1, bp.cp1, bp.cp2, bp.p2],
SectionConsts.lineWidth
)
) {
return true;
}
}
} else {
for (let i = 1; i < this.section.datas.points.length; i++) {
const p1 = this.section.datas.points[i - 1];
const p2 = this.section.datas.points[i];
if (linePoint(p1, p2, { x, y }, SectionConsts.lineWidth)) {
return true;
}
}
}
return false;
}
}
function buildAbsorbablePositions(
section: Section | ConcentrationDividingLine,
dp: DraggablePoint
): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const changePoints = section.localToCanvasPoints(...section.datas.points);
for (let i = 0; i < changePoints.length; i++) {
if (changePoints[i].equals(dp)) {
const { width, height } = section.getGraphicApp().canvas;
if (i == 0 || i == changePoints.length - 1) {
const ps =
i == 0 ? changePoints[1] : changePoints[changePoints.length - 2];
const x = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y });
const y = new AbsorbableLine({ x: ps.x, y: 0 }, { x: ps.x, y: height });
aps.push(x, y);
} else {
const generateAxisPoint = [changePoints[i - 1], changePoints[i + 1]];
generateAxisPoint.forEach((point) => {
const x = new AbsorbableLine(
{ x: 0, y: point.y },
{ x: width, y: point.y }
);
const y = new AbsorbableLine(
{ x: point.x, y: 0 },
{ x: point.x, y: height }
);
aps.push(x, y);
});
}
break;
}
}
if (section instanceof Section) {
const sections = section.queryStore
.queryByType<Section>(Section.Type)
.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);
turnouts.forEach((turnout) => {
turnout.getPortPoints().forEach((points) => {
turnout.localToCanvasPoints(...points).forEach((p) => {
aps.push(new AbsorbablePoint(p));
});
});
});
}
return aps;
}
export function onEditPointCreate(g: ILineGraphic, dp: DraggablePoint): void {
const section = g as Section | ConcentrationDividingLine;
dp.on('transformstart', (e: GraphicTransformEvent) => {
if (e.isShift()) {
section.getGraphicApp().setOptions({
absorbablePositions: buildAbsorbablePositions(section, dp),
});
}
});
}
class SectionPolylineEditPlugin extends PolylineEditPlugin {
static Name = 'SectionPolylineEditPlugin';
labels: VectorText[] = [];
constructor(g: ILineGraphic, options?: IEditPointOptions) {
super(g, options);
this.name = SectionPolylineEditPlugin.Name;
this.initLabels();
}
initLabels() {
this.labels = ['A', 'B'].map((str) => {
const vc = new VectorText(str, { fill: AppConsts.assistantElementColor });
vc.setVectorFontSize(14);
vc.anchor.set(0.5);
return vc;
});
this.addChild(...this.labels);
this.updateEditedPointsPosition();
}
updateEditedPointsPosition() {
super.updateEditedPointsPosition();
this.labels[0]?.position.set(
this.editedPoints[0].x,
this.editedPoints[0].y + 10
);
this.labels[1]?.position.set(
this.editedPoints[this.editedPoints.length - 1].x,
this.editedPoints[this.editedPoints.length - 1].y + 10
);
}
}
class SectionBazierCurveEditPlugin extends BezierCurveEditPlugin {
static Name = 'SectionBazierCurveEditPlugin';
labels: VectorText[] = [];
constructor(g: ILineGraphic, options?: IEditPointOptions) {
super(g, options);
this.name = SectionBazierCurveEditPlugin.Name;
this.initLabels();
}
initLabels() {
this.labels = [new VectorText('A'), new VectorText('B')];
this.labels.forEach((label) => {
label.setVectorFontSize(14);
this.addChild(label);
});
this.updateEditedPointsPosition();
}
updateEditedPointsPosition() {
super.updateEditedPointsPosition();
this.labels[0]?.position.set(
this.editedPoints[0].x,
this.editedPoints[0].y + 10
);
this.labels[1]?.position.set(
this.editedPoints[this.editedPoints.length - 1].x,
this.editedPoints[this.editedPoints.length - 1].y + 10
);
}
}
export const addWaypointConfig: MenuItemOptions = {
name: '添加路径点',
};
export const clearWaypointsConfig: MenuItemOptions = {
name: '清除所有路径点',
};
export const splitSectionConfig: MenuItemOptions = {
name: '拆分',
// disabled: true,
};
const SectionEditMenu: ContextMenu = ContextMenu.init({
name: '区段编辑菜单',
groups: [
{
items: [addWaypointConfig, clearWaypointsConfig],
},
{
items: [splitSectionConfig],
},
],
});
export class SectionPointEditPlugin extends GraphicInteractionPlugin<Section> {
static Name = 'SectionPointDrag';
drawAssistant: SectionDraw;
constructor(app: IGraphicApp, da: SectionDraw) {
super(SectionPointEditPlugin.Name, app);
this.drawAssistant = da;
app.registerMenu(SectionEditMenu);
}
static init(app: IGraphicApp, da: SectionDraw) {
return new SectionPointEditPlugin(app, da);
}
filter(...grahpics: JlGraphic[]): Section[] | undefined {
return grahpics.filter((g) => g.type == Section.Type) as Section[];
}
bind(g: Section): void {
g.lineGraphic.eventMode = 'static';
g.lineGraphic.cursor = 'pointer';
g.lineGraphic.hitArea = new SectionGraphicHitArea(g);
g.transformSave = true;
g.labelGraphic.eventMode = 'static';
g.labelGraphic.cursor = 'pointer';
g.labelGraphic.selectable = true;
g.labelGraphic.draggable = true;
g.on('selected', this.onSelected, this);
g.on('unselected', this.onUnselected, this);
g.on('transformstart', this.onDragMove, this);
// g.on('_rightclick', this.onContextMenu, this);
}
unbind(g: Section): void {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnselected, this);
g.off('transformstart', this.onDragMove, this);
// g.off('_rightclick', this.onContextMenu, this);
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const section = target.getGraphic() as Section;
this.app.updateSelected(section);
if (section.datas.sectionType === SectionType.TurnoutPhysical) {
return;
}
const p = section.screenToLocalPoint(e.global);
addWaypointConfig.handler = () => {
const linePoints = section.linePoints;
const { start, end } = getWaypointRangeIndex(
linePoints,
false,
p,
SectionConsts.lineWidth
);
addWayPoint(section, false, start, end, p);
};
clearWaypointsConfig.handler = () => {
clearWayPoint(section, false);
};
splitSectionConfig.disabled = false;
splitSectionConfig.handler = () => {
Dialog.create({
title: '拆分区段',
message: '请选择生成数量和方向',
component: SectionSplitDialog,
cancel: true,
persistent: true,
}).onOk((data: { num: number; dir: 'ltr' | 'rtl' }) => {
const { num, dir } = data;
const sectionData = section.datas;
const points = section.getSplitPoints(num);
const children: LogicSection[] = [];
let codeAppend = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.slice(0, num);
if (
(dir === 'ltr' &&
sectionData.points[0].x >
sectionData.points[sectionData.points.length - 1].x) ||
(dir === 'rtl' &&
sectionData.points[0].x <
sectionData.points[sectionData.points.length - 1].x)
) {
codeAppend = codeAppend.split('').reverse().join('');
}
points.forEach((ps, i) => {
const data = new LogicSectionData();
const logicSectionDraw =
this.drawAssistant.app.getDrawAssistant<LogicSectionDraw>(
LogicSection.Type
);
data.id = logicSectionDraw.nextId();
data.code = `${sectionData.code}-${codeAppend.charAt(i % 26)}`;
data.points = ps.map(
(p) => new graphicData.Point({ x: p.x, y: p.y })
);
data.id = this.drawAssistant.nextId();
data.childTransforms = [
new ChildTransform(
'label',
new GraphicTransform(
{
x:
data.points[0].x +
(data.points[1].x - data.points[0].x) / 2,
y:
data.points[0].y +
(data.points[1].y - data.points[0].y) / 2 +
20,
},
{ x: 0, y: 0 },
0,
{ x: 0, y: 0 }
)
),
];
const g = logicSectionDraw.graphicTemplate.new();
g.loadData(data);
logicSectionDraw.storeGraphic(g);
children.push(g);
});
// sectionData.children = children.map((g) => g.datas.id);
section.repaint();
section.buildRelation();
section.draggable = false;
children.forEach((c) => c.buildRelation());
this.app.updateSelected(...children);
});
};
SectionEditMenu.open(e.global);
}
onSelected(g: DisplayObject): void {
const section = g as Section;
if (section.datas.sectionType === SectionType.TurnoutPhysical) {
return;
}
if (section.datas.isCurve) {
let lep = section.getAssistantAppend<SectionBazierCurveEditPlugin>(
SectionBazierCurveEditPlugin.Name
);
if (!lep) {
lep = new SectionBazierCurveEditPlugin(section, {
onEditPointCreate,
});
section.addAssistantAppend(lep);
}
lep.showAll();
} else {
let lep = section.getAssistantAppend<SectionPolylineEditPlugin>(
SectionPolylineEditPlugin.Name
);
if (!lep) {
lep = new SectionPolylineEditPlugin(section, { onEditPointCreate });
section.addAssistantAppend(lep);
}
lep.showAll();
}
}
onUnselected(g: DisplayObject): void {
const section = g as Section;
if (section.datas.isCurve) {
const lep = section.getAssistantAppend<SectionBazierCurveEditPlugin>(
SectionBazierCurveEditPlugin.Name
);
if (lep) {
lep.hideAll();
}
} else {
const lep = section.getAssistantAppend<SectionPolylineEditPlugin>(
SectionPolylineEditPlugin.Name
);
if (lep) {
lep.hideAll();
}
}
}
onDragMove(e: GraphicTransformEvent) {
const section = e.target as Section;
this.app.setOptions({
absorbablePositions: buildDragMoveAbsorbablePositions(section),
});
}
}

View File

@ -0,0 +1,51 @@
import { Graphics, IPointData } from 'pixi.js';
import { assertBezierPoints, convertToBezierParams } from 'jl-graphic';
export class SectionGraphic extends Graphics {
static Type = 'SectionGraphic';
private _points: IPointData[] = [];
public get points(): IPointData[] {
return this._points;
}
public set points(value: IPointData[]) {
if (!this.isCurve) {
if (value.length < 2) {
throw Error('Polyline must have at least 2 points');
}
} else {
assertBezierPoints(value);
}
this._points = value;
}
private _segmentsCount = 10;
public get segmentsCount(): number {
return this._segmentsCount;
}
public set segmentsCount(value: number) {
if (value < 1) {
throw Error('segmentsCount must be at least 1');
}
this._segmentsCount = value;
}
isCurve = false;
constructor() {
super();
}
paint() {
if (this.isCurve) {
const bps = convertToBezierParams(this.points);
bps.forEach((bp) => {
this.drawBezierCurve(bp.p1, bp.p2, bp.cp1, bp.cp2, this.segmentsCount);
});
} else {
this.moveTo(this.points[0].x, this.points[0].y);
for (let i = 1; i < this.points.length; i++) {
this.lineTo(this.points[i].x, this.points[i].y);
}
}
}
}

View File

@ -0,0 +1,141 @@
import { Graphics, Point } from 'pixi.js';
import { calculateMirrorPoint, GraphicAnimation, JlGraphic } from 'jl-graphic';
import { ISignalState, SignalColorEnum, signalConsts } from './Signal';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
// export enum LampEnum {
// lampPostColor = '0xFFFFFF',
// redLamp = '0XFF0000',
// greenLamp = '0X00FF00',
// yellowLamp = '0XFFFF00',
// whiteLamp = '0XFFFFFF',
// blueLamp = '0X0033FF',
// }
// const lampConsts = {
// verticalLampPostLength: 16,
// levelLampPostLength: 4,
// postLineWidth: 3,
// lampRadius: 8,
// };
/* const anmiationNameConst = {
signaRedFlash: 'signal_red_flash',
signalGreenFlash: 'signal_green_flash',
signalYellowFlash: 'signal_yellow_flash',
signalWhiteFlash: 'signal_white_flash',
signalBlueFlash: 'signal_blue_flash',
}; */
export class LampMainBody extends JlGraphic {
static Type = 'LampMainBody';
lampNum = 1;
lampPost: Graphics = new Graphics();
lamps: Graphics = new Graphics();
redFlashAnimation: GraphicAnimation | null = null;
mirror = false;
deltaTime = 0;
states: ISignalState | null = null;
constructor() {
super(LampMainBody.Type);
this.addChild(this.lampPost);
this.addChild(this.lamps);
}
paint(
mt: electronicMapGraphicData.Signal.Model,
mirror: boolean,
states: ISignalState
) {
this.lampPost.clear();
this.lamps.clear();
this.mirror = mirror;
this.states = states;
if (
mt === electronicMapGraphicData.Signal.Model.HL ||
mt === electronicMapGraphicData.Signal.Model.AB
) {
this.lampNum = 2;
} else {
this.lampNum = 3;
}
let lpp = new Point(signalConsts.levelLampPostLength, 0);
if (mirror) {
lpp = calculateMirrorPoint(new Point(0, 0), lpp);
}
this.lampPost
.lineStyle(signalConsts.postLineWidth, SignalColorEnum.lampPostColor)
.moveTo(0, -signalConsts.verticalLampPostLength / 2)
.lineTo(0, signalConsts.verticalLampPostLength / 2)
.moveTo(0, 0)
.lineTo(lpp.x, lpp.y);
this.chagneState(this.states);
}
doRepaint() {
// this.paint(this.lampNum, this.mirror, this.states);
}
// stopAnmiation() {
// const redFlashA = this.animation(anmiationNameConst.signaRedFlash);
// const greenFlashA = this.animation(anmiationNameConst.signalGreenFlash);
// const blueFlashA = this.animation(anmiationNameConst.signalBlueFlash);
// const yellowFlashA = this.animation(anmiationNameConst.signalYellowFlash);
// const whiteFlashA = this.animation(anmiationNameConst.signalWhiteFlash);
// if (redFlashA) {
// redFlashA.pause();
// }
// if (greenFlashA) {
// greenFlashA.pause();
// }
// if (blueFlashA) {
// blueFlashA.pause();
// }
// if (yellowFlashA) {
// yellowFlashA.pause();
// }
// if (whiteFlashA) {
// whiteFlashA.pause();
// }
// }
paintLamp(colors: string[]) {
this.lamps.lineStyle(
signalConsts.lampLineWidth,
SignalColorEnum.lampLineColor
);
for (let i = 0; i < this.lampNum; i++) {
const radiusX =
(1 + i * 2) * signalConsts.lampRadius +
signalConsts.levelLampPostLength;
let lrp = new Point(radiusX, 0);
if (this.mirror) {
lrp = calculateMirrorPoint(new Point(0, 0), lrp);
}
const color = colors[i] ? colors[i] : SignalColorEnum.closeLamp;
const alpha = color === SignalColorEnum.closeLamp ? 0 : 1;
this.lamps.beginFill(color, alpha);
this.lamps.drawCircle(lrp.x, lrp.y, signalConsts.lampRadius);
this.lamps.endFill();
}
}
chagneState(states: ISignalState) {
console.log(states); //待之后处理
try {
/* if (states.aspect === state.Signal.Aspect.H) {
this.paintLamp([SignalColorEnum.redLamp]);
} else if (states.aspect === state.Signal.Aspect.L) {
this.paintLamp(['', SignalColorEnum.greenLamp]);
} else if (states.aspect === state.Signal.Aspect.U) {
this.paintLamp(['', '', SignalColorEnum.yellowLamp]);
} else if (states.aspect === state.Signal.Aspect.HU) {
this.paintLamp([SignalColorEnum.redLamp, '', SignalColorEnum.yellowLamp]);
} else if (states.aspect === state.Signal.Aspect.A) {
this.paintLamp([SignalColorEnum.blueLamp]);
} else if (states.aspect === state.Signal.Aspect.B) {
this.paintLamp([SignalColorEnum.whiteLamp]);
} else if (states.aspect === state.Signal.Aspect.OFF) {
this.paintLamp([]);
} */
} catch (error) {
console.error('信号机状态处理异常!', error);
}
}
}

View File

@ -0,0 +1,311 @@
import { Graphics, Point } from 'pixi.js';
import {
calculateDistanceFromPointToLine,
calculateFootPointFromPointToLine,
GraphicData,
GraphicRelationParam,
GraphicState,
isPointOnLine,
JlGraphic,
JlGraphicTemplate,
} from 'jl-graphic';
import { calculateMirrorPoint } from 'jl-graphic';
import { LampMainBody } from './LampMainBody';
import {
drawArrow,
IRelatedRefData,
createRelatedRefProto,
} from '../CommonGraphics';
import { SignalCode } from './SignalCode';
import { Section, DevicePort, SectionType } from '../section/Section';
import { Turnout } from '../turnout/Turnout';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
export enum Direction {
LEFT = 0,
RIGHT = 1,
}
export interface KilometerSystem {
get coordinateSystem(): string;
set coordinateSystem(v: string);
get kilometer(): number;
set kilometer(v: number);
get direction(): Direction;
set direction(v: Direction);
}
export interface ISignalData extends GraphicData {
get code(): string; // 编号
set code(v: string);
get mirror(): boolean;
set mirror(v: boolean);
get kilometerSystem(): KilometerSystem;
set kilometerSystem(v: KilometerSystem);
get refDev(): IRelatedRefData;
set refDev(v: IRelatedRefData);
get centralizedStations(): number[];
set centralizedStations(v: number[]);
get mt(): electronicMapGraphicData.Signal.Model;
set mt(v: electronicMapGraphicData.Signal.Model);
get direction(): electronicMapGraphicData.Direction;
set direction(v: electronicMapGraphicData.Direction);
clone(): ISignalData;
copyFrom(data: ISignalData): void;
eq(other: ISignalData): boolean;
}
export interface ISignalState extends GraphicState {
id?: string;
get aspect(): number;
set aspect(v: number);
}
export enum SignalColorEnum {
humanControlColor = '0xffff00',
fleetModeColor = '0x00ff00',
blockedColor = '0XFF0000',
defaultCodeColor = '0XFFFFFF',
lampPostColor = '0xFFFFFF',
//redLamp = '0XFF0000',
greenLamp = '0X00FF00',
yellowLamp = '0XFFFF00',
//whiteLamp = '0XFFFFFF',
blueLamp = '0X0033FF',
closeLamp = '0X000000',
logicModeColor = '0x000000',
lampLineColor = '0x3149c3',
}
export const signalConsts = {
fleetModeLength: 24,
fleetModeRadius: 8,
fleetModeLineWidth: 6,
humanControlRadius: 8,
codeOffset: 20,
codeFontSize: 11,
blockedLineWidth: 1,
verticalLampPostLength: 16,
levelLampPostLength: 4,
postLineWidth: 3,
lampRadius: 8,
logicModeLineWidth: 2,
logicModeDistance: 5,
lampLineWidth: 1,
};
export class Signal extends JlGraphic {
static Type = 'signal';
signalCode: SignalCode = new SignalCode();
humanControl: Graphics = new Graphics();
fleetMode: Graphics = new Graphics();
lampMainBody: LampMainBody = new LampMainBody();
blockedMode: Graphics = new Graphics();
constructor() {
super(Signal.Type);
this.addChild(this.humanControl);
this.addChild(this.fleetMode);
this.addChild(this.lampMainBody);
this.addChild(this.signalCode);
}
get code(): string {
return this.datas.code;
}
get datas(): ISignalData {
return this.getDatas<ISignalData>();
}
get states(): ISignalState {
return this.getStates<ISignalState>();
}
get mirror(): boolean {
return this.datas.mirror;
}
set mirror(v: boolean) {
const old = this.datas.clone();
old.mirror = v;
this.updateData(old);
}
paint(): void {
const mirror = this.datas.mirror;
this.lampMainBody.paint(this.datas.mt, mirror, this.states);
this.signalCode.paint(this.datas);
const codeTransform = this.datas?.childTransforms?.find(
(item) => item.name === 'signalCode'
);
if (codeTransform) {
const position = codeTransform?.transform.position;
const rotation = codeTransform?.transform?.rotation;
this.signalCode.position.set(position?.x, position?.y);
this.signalCode.rotation = rotation || 0;
} else {
this.signalCode.position.set(0, signalConsts.codeOffset);
}
}
doRepaint(): void {
this.paint();
this.fleetMode.clear();
// if (this.states.fleetMode) {
// this.createFleetMode();
// }
// this.humanControl.clear();
// if (this.states.autoRouteDisable) {
// this.createHumanControl();
// }
}
createFleetMode(): void {
const mirror = this.datas.mirror;
this.fleetMode.beginFill(SignalColorEnum.fleetModeColor, 1);
let lmp = new Point(
this.lampMainBody.width + signalConsts.fleetModeLength,
0
);
if (mirror) {
lmp = calculateMirrorPoint(new Point(0, 0), lmp);
}
drawArrow(
this.fleetMode,
lmp.x,
0,
signalConsts.fleetModeLength,
signalConsts.fleetModeRadius,
signalConsts.fleetModeLineWidth,
mirror
);
this.fleetMode.endFill();
}
createHumanControl(): void {
const mirror = this.datas.mirror;
this.humanControl.beginFill(SignalColorEnum.humanControlColor, 1);
if (this.humanControl.drawRegularPolygon) {
let hmp = new Point(-signalConsts.humanControlRadius, 0);
if (mirror) {
hmp = calculateMirrorPoint(new Point(0, 0), hmp);
}
this.humanControl.drawRegularPolygon(
hmp.x,
hmp.y,
signalConsts.humanControlRadius,
3,
(Math.PI / 2) * (mirror ? -1 : 1)
);
}
this.humanControl.endFill();
}
buildRelation() {
const sections = this.queryStore
.queryByType<Section>(Section.Type)
.filter((s) => s.datas.sectionType === SectionType.Physical);
const turnouts = this.queryStore.queryByType<Turnout>(Turnout.Type);
let deviceId = 0;
let deviceType = '';
let minD = Number.MAX_SAFE_INTEGER;
let port: DevicePort = DevicePort.A;
sections.forEach((sec: Section) => {
const verticesList = sec.getVerticesList();
for (let i = 0; i < verticesList.length - 1; i++) {
const d = calculateDistanceFromPointToLine(
sec.localToCanvasPoint(verticesList[i]),
sec.localToCanvasPoint(verticesList[i + 1]),
this.position
);
const p = calculateFootPointFromPointToLine(
sec.localToCanvasPoint(verticesList[i]),
sec.localToCanvasPoint(verticesList[i + 1]),
this.position
);
const onLine = isPointOnLine(
sec.localToCanvasPoint(verticesList[i]),
sec.localToCanvasPoint(verticesList[i + 1]),
p
);
if (onLine && d < minD) {
minD = d;
deviceId = sec.id;
deviceType = sec.type;
port = DevicePort.A;
}
}
});
turnouts.forEach((turnout: Turnout) => {
for (let i = 0; i < turnout.datas.pointA.length; i++) {
const p1 = turnout.localToCanvasPoint(
i === 0 ? new Point(0, 0) : turnout.datas.pointA[i - 1]
);
const p2 = turnout.localToCanvasPoint(turnout.datas.pointA[i]);
const d = calculateDistanceFromPointToLine(p1, p2, this.position);
const p = calculateFootPointFromPointToLine(p1, p2, this.position);
const onLine = isPointOnLine(p1, p2, p);
if (onLine && d < minD) {
minD = d;
deviceId = turnout.id;
deviceType = turnout.type;
port = DevicePort.A;
}
}
for (let i = 0; i < turnout.datas.pointB.length; i++) {
const p1 = turnout.localToCanvasPoint(
i === 0 ? new Point(0, 0) : turnout.datas.pointB[i - 1]
);
const p2 = turnout.localToCanvasPoint(turnout.datas.pointB[i]);
const d = calculateDistanceFromPointToLine(p1, p2, this.position);
const p = calculateFootPointFromPointToLine(p1, p2, this.position);
const onLine = isPointOnLine(p1, p2, p);
if (onLine && d < minD) {
minD = d;
deviceId = turnout.id;
deviceType = turnout.type;
port = DevicePort.B;
}
}
for (let i = 0; i < turnout.datas.pointC.length; i++) {
const p1 = turnout.localToCanvasPoint(
i === 0 ? new Point(0, 0) : turnout.datas.pointC[i - 1]
);
const p2 = turnout.localToCanvasPoint(turnout.datas.pointC[i]);
const d = calculateDistanceFromPointToLine(p1, p2, this.position);
const p = calculateFootPointFromPointToLine(p1, p2, this.position);
const onLine = isPointOnLine(p1, p2, p);
if (onLine && d < minD) {
minD = d;
deviceId = turnout.id;
deviceType = turnout.type;
port = DevicePort.C;
}
}
});
if (deviceId) {
this.datas.refDev = createRelatedRefProto(deviceType, deviceId, port);
}
}
loadRelations() {
if (this.datas.refDev) {
this.relationManage.addRelation(
new GraphicRelationParam(this, ''),
new GraphicRelationParam(
this.queryStore.queryById(this.datas.refDev.id),
this.datas.refDev.devicePort
)
);
}
}
}
export class SignalTemplate extends JlGraphicTemplate<Signal> {
constructor(dataTemplate: ISignalData, stateTemplate: ISignalState) {
super(Signal.Type, { dataTemplate, stateTemplate });
}
new(): Signal {
const g = new Signal();
g.loadData(this.datas);
g.loadState(this.states);
return g;
}
}

View File

@ -0,0 +1,44 @@
import { Container, Graphics, Point } from 'pixi.js';
import { VectorText } from 'jl-graphic';
import {
ISignalData,
SignalColorEnum,
signalConsts,
} from './Signal';
export class SignalCode extends Container {
blockedMode: Graphics = new Graphics();
codeGraph: VectorText = new VectorText('');
name = 'signalCode';
constructor() {
super();
this.addChild(this.blockedMode);
this.addChild(this.codeGraph);
}
paint(datas: ISignalData) {
this.codeGraph.text = datas?.code || '信号机编号';
this.codeGraph.style.fill = SignalColorEnum.defaultCodeColor;
this.codeGraph.setVectorFontSize(signalConsts.codeFontSize);
this.codeGraph.anchor.set(0.5);
this.codeGraph.position.set(0, 0);
this.blockedMode.clear();
// if (states.blocked) {
// this.createBlockedMode();
// }
}
createBlockedMode() {
const codeRect = this.codeGraph.getBounds();
const rectP = this.screenToLocalPoint(new Point(codeRect.x, codeRect.y));
this.blockedMode.clear();
this.blockedMode.lineStyle(
signalConsts.blockedLineWidth,
SignalColorEnum.blockedColor
);
this.blockedMode.drawRect(
rectP.x,
rectP.y,
codeRect.width,
codeRect.height
);
}
}

View File

@ -0,0 +1,181 @@
import { DisplayObject, FederatedPointerEvent, IHitArea, Point } from 'pixi.js';
import {
AbsorbableLine,
AbsorbablePosition,
GraphicDrawAssistant,
GraphicInteractionPlugin,
GraphicTransformEvent,
IDrawApp,
JlGraphic,
} from 'jl-graphic';
import { ISignalData, Signal, SignalTemplate } from './Signal';
export interface ISignalDrawOptions {
newData: () => ISignalData;
}
export class SignalDraw extends GraphicDrawAssistant<
SignalTemplate,
ISignalData
> {
_signal: Signal | null = null;
constructor(app: IDrawApp, template: SignalTemplate) {
super(
app,
template,
'svguse: ../../drawIcon.svg#icon-signal',
'信号机Signal'
);
SignalInteraction.init(app);
}
public get signal(): Signal {
if (!this._signal) {
this._signal = this.graphicTemplate.new();
this._signal.loadData(this.graphicTemplate.datas);
this.container.addChild(this._signal);
}
return this._signal;
}
onLeftUp(e: FederatedPointerEvent): void {
this.container.position.copyFrom(this.toCanvasCoordinates(e.global));
this.createAndStore(true);
}
redraw(p: Point): void {
this.signal.paint();
this.container.position.set(p.x, p.y);
}
prepareData(data: ISignalData): boolean {
data.transform = this.container.saveTransform();
return true;
}
}
export class SignalGraphicHitArea implements IHitArea {
signal: Signal;
constructor(signal: Signal) {
this.signal = signal;
}
contains(x: number, y: number): boolean {
const bound = this.signal.getLocalBounds();
const maxX = bound.x + bound.width;
const minX = bound.x;
const maxY = bound.y + bound.height;
const minY = bound.y;
return maxX >= x && x >= minX && maxY >= y && y >= minY;
}
}
/**
* 线
* @param signal
*/
function buildAbsorbablePositions(signal: Signal): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const signals = signal.queryStore.queryByType<Signal>(Signal.Type);
const canvas = signal.getCanvas();
signals.forEach((item) => {
if (item.id === signal.id) {
return;
}
const ala = new AbsorbableLine(
new Point(item.x, 0),
new Point(item.x, canvas.height)
);
const alb = new AbsorbableLine(
new Point(0, item.y),
new Point(canvas.width, item.y)
);
aps.push(ala);
aps.push(alb);
});
return aps;
}
/**
* 线
* @param signal
*/
function buildCodeAbsorbablePositions(signal: Signal): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const signals = signal.queryStore.queryByType<Signal>(Signal.Type);
const canvas = signal.getCanvas();
signals.forEach((item) => {
if (item.id === signal.id) {
return;
}
const codePoint = item.signalCode.getPositionOnCanvas();
const ala = new AbsorbableLine(
new Point(codePoint.x, 0),
new Point(codePoint.x, canvas.height)
);
const alb = new AbsorbableLine(
new Point(0, codePoint.y),
new Point(canvas.width, codePoint.y)
);
aps.push(ala);
aps.push(alb);
});
return aps;
}
export class SignalInteraction extends GraphicInteractionPlugin<Signal> {
static Name = 'signal_transform';
constructor(app: IDrawApp) {
super(SignalInteraction.Name, app);
}
static init(app: IDrawApp) {
return new SignalInteraction(app);
}
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
return grahpics
.filter((g) => g.type === Signal.Type)
.map((g) => g as Signal);
}
bind(g: Signal): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.scalable = true;
g.rotatable = true;
g.lampMainBody.hitArea = new SignalGraphicHitArea(g);
g.on('transformstart', this.transformstart, this);
g.signalCode.on('transformstart', this.codetransformstart, this);
g.signalCode.draggable = true;
g.signalCode.selectable = true;
g.signalCode.rotatable = true;
g.signalCode.transformSave = true;
g.signalCode.eventMode = 'static';
}
unbind(g: Signal): void {
g.eventMode = 'none';
g.scalable = false;
g.rotatable = false;
g.off('transformstart', this.transformstart, this);
g.signalCode.off('transformstart', this.codetransformstart, this);
g.signalCode.draggable = false;
g.signalCode.selectable = false;
g.signalCode.rotatable = false;
g.signalCode.transformSave = false;
g.signalCode.eventMode = 'none';
}
transformstart(e: GraphicTransformEvent) {
const target = e.target as DisplayObject;
const signal = target.getGraphic() as Signal;
signal.getGraphicApp().setOptions({
absorbablePositions: buildAbsorbablePositions(signal),
});
}
codetransformstart(e: GraphicTransformEvent) {
const target = e.target as DisplayObject;
const signal = target.getGraphic() as Signal;
signal.getGraphicApp().setOptions({
absorbablePositions: buildCodeAbsorbablePositions(signal),
});
}
}

View File

@ -0,0 +1,111 @@
import {
GraphicData,
GraphicState,
JlGraphic,
JlGraphicTemplate,
VectorText,
} from 'jl-graphic';
import { KilometerSystem } from '../signal/Signal';
import { Platform } from '../platform/Platform';
export interface IStationData extends GraphicData {
get code(): string; // 车站站名
set code(v: string);
get stationName(): string; // 车站名
set stationName(v: string);
get stationNameAcronym(): string; // 车站名拼音简写
set stationNameAcronym(v: string);
get kilometerSystem(): KilometerSystem;
set kilometerSystem(v: KilometerSystem);
get concentrationStations(): boolean; //是否集中站
set concentrationStations(v: boolean);
get depots(): boolean; //是否车辆段
set depots(v: boolean);
get manageStations(): number[]; //集中站管理的车站
set manageStations(v: number[]);
clone(): IStationData;
copyFrom(data: IStationData): void;
eq(other: IStationData): boolean;
}
export interface IStationState extends GraphicState {
id: number;
}
const stationConsts = {
codeColor: '0xF48815',
codeFontSize: 22,
kilometerCodeColor: '0xFFFFFF',
kilometerCodeFontSize: 8,
kilometerCodeOffsetY: -25,
};
export class Station extends JlGraphic {
static Type = 'station';
codeGraph: VectorText = new VectorText(''); //车站站名
kilometerGraph: VectorText = new VectorText(''); //公里标
constructor() {
super(Station.Type);
this.addChild(this.codeGraph);
this.addChild(this.kilometerGraph);
}
get code(): string {
return this.datas.code;
}
get datas(): IStationData {
return this.getDatas<IStationData>();
}
get states(): IStationState {
return this.getStates<IStationState>();
}
doRepaint(): void {
const codeGraph = this.codeGraph;
const kilometerGraph = this.kilometerGraph;
codeGraph.text = this.datas?.code || '车站Station';
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);
}
getPlatforms(): Platform[] {
const relations = this.relationManage.getRelationsOfGraphicAndOtherType(
this,
Platform.Type
);
return relations.map((r) => r.getOtherGraphic(this));
}
}
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

@ -0,0 +1,102 @@
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();
}
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();
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.on('selected', this.onSelected, this);
}
unbind(g: Station): void {
g.eventMode = 'none';
g.scalable = false;
g.rotatable = 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

@ -0,0 +1,74 @@
import { Color, FederatedPointerEvent, Graphics, Point } from 'pixi.js';
import {
GraphicData,
GraphicDrawAssistant,
IDrawApp,
JlGraphic,
JlGraphicTemplate,
} from 'jl-graphic';
import { AxleCounting } from '../axleCounting/AxleCounting';
import { AxleCountingDraw } from '../axleCounting/AxleCountingDrawAssistant';
import { useDrawStore } from 'src/stores/electronicMap-draw-store';
interface IOneClickData extends GraphicData {
get code(): string; // 编号
}
export class OneClickGenerate extends JlGraphic {
static Type = 'OneClickGenerate';
lineGraphic: Graphics = new Graphics();
constructor() {
super(OneClickGenerate.Type);
this.addChild(this.lineGraphic);
}
doRepaint(): void {
this.lineGraphic.clear();
this.lineGraphic.lineStyle(1, new Color('0xff0000'));
this.lineGraphic.moveTo(-1920, 0);
this.lineGraphic.lineTo(1920, 0);
}
}
export class OneClickGenerateTemplate extends JlGraphicTemplate<OneClickGenerate> {
constructor() {
super(OneClickGenerate.Type, {});
}
new(): OneClickGenerate {
return new OneClickGenerate();
}
}
export class OneClickGenerateDraw extends GraphicDrawAssistant<
OneClickGenerateTemplate,
IOneClickData
> {
lineGraph: OneClickGenerate;
constructor(app: IDrawApp, template: OneClickGenerateTemplate) {
super(app, template, 'sym_o_square', '不展示');
this.lineGraph = this.graphicTemplate.new();
this.container.addChild(this.lineGraph);
}
bind(): void {
super.bind();
this.lineGraph.doRepaint();
}
onLeftDown(e: FederatedPointerEvent): void {
const type = useDrawStore().oneClickType;
if (type == 'AxleCounting') {
const axleCountingDraw = this.app.getDrawAssistant(
AxleCounting.Type
) as AxleCountingDraw;
axleCountingDraw.oneGenerates(this.toCanvasCoordinates(e.global));
}
this.finish();
}
redraw(p: Point): void {
this.container.position.copyFrom(p);
}
prepareData(): boolean {
return true;
}
}

View File

@ -0,0 +1,463 @@
import { Graphics, IPointData } from 'pixi.js';
import {
GraphicAnimation,
GraphicData,
GraphicRelationParam,
GraphicState,
JlGraphic,
JlGraphicTemplate,
VectorText,
angleOfIncludedAngle,
distance2,
} from 'jl-graphic';
import { Section, DevicePort, SectionType } from '../section/Section';
import {
IRelatedRefData,
createRelatedRefProto,
protoPort2Data,
} from '../CommonGraphics';
import { KilometerSystem } from '../signal/Signal';
import { electronicMapGraphicData } from 'src/protos/electronicMap_graphic_data';
const tolerance = 0.01;
export interface ITurnoutData extends GraphicData {
get code(): string;
set code(code: string);
get pointA(): IPointData[]; //A端点列表(从岔心向外)
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 paTrackSectionId(): number;
set paTrackSectionId(v: number);
get pbTrackSectionId(): number;
set pbTrackSectionId(v: number);
get pcTrackSectionId(): number;
set pcTrackSectionId(v: number);
get switchMachineType(): electronicMapGraphicData.Turnout.SwitchMachineType;
set switchMachineType(v: electronicMapGraphicData.Turnout.SwitchMachineType);
get centralizedStations(): number[];
set centralizedStations(v: number[]);
clone(): ITurnoutData;
copyFrom(data: ITurnoutData): void;
eq(other: ITurnoutData): boolean;
}
export interface ITurnoutState extends GraphicState {
id?: number;
normal?: boolean;
reverse?: boolean;
dw?: boolean;
fw?: boolean;
qdc?: boolean;
qfc?: boolean;
qyc?: boolean;
dc?: boolean;
fc?: boolean;
yc?: boolean;
occupied?: boolean;
}
export const TurnoutConsts = {
lineColor: '#5578b6',
occupiedColor: '#f00',
lineWidth: 5,
forkLenth: 20,
labelFontSize: 12,
normalLabelColor: '#0f0',
reverseLabelColor: '#ff0',
};
export enum TurnoutPosition {
NORMAL = 0,
REVERSE = 1,
}
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: DevicePort;
constructor(turnout: Turnout, port: DevicePort) {
super();
this.turnout = turnout;
this.port = port;
}
paint() {
let pList: IPointData[] = [];
switch (this.port) {
case DevicePort.A:
pList = this.turnout.datas.pointA;
break;
case DevicePort.B:
pList = this.turnout.datas.pointB;
break;
case DevicePort.C:
pList = this.turnout.datas.pointC;
break;
}
const gap = this.port === DevicePort.A ? 0 : TurnoutConsts.forkLenth;
let color = TurnoutConsts.lineColor;
if (this.turnout.states.occupied) {
if (
this.port === DevicePort.A ||
(this.turnout.states.dw && this.port === DevicePort.B) ||
(this.turnout.states.fw && this.port === DevicePort.C)
) {
color = TurnoutConsts.occupiedColor;
}
}
const start = getForkPoint(gap, pList[0]);
this.clear()
.lineStyle(TurnoutConsts.lineWidth, color)
.moveTo(start.x, start.y);
pList.forEach((p) => {
const { x, y } = p;
this.lineTo(x, y);
});
}
}
class ForkGraphic extends Graphics {
turnout: Turnout;
constructor(turnout: Turnout) {
super();
this.turnout = turnout;
}
paint(p: IPointData) {
const target = getForkPoint(TurnoutConsts.forkLenth, p);
const color = this.turnout.states.occupied
? TurnoutConsts.occupiedColor
: TurnoutConsts.lineColor;
this.clear()
.lineStyle(TurnoutConsts.lineWidth, color)
.moveTo(0, 0)
.lineTo(target.x, target.y);
}
}
export class Turnout extends JlGraphic {
static Type = 'Turnout';
graphics: {
fork: ForkGraphic;
sections: [TurnoutSection, TurnoutSection, TurnoutSection];
label: VectorText;
};
deltaTime: number;
constructor() {
super(Turnout.Type);
this.name = 'turnout';
this.graphics = {
fork: new ForkGraphic(this),
sections: [
new TurnoutSection(this, DevicePort.A),
new TurnoutSection(this, DevicePort.B),
new TurnoutSection(this, DevicePort.C),
],
label: new VectorText(),
};
this.deltaTime = 0;
this.addChild(this.graphics.fork);
// this.addChild(...this.graphics.sections);
this.addChild(this.graphics.sections[0]);
this.addChild(this.graphics.sections[1]);
this.addChild(this.graphics.sections[2]);
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);
}
get code(): string {
return this.datas.code;
}
get datas(): ITurnoutData {
return this.getDatas<ITurnoutData>();
}
get states(): ITurnoutState {
return this.getStates<ITurnoutState>();
}
getPortPoints() {
return [this.datas.pointA, this.datas.pointB, this.datas.pointC];
}
doRepaint(): void {
const { pointB, pointC } = this.datas;
if (this.states.dw) {
this.graphics.fork.paint(pointB[0]);
this.graphics.label.style.stroke = TurnoutConsts.normalLabelColor;
} else if (this.states.fw) {
this.graphics.fork.paint(pointC[0]);
this.graphics.label.style.stroke = TurnoutConsts.reverseLabelColor;
}
this.graphics.label.text = this.datas.code;
this.graphics.sections.forEach((sectionGraphic) => sectionGraphic.paint());
if (!this.states.dw && !this.states.fw) {
// 失表
this.graphics.fork.visible = false;
} else {
this.graphics.fork.visible = true;
}
}
initTurnoutSplit() {
// 道岔失表
const name = `${this.datas.id}_turnout_split`;
let turnoutSplit = this.animation(name);
if (!turnoutSplit) {
turnoutSplit = GraphicAnimation.init({
name: name,
run: (dt: number) => {
this.deltaTime += dt;
this.deltaTime = this.deltaTime % 60;
this.graphics.fork.visible = this.deltaTime > 30;
},
});
this.addAnimation(turnoutSplit);
}
turnoutSplit.resume();
}
stopTurnoutSplit() {
const name = `${this.datas.id}_turnout_split`;
const turnoutSplit = this.animation(name);
if (turnoutSplit) {
turnoutSplit.pause();
this.deltaTime = 0;
}
}
getConnectElement(port: DevicePort) {
const relation = this.relationManage
.getRelationsOfGraphic(this)
.find(
(relation) =>
relation.getRelationParam(this).getParam<DevicePort>() === port &&
(relation.getOtherGraphic(this) instanceof Section ||
relation.getOtherGraphic(this) instanceof Turnout)
);
if (!relation) {
return;
}
return {
g: relation?.getOtherGraphic(this) as Section | Turnout,
port: relation?.getOtherRelationParam(this).getParam<DevicePort>(),
};
}
buildRelation(): void {
this.relationManage.deleteRelationOfGraphic(this);
/** 道岔和区段 */
this.queryStore.queryByType<Section>(Section.Type).forEach((section) => {
if (section.datas.sectionType !== SectionType.Physical) return;
this.getPortPoints().forEach((port, i) => {
if (
distance2(
section.localToCanvasPoint(section.getStartPoint()),
this.localToCanvasPoint(port[port.length - 1])
) <= tolerance
) {
this.relationManage.addRelation(
new GraphicRelationParam(
this,
[DevicePort.A, DevicePort.B, DevicePort.C][i]
),
new GraphicRelationParam(section, DevicePort.A)
);
}
if (
distance2(
section.localToCanvasPoint(section.getEndPoint()),
this.localToCanvasPoint(port[port.length - 1])
) <= tolerance
) {
this.relationManage.addRelation(
new GraphicRelationParam(
this,
[DevicePort.A, DevicePort.B, DevicePort.C][i]
),
new GraphicRelationParam(section, DevicePort.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])
) <= tolerance
) {
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,
[DevicePort.A, DevicePort.B, DevicePort.C][i]
),
new GraphicRelationParam(
turnout,
[DevicePort.A, DevicePort.B, DevicePort.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 === DevicePort.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 === DevicePort.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 === DevicePort.C
);
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, DevicePort.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, DevicePort.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, DevicePort.C),
new GraphicRelationParam(
this.queryStore.queryById(this.datas.pcRef.id),
protoPort2Data(this.datas.pcRef.devicePort)
)
);
}
}
getGraphicOfPort(port: DevicePort) {
return this.relationManage
.getRelationsOfGraphic(this)
.filter(
(relation) =>
relation.getRelationParam(this).getParam<DevicePort>() === port
)
.map((relation) => {
return relation.getOtherGraphic(this);
});
}
}
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

@ -0,0 +1,567 @@
import {
AbsorbablePosition,
DraggablePoint,
IGraphicApp,
GraphicDrawAssistant,
GraphicInteractionPlugin,
GraphicTransformEvent,
IDrawApp,
JlGraphic,
VectorText,
linePoint,
polylinePoint,
AppConsts,
GraphicEditPlugin,
getWaypointRangeIndex,
ContextMenu,
MenuItemOptions,
AbsorbablePoint,
AbsorbableLine,
distance,
} from 'jl-graphic';
import {
ITurnoutData,
Turnout,
TurnoutConsts,
TurnoutSection,
TurnoutTemplate,
getForkPoint,
} from './Turnout';
import {
DisplayObject,
FederatedMouseEvent,
IHitArea,
IPointData,
Point,
} from 'pixi.js';
import { DevicePort, Section } from '../section/Section';
export class TurnoutDraw extends GraphicDrawAssistant<
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 DevicePort.A:
points = this.section.turnout.datas.pointA;
start = { x: 0, y: 0 };
break;
case DevicePort.B:
points = this.section.turnout.datas.pointB;
start = getForkPoint(TurnoutConsts.forkLenth, points[0]);
break;
case DevicePort.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;
}
type dragType = Turnout | Section;
class DragMoveAbsorbablePoint extends AbsorbablePoint {
moveTarget:
| {
position: IPointData;
portPos: IPointData[];
}
| undefined;
constructor(point: IPointData, absorbRange = 15) {
super(point, absorbRange);
}
tryAbsorb(...dragTargets: dragType[]): void {
const dragTarget = dragTargets[0];
if (dragTarget instanceof Turnout) {
if (this.moveTarget == undefined) {
const {
pointA: [A],
pointB: [B],
pointC: [C],
} = dragTarget.datas;
this.moveTarget = {
position: dragTarget
.getGraphicApp()
.toCanvasCoordinates(dragTarget.getGlobalPosition()),
portPos: [
dragTarget.localToCanvasPoint(A),
dragTarget.localToCanvasPoint(B),
dragTarget.localToCanvasPoint(C),
],
};
}
const {
pointA: [A],
pointB: [B],
pointC: [C],
} = dragTarget.datas;
[A, B, C].forEach((p, i) => {
const changePos = dragTarget.localToCanvasPoint(p);
if (
distance(this._point.x, this._point.y, changePos.x, changePos.y) <
this.absorbRange &&
this.moveTarget
) {
dragTarget.updatePositionByCanvasPosition(
new Point(
this.moveTarget.position.x +
this._point.x -
this.moveTarget.portPos[i].x,
this.moveTarget.position.y +
this._point.y -
this.moveTarget.portPos[i].y
)
);
}
});
} else {
if (this.moveTarget == undefined) {
this.moveTarget = {
position: dragTarget
.getGraphicApp()
.toCanvasCoordinates(dragTarget.getGlobalPosition()),
portPos: [
dragTarget.localToCanvasPoint(dragTarget.getStartPoint()),
dragTarget.localToCanvasPoint(dragTarget.getEndPoint()),
],
};
}
dragTarget
.localToCanvasPoints(...dragTarget.datas.points)
.forEach((p, i) => {
if (
distance(this._point.x, this._point.y, p.x, p.y) <
this.absorbRange &&
this.moveTarget
) {
dragTarget.updatePositionByCanvasPosition(
new Point(
this.moveTarget.position.x +
this._point.x -
this.moveTarget.portPos[i].x,
this.moveTarget.position.y +
this._point.y -
this.moveTarget.portPos[i].y
)
);
}
});
}
}
}
export function buildDragMoveAbsorbablePositions(
target: dragType
): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const sections = target.queryStore.queryByType<Section>(Section.Type);
sections.forEach((section) => {
if (section.id !== target.id) {
section.localToCanvasPoints(...section.datas.points).forEach((p) => {
aps.push(new DragMoveAbsorbablePoint(p)); //区段端点
});
}
});
const turnouts = target.queryStore.queryByType<Turnout>(Turnout.Type);
turnouts.forEach((otherTurnout) => {
if (otherTurnout.id !== target.id) {
const {
pointA: [A],
pointB: [B],
pointC: [C],
} = otherTurnout.datas;
[A, B, C].forEach((p) => {
aps.push(
new DragMoveAbsorbablePoint(otherTurnout.localToCanvasPoint(p)) //道岔端点
);
});
}
});
return aps;
}
function onEditPointCreate(turnout: 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) {
const p = section.turnout.screenToLocalPoint(e.global);
addPointConfig.handler = () => {
if (section.port === DevicePort.A) {
const { start } = getWaypointRangeIndex(
[{ x: 0, y: 0 }, ...section.turnout.datas.pointA],
false,
p,
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 === DevicePort.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 === DevicePort.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 (section.port === DevicePort.A)
section.turnout.datas.pointA = [
section.turnout.datas.pointA[section.turnout.datas.pointA.length - 1],
];
if (section.port === DevicePort.B)
section.turnout.datas.pointB = [
section.turnout.datas.pointB[section.turnout.datas.pointB.length - 1],
];
if (section.port === DevicePort.C)
section.turnout.datas.pointC = [
section.turnout.datas.pointC[section.turnout.datas.pointC.length - 1],
];
const tep = section.turnout.getAssistantAppend<TurnoutEditPlugin>(
TurnoutEditPlugin.Name
);
if (tep) {
tep.reset();
}
section.turnout.repaint();
};
turnoutSectionEditMenu.open(e.global);
}
bind(g: 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);
g.on('transformstart', this.onDragMove, 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');
});
g.off('transformstart', this.onDragMove, this);
}
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.editPoints = [[], [], []];
tep.removeChildren();
tep.initEditPoints();
//tep.reset();
tep.showAll();
}
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[];
}
onDragMove(e: GraphicTransformEvent) {
const turnout = e.target as Turnout;
this.app.setOptions({
absorbablePositions: buildDragMoveAbsorbablePositions(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();
}
initEditPoints() {
const cpA = this.graphic.localToCanvasPoints(...this.graphic.datas.pointA);
const cpB = this.graphic.localToCanvasPoints(...this.graphic.datas.pointB);
const cpC = this.graphic.localToCanvasPoints(...this.graphic.datas.pointC);
const cpMap: Map<Point[], IPointData[]> = new Map([
[cpA, this.graphic.datas.pointA],
[cpB, this.graphic.datas.pointB],
[cpC, this.graphic.datas.pointC],
]);
Array.from(cpMap.entries()).forEach(([cpDatas, dataPoints], i) => {
cpDatas.forEach((cpData, j) => {
const dp = new DraggablePoint(cpData);
dp.on('transforming', () => {
const localPoint = this.graphic.canvasToLocalPoint(dp.position);
dataPoints[j].x = localPoint.x;
dataPoints[j].y = localPoint.y;
this.graphic.repaint();
});
if (this.options.onEditPointCreate) {
this.options.onEditPointCreate(this.graphic, dp);
}
this.editPoints[i].push(dp);
});
});
this.editPoints.forEach((cps) => {
this.addChild(...cps);
});
this.labels = ['A', 'B', 'C'].map((str) => {
const vc = new VectorText(str, { fill: AppConsts.assistantElementColor });
vc.setVectorFontSize(14);
vc.anchor.set(0.5);
return vc;
});
this.addChild(...this.labels);
}
destoryEditPoints() {
this.editPoints.forEach((dps) => {
dps.forEach((dp) => {
dp.off('transforming');
dp.destroy();
this.removeChild(dp);
});
});
this.editPoints = [[], [], []];
}
updateEditedPointsPosition() {
const cpA = this.graphic.localToCanvasPoints(...this.graphic.datas.pointA);
const cpB = this.graphic.localToCanvasPoints(...this.graphic.datas.pointB);
const cpC = this.graphic.localToCanvasPoints(...this.graphic.datas.pointC);
[cpA, cpB, cpC].forEach((cps, i) => {
cps.forEach((cp, j) => {
this.editPoints[i][j].position.copyFrom(cp);
if (j === cps.length - 1) {
this.labels[i].position.copyFrom({ x: cp.x, y: cp.y + 12 });
}
});
});
}
}

View File

@ -0,0 +1,311 @@
<template>
<q-layout view="hHh LpR fFf">
<q-header reveal class="bg-primary text-white">
<q-toolbar>
<q-toolbar-title class="q-gutter-sm">
<q-btn color="accent" label="功能菜单">
<q-menu>
<q-list style="min-width: 100px">
<q-item
v-for="item in leftMenuConfig"
:key="item.label"
clickable
v-close-popup
@click="item.click"
>
<q-item-section>{{ item.label }}</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
<q-btn-toggle
v-model="selectUtil"
color="brown"
text-color="white"
toggle-color="orange"
toggle-text-color="black"
:options="utilsOption"
@update:model-value="drawSelect"
>
<template
v-for="(ctl, idx) in utilsOption"
:key="idx"
v-slot:[ctl.value]
>
<q-tooltip>{{ ctl.tip }}</q-tooltip>
</template>
</q-btn-toggle>
</q-toolbar-title>
<q-btn square color="purple" style="margin-right: 10px" icon="search">
<q-popup-edit
ref="popupEdit"
v-model="searchId"
:cover="false"
:offset="[0, 10]"
v-slot="scope"
>
<q-input
color="accent"
v-model="scope.value"
label="设备Id"
dense
autofocus
@keyup.enter="scope.set"
>
<template v-slot:prepend>
<q-icon name="search" color="accent" />
</template>
</q-input>
</q-popup-edit>
</q-btn>
<q-btn-dropdown
color="orange"
label="数据管理"
style="margin-right: 10px"
>
<q-list>
<q-item
v-for="item in dataManageConfig"
:key="item.label"
clickable
v-close-popup
@click="item.click"
>
<q-item-section>{{ item.label }}</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-btn color="info" label="返回" @click="backConfirm" />
<q-btn dense flat round icon="menu" @click="toggleRightDrawer" />
</q-toolbar>
<q-resize-observer @resize="onHeaderResize" />
</q-header>
<q-drawer show-if-above bordered v-model="rightDrawerOpen" side="right">
<q-resize-observer @resize="onRightResize" />
<axleCounting-config
v-if="showGenerateAxleCountingConfig"
@close="closeGenerateAxleCountingConfig"
/>
<draw-properties v-else></draw-properties>
</q-drawer>
<q-page-container>
<div id="draw-app-container" class="overflow-hidden"></div>
</q-page-container>
<q-dialog
v-model="saveAsDialog"
persistent
transition-show="scale"
transition-hide="scale"
>
<q-card style="width: 300px">
<q-card-section>
<div class="text-h6">另存为</div>
</q-card-section>
<q-card-section>
<q-input
outlined
label="草稿名称"
v-model="saveAsName"
:rules="[(val) => val.trim() != '' || '草稿名称不能为空']"
/>
</q-card-section>
<q-card-actions align="right">
<q-btn color="primary" label="提交" @click="saveAs(saveAsName)" />
<q-btn label="取消" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
<q-resize-observer @resize="onResize" />
</q-layout>
</template>
<script setup lang="ts">
import DrawProperties from 'src/components/draw-app/electronicMapDrawProperties.vue';
import { useDrawStore } from 'src/stores/electronicMap-draw-store';
import { onMounted, reactive, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import AxleCountingConfig from 'src/components/draw-app/properties/electronicMap/AxleCountingConfig.vue';
//import { Station } from 'src/graphics/electronicMap/station/Station';
import { Platform } from 'src/graphics/electronicMap/platform/Platform';
/* import { ScreenDoor } from 'src/graphics/electronicMap/screenDoor/ScreenDoor';
import { Section } from 'src/graphics/electronicMap/section/Section';
import { Turnout } from 'src/graphics/electronicMap/turnout/Turnout';
import { Signal } from 'src/graphics/electronicMap/signal/Signal'; */
import { saveDrawToServer } from 'src/drawApp/commonApp';
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
const searchId = ref(0);
const drawStore = useDrawStore();
watch(
() => drawStore.drawMode,
(drawMode) => {
if (!drawMode) {
selectUtil.value = '';
}
}
);
watch(
() => searchId.value,
() => {
try {
if (searchId.value) {
const device = drawStore
.getDrawApp()
.queryStore.queryById(searchId.value);
drawStore.getDrawApp().makeGraphicCenterShow(device);
drawStore.getDrawApp().updateSelected(device);
searchId.value = 0;
}
} catch (err) {
$q.notify({
type: 'negative',
message: `未查找到id为【${searchId.value}】的设备`,
});
}
}
);
const rightDrawerOpen = ref(false);
function toggleRightDrawer() {
rightDrawerOpen.value = !rightDrawerOpen.value;
onResize();
}
//
const selectUtil = ref();
const utilsOption: ControlItem[] = reactive([]);
const drawSelect = (item: string) => {
drawStore.getDrawApp().interactionPlugin(item).resume();
};
class ControlItem {
value: string;
slot: string;
icon: string;
tip: string;
show = true;
constructor(value: string, icon: string, tip: string, show?: boolean) {
this.value = value;
this.slot = value;
this.icon = icon;
this.tip = tip;
if (show != undefined) {
this.show = show;
}
}
}
//
const leftMenuConfig = [
{ label: '保存', click: saveAllDrawDatas },
{ label: '另存为', click: () => (saveAsDialog.value = true) },
{ label: '一键关联', click: buildRelations },
{ label: '一键生成计轴', click: oneClickAxleCounting },
];
//
const showScreenDoorConfig = ref(false);
const showGenerateAxleCountingConfig = ref(false);
const closeGenerateAxleCountingConfig = () => {
showGenerateAxleCountingConfig.value = false;
};
const dataManageConfig = [
{ label: '屏蔽门配置', click: () => (showScreenDoorConfig.value = true) },
];
onMounted(() => {
console.log('绘制应用layout mounted');
drawStore.setDraftId(+route.params.id as number);
const dom = document.getElementById('draw-app-container');
if (dom) {
drawStore.setDraftId(+route.params.id as number);
const drawApp = drawStore.initDrawApp();
drawApp.bindDom(dom);
drawApp.reload();
onResize();
} else {
drawStore.setDraftId(null);
}
/* const drawAssistantsTypes = [
Station.Type,
Platform.Type,
ScreenDoor.Type,
Section.Type,
Turnout.Type,
Signal.Type,
]; */
const drawAssistantsTypes = [Platform.Type];
drawAssistantsTypes.forEach((type) => {
const drawAssistant = drawStore.getDrawApp().getDrawAssistant(type);
if (drawAssistant) {
utilsOption.push(
new ControlItem(
drawAssistant.name,
drawAssistant.icon,
drawAssistant.description || drawAssistant.name
)
);
}
});
});
const canvasWidth = ref(0);
const canvasHeight = ref(0);
const headerHeight = ref(0);
const rightWidth = ref(0);
function onHeaderResize(size: { height: number; width: number }) {
headerHeight.value = size.height;
onResize();
}
function onRightResize(size: { height: number; width: number }) {
rightWidth.value = size.width;
onResize();
}
function onResize() {
const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;
canvasWidth.value =
clientWidth - (rightDrawerOpen.value ? rightWidth.value : 0);
canvasHeight.value = clientHeight - headerHeight.value;
const dom = document.getElementById('draw-app-container');
if (dom) {
dom.style.width = canvasWidth.value + 'px';
dom.style.height = canvasHeight.value + 'px';
}
}
function saveAllDrawDatas() {
let base64 = '';
saveDrawToServer(base64);
}
function buildRelations() {
const app = drawStore.getDrawApp();
app?.detectRelations();
}
function oneClickAxleCounting() {
//--
drawStore.oneClickType;
showGenerateAxleCountingConfig.value = true;
}
function backConfirm() {
router.go(-1);
}
const saveAsDialog = ref(false);
const saveAsName = ref('');
</script>

View File

@ -72,6 +72,7 @@ const visible = ref(false);
const loginInfo = reactive({ const loginInfo = reactive({
username: '', username: '',
password: '', password: '',
orgId: 0,
}); });
let orgInfo; let orgInfo;
@ -81,7 +82,7 @@ onMounted(async () => {
} else { } else {
orgInfo = await getDefaultOrg(); orgInfo = await getDefaultOrg();
} }
console.log(orgInfo, 6666); loginInfo.orgId = orgInfo.id;
}); });
const authStore = useAuthStore(); const authStore = useAuthStore();

View File

@ -8,13 +8,23 @@ export namespace common {
export enum Role { export enum Role {
Role_Unknown = 0, Role_Unknown = 0,
Role_Admin = 1, Role_Admin = 1,
Role_User = 2 Role_User = 2,
Role_OrgManager = 11,
Role_OrgTeacher = 12,
Role_OrgStudent = 13,
Role_OrgGuest = 14
} }
export enum DataType { export enum DataType {
DataType_Unknown = 0, DataType_Unknown = 0,
DataType_Em = 1, DataType_Em = 1,
DataType_Iscs = 4 DataType_Iscs = 4
} }
export enum LineType {
LineType_Unknown = 0,
LineType_Ur = 1,
LineType_Ir = 2,
LineType_Cr = 3
}
export enum IscsStyle { export enum IscsStyle {
IscsStyle_Unknown = 0, IscsStyle_Unknown = 0,
IscsStyle_DaShiZhiNeng = 1 IscsStyle_DaShiZhiNeng = 1

File diff suppressed because it is too large Load Diff

View File

@ -1,426 +0,0 @@
/**
* Generated by the protoc-gen-ts. DO NOT EDIT!
* compiler version: 5.27.4
* source: em_data.proto
* git: https://github.com/thesayyn/protoc-gen-ts */
import * as dependency_1 from "./common";
import * as pb_1 from "google-protobuf";
export namespace em_data {
export class Em extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
canvas?: dependency_1.common.Canvas;
stations?: Station[];
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [2], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("canvas" in data && data.canvas != undefined) {
this.canvas = data.canvas;
}
if ("stations" in data && data.stations != undefined) {
this.stations = data.stations;
}
}
}
get canvas() {
return pb_1.Message.getWrapperField(this, dependency_1.common.Canvas, 1) as dependency_1.common.Canvas;
}
set canvas(value: dependency_1.common.Canvas) {
pb_1.Message.setWrapperField(this, 1, value);
}
get has_canvas() {
return pb_1.Message.getField(this, 1) != null;
}
get stations() {
return pb_1.Message.getRepeatedWrapperField(this, Station, 2) as Station[];
}
set stations(value: Station[]) {
pb_1.Message.setRepeatedWrapperField(this, 2, value);
}
static fromObject(data: {
canvas?: ReturnType<typeof dependency_1.common.Canvas.prototype.toObject>;
stations?: ReturnType<typeof Station.prototype.toObject>[];
}): Em {
const message = new Em({});
if (data.canvas != null) {
message.canvas = dependency_1.common.Canvas.fromObject(data.canvas);
}
if (data.stations != null) {
message.stations = data.stations.map(item => Station.fromObject(item));
}
return message;
}
toObject() {
const data: {
canvas?: ReturnType<typeof dependency_1.common.Canvas.prototype.toObject>;
stations?: ReturnType<typeof Station.prototype.toObject>[];
} = {};
if (this.canvas != null) {
data.canvas = this.canvas.toObject();
}
if (this.stations != null) {
data.stations = this.stations.map((item: Station) => item.toObject());
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.has_canvas)
writer.writeMessage(1, this.canvas, () => this.canvas.serialize(writer));
if (this.stations.length)
writer.writeRepeatedMessage(2, this.stations, (item: Station) => item.serialize(writer));
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Em {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Em();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
reader.readMessage(message.canvas, () => message.canvas = dependency_1.common.Canvas.deserialize(reader));
break;
case 2:
reader.readMessage(message.stations, () => pb_1.Message.addToRepeatedWrapperField(message, 2, Station.deserialize(reader), Station));
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): Em {
return Em.deserialize(bytes);
}
}
export class KilometerMark extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
coordinate?: string;
value?: number;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("coordinate" in data && data.coordinate != undefined) {
this.coordinate = data.coordinate;
}
if ("value" in data && data.value != undefined) {
this.value = data.value;
}
}
}
get coordinate() {
return pb_1.Message.getFieldWithDefault(this, 1, "") as string;
}
set coordinate(value: string) {
pb_1.Message.setField(this, 1, value);
}
get value() {
return pb_1.Message.getFieldWithDefault(this, 2, 0) as number;
}
set value(value: number) {
pb_1.Message.setField(this, 2, value);
}
static fromObject(data: {
coordinate?: string;
value?: number;
}): KilometerMark {
const message = new KilometerMark({});
if (data.coordinate != null) {
message.coordinate = data.coordinate;
}
if (data.value != null) {
message.value = data.value;
}
return message;
}
toObject() {
const data: {
coordinate?: string;
value?: number;
} = {};
if (this.coordinate != null) {
data.coordinate = this.coordinate;
}
if (this.value != null) {
data.value = this.value;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.coordinate.length)
writer.writeString(1, this.coordinate);
if (this.value != 0)
writer.writeInt64(2, this.value);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): KilometerMark {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new KilometerMark();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
message.coordinate = reader.readString();
break;
case 2:
message.value = reader.readInt64();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): KilometerMark {
return KilometerMark.deserialize(bytes);
}
}
export class Station extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
common?: dependency_1.common.CommonInfo;
name?: string;
zhanName?: string;
namePinyin?: string;
km?: KilometerMark;
concentration?: boolean;
depots?: boolean;
manageStationIds?: number[];
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [13], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("common" in data && data.common != undefined) {
this.common = data.common;
}
if ("name" in data && data.name != undefined) {
this.name = data.name;
}
if ("zhanName" in data && data.zhanName != undefined) {
this.zhanName = data.zhanName;
}
if ("namePinyin" in data && data.namePinyin != undefined) {
this.namePinyin = data.namePinyin;
}
if ("km" in data && data.km != undefined) {
this.km = data.km;
}
if ("concentration" in data && data.concentration != undefined) {
this.concentration = data.concentration;
}
if ("depots" in data && data.depots != undefined) {
this.depots = data.depots;
}
if ("manageStationIds" in data && data.manageStationIds != undefined) {
this.manageStationIds = data.manageStationIds;
}
}
}
get common() {
return pb_1.Message.getWrapperField(this, dependency_1.common.CommonInfo, 1) as dependency_1.common.CommonInfo;
}
set common(value: dependency_1.common.CommonInfo) {
pb_1.Message.setWrapperField(this, 1, value);
}
get has_common() {
return pb_1.Message.getField(this, 1) != null;
}
get name() {
return pb_1.Message.getFieldWithDefault(this, 2, "") as string;
}
set name(value: string) {
pb_1.Message.setField(this, 2, value);
}
get zhanName() {
return pb_1.Message.getFieldWithDefault(this, 3, "") as string;
}
set zhanName(value: string) {
pb_1.Message.setField(this, 3, value);
}
get namePinyin() {
return pb_1.Message.getFieldWithDefault(this, 4, "") as string;
}
set namePinyin(value: string) {
pb_1.Message.setField(this, 4, value);
}
get km() {
return pb_1.Message.getWrapperField(this, KilometerMark, 6) as KilometerMark;
}
set km(value: KilometerMark) {
pb_1.Message.setWrapperField(this, 6, value);
}
get has_km() {
return pb_1.Message.getField(this, 6) != null;
}
get concentration() {
return pb_1.Message.getFieldWithDefault(this, 10, false) as boolean;
}
set concentration(value: boolean) {
pb_1.Message.setField(this, 10, value);
}
get depots() {
return pb_1.Message.getFieldWithDefault(this, 11, false) as boolean;
}
set depots(value: boolean) {
pb_1.Message.setField(this, 11, value);
}
get manageStationIds() {
return pb_1.Message.getFieldWithDefault(this, 13, []) as number[];
}
set manageStationIds(value: number[]) {
pb_1.Message.setField(this, 13, value);
}
static fromObject(data: {
common?: ReturnType<typeof dependency_1.common.CommonInfo.prototype.toObject>;
name?: string;
zhanName?: string;
namePinyin?: string;
km?: ReturnType<typeof KilometerMark.prototype.toObject>;
concentration?: boolean;
depots?: boolean;
manageStationIds?: number[];
}): Station {
const message = new Station({});
if (data.common != null) {
message.common = dependency_1.common.CommonInfo.fromObject(data.common);
}
if (data.name != null) {
message.name = data.name;
}
if (data.zhanName != null) {
message.zhanName = data.zhanName;
}
if (data.namePinyin != null) {
message.namePinyin = data.namePinyin;
}
if (data.km != null) {
message.km = KilometerMark.fromObject(data.km);
}
if (data.concentration != null) {
message.concentration = data.concentration;
}
if (data.depots != null) {
message.depots = data.depots;
}
if (data.manageStationIds != null) {
message.manageStationIds = data.manageStationIds;
}
return message;
}
toObject() {
const data: {
common?: ReturnType<typeof dependency_1.common.CommonInfo.prototype.toObject>;
name?: string;
zhanName?: string;
namePinyin?: string;
km?: ReturnType<typeof KilometerMark.prototype.toObject>;
concentration?: boolean;
depots?: boolean;
manageStationIds?: number[];
} = {};
if (this.common != null) {
data.common = this.common.toObject();
}
if (this.name != null) {
data.name = this.name;
}
if (this.zhanName != null) {
data.zhanName = this.zhanName;
}
if (this.namePinyin != null) {
data.namePinyin = this.namePinyin;
}
if (this.km != null) {
data.km = this.km.toObject();
}
if (this.concentration != null) {
data.concentration = this.concentration;
}
if (this.depots != null) {
data.depots = this.depots;
}
if (this.manageStationIds != null) {
data.manageStationIds = this.manageStationIds;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.has_common)
writer.writeMessage(1, this.common, () => this.common.serialize(writer));
if (this.name.length)
writer.writeString(2, this.name);
if (this.zhanName.length)
writer.writeString(3, this.zhanName);
if (this.namePinyin.length)
writer.writeString(4, this.namePinyin);
if (this.has_km)
writer.writeMessage(6, this.km, () => this.km.serialize(writer));
if (this.concentration != false)
writer.writeBool(10, this.concentration);
if (this.depots != false)
writer.writeBool(11, this.depots);
if (this.manageStationIds.length)
writer.writePackedUint32(13, this.manageStationIds);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Station {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Station();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
reader.readMessage(message.common, () => message.common = dependency_1.common.CommonInfo.deserialize(reader));
break;
case 2:
message.name = reader.readString();
break;
case 3:
message.zhanName = reader.readString();
break;
case 4:
message.namePinyin = reader.readString();
break;
case 6:
reader.readMessage(message.km, () => message.km = KilometerMark.deserialize(reader));
break;
case 10:
message.concentration = reader.readBool();
break;
case 11:
message.depots = reader.readBool();
break;
case 13:
message.manageStationIds = reader.readPackedUint32();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): Station {
return Station.deserialize(bytes);
}
}
}

View File

@ -1,18 +0,0 @@
/**
* Generated by the protoc-gen-ts. DO NOT EDIT!
* compiler version: 5.27.4
* source: picture.proto
* git: https://github.com/thesayyn/protoc-gen-ts */
import * as pb_1 from "google-protobuf";
export enum PictureType {
FireAlarm = 0,
Electromechanical = 1,
Broadcast = 2,
PassengerInformation = 3,
CCTV = 4,
PSD = 5,
TicketSalesAndChecking = 6,
AccessControl = 7,
FloodGate = 8,
NetworkStatus = 9
}

187
src/protos/simulation.ts Normal file
View File

@ -0,0 +1,187 @@
/**
* Generated by the protoc-gen-ts. DO NOT EDIT!
* compiler version: 5.27.4
* source: simulation.proto
* git: https://github.com/thesayyn/protoc-gen-ts */
import * as dependency_1 from "./google\\protobuf\\any";
import * as pb_1 from "google-protobuf";
export namespace simulation {
export enum OperationType {
Unknown = 0,
Pause = 1,
Unpause = 2,
Reset = 3,
SetSpeed = 4,
Destroy = 5
}
export class Operation extends pb_1.Message {
#one_of_decls: number[][] = [[2]];
constructor(data?: any[] | ({
otype?: OperationType;
} & (({
setSpeedParam?: SetSpeedParam;
})))) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("otype" in data && data.otype != undefined) {
this.otype = data.otype;
}
if ("setSpeedParam" in data && data.setSpeedParam != undefined) {
this.setSpeedParam = data.setSpeedParam;
}
}
}
get otype() {
return pb_1.Message.getFieldWithDefault(this, 1, OperationType.Unknown) as OperationType;
}
set otype(value: OperationType) {
pb_1.Message.setField(this, 1, value);
}
get setSpeedParam() {
return pb_1.Message.getWrapperField(this, SetSpeedParam, 2) as SetSpeedParam;
}
set setSpeedParam(value: SetSpeedParam) {
pb_1.Message.setOneofWrapperField(this, 2, this.#one_of_decls[0], value);
}
get has_setSpeedParam() {
return pb_1.Message.getField(this, 2) != null;
}
get param() {
const cases: {
[index: number]: "none" | "setSpeedParam";
} = {
0: "none",
2: "setSpeedParam"
};
return cases[pb_1.Message.computeOneofCase(this, [2])];
}
static fromObject(data: {
otype?: OperationType;
setSpeedParam?: ReturnType<typeof SetSpeedParam.prototype.toObject>;
}): Operation {
const message = new Operation({});
if (data.otype != null) {
message.otype = data.otype;
}
if (data.setSpeedParam != null) {
message.setSpeedParam = SetSpeedParam.fromObject(data.setSpeedParam);
}
return message;
}
toObject() {
const data: {
otype?: OperationType;
setSpeedParam?: ReturnType<typeof SetSpeedParam.prototype.toObject>;
} = {};
if (this.otype != null) {
data.otype = this.otype;
}
if (this.setSpeedParam != null) {
data.setSpeedParam = this.setSpeedParam.toObject();
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.otype != OperationType.Unknown)
writer.writeEnum(1, this.otype);
if (this.has_setSpeedParam)
writer.writeMessage(2, this.setSpeedParam, () => this.setSpeedParam.serialize(writer));
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Operation {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Operation();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
message.otype = reader.readEnum();
break;
case 2:
reader.readMessage(message.setSpeedParam, () => message.setSpeedParam = SetSpeedParam.deserialize(reader));
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): Operation {
return Operation.deserialize(bytes);
}
}
export class SetSpeedParam extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
speed?: number;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("speed" in data && data.speed != undefined) {
this.speed = data.speed;
}
}
}
get speed() {
return pb_1.Message.getFieldWithDefault(this, 1, 0) as number;
}
set speed(value: number) {
pb_1.Message.setField(this, 1, value);
}
static fromObject(data: {
speed?: number;
}): SetSpeedParam {
const message = new SetSpeedParam({});
if (data.speed != null) {
message.speed = data.speed;
}
return message;
}
toObject() {
const data: {
speed?: number;
} = {};
if (this.speed != null) {
data.speed = this.speed;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.speed != 0)
writer.writeFloat(1, this.speed);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): SetSpeedParam {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new SetSpeedParam();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
message.speed = reader.readFloat();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): SetSpeedParam {
return SetSpeedParam.deserialize(bytes);
}
}
}

View File

@ -94,6 +94,14 @@ const routes: RouteRecordRaw[] = [
hidden: true, hidden: true,
}, },
}, },
{
path: '/electronicMapPainting/:id',
name: 'electronicMapPainting',
component: () => import('layouts/electronicMapDrawLayout.vue'),
meta: {
hidden: true,
},
},
{ {
path: '/:catchAll(.*)*', path: '/:catchAll(.*)*',
meta: { meta: {

View File

@ -12,14 +12,12 @@ import {
JlGraphic, JlGraphic,
} from 'jl-graphic'; } from 'jl-graphic';
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import { PictureType } from 'src/protos/picture';
export const useDrawStore = defineStore('draw', { export const useDrawStore = defineStore('draw', {
state: () => ({ state: () => ({
drawAssistant: null as DrawAssistant | null, drawAssistant: null as DrawAssistant | null,
selectedGraphics: null as JlGraphic[] | null, selectedGraphics: null as JlGraphic[] | null,
draftId: null as number | null, draftId: null as number | null,
drawPictureType: null as PictureType | null,
selectSubmenuAndStation: { submenu: '', station: '', partition: '' }, selectSubmenuAndStation: { submenu: '', station: '', partition: '' },
}), }),
getters: { getters: {

View File

@ -0,0 +1,107 @@
import { defineStore } from 'pinia';
import {
initElectronicMapDrawApp,
destroyElectronicMapDrawApp,
getElectronicMapDrawApp,
} from 'src/drawApp/electronicMapApp';
import {
DrawAssistant,
GraphicData,
IDrawApp,
IJlCanvas,
JlGraphic,
} from 'jl-graphic';
import { markRaw } from 'vue';
export const useDrawStore = defineStore('draw', {
state: () => ({
drawAssistant: null as DrawAssistant | null,
selectedGraphics: null as JlGraphic[] | null,
draftId: null as number | null,
oneClickType: '',
}),
getters: {
drawMode: (state) => state.drawAssistant != null,
drawGraphicType: (state) => state.drawAssistant?.type,
drawGraphicName: (state) => state.drawAssistant?.description,
drawGraphicTemplate: (state) => state.drawAssistant?.graphicTemplate,
selectedGraphicType: (state) => {
if (state.selectedGraphics) {
if (state.selectedGraphics.length === 1) {
return state.selectedGraphics[0].type;
}
}
},
selectedObjName(state): string {
if (state.selectedGraphics) {
if (state.selectedGraphics.length == 0) {
return '画布';
} else if (state.selectedGraphics.length == 1) {
const name = getElectronicMapDrawApp()?.getDrawAssistant(
state.selectedGraphics[0].type
).description;
return name || '';
}
return '批量设置';
}
return '';
},
selectedGraphic: (state) => {
if (state.selectedGraphics) {
if (state.selectedGraphics.length === 1) {
return state.selectedGraphics[0];
}
}
return null;
},
},
actions: {
getDrawApp(): IDrawApp {
const app = getElectronicMapDrawApp();
if (app == null) {
throw new Error('未初始化app');
}
return app;
},
getJlCanvas(): IJlCanvas {
return this.getDrawApp().canvas;
},
bindFormData(form: GraphicData): void {
const app = this.getDrawApp();
app.bindFormData(form);
},
unbindFormData(form: GraphicData): void {
const app = this.getDrawApp();
app.unbindFormData(form);
},
initDrawApp() {
const app = initElectronicMapDrawApp();
if (app == null) {
throw new Error('未初始化app');
}
app.on('interaction-plugin-resume', (plugin) => {
if (plugin.isAppPlugin()) {
if (Object.hasOwn(plugin, '__GraphicDrawAssistant')) {
this.drawAssistant = plugin as DrawAssistant;
} else {
this.drawAssistant = null;
}
}
});
app.on('graphicselected', (graphics) => {
this.selectedGraphics = markRaw(graphics);
});
this.selectedGraphics = [];
return app;
},
destroy() {
// console.log('绘制状态清空,绘制应用销毁');
this.drawAssistant = null;
this.selectedGraphics = null;
destroyElectronicMapDrawApp();
},
setDraftId(id: number | null) {
this.draftId = id;
},
},
});