代码迁移
This commit is contained in:
parent
b3da1703b5
commit
6848ea4b2e
@ -1 +1 @@
|
|||||||
Subproject commit 0a0cb0a77afd9783081c2dc6ba19687b0b3aa0f7
|
Subproject commit 528873959ed9ed3d62c659347174ba6e55bec58f
|
@ -39,6 +39,7 @@
|
|||||||
"eslint-config-prettier": "^8.1.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
"eslint-plugin-vue": "^9.0.0",
|
"eslint-plugin-vue": "^9.0.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
|
"protoc-gen-ts": "^0.8.6",
|
||||||
"ts-md5": "^1.3.1",
|
"ts-md5": "^1.3.1",
|
||||||
"typescript": "^4.5.4"
|
"typescript": "^4.5.4"
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,7 @@ module.exports = configure(function (/* ctx */) {
|
|||||||
// rawOptions: {},
|
// rawOptions: {},
|
||||||
warnings: true,
|
warnings: true,
|
||||||
errors: true,
|
errors: true,
|
||||||
exclude: ['src/proto/*'],
|
exclude: ['src/protos/*'],
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
|
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
|
||||||
|
@ -9,7 +9,7 @@ const { exec } = require('child_process');
|
|||||||
|
|
||||||
const messageDir = resolve(__dirname, '../xian-ncc-da-message');
|
const messageDir = resolve(__dirname, '../xian-ncc-da-message');
|
||||||
const protoDir = resolve(messageDir, 'protos');
|
const protoDir = resolve(messageDir, 'protos');
|
||||||
const destDir = resolve(__dirname, '../src/proto');
|
const destDir = resolve(__dirname, '../src/protos');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归处理所有proto文件生成
|
* 递归处理所有proto文件生成
|
||||||
@ -47,7 +47,7 @@ function buildGenerateCmd(name, path = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
const protocDir = resolve(messageDir, 'protoc-22.2');
|
const protocDir = resolve(messageDir, 'protoc-23.2');
|
||||||
const protocBin = resolve(
|
const protocBin = resolve(
|
||||||
protocDir,
|
protocDir,
|
||||||
`bin/${isLinux ? 'linux-x86_64' : 'win64'}`
|
`bin/${isLinux ? 'linux-x86_64' : 'win64'}`
|
||||||
|
87
src/components/draw-app/DrawProperties.vue
Normal file
87
src/components/draw-app/DrawProperties.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<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-section>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Link.Type">
|
||||||
|
<link-template></link-template>
|
||||||
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Rect.Type">
|
||||||
|
<rect-template></rect-template>
|
||||||
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Platform.Type">
|
||||||
|
<platform-template></platform-template>
|
||||||
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Station.Type">
|
||||||
|
<station-template></station-template>
|
||||||
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Train.Type">
|
||||||
|
<TrainProperty></TrainProperty>
|
||||||
|
</template>
|
||||||
|
</q-card-section>
|
||||||
|
</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>
|
||||||
|
<link-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Link.Type"
|
||||||
|
></link-property>
|
||||||
|
<rect-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Rect.Type"
|
||||||
|
></rect-property>
|
||||||
|
<platform-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Platform.Type"
|
||||||
|
></platform-property>
|
||||||
|
<station-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Station.Type"
|
||||||
|
></station-property>
|
||||||
|
<train-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Train.Type"
|
||||||
|
></train-property>
|
||||||
|
<iscs-fan-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === IscsFan.Type"
|
||||||
|
></iscs-fan-property>
|
||||||
|
</q-card-section>
|
||||||
|
</template>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import LinkTemplate from './templates/LinkTemplate.vue';
|
||||||
|
import RectTemplate from './templates/RectTemplate.vue';
|
||||||
|
import PlatformTemplate from './templates/PlatformTemplate.vue';
|
||||||
|
import StationTemplate from './templates/StationTemplate.vue';
|
||||||
|
import CanvasProperty from './properties/CanvasProperty.vue';
|
||||||
|
import LinkProperty from './properties/LinkProperty.vue';
|
||||||
|
import RectProperty from './properties/RectProperty.vue';
|
||||||
|
import PlatformProperty from './properties/PlatformProperty.vue';
|
||||||
|
import StationProperty from './properties/StationProperty.vue';
|
||||||
|
import TrainProperty from './properties/TrainProperty.vue';
|
||||||
|
import IscsFanProperty from './properties/IscsFanProperty.vue';
|
||||||
|
import { Link } from 'src/graphics/link/Link';
|
||||||
|
import { Rect } from 'src/graphics/rect/Rect';
|
||||||
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
|
import { Station } from 'src/graphics/station/Station';
|
||||||
|
import { Train } from 'src/graphics/train/Train';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
</script>
|
80
src/components/draw-app/properties/CanvasProperty.vue
Normal file
80
src/components/draw-app/properties/CanvasProperty.vue
Normal 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/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>
|
42
src/components/draw-app/properties/IscsFanProperty.vue
Normal file
42
src/components/draw-app/properties/IscsFanProperty.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="model.id" label="id" :rules="[]" />
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { IscsFanData } from 'src/drawApp/graphics/IscsFanInteraction';
|
||||||
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const model = reactive(new IscsFanData());
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == IscsFan.Type) {
|
||||||
|
// console.log('Iscs风机变更');
|
||||||
|
model.copyFrom(val.saveData() as IscsFanData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// console.log('Iscs风机 属性表单 mounted');
|
||||||
|
const g = drawStore.selectedGraphic as IscsFan;
|
||||||
|
if (g) {
|
||||||
|
model.copyFrom(g.saveData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
console.log('Iscs风机 属性更新');
|
||||||
|
const g = drawStore.selectedGraphic as IscsFan;
|
||||||
|
if (g) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(g, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
96
src/components/draw-app/properties/LinkProperty.vue
Normal file
96
src/components/draw-app/properties/LinkProperty.vue
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="linkModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="linkModel.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="linkModel.lineColor"
|
||||||
|
@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
|
||||||
|
v-model="linkModel.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
linkModel.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<!-- <q-btn-toggle
|
||||||
|
disable
|
||||||
|
v-model="linkModel.curve"
|
||||||
|
:options="[
|
||||||
|
{ label: '直线', value: false },
|
||||||
|
{ label: '曲线', value: true },
|
||||||
|
]"
|
||||||
|
/> -->
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-if="linkModel.curve"
|
||||||
|
outlined
|
||||||
|
v-model.number="linkModel.segmentsCount"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="曲线分段数量"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '曲线分段数量必须大于0']"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkData } from 'src/drawApp/graphics/LinkInteraction';
|
||||||
|
import { Link } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const linkModel = reactive(new LinkData());
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Link.Type) {
|
||||||
|
// console.log('link变更');
|
||||||
|
linkModel.copyFrom(val.saveData() as LinkData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// console.log('link 属性表单 mounted');
|
||||||
|
const link = drawStore.selectedGraphic as Link;
|
||||||
|
if (link) {
|
||||||
|
linkModel.copyFrom(link.saveData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
console.log('link 属性更新');
|
||||||
|
const link = drawStore.selectedGraphic as Link;
|
||||||
|
if (link) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(link, linkModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
112
src/components/draw-app/properties/PlatformProperty.vue
Normal file
112
src/components/draw-app/properties/PlatformProperty.vue
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="platformModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="platformModel.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="platformModel.lineColor"
|
||||||
|
@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
|
||||||
|
v-model="platformModel.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
platformModel.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasDoor"
|
||||||
|
:options="optionsDoor"
|
||||||
|
label="是否有屏蔽门"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="trainDirection"
|
||||||
|
:options="optionsDirection"
|
||||||
|
label="行驶方向"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PlatformData } from 'src/drawApp/graphics/PlatformInteraction';
|
||||||
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const platformModel = reactive(new PlatformData());
|
||||||
|
const hasDoor = ref('是');
|
||||||
|
const optionsDoor = ['是', '否'];
|
||||||
|
const trainDirection = ref('向左');
|
||||||
|
const optionsDirection = ['向左', '向右'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
向左 = 'left',
|
||||||
|
向右 = 'right',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
left = '向左',
|
||||||
|
right = '向右',
|
||||||
|
}
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Platform.Type) {
|
||||||
|
platformModel.copyFrom(val.saveData() as PlatformData);
|
||||||
|
hasDoor.value = (showSelectData as never)[platformModel.hasdoor + ''];
|
||||||
|
trainDirection.value = (showSelectData as never)[
|
||||||
|
platformModel.trainDirection
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const platform = drawStore.selectedGraphic as Platform;
|
||||||
|
if (platform) {
|
||||||
|
platformModel.copyFrom(platform.saveData());
|
||||||
|
hasDoor.value = (showSelectData as never)[platformModel.hasdoor + ''];
|
||||||
|
trainDirection.value = (showSelectData as never)[
|
||||||
|
platformModel.trainDirection
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
platformModel.hasdoor = JSON.parse((showSelect as never)[hasDoor.value]);
|
||||||
|
platformModel.trainDirection = (showSelect as never)[trainDirection.value];
|
||||||
|
const platform = drawStore.selectedGraphic as Platform;
|
||||||
|
if (platform) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(platform, platformModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
74
src/components/draw-app/properties/RectProperty.vue
Normal file
74
src/components/draw-app/properties/RectProperty.vue
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="stationModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.lineColor"
|
||||||
|
@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
|
||||||
|
v-model="stationModel.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { RectData } from 'src/drawApp/graphics/RectInteraction';
|
||||||
|
import { Rect } from 'src/graphics/rect/Rect';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const stationModel = reactive(new RectData());
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Rect.Type) {
|
||||||
|
stationModel.copyFrom(val.saveData() as RectData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const station = drawStore.selectedGraphic as Rect;
|
||||||
|
|
||||||
|
if (station) {
|
||||||
|
stationModel.copyFrom(station.saveData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const station = drawStore.selectedGraphic as Rect;
|
||||||
|
if (station) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(station, stationModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
165
src/components/draw-app/properties/StationProperty.vue
Normal file
165
src/components/draw-app/properties/StationProperty.vue
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="stationModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
label="车站名称"
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="stationModel.code"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.codeFontSize"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '字体大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.codeColor"
|
||||||
|
@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
|
||||||
|
v-model="stationModel.codeColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.codeColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasCircle"
|
||||||
|
:options="optionsCircle"
|
||||||
|
label="是否有圆圈"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.radius"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="半径"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '半径大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.fillColor"
|
||||||
|
@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
|
||||||
|
v-model="stationModel.fillColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.fillColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.borderWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="边框宽度"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.borderColor"
|
||||||
|
@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
|
||||||
|
v-model="stationModel.borderColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.borderColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { StationData } from 'src/drawApp/graphics/StationInteraction';
|
||||||
|
import { Station } from 'src/graphics/station/Station';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const stationModel = reactive(new StationData());
|
||||||
|
const hasCircle = ref('是');
|
||||||
|
const optionsCircle = ['是', '否'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
}
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Station.Type) {
|
||||||
|
stationModel.copyFrom(val.saveData() as StationData);
|
||||||
|
hasCircle.value = (showSelectData as never)[stationModel.hasCircle + ''];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const station = drawStore.selectedGraphic as Station;
|
||||||
|
if (station) {
|
||||||
|
stationModel.copyFrom(station.saveData());
|
||||||
|
hasCircle.value = (showSelectData as never)[stationModel.hasCircle + ''];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
stationModel.hasCircle = JSON.parse((showSelect as never)[hasCircle.value]);
|
||||||
|
const station = drawStore.selectedGraphic as Station;
|
||||||
|
if (station) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(station, stationModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
198
src/components/draw-app/properties/TrainProperty.vue
Normal file
198
src/components/draw-app/properties/TrainProperty.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="trainModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.code"
|
||||||
|
label="车号"
|
||||||
|
hint=""
|
||||||
|
@blur="onUpdate"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.codeColor"
|
||||||
|
@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
|
||||||
|
v-model="trainModel.codeColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.codeColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.headColor"
|
||||||
|
@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
|
||||||
|
v-model="trainModel.headColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.headColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="trainModel.codeFontSize"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '字体大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasBorder"
|
||||||
|
:options="optionsDoor"
|
||||||
|
label="是否有边框"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="trainModel.borderWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="边框线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '边框线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.borderColor"
|
||||||
|
@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
|
||||||
|
v-model="trainModel.borderColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.borderColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.bodyColor"
|
||||||
|
@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
|
||||||
|
v-model="trainModel.bodyColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.bodyColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="trainDirection"
|
||||||
|
:options="optionsDirection"
|
||||||
|
label="行驶方向"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TrainData } from 'src/drawApp/graphics/TrainInteraction';
|
||||||
|
import { Train } from 'src/graphics/train/Train';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const trainModel = reactive(new TrainData());
|
||||||
|
const hasBorder = ref('是');
|
||||||
|
const optionsDoor = ['是', '否'];
|
||||||
|
const trainDirection = ref('向左');
|
||||||
|
const optionsDirection = ['向左', '向右'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
向左 = 'left',
|
||||||
|
向右 = 'right',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
left = '向左',
|
||||||
|
right = '向右',
|
||||||
|
}
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Train.Type) {
|
||||||
|
trainModel.copyFrom(val.saveData() as TrainData);
|
||||||
|
hasBorder.value = (showSelectData as never)[trainModel.hasBorder + ''];
|
||||||
|
trainDirection.value = (showSelectData as never)[
|
||||||
|
trainModel.trainDirection
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const train = drawStore.selectedGraphic as Train;
|
||||||
|
if (train) {
|
||||||
|
trainModel.copyFrom(train.saveData());
|
||||||
|
hasBorder.value = (showSelectData as never)[trainModel.hasBorder + ''];
|
||||||
|
trainDirection.value = (showSelectData as never)[trainModel.trainDirection];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
trainModel.hasBorder = JSON.parse((showSelect as never)[hasBorder.value]);
|
||||||
|
trainModel.trainDirection = (showSelect as never)[trainDirection.value];
|
||||||
|
const train = drawStore.selectedGraphic as Train;
|
||||||
|
if (train) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(train, trainModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
96
src/components/draw-app/templates/LinkTemplate.vue
Normal file
96
src/components/draw-app/templates/LinkTemplate.vue
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@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
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<q-btn-toggle
|
||||||
|
v-model="template.curve"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
:options="[
|
||||||
|
{ label: '直线', value: false },
|
||||||
|
{ label: '曲线', value: true },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-if="template.curve"
|
||||||
|
outlined
|
||||||
|
v-model.number="template.segmentsCount"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="曲线分段数量 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '曲线分段数量必须大于0']"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
curve: false,
|
||||||
|
segmentsCount: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
template.curve = lt.curve;
|
||||||
|
template.segmentsCount = lt.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
gt.curve = template.curve;
|
||||||
|
gt.segmentsCount = template.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
76
src/components/draw-app/templates/PlatformTemplate.vue
Normal file
76
src/components/draw-app/templates/PlatformTemplate.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@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
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
curve: false,
|
||||||
|
segmentsCount: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
template.curve = lt.curve;
|
||||||
|
template.segmentsCount = lt.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
gt.curve = template.curve;
|
||||||
|
gt.segmentsCount = template.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
76
src/components/draw-app/templates/RectTemplate.vue
Normal file
76
src/components/draw-app/templates/RectTemplate.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@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
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
curve: false,
|
||||||
|
segmentsCount: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
template.curve = lt.curve;
|
||||||
|
template.segmentsCount = lt.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
gt.curve = template.curve;
|
||||||
|
gt.segmentsCount = template.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
70
src/components/draw-app/templates/StationTemplate.vue
Normal file
70
src/components/draw-app/templates/StationTemplate.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@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
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
69
src/components/draw-app/templates/TrainTemplate.vue
Normal file
69
src/components/draw-app/templates/TrainTemplate.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.codeFontSize"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '字体大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.headColor"
|
||||||
|
@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
|
||||||
|
v-model="template.headColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.headColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TrainTemplate } from 'src/graphics/train/Train';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
codeFontSize: 22,
|
||||||
|
headColor: '#00FF00',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as TrainTemplate;
|
||||||
|
template.codeFontSize = lt.codeFontSize;
|
||||||
|
template.headColor = lt.headColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as TrainTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.codeFontSize = template.codeFontSize;
|
||||||
|
gt.headColor = template.headColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
101
src/drawApp/graphics/GraphicDataBase.ts
Normal file
101
src/drawApp/graphics/GraphicDataBase.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
ChildTransform,
|
||||||
|
GraphicData,
|
||||||
|
GraphicTransform,
|
||||||
|
IChildTransform,
|
||||||
|
IGraphicTransform,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { toStorageTransform } from '..';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
|
||||||
|
export interface ICommonInfo {
|
||||||
|
id: string;
|
||||||
|
graphicType: string;
|
||||||
|
transform: IGraphicTransform;
|
||||||
|
childTransforms: IChildTransform[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IProtoGraphicData extends pb_1.Message {
|
||||||
|
common: ICommonInfo;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class GraphicDataBase implements GraphicData {
|
||||||
|
_data: IProtoGraphicData;
|
||||||
|
constructor(data: IProtoGraphicData) {
|
||||||
|
this._data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultCommonInfo(): graphicData.CommonInfo {
|
||||||
|
return new graphicData.CommonInfo({
|
||||||
|
id: '',
|
||||||
|
graphicType: '',
|
||||||
|
transform: new graphicData.Transform({
|
||||||
|
position: new graphicData.Point({ x: 0, y: 0 }),
|
||||||
|
scale: new graphicData.Point({ x: 1, y: 1 }),
|
||||||
|
rotation: 0,
|
||||||
|
skew: new graphicData.Point({ x: 0, y: 0 }),
|
||||||
|
}),
|
||||||
|
childTransforms: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getData<D extends IProtoGraphicData>(): D {
|
||||||
|
return this._data as D;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string {
|
||||||
|
return this._data.common.id;
|
||||||
|
}
|
||||||
|
set id(v: string) {
|
||||||
|
this._data.common.id = v;
|
||||||
|
}
|
||||||
|
get graphicType(): string {
|
||||||
|
return this._data.common.graphicType;
|
||||||
|
}
|
||||||
|
set graphicType(v: string) {
|
||||||
|
this._data.common.graphicType = v;
|
||||||
|
}
|
||||||
|
get transform(): GraphicTransform {
|
||||||
|
return GraphicTransform.from(this._data.common.transform);
|
||||||
|
}
|
||||||
|
set transform(v: GraphicTransform) {
|
||||||
|
this._data.common.transform = toStorageTransform(v);
|
||||||
|
}
|
||||||
|
get childTransforms(): ChildTransform[] | undefined {
|
||||||
|
const cts: ChildTransform[] = [];
|
||||||
|
if (this._data.common.childTransforms) {
|
||||||
|
this._data.common.childTransforms.forEach((ct) => {
|
||||||
|
cts.push(ChildTransform.from(ct));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return cts;
|
||||||
|
}
|
||||||
|
set childTransforms(v: ChildTransform[] | undefined) {
|
||||||
|
if (v) {
|
||||||
|
const cts: graphicData.ChildTransform[] = [];
|
||||||
|
v.forEach((ct) =>
|
||||||
|
cts.push(
|
||||||
|
new graphicData.ChildTransform({
|
||||||
|
...ct,
|
||||||
|
transform: toStorageTransform(ct.transform),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this._data.common.childTransforms = cts;
|
||||||
|
} else {
|
||||||
|
this._data.common.childTransforms = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): GraphicData {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
copyFrom(gd: GraphicDataBase): void {
|
||||||
|
pb_1.Message.copyInto(gd._data, this._data);
|
||||||
|
}
|
||||||
|
eq(other: GraphicDataBase): boolean {
|
||||||
|
return pb_1.Message.equals(this._data, other._data);
|
||||||
|
}
|
||||||
|
}
|
38
src/drawApp/graphics/IscsFanInteraction.ts
Normal file
38
src/drawApp/graphics/IscsFanInteraction.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IIscsFanData } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class IscsFanData extends GraphicDataBase implements IIscsFanData {
|
||||||
|
constructor(data?: graphicData.IscsFan) {
|
||||||
|
let fan;
|
||||||
|
if (data) {
|
||||||
|
fan = data;
|
||||||
|
} else {
|
||||||
|
fan = new graphicData.IscsFan({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super(fan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.IscsFan {
|
||||||
|
return this.getData<graphicData.IscsFan>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
clone(): IscsFanData {
|
||||||
|
return new IscsFanData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: IscsFanData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: IscsFanData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
77
src/drawApp/graphics/LinkInteraction.ts
Normal file
77
src/drawApp/graphics/LinkInteraction.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { ILinkData } from 'src/graphics/link/Link';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class LinkData extends GraphicDataBase implements ILinkData {
|
||||||
|
constructor(data?: graphicData.Link) {
|
||||||
|
let link;
|
||||||
|
if (!data) {
|
||||||
|
link = new graphicData.Link({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
link = data;
|
||||||
|
}
|
||||||
|
super(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Link {
|
||||||
|
return this.getData<graphicData.Link>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get curve(): boolean {
|
||||||
|
return this.data.curve;
|
||||||
|
}
|
||||||
|
set curve(v: boolean) {
|
||||||
|
this.data.curve = v;
|
||||||
|
}
|
||||||
|
get curveNumber(): number {
|
||||||
|
return this.data.curve ? 1 : 0;
|
||||||
|
}
|
||||||
|
set curveNumber(v: number) {
|
||||||
|
this.data.curve = v === 0 ? false : true;
|
||||||
|
}
|
||||||
|
get segmentsCount(): number {
|
||||||
|
return this.data.segmentsCount;
|
||||||
|
}
|
||||||
|
set segmentsCount(v: number) {
|
||||||
|
this.data.segmentsCount = 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 lineWidth(): number {
|
||||||
|
return this.data.lineWidth;
|
||||||
|
}
|
||||||
|
set lineWidth(v: number) {
|
||||||
|
this.data.lineWidth = v;
|
||||||
|
}
|
||||||
|
get lineColor(): string {
|
||||||
|
return this.data.lineColor;
|
||||||
|
}
|
||||||
|
set lineColor(v: string) {
|
||||||
|
this.data.lineColor = v;
|
||||||
|
}
|
||||||
|
clone(): LinkData {
|
||||||
|
return new LinkData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: LinkData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: LinkData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
88
src/drawApp/graphics/PlatformInteraction.ts
Normal file
88
src/drawApp/graphics/PlatformInteraction.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { IPlatformData } from 'src/graphics/platform/Platform';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class PlatformData extends GraphicDataBase implements IPlatformData {
|
||||||
|
constructor(data?: graphicData.Platform) {
|
||||||
|
let platform;
|
||||||
|
if (!data) {
|
||||||
|
platform = new graphicData.Platform({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
platform = data;
|
||||||
|
}
|
||||||
|
super(platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Platform {
|
||||||
|
return this.getData<graphicData.Platform>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get hasdoor(): boolean {
|
||||||
|
return this.data.hasdoor;
|
||||||
|
}
|
||||||
|
set hasdoor(v: boolean) {
|
||||||
|
this.data.hasdoor = v;
|
||||||
|
}
|
||||||
|
get trainDirection(): string {
|
||||||
|
return this.data.trainDirection;
|
||||||
|
}
|
||||||
|
set trainDirection(v: string) {
|
||||||
|
this.data.trainDirection = v;
|
||||||
|
}
|
||||||
|
get lineWidth(): number {
|
||||||
|
return this.data.lineWidth;
|
||||||
|
}
|
||||||
|
set lineWidth(v: number) {
|
||||||
|
this.data.lineWidth = v;
|
||||||
|
}
|
||||||
|
get lineColor(): string {
|
||||||
|
return this.data.lineColor;
|
||||||
|
}
|
||||||
|
set lineColor(v: string) {
|
||||||
|
this.data.lineColor = v;
|
||||||
|
}
|
||||||
|
get lineColorDoor(): string {
|
||||||
|
return this.data.lineColorDoor;
|
||||||
|
}
|
||||||
|
set lineColorDoor(v: string) {
|
||||||
|
this.data.lineColorDoor = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
get width(): number {
|
||||||
|
return this.data.width;
|
||||||
|
}
|
||||||
|
set width(v: number) {
|
||||||
|
this.data.width = v;
|
||||||
|
}
|
||||||
|
get height(): number {
|
||||||
|
return this.data.height;
|
||||||
|
}
|
||||||
|
set height(v: number) {
|
||||||
|
this.data.height = 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);
|
||||||
|
}
|
||||||
|
}
|
77
src/drawApp/graphics/RectInteraction.ts
Normal file
77
src/drawApp/graphics/RectInteraction.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { IRectData } from 'src/graphics/rect/Rect';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class RectData extends GraphicDataBase implements IRectData {
|
||||||
|
constructor(data?: graphicData.Rect) {
|
||||||
|
let rect;
|
||||||
|
if (!data) {
|
||||||
|
rect = new graphicData.Rect({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rect = data;
|
||||||
|
}
|
||||||
|
super(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Rect {
|
||||||
|
return this.getData<graphicData.Rect>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get lineWidth(): number {
|
||||||
|
return this.data.lineWidth;
|
||||||
|
}
|
||||||
|
set lineWidth(v: number) {
|
||||||
|
this.data.lineWidth = v;
|
||||||
|
}
|
||||||
|
get lineColor(): string {
|
||||||
|
return this.data.lineColor;
|
||||||
|
}
|
||||||
|
set lineColor(v: string) {
|
||||||
|
this.data.lineColor = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
get width(): number {
|
||||||
|
return this.data.width;
|
||||||
|
}
|
||||||
|
set width(v: number) {
|
||||||
|
this.data.width = v;
|
||||||
|
}
|
||||||
|
get height(): number {
|
||||||
|
return this.data.height;
|
||||||
|
}
|
||||||
|
set height(v: number) {
|
||||||
|
this.data.height = 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 })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
clone(): RectData {
|
||||||
|
return new RectData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: RectData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: RectData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
92
src/drawApp/graphics/StationInteraction.ts
Normal file
92
src/drawApp/graphics/StationInteraction.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { IStationData } from 'src/graphics/station/Station';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class StationData extends GraphicDataBase implements IStationData {
|
||||||
|
constructor(data?: graphicData.Station) {
|
||||||
|
let station;
|
||||||
|
if (!data) {
|
||||||
|
station = new graphicData.Station({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} 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 hasCircle(): boolean {
|
||||||
|
return this.data.hasCircle;
|
||||||
|
}
|
||||||
|
set hasCircle(v: boolean) {
|
||||||
|
this.data.hasCircle = v;
|
||||||
|
}
|
||||||
|
get radius(): number {
|
||||||
|
return this.data.radius;
|
||||||
|
}
|
||||||
|
set radius(v: number) {
|
||||||
|
this.data.radius = v;
|
||||||
|
}
|
||||||
|
get borderWidth(): number {
|
||||||
|
return this.data.borderWidth;
|
||||||
|
}
|
||||||
|
set borderWidth(v: number) {
|
||||||
|
this.data.borderWidth = v;
|
||||||
|
}
|
||||||
|
get borderColor(): string {
|
||||||
|
return this.data.borderColor;
|
||||||
|
}
|
||||||
|
set borderColor(v: string) {
|
||||||
|
this.data.borderColor = v;
|
||||||
|
}
|
||||||
|
get fillColor(): string {
|
||||||
|
return this.data.fillColor;
|
||||||
|
}
|
||||||
|
set fillColor(v: string) {
|
||||||
|
this.data.fillColor = v;
|
||||||
|
}
|
||||||
|
get codeColor(): string {
|
||||||
|
return this.data.codeColor;
|
||||||
|
}
|
||||||
|
set codeColor(v: string) {
|
||||||
|
this.data.codeColor = v;
|
||||||
|
}
|
||||||
|
get codeFontSize(): number {
|
||||||
|
return this.data.codeFontSize;
|
||||||
|
}
|
||||||
|
set codeFontSize(v: number) {
|
||||||
|
this.data.codeFontSize = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
get circlePoint(): IPointData {
|
||||||
|
return this.data.circlePoint;
|
||||||
|
}
|
||||||
|
set circlePoint(point: IPointData) {
|
||||||
|
this.data.circlePoint = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
92
src/drawApp/graphics/TrainInteraction.ts
Normal file
92
src/drawApp/graphics/TrainInteraction.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { ITrainData } from 'src/graphics/train/Train';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class TrainData extends GraphicDataBase implements ITrainData {
|
||||||
|
constructor(data?: graphicData.Train) {
|
||||||
|
let train;
|
||||||
|
if (!data) {
|
||||||
|
train = new graphicData.Train({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
train = data;
|
||||||
|
}
|
||||||
|
super(train);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Train {
|
||||||
|
return this.getData<graphicData.Train>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get codeColor(): string {
|
||||||
|
return this.data.codeColor;
|
||||||
|
}
|
||||||
|
set codeColor(v: string) {
|
||||||
|
this.data.codeColor = v;
|
||||||
|
}
|
||||||
|
get codeFontSize(): number {
|
||||||
|
return this.data.codeFontSize;
|
||||||
|
}
|
||||||
|
set codeFontSize(v: number) {
|
||||||
|
this.data.codeFontSize = v;
|
||||||
|
}
|
||||||
|
get trainDirection(): string {
|
||||||
|
return this.data.trainDirection;
|
||||||
|
}
|
||||||
|
set trainDirection(v: string) {
|
||||||
|
this.data.trainDirection = v;
|
||||||
|
}
|
||||||
|
get hasBorder(): boolean {
|
||||||
|
return this.data.hasBorder;
|
||||||
|
}
|
||||||
|
set hasBorder(v: boolean) {
|
||||||
|
this.data.hasBorder = v;
|
||||||
|
}
|
||||||
|
get borderWidth(): number {
|
||||||
|
return this.data.borderWidth;
|
||||||
|
}
|
||||||
|
set borderWidth(v: number) {
|
||||||
|
this.data.borderWidth = v;
|
||||||
|
}
|
||||||
|
get borderColor(): string {
|
||||||
|
return this.data.borderColor;
|
||||||
|
}
|
||||||
|
set borderColor(v: string) {
|
||||||
|
this.data.borderColor = v;
|
||||||
|
}
|
||||||
|
get headColor(): string {
|
||||||
|
return this.data.headColor;
|
||||||
|
}
|
||||||
|
set headColor(v: string) {
|
||||||
|
this.data.headColor = v;
|
||||||
|
}
|
||||||
|
get bodyColor(): string {
|
||||||
|
return this.data.bodyColor;
|
||||||
|
}
|
||||||
|
set bodyColor(v: string) {
|
||||||
|
this.data.bodyColor = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
clone(): TrainData {
|
||||||
|
return new TrainData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: TrainData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: TrainData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
223
src/drawApp/index.ts
Normal file
223
src/drawApp/index.ts
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
import { fromUint8Array, toUint8Array } from 'js-base64';
|
||||||
|
import { IPointData, Point } from 'pixi.js';
|
||||||
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { IscsFanDraw } from 'src/graphics/iscs-fan/IscsFanDrawAssistant';
|
||||||
|
import { Link } from 'src/graphics/link/Link';
|
||||||
|
import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant';
|
||||||
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
|
import { PlatformDraw } from 'src/graphics/platform/PlatformDrawAssistant';
|
||||||
|
import {
|
||||||
|
CombinationKey,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicData,
|
||||||
|
GraphicTransform,
|
||||||
|
JlDrawApp,
|
||||||
|
KeyListener,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
|
||||||
|
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
|
||||||
|
import { IscsFanData } from './graphics/IscsFanInteraction';
|
||||||
|
import { LinkData } from './graphics/LinkInteraction';
|
||||||
|
import { PlatformData } from './graphics/PlatformInteraction';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
|
||||||
|
export function fromStoragePoint(p: graphicData.Point): Point {
|
||||||
|
return new Point(p.x, p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toStoragePoint(p: IPointData): graphicData.Point {
|
||||||
|
return new graphicData.Point({ x: p.x, y: p.y });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fromStorageTransfrom(
|
||||||
|
transfrom: graphicData.Transform
|
||||||
|
): GraphicTransform {
|
||||||
|
return new GraphicTransform(
|
||||||
|
fromStoragePoint(transfrom.position),
|
||||||
|
fromStoragePoint(transfrom.scale),
|
||||||
|
transfrom.rotation,
|
||||||
|
fromStoragePoint(transfrom.skew)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toStorageTransform(
|
||||||
|
transform: GraphicTransform
|
||||||
|
): graphicData.Transform {
|
||||||
|
return new graphicData.Transform({
|
||||||
|
position: toStoragePoint(transform.position),
|
||||||
|
scale: toStoragePoint(transform.scale),
|
||||||
|
rotation: transform.rotation,
|
||||||
|
skew: toStoragePoint(transform.skew),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const UndoOptions: MenuItemOptions = {
|
||||||
|
name: '撤销',
|
||||||
|
};
|
||||||
|
const RedoOptions: MenuItemOptions = {
|
||||||
|
name: '重做',
|
||||||
|
};
|
||||||
|
const SelectAllOptions: MenuItemOptions = {
|
||||||
|
name: '全选',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultCanvasMenu = new ContextMenu({
|
||||||
|
name: '绘制-画布菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [UndoOptions, RedoOptions],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [SelectAllOptions],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let drawApp: JlDrawApp | null = null;
|
||||||
|
|
||||||
|
export function getDrawApp(): JlDrawApp | null {
|
||||||
|
return drawApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function destroyDrawApp(): void {
|
||||||
|
if (drawApp) {
|
||||||
|
drawApp.destroy();
|
||||||
|
drawApp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initDrawApp(dom: HTMLElement): JlDrawApp {
|
||||||
|
drawApp = new JlDrawApp(dom);
|
||||||
|
const app = drawApp;
|
||||||
|
app.setOptions({
|
||||||
|
drawAssistants: [
|
||||||
|
new LinkDraw(app, () => {
|
||||||
|
return new LinkData();
|
||||||
|
}),
|
||||||
|
new IscsFanDraw(app, () => {
|
||||||
|
return new IscsFanData();
|
||||||
|
}),
|
||||||
|
new PlatformDraw(app, () => {
|
||||||
|
return new PlatformData();
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 画布右键菜单
|
||||||
|
app.registerMenu(DefaultCanvasMenu);
|
||||||
|
app.canvas.on('_rightclick', (e) => {
|
||||||
|
if (app._drawing) return;
|
||||||
|
UndoOptions.disabled = !app.opRecord.hasUndo;
|
||||||
|
RedoOptions.disabled = !app.opRecord.hasRedo;
|
||||||
|
UndoOptions.handler = () => {
|
||||||
|
app.opRecord.undo();
|
||||||
|
};
|
||||||
|
RedoOptions.handler = () => {
|
||||||
|
app.opRecord.redo();
|
||||||
|
};
|
||||||
|
SelectAllOptions.handler = () => {
|
||||||
|
app.selectAllGraphics();
|
||||||
|
};
|
||||||
|
DefaultCanvasMenu.open(e.global);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyL',
|
||||||
|
onPress: () => {
|
||||||
|
app.interactionPlugin(Link.Type).resume();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyF',
|
||||||
|
onPress: () => {
|
||||||
|
app.interactionPlugin(IscsFan.Type).resume();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyP',
|
||||||
|
onPress: () => {
|
||||||
|
app.interactionPlugin(Platform.Type).resume();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: '1',
|
||||||
|
onPress: () => {
|
||||||
|
app.queryStore.queryByType<IscsFan>(IscsFan.Type).forEach((fan) => {
|
||||||
|
fan.__state = fan.__state + 1;
|
||||||
|
fan.__state = fan.__state % 5;
|
||||||
|
fan.repaint();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyS',
|
||||||
|
global: true,
|
||||||
|
combinations: [CombinationKey.Ctrl],
|
||||||
|
onPress: () => {
|
||||||
|
saveDrawDatas(app);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return drawApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StorageKey = 'graphic-storage';
|
||||||
|
export function saveDrawDatas(app: JlDrawApp) {
|
||||||
|
const storage = new graphicData.RtssGraphicStorage();
|
||||||
|
const canvasData = app.canvas.saveData();
|
||||||
|
storage.canvas = new graphicData.Canvas({
|
||||||
|
...canvasData,
|
||||||
|
viewportTransform: toStorageTransform(canvasData.viewportTransform),
|
||||||
|
});
|
||||||
|
const graphics = app.queryStore.getAllGraphics();
|
||||||
|
graphics.forEach((g) => {
|
||||||
|
if (Link.Type === g.type) {
|
||||||
|
const linkData = (g as Link).saveData();
|
||||||
|
storage.links.push((linkData as LinkData).data);
|
||||||
|
} else if (IscsFan.Type === g.type) {
|
||||||
|
const IscsFanData = (g as IscsFan).saveData();
|
||||||
|
storage.iscsFans.push((IscsFanData as IscsFanData).data);
|
||||||
|
} else if (Platform.Type === g.type) {
|
||||||
|
const platformData = (g as Platform).saveData();
|
||||||
|
storage.Platforms.push((platformData as PlatformData).data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const base64 = fromUint8Array(storage.serialize());
|
||||||
|
console.log('保存数据', storage);
|
||||||
|
localStorage.setItem(StorageKey, base64);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadDrawDatas(app: GraphicApp) {
|
||||||
|
// localStorage.removeItem(StorageKey);
|
||||||
|
const base64 = localStorage.getItem(StorageKey);
|
||||||
|
// console.log('加载数据', base64);
|
||||||
|
if (base64) {
|
||||||
|
const storage = graphicData.RtssGraphicStorage.deserialize(
|
||||||
|
toUint8Array(base64)
|
||||||
|
);
|
||||||
|
console.log('加载数据', storage);
|
||||||
|
app.updateCanvas(storage.canvas);
|
||||||
|
const datas: GraphicData[] = [];
|
||||||
|
storage.links.forEach((link) => {
|
||||||
|
datas.push(new LinkData(link));
|
||||||
|
});
|
||||||
|
storage.iscsFans.forEach((fan) => {
|
||||||
|
datas.push(new IscsFanData(fan));
|
||||||
|
});
|
||||||
|
storage.Platforms.forEach((platform) => {
|
||||||
|
datas.push(new PlatformData(platform));
|
||||||
|
});
|
||||||
|
app.loadGraphic(datas);
|
||||||
|
} else {
|
||||||
|
app.loadGraphic([]);
|
||||||
|
}
|
||||||
|
}
|
125
src/graphics/iscs-fan/IscsFan.ts
Normal file
125
src/graphics/iscs-fan/IscsFan.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import {
|
||||||
|
GraphicAnimation,
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import ISCS_FAN_Assets from './iscs-fan-spritesheet.png';
|
||||||
|
import ISCS_FAN_JSON from './iscs-fan-data.json';
|
||||||
|
|
||||||
|
import { Assets, Sprite, Spritesheet, Texture } from 'pixi.js';
|
||||||
|
|
||||||
|
interface FanTextures {
|
||||||
|
border: Texture;
|
||||||
|
blue: Texture;
|
||||||
|
gray: Texture;
|
||||||
|
green: Texture;
|
||||||
|
red: Texture;
|
||||||
|
yellow: Texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IIscsFanData extends GraphicData {
|
||||||
|
get code(): string;
|
||||||
|
set code(v: string);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IscsFan extends JlGraphic {
|
||||||
|
static Type = 'IscsFan';
|
||||||
|
_border: Sprite;
|
||||||
|
_fan: Sprite;
|
||||||
|
fanTextures: FanTextures;
|
||||||
|
__state = 0;
|
||||||
|
|
||||||
|
constructor(fanTextures: FanTextures) {
|
||||||
|
super(IscsFan.Type);
|
||||||
|
this.fanTextures = fanTextures;
|
||||||
|
this._border = new Sprite();
|
||||||
|
this._border.texture = this.fanTextures.border;
|
||||||
|
this._border.anchor.set(0.5);
|
||||||
|
this._fan = new Sprite();
|
||||||
|
this._fan.texture = this.fanTextures.gray;
|
||||||
|
this._fan.anchor.set(0.5);
|
||||||
|
this.addChild(this._border);
|
||||||
|
this.addChild(this._fan);
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
if (this.__state === 0) {
|
||||||
|
// 停止
|
||||||
|
this.stopFanRun();
|
||||||
|
this._fan.rotation = 0;
|
||||||
|
this._fan.texture = this.fanTextures.gray;
|
||||||
|
} else if (this.__state === 1) {
|
||||||
|
// 正常运行
|
||||||
|
this._fan.texture = this.fanTextures.green;
|
||||||
|
// 动画
|
||||||
|
this.initFanRun();
|
||||||
|
} else if (this.__state === 2) {
|
||||||
|
// 报警运行
|
||||||
|
this._fan.texture = this.fanTextures.yellow;
|
||||||
|
// 动画
|
||||||
|
this.initFanRun();
|
||||||
|
} else if (this.__state === 3) {
|
||||||
|
// 故障
|
||||||
|
this.stopFanRun();
|
||||||
|
this._fan.rotation = 0;
|
||||||
|
this._fan.texture = this.fanTextures.red;
|
||||||
|
} else if (this.__state === 4) {
|
||||||
|
// 通信故障
|
||||||
|
// 停止
|
||||||
|
this.stopFanRun();
|
||||||
|
this._fan.rotation = 0;
|
||||||
|
this._fan.texture = this.fanTextures.blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initFanRun() {
|
||||||
|
// 动画
|
||||||
|
const name = 'fan_run';
|
||||||
|
let fanRun = this.animation(name);
|
||||||
|
if (!fanRun) {
|
||||||
|
fanRun = GraphicAnimation.init({
|
||||||
|
name: 'fan_run',
|
||||||
|
run: (dt: number) => {
|
||||||
|
this._fan.angle = (this._fan.angle + dt) % 360;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.addAnimation(fanRun);
|
||||||
|
}
|
||||||
|
const speed = Math.round(Math.random() * 10) + 1;
|
||||||
|
fanRun.xSpeed = speed;
|
||||||
|
fanRun.resume();
|
||||||
|
}
|
||||||
|
stopFanRun() {
|
||||||
|
const name = 'fan_run';
|
||||||
|
const fanRun = this.animation(name);
|
||||||
|
if (fanRun) {
|
||||||
|
fanRun.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IscsFanTemplate extends JlGraphicTemplate<IscsFan> {
|
||||||
|
fanTextures?: FanTextures;
|
||||||
|
constructor() {
|
||||||
|
super(IscsFan.Type);
|
||||||
|
}
|
||||||
|
new(): IscsFan {
|
||||||
|
if (this.fanTextures) {
|
||||||
|
return new IscsFan(this.fanTextures);
|
||||||
|
}
|
||||||
|
throw new Error('资源未加载/加载失败');
|
||||||
|
}
|
||||||
|
async loadAssets(): Promise<FanTextures> {
|
||||||
|
const texture = await Assets.load(ISCS_FAN_Assets);
|
||||||
|
const iscsFanSheet = new Spritesheet(texture, ISCS_FAN_JSON);
|
||||||
|
const result = await iscsFanSheet.parse();
|
||||||
|
this.fanTextures = {
|
||||||
|
border: result['fan-border.png'],
|
||||||
|
blue: result['fan-blue.png'],
|
||||||
|
gray: result['fan-gray.png'],
|
||||||
|
green: result['fan-green.png'],
|
||||||
|
red: result['fan-red.png'],
|
||||||
|
yellow: result['fan-yellow.png'],
|
||||||
|
};
|
||||||
|
return this.fanTextures as FanTextures;
|
||||||
|
}
|
||||||
|
}
|
77
src/graphics/iscs-fan/IscsFanDrawAssistant.ts
Normal file
77
src/graphics/iscs-fan/IscsFanDrawAssistant.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { FederatedMouseEvent, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { IIscsFanData, IscsFan, IscsFanTemplate } from './IscsFan';
|
||||||
|
|
||||||
|
export class IscsFanDraw extends GraphicDrawAssistant<
|
||||||
|
IscsFanTemplate,
|
||||||
|
IIscsFanData
|
||||||
|
> {
|
||||||
|
_iscsFan: IscsFan | null = null;
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => IIscsFanData) {
|
||||||
|
const template = new IscsFanTemplate();
|
||||||
|
super(app, template, createData, IscsFan.Type, '风机');
|
||||||
|
IscsFanInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
if (!this._iscsFan) {
|
||||||
|
this._iscsFan = this.graphicTemplate.new();
|
||||||
|
this.container.addChild(this._iscsFan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get iscsFan(): IscsFan {
|
||||||
|
if (!this._iscsFan) {
|
||||||
|
throw new Error('风机绘制逻辑异常');
|
||||||
|
}
|
||||||
|
return this._iscsFan;
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(cp: Point): void {
|
||||||
|
this.iscsFan.position.copyFrom(cp);
|
||||||
|
}
|
||||||
|
onLeftUp(e: FederatedMouseEvent): void {
|
||||||
|
this.iscsFan.position.copyFrom(this.toCanvasCoordinates(e.global));
|
||||||
|
this.createAndStore(false);
|
||||||
|
}
|
||||||
|
prepareData(data: IIscsFanData): boolean {
|
||||||
|
data.transform = this.iscsFan.saveTransform();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
onEsc(): void {
|
||||||
|
this.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IscsFanInteraction extends GraphicInteractionPlugin<IscsFan> {
|
||||||
|
static Name = 'iscs_fan_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(IscsFanInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new IscsFanInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): IscsFan[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === IscsFan.Type)
|
||||||
|
.map((g) => g as IscsFan);
|
||||||
|
}
|
||||||
|
bind(g: IscsFan): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
}
|
||||||
|
unbind(g: IscsFan): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
66
src/graphics/iscs-fan/iscs-fan-data.json
Normal file
66
src/graphics/iscs-fan/iscs-fan-data.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{"frames": {
|
||||||
|
|
||||||
|
"fan-blue.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-border.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":41,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-gray.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":82,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-green.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":123,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-red.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":164,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-yellow.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":205,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
}},
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "1.1",
|
||||||
|
"image": "test-iscs-fan.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {"w":246,"h":41},
|
||||||
|
"scale": "1",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:e7620bd2d73cc0b3e2deea9704e7eefc:f129a1d9e4b9ba57720b3861c22b155b:eb2d421f7759984b7713aa4aa5354134$"
|
||||||
|
}
|
||||||
|
}
|
BIN
src/graphics/iscs-fan/iscs-fan-spritesheet.png
Normal file
BIN
src/graphics/iscs-fan/iscs-fan-spritesheet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
102
src/graphics/link/Link.ts
Normal file
102
src/graphics/link/Link.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { Color, Graphics, IPointData } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
convertToBezierParams,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { ILineGraphic } from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
|
||||||
|
export interface ILinkData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get curve(): boolean; // 是否曲线
|
||||||
|
set curve(v: boolean);
|
||||||
|
get segmentsCount(): number; // 曲线分段数
|
||||||
|
set segmentsCount(v: number);
|
||||||
|
get points(): IPointData[]; // 线坐标点
|
||||||
|
set points(points: IPointData[]);
|
||||||
|
get lineWidth(): number; // 线宽
|
||||||
|
set lineWidth(v: number);
|
||||||
|
get lineColor(): string; // 线色
|
||||||
|
set lineColor(v: string);
|
||||||
|
clone(): ILinkData;
|
||||||
|
copyFrom(data: ILinkData): void;
|
||||||
|
eq(other: ILinkData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Link extends JlGraphic implements ILineGraphic {
|
||||||
|
static Type = 'Link';
|
||||||
|
lineGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super(Link.Type);
|
||||||
|
this.lineGraphic = new Graphics();
|
||||||
|
this.addChild(this.lineGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): ILinkData {
|
||||||
|
return this.getDatas<ILinkData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
if (this.datas.points.length < 2) {
|
||||||
|
throw new Error('Link坐标数据异常');
|
||||||
|
}
|
||||||
|
this.lineGraphic.clear();
|
||||||
|
this.lineGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColor)
|
||||||
|
);
|
||||||
|
if (this.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
const bps = convertToBezierParams(this.datas.points);
|
||||||
|
bps.forEach((bp) => {
|
||||||
|
this.lineGraphic.drawBezierCurve(
|
||||||
|
bp.p1,
|
||||||
|
bp.p2,
|
||||||
|
bp.cp1,
|
||||||
|
bp.cp2,
|
||||||
|
this.datas.segmentsCount
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
const start = this.getStartPoint();
|
||||||
|
this.lineGraphic.moveTo(start.x, start.y);
|
||||||
|
for (let i = 0; i < this.datas.points.length; i++) {
|
||||||
|
const p = this.datas.points[i];
|
||||||
|
this.lineGraphic.lineTo(p.x, p.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get linePoints(): IPointData[] {
|
||||||
|
return this.datas.points;
|
||||||
|
}
|
||||||
|
set linePoints(points: IPointData[]) {
|
||||||
|
const old = this.datas.clone();
|
||||||
|
old.points = points;
|
||||||
|
this.updateData(old);
|
||||||
|
}
|
||||||
|
getStartPoint(): IPointData {
|
||||||
|
return this.datas.points[0];
|
||||||
|
}
|
||||||
|
getEndPoint(): IPointData {
|
||||||
|
return this.datas.points[this.datas.points.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkTemplate extends JlGraphicTemplate<Link> {
|
||||||
|
curve: boolean;
|
||||||
|
lineWidth: number;
|
||||||
|
lineColor: string;
|
||||||
|
segmentsCount: number;
|
||||||
|
constructor() {
|
||||||
|
super(Link.Type);
|
||||||
|
this.lineWidth = 2;
|
||||||
|
this.lineColor = '#000000';
|
||||||
|
this.curve = false;
|
||||||
|
this.segmentsCount = 10;
|
||||||
|
}
|
||||||
|
new(): Link {
|
||||||
|
return new Link();
|
||||||
|
}
|
||||||
|
}
|
375
src/graphics/link/LinkDrawAssistant.ts
Normal file
375
src/graphics/link/LinkDrawAssistant.ts
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
import {
|
||||||
|
Color,
|
||||||
|
DisplayObject,
|
||||||
|
FederatedMouseEvent,
|
||||||
|
FederatedPointerEvent,
|
||||||
|
Graphics,
|
||||||
|
IHitArea,
|
||||||
|
Point,
|
||||||
|
} from 'pixi.js';
|
||||||
|
import {
|
||||||
|
AbsorbablePosition,
|
||||||
|
DraggablePoint,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicTransformEvent,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
KeyListener,
|
||||||
|
calculateMirrorPoint,
|
||||||
|
convertToBezierParams,
|
||||||
|
linePoint,
|
||||||
|
pointPolygon,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import AbsorbablePoint, {
|
||||||
|
AbsorbableCircle,
|
||||||
|
} from 'src/jl-graphic/graphic/AbsorbablePosition';
|
||||||
|
import {
|
||||||
|
BezierCurveEditPlugin,
|
||||||
|
ILineGraphic,
|
||||||
|
PolylineEditPlugin,
|
||||||
|
addWayPoint,
|
||||||
|
addWaypointConfig,
|
||||||
|
clearWayPoint,
|
||||||
|
clearWaypointsConfig,
|
||||||
|
getWaypointRangeIndex,
|
||||||
|
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
|
||||||
|
import { ILinkData, Link, LinkTemplate } from './Link';
|
||||||
|
|
||||||
|
export interface ILinkDrawOptions {
|
||||||
|
newData: () => ILinkData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkDraw extends GraphicDrawAssistant<LinkTemplate, ILinkData> {
|
||||||
|
points: Point[] = [];
|
||||||
|
graphic: Graphics = new Graphics();
|
||||||
|
|
||||||
|
// 快捷切曲线绘制
|
||||||
|
keyqListener: KeyListener = new KeyListener({
|
||||||
|
value: 'KeyQ',
|
||||||
|
global: true,
|
||||||
|
onPress: () => {
|
||||||
|
if (this.points.length == 0) {
|
||||||
|
this.graphicTemplate.curve = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 快捷切直线绘制
|
||||||
|
keyzListener: KeyListener = new KeyListener({
|
||||||
|
value: 'KeyZ',
|
||||||
|
global: true,
|
||||||
|
onPress: () => {
|
||||||
|
if (this.points.length == 0) {
|
||||||
|
this.graphicTemplate.curve = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => ILinkData) {
|
||||||
|
super(app, new LinkTemplate(), createData, Link.Type, '轨道Link');
|
||||||
|
this.container.addChild(this.graphic);
|
||||||
|
this.graphicTemplate.curve = true;
|
||||||
|
|
||||||
|
LinkPointsEditPlugin.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
this.app.addKeyboardListener(this.keyqListener, this.keyzListener);
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
this.app.removeKeyboardListener(this.keyqListener, this.keyzListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.points = [];
|
||||||
|
this.graphic.clear();
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
if (this.graphicTemplate.curve) {
|
||||||
|
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.curve) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
if (this.points.length < 1) return;
|
||||||
|
this.graphic.clear();
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
this.graphic.lineStyle(template.lineWidth, new Color(template.lineColor));
|
||||||
|
const ps = [...this.points];
|
||||||
|
if (template.curve) {
|
||||||
|
// 曲线
|
||||||
|
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);
|
||||||
|
// 直线
|
||||||
|
this.graphic.moveTo(ps[0].x, ps[0].y);
|
||||||
|
for (let i = 1; i < ps.length; i++) {
|
||||||
|
const p = ps[i];
|
||||||
|
this.graphic.lineTo(p.x, p.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prepareData(data: ILinkData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
if (
|
||||||
|
(!template.curve && this.points.length < 2) ||
|
||||||
|
(template.curve && this.points.length < 4)
|
||||||
|
) {
|
||||||
|
console.log('Link绘制因点不够取消绘制');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (template.curve) {
|
||||||
|
this.points.pop();
|
||||||
|
}
|
||||||
|
data.curve = template.curve;
|
||||||
|
data.segmentsCount = template.segmentsCount;
|
||||||
|
data.points = this.points;
|
||||||
|
data.lineWidth = template.lineWidth;
|
||||||
|
data.lineColor = template.lineColor;
|
||||||
|
data.segmentsCount = template.segmentsCount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkGraphicHitArea implements IHitArea {
|
||||||
|
link: Link;
|
||||||
|
constructor(link: Link) {
|
||||||
|
this.link = link;
|
||||||
|
}
|
||||||
|
contains(x: number, y: number): boolean {
|
||||||
|
const p = new Point(x, y);
|
||||||
|
if (this.link.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
const bps = convertToBezierParams(this.link.datas.points);
|
||||||
|
for (let i = 0; i < bps.length; i++) {
|
||||||
|
const bp = bps[i];
|
||||||
|
if (
|
||||||
|
pointPolygon(
|
||||||
|
p,
|
||||||
|
[bp.p1, bp.cp1, bp.cp2, bp.p2],
|
||||||
|
this.link.datas.lineWidth
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
for (let i = 1; i < this.link.datas.points.length; i++) {
|
||||||
|
const p1 = this.link.datas.points[i - 1];
|
||||||
|
const p2 = this.link.datas.points[i];
|
||||||
|
if (linePoint(p1, p2, p, this.link.datas.lineWidth)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建吸附位置
|
||||||
|
* @param link
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function buildAbsorbablePositions(link: Link): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
const links = link.queryStore.queryByType<Link>(Link.Type);
|
||||||
|
links.forEach((other) => {
|
||||||
|
if (other.id == link.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const apa = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getStartPoint())
|
||||||
|
);
|
||||||
|
const apb = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getEndPoint())
|
||||||
|
);
|
||||||
|
aps.push(apa, apb);
|
||||||
|
});
|
||||||
|
|
||||||
|
aps.push(new AbsorbableCircle(new Point(450, 410), 30));
|
||||||
|
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 端点拖拽添加吸附位置
|
||||||
|
* @param g
|
||||||
|
* @param dp
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
function onEditPointCreate(
|
||||||
|
g: ILineGraphic,
|
||||||
|
dp: DraggablePoint,
|
||||||
|
index: number
|
||||||
|
): void {
|
||||||
|
const link = g as Link;
|
||||||
|
if (index === 0 || index == link.datas.points.length - 1) {
|
||||||
|
// 端点
|
||||||
|
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
link.getGraphicApp().setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(link),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinkEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '轨道编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [addWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* link路径编辑
|
||||||
|
*/
|
||||||
|
export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
|
||||||
|
static Name = 'LinkPointsDrag';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(LinkPointsEditPlugin.Name, app);
|
||||||
|
app.registerMenu(LinkEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp): LinkPointsEditPlugin {
|
||||||
|
return new LinkPointsEditPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Link[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type == Link.Type) as Link[];
|
||||||
|
}
|
||||||
|
bind(g: Link): void {
|
||||||
|
g.lineGraphic.eventMode = 'static';
|
||||||
|
g.lineGraphic.cursor = 'pointer';
|
||||||
|
g.lineGraphic.hitArea = new LinkGraphicHitArea(g);
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
g.on('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
unbind(g: Link): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
g.off('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const link = target.getGraphic() as Link;
|
||||||
|
this.app.updateSelected(link);
|
||||||
|
|
||||||
|
addWaypointConfig.handler = () => {
|
||||||
|
const linePoints = link.linePoints;
|
||||||
|
const p = link.screenToLocalPoint(e.global);
|
||||||
|
const { start, end } = getWaypointRangeIndex(
|
||||||
|
linePoints,
|
||||||
|
link.datas.curve,
|
||||||
|
p
|
||||||
|
);
|
||||||
|
addWayPoint(link, link.datas.curve, start, end, p);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(link, link.datas.curve);
|
||||||
|
};
|
||||||
|
LinkEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject): void {
|
||||||
|
const link = g as Link;
|
||||||
|
let lep;
|
||||||
|
if (link.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
lep = link.getAssistantAppend<BezierCurveEditPlugin>(
|
||||||
|
BezierCurveEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (!lep) {
|
||||||
|
lep = new BezierCurveEditPlugin(link, {
|
||||||
|
onEditPointCreate,
|
||||||
|
});
|
||||||
|
link.addAssistantAppend(lep);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
lep = link.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (!lep) {
|
||||||
|
lep = new PolylineEditPlugin(link, { onEditPointCreate });
|
||||||
|
link.addAssistantAppend(lep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lep.showAll();
|
||||||
|
}
|
||||||
|
onUnselected(g: DisplayObject): void {
|
||||||
|
const link = g as Link;
|
||||||
|
let lep;
|
||||||
|
if (link.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
lep = link.getAssistantAppend<BezierCurveEditPlugin>(
|
||||||
|
BezierCurveEditPlugin.Name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
lep = link.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (lep) {
|
||||||
|
lep.hideAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
203
src/graphics/platform/Platform.ts
Normal file
203
src/graphics/platform/Platform.ts
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
import { Color, Graphics, IPointData, Rectangle } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
getRectangleCenter,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface IPlatformData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get hasdoor(): boolean; // 是否有屏蔽门
|
||||||
|
set hasdoor(v: boolean);
|
||||||
|
get trainDirection(): string; // 行驶方向--屏蔽门上下
|
||||||
|
set trainDirection(v: string);
|
||||||
|
get lineWidth(): number; // 线宽
|
||||||
|
set lineWidth(v: number);
|
||||||
|
get lineColor(): string; // 站台线色
|
||||||
|
set lineColor(v: string);
|
||||||
|
get lineColorDoor(): string; // 屏蔽门线色
|
||||||
|
set lineColorDoor(v: string);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
get width(): number; // 宽度
|
||||||
|
set width(v: number);
|
||||||
|
get height(): number; // 高度
|
||||||
|
set height(v: number);
|
||||||
|
clone(): IPlatformData;
|
||||||
|
copyFrom(data: IPlatformData): void;
|
||||||
|
eq(other: IPlatformData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
//站台颜色
|
||||||
|
export enum PlatformColorEnum {
|
||||||
|
blue = '0x0fe81f', //站台的颜色
|
||||||
|
lightBlue = '0x55d15d',
|
||||||
|
yellow = '0xfbff00',
|
||||||
|
white = '0xffffff',
|
||||||
|
lozengeRed = '0xff0000', //站台旁的菱形图标
|
||||||
|
whiteNumbers = '0xffffff', //站台旁白色数字
|
||||||
|
HCharYellow = '0xfbff00', //站台旁的H字符
|
||||||
|
HCharWhite = '0xffffff',
|
||||||
|
HCharRed = '0xff0000',
|
||||||
|
doorBlue = '0x008000', //屏蔽门的颜色
|
||||||
|
doorRed = '0xff0000',
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformConsts = {
|
||||||
|
width: 60,
|
||||||
|
height: 20,
|
||||||
|
lineWidth: 3,
|
||||||
|
besideFontSize: 12,
|
||||||
|
doorOpenSpacing: 5,
|
||||||
|
doorPlatformSpacing: 10,
|
||||||
|
besideSpacing: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Platform extends JlGraphic {
|
||||||
|
static Type = 'Platform';
|
||||||
|
|
||||||
|
platformGraphic: Graphics;
|
||||||
|
doorGraphic: Graphics;
|
||||||
|
doorCloseGraphic: Graphics;
|
||||||
|
besideGraphic: Graphics;
|
||||||
|
codeGraph: VectorText = new VectorText(''); //站台旁数字、字符
|
||||||
|
constructor() {
|
||||||
|
super(Platform.Type);
|
||||||
|
this.platformGraphic = new Graphics();
|
||||||
|
this.doorGraphic = new Graphics();
|
||||||
|
this.doorCloseGraphic = new Graphics();
|
||||||
|
this.besideGraphic = new Graphics();
|
||||||
|
this.addChild(this.platformGraphic);
|
||||||
|
this.addChild(this.doorGraphic);
|
||||||
|
this.addChild(this.doorCloseGraphic);
|
||||||
|
this.addChild(this.besideGraphic);
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
this.codeGraph.setVectorFontSize(platformConsts.besideFontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IPlatformData {
|
||||||
|
return this.getDatas<IPlatformData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
const width = this.datas.width;
|
||||||
|
const height = this.datas.height;
|
||||||
|
//屏蔽门
|
||||||
|
const doorGraphic = this.doorGraphic;
|
||||||
|
const doorCloseGraphic = this.doorCloseGraphic;
|
||||||
|
doorGraphic.clear();
|
||||||
|
doorCloseGraphic.clear();
|
||||||
|
if (this.datas.hasdoor) {
|
||||||
|
doorGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColorDoor)
|
||||||
|
);
|
||||||
|
doorGraphic.moveTo(-width / 2 - this.datas.lineWidth / 2, 0);
|
||||||
|
doorGraphic.lineTo(-platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorGraphic.moveTo(platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorGraphic.lineTo(width / 2 + this.datas.lineWidth / 2, 0);
|
||||||
|
//屏蔽门闭合
|
||||||
|
doorCloseGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColorDoor)
|
||||||
|
);
|
||||||
|
doorCloseGraphic.moveTo(-platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorCloseGraphic.lineTo(platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorGraphic.position.set(
|
||||||
|
0,
|
||||||
|
-height / 2 - platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
doorCloseGraphic.position.set(
|
||||||
|
0,
|
||||||
|
-height / 2 - platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//站台
|
||||||
|
const platformGraphic = this.platformGraphic;
|
||||||
|
platformGraphic.clear();
|
||||||
|
platformGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColor)
|
||||||
|
);
|
||||||
|
platformGraphic.beginFill(this.datas.lineColor, 1);
|
||||||
|
platformGraphic.drawRect(0, 0, this.datas.width, this.datas.height);
|
||||||
|
platformGraphic.endFill;
|
||||||
|
const rectP = new Rectangle(0, 0, this.datas.width, this.datas.height);
|
||||||
|
platformGraphic.pivot = getRectangleCenter(rectP);
|
||||||
|
this.position.set(this.datas.point.x, this.datas.point.y);
|
||||||
|
//站台旁菱形图标
|
||||||
|
const besideGraphic = this.besideGraphic;
|
||||||
|
besideGraphic.clear();
|
||||||
|
besideGraphic.lineStyle(1, new Color(PlatformColorEnum.lozengeRed));
|
||||||
|
besideGraphic.drawRect(0, 0, this.datas.height / 4, this.datas.height / 4);
|
||||||
|
const rect = new Rectangle(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.datas.height / 4,
|
||||||
|
this.datas.height / 4
|
||||||
|
);
|
||||||
|
besideGraphic.pivot = getRectangleCenter(rect);
|
||||||
|
besideGraphic.rotation = Math.PI / 4;
|
||||||
|
besideGraphic.position.set(
|
||||||
|
-width / 2 - this.datas.lineWidth / 2 - platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
//站台旁的数字、字符
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
codeGraph.text = 'H';
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
codeGraph.position.set(
|
||||||
|
-width / 2 - this.datas.lineWidth / 2 - platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
codeGraph.style.fill = PlatformColorEnum.HCharYellow;
|
||||||
|
//站台方向
|
||||||
|
if (this.datas.trainDirection == 'right') {
|
||||||
|
doorGraphic.position.set(
|
||||||
|
0,
|
||||||
|
height / 2 + platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
doorCloseGraphic.position.set(
|
||||||
|
0,
|
||||||
|
height / 2 + platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
besideGraphic.position.set(
|
||||||
|
width / 2 + this.datas.lineWidth / 2 + platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
codeGraph.position.set(
|
||||||
|
width / 2 + this.datas.lineWidth / 2 + platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//子元素显隐
|
||||||
|
doorCloseGraphic.visible = false;
|
||||||
|
/* besideGraphic.visible = false;
|
||||||
|
codeGraph.visible = false; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlatformTemplate extends JlGraphicTemplate<Platform> {
|
||||||
|
hasdoor: boolean;
|
||||||
|
trainDirection: string;
|
||||||
|
lineWidth: number;
|
||||||
|
lineColor: string;
|
||||||
|
lineColorDoor: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
constructor() {
|
||||||
|
super(Platform.Type);
|
||||||
|
this.hasdoor = true;
|
||||||
|
this.trainDirection = 'left';
|
||||||
|
this.lineWidth = platformConsts.lineWidth;
|
||||||
|
this.lineColor = PlatformColorEnum.yellow;
|
||||||
|
this.lineColorDoor = PlatformColorEnum.doorBlue;
|
||||||
|
this.width = platformConsts.width;
|
||||||
|
this.height = platformConsts.height;
|
||||||
|
}
|
||||||
|
new(): Platform {
|
||||||
|
return new Platform();
|
||||||
|
}
|
||||||
|
}
|
132
src/graphics/platform/PlatformDrawAssistant.ts
Normal file
132
src/graphics/platform/PlatformDrawAssistant.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import {
|
||||||
|
Color,
|
||||||
|
FederatedPointerEvent,
|
||||||
|
Graphics,
|
||||||
|
Point,
|
||||||
|
Rectangle,
|
||||||
|
} from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
getRectangleCenter,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
import { IPlatformData, Platform, PlatformTemplate } from './Platform';
|
||||||
|
|
||||||
|
export interface IPlatformDrawOptions {
|
||||||
|
newData: () => IPlatformData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlatformDraw extends GraphicDrawAssistant<
|
||||||
|
PlatformTemplate,
|
||||||
|
IPlatformData
|
||||||
|
> {
|
||||||
|
point: Point = new Point(0, 0);
|
||||||
|
platformGraphic: Graphics = new Graphics();
|
||||||
|
doorGraphic: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => IPlatformData) {
|
||||||
|
super(
|
||||||
|
app,
|
||||||
|
new PlatformTemplate(),
|
||||||
|
createData,
|
||||||
|
Platform.Type,
|
||||||
|
'站台Platform'
|
||||||
|
);
|
||||||
|
this.container.addChild(this.platformGraphic);
|
||||||
|
this.container.addChild(this.doorGraphic);
|
||||||
|
this.graphicTemplate.hasdoor = true;
|
||||||
|
platformInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.platformGraphic.clear();
|
||||||
|
this.doorGraphic.clear();
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
this.point = p;
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
//屏蔽门
|
||||||
|
if (template.hasdoor) {
|
||||||
|
const doorGraphic = this.doorGraphic;
|
||||||
|
doorGraphic.clear();
|
||||||
|
doorGraphic.lineStyle(
|
||||||
|
template.lineWidth,
|
||||||
|
new Color(template.lineColorDoor)
|
||||||
|
);
|
||||||
|
const width = template.width;
|
||||||
|
const height = template.height;
|
||||||
|
doorGraphic.moveTo(-width / 2 - template.lineWidth / 2, -height / 2 - 10);
|
||||||
|
doorGraphic.lineTo(width / 2 + template.lineWidth / 2, -height / 2 - 10);
|
||||||
|
doorGraphic.position.set(p.x, p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//站台
|
||||||
|
const platformGraphic = this.platformGraphic;
|
||||||
|
platformGraphic.clear();
|
||||||
|
this.point.set(p.x, p.y);
|
||||||
|
const rect = new Rectangle(0, 0, template.width, template.height);
|
||||||
|
platformGraphic.pivot = getRectangleCenter(rect);
|
||||||
|
platformGraphic.lineStyle(template.lineWidth, template.lineColor);
|
||||||
|
platformGraphic.beginFill(template.lineColor, 1);
|
||||||
|
platformGraphic.drawRect(0, 0, template.width, template.height);
|
||||||
|
platformGraphic.endFill;
|
||||||
|
platformGraphic.position.set(this.point.x, this.point.y);
|
||||||
|
}
|
||||||
|
prepareData(data: IPlatformData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.hasdoor = template.hasdoor;
|
||||||
|
data.trainDirection = template.trainDirection;
|
||||||
|
data.point = this.point;
|
||||||
|
data.lineWidth = template.lineWidth;
|
||||||
|
data.lineColor = template.lineColor;
|
||||||
|
data.lineColorDoor = template.lineColorDoor;
|
||||||
|
data.width = template.width;
|
||||||
|
data.height = template.height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class platformInteraction extends GraphicInteractionPlugin<Platform> {
|
||||||
|
static Name = 'platform_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(platformInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
unbind(g: Platform): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
86
src/graphics/rect/Rect.ts
Normal file
86
src/graphics/rect/Rect.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { Color, Graphics, IPointData, Point } from 'pixi.js';
|
||||||
|
import { GraphicData, JlGraphic, JlGraphicTemplate } from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface IRectData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get lineWidth(): number; // 线宽
|
||||||
|
set lineWidth(v: number);
|
||||||
|
get lineColor(): string; // 线色
|
||||||
|
set lineColor(v: string);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
get width(): number; // 宽度
|
||||||
|
set width(v: number);
|
||||||
|
get height(): number; // 高度
|
||||||
|
set height(v: number);
|
||||||
|
get points(): IPointData[]; // 线坐标点
|
||||||
|
set points(points: IPointData[]);
|
||||||
|
clone(): IRectData;
|
||||||
|
copyFrom(data: IRectData): void;
|
||||||
|
eq(other: IRectData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rectConsts = {
|
||||||
|
lineWidth: 2,
|
||||||
|
lineColor: '0xff0000',
|
||||||
|
width: 60,
|
||||||
|
height: 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Rect extends JlGraphic {
|
||||||
|
static Type = 'Rect';
|
||||||
|
rectGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super(Rect.Type);
|
||||||
|
this.rectGraphic = new Graphics();
|
||||||
|
this.addChild(this.rectGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IRectData {
|
||||||
|
return this.getDatas<IRectData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
const width = this.datas.width;
|
||||||
|
const height = this.datas.height;
|
||||||
|
if (this.linePoints.length == 0) {
|
||||||
|
const r1 = new Point(this.datas.point.x, this.datas.point.y);
|
||||||
|
const r2 = new Point(r1.x + width, r1.y);
|
||||||
|
const r3 = new Point(r1.x + width, r1.y + height);
|
||||||
|
const r4 = new Point(r1.x, r1.y + height);
|
||||||
|
this.datas.points = [r1, r2, r3, r4, r1];
|
||||||
|
}
|
||||||
|
const rectGraphic = this.rectGraphic;
|
||||||
|
rectGraphic.clear();
|
||||||
|
rectGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColor)
|
||||||
|
);
|
||||||
|
rectGraphic.drawPolygon(this.datas.points);
|
||||||
|
}
|
||||||
|
get linePoints(): IPointData[] {
|
||||||
|
return this.datas.points;
|
||||||
|
}
|
||||||
|
set linePoints(points: IPointData[]) {
|
||||||
|
const old = this.datas.clone();
|
||||||
|
old.points = points;
|
||||||
|
this.updateData(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RectTemplate extends JlGraphicTemplate<Rect> {
|
||||||
|
lineWidth: number;
|
||||||
|
lineColor: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
constructor() {
|
||||||
|
super(Rect.Type);
|
||||||
|
this.lineWidth = rectConsts.lineWidth;
|
||||||
|
this.lineColor = rectConsts.lineColor;
|
||||||
|
this.width = rectConsts.width;
|
||||||
|
this.height = rectConsts.height;
|
||||||
|
}
|
||||||
|
new(): Rect {
|
||||||
|
return new Rect();
|
||||||
|
}
|
||||||
|
}
|
262
src/graphics/rect/RectDrawAssistant.ts
Normal file
262
src/graphics/rect/RectDrawAssistant.ts
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
import {
|
||||||
|
FederatedPointerEvent,
|
||||||
|
Graphics,
|
||||||
|
Point,
|
||||||
|
IHitArea,
|
||||||
|
DisplayObject,
|
||||||
|
FederatedMouseEvent,
|
||||||
|
} from 'pixi.js';
|
||||||
|
import {
|
||||||
|
DraggablePoint,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicTransformEvent,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
linePoint,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
import AbsorbablePoint, {
|
||||||
|
AbsorbablePosition,
|
||||||
|
} from 'src/jl-graphic/graphic/AbsorbablePosition';
|
||||||
|
import {
|
||||||
|
ILineGraphic,
|
||||||
|
PolylineEditPlugin,
|
||||||
|
addWaySegmentingConfig,
|
||||||
|
addPolygonSegmentingPoint,
|
||||||
|
clearWayPoint,
|
||||||
|
clearWaypointsConfig,
|
||||||
|
getWayLineIndex,
|
||||||
|
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
|
||||||
|
|
||||||
|
import { IRectData, Rect, RectTemplate } from './Rect';
|
||||||
|
import { Link } from '../link/Link';
|
||||||
|
|
||||||
|
export interface IRectDrawOptions {
|
||||||
|
newData: () => IRectData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RectDraw extends GraphicDrawAssistant<RectTemplate, IRectData> {
|
||||||
|
point1: Point | null = null;
|
||||||
|
point2: Point | null = null;
|
||||||
|
rectGraphic: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => IRectData) {
|
||||||
|
super(app, new RectTemplate(), createData, Rect.Type, '站台Rect');
|
||||||
|
this.container.addChild(this.rectGraphic);
|
||||||
|
RectPointsEditPlugin.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.rectGraphic.clear();
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
if (this.point1 === null) {
|
||||||
|
this.point1 = p;
|
||||||
|
} else {
|
||||||
|
this.point2 = p;
|
||||||
|
this.createAndStore(true);
|
||||||
|
this.point1 = null;
|
||||||
|
this.point2 = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
if (this.point1 === null) return;
|
||||||
|
const rectGraphic = this.rectGraphic;
|
||||||
|
rectGraphic.clear();
|
||||||
|
rectGraphic.lineStyle(template.lineWidth, template.lineColor);
|
||||||
|
rectGraphic.drawRect(...this.normalize(this.point1, p));
|
||||||
|
}
|
||||||
|
//根据画的两个点确定左上角的点的坐标和矩形宽高
|
||||||
|
private normalize(p1: Point, p2: Point): [number, number, number, number] {
|
||||||
|
const { abs } = Math;
|
||||||
|
const x = p1.x < p2.x ? p1.x : p2.x;
|
||||||
|
const y = p1.y < p2.y ? p1.y : p2.y;
|
||||||
|
const w = abs(p1.x - p2.x);
|
||||||
|
const h = abs(p1.y - p2.y);
|
||||||
|
return [x, y, w, h];
|
||||||
|
}
|
||||||
|
prepareData(data: IRectData): boolean {
|
||||||
|
if (this.point1 == null) {
|
||||||
|
console.log('Rect绘制因点不够取消绘制');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const p1 = this.point1 as Point;
|
||||||
|
const p2 = this.point2 as Point;
|
||||||
|
const [x, y, width, height] = this.normalize(p1, p2);
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.point = new Point(x, y);
|
||||||
|
data.lineWidth = template.lineWidth;
|
||||||
|
data.lineColor = template.lineColor;
|
||||||
|
data.width = width;
|
||||||
|
data.height = height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//碰撞检测
|
||||||
|
export class RectGraphicHitArea implements IHitArea {
|
||||||
|
rect: Rect;
|
||||||
|
constructor(rect: Rect) {
|
||||||
|
this.rect = rect;
|
||||||
|
}
|
||||||
|
contains(x: number, y: number): boolean {
|
||||||
|
let contains = false;
|
||||||
|
const p = new Point(x, y);
|
||||||
|
const rectData = this.rect.datas;
|
||||||
|
//contains = pointPolygon(p, rectData.points, rectData.lineWidth);是否包含多边形内部
|
||||||
|
const tolerance = rectData.lineWidth;
|
||||||
|
for (let i = 0; i < rectData.points.length - 1; i++) {
|
||||||
|
const p1 = rectData.points[i];
|
||||||
|
const p2 = rectData.points[i + 1];
|
||||||
|
contains = contains || linePoint(p1, p2, p, tolerance);
|
||||||
|
if (contains) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contains;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建吸附位置
|
||||||
|
* @param rect
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function buildAbsorbablePositions(rect: Rect): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
const rects = rect.queryStore.queryByType<Rect>(Rect.Type);
|
||||||
|
const links = rect.queryStore.queryByType<Link>(Link.Type);
|
||||||
|
|
||||||
|
links.forEach((other) => {
|
||||||
|
const apa = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getStartPoint())
|
||||||
|
);
|
||||||
|
const apb = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getEndPoint())
|
||||||
|
);
|
||||||
|
aps.push(apa, apb);
|
||||||
|
});
|
||||||
|
|
||||||
|
rects.forEach((other) => {
|
||||||
|
if (other.id == rect.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
other.linePoints.forEach((point) => {
|
||||||
|
const absorbablePoint = new AbsorbablePoint(point);
|
||||||
|
aps.push(absorbablePoint);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 端点拖拽添加吸附位置
|
||||||
|
* @param g
|
||||||
|
* @param dp
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
function onEditPointCreate(g: ILineGraphic, dp: DraggablePoint): void {
|
||||||
|
const rect = g as Rect;
|
||||||
|
// 端点
|
||||||
|
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
rect.getGraphicApp().setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(rect),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const RectEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '矩形编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [addWaySegmentingConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rect路径编辑
|
||||||
|
*/
|
||||||
|
export class RectPointsEditPlugin extends GraphicInteractionPlugin<Rect> {
|
||||||
|
static Name = 'RectPointsDrag';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(RectPointsEditPlugin.Name, app);
|
||||||
|
app.registerMenu(RectEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp): RectPointsEditPlugin {
|
||||||
|
return new RectPointsEditPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Rect[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type == Rect.Type) as Rect[];
|
||||||
|
}
|
||||||
|
bind(g: Rect): void {
|
||||||
|
g.rectGraphic.eventMode = 'static';
|
||||||
|
g.rectGraphic.cursor = 'pointer';
|
||||||
|
g.rectGraphic.hitArea = new RectGraphicHitArea(g);
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
g.on('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
unbind(g: Rect): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
g.off('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const rect = target.getGraphic() as Rect;
|
||||||
|
this.app.updateSelected(rect);
|
||||||
|
addWaySegmentingConfig.handler = () => {
|
||||||
|
const linePoints = rect.linePoints;
|
||||||
|
const p = rect.screenToLocalPoint(e.global);
|
||||||
|
const { start, end } = getWayLineIndex(linePoints, p);
|
||||||
|
addPolygonSegmentingPoint(rect, start, end);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(rect, false);
|
||||||
|
};
|
||||||
|
RectEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject): void {
|
||||||
|
const rect = g as Rect;
|
||||||
|
let lep = rect.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (!lep) {
|
||||||
|
lep = new PolylineEditPlugin(rect, { onEditPointCreate });
|
||||||
|
rect.addAssistantAppend(lep);
|
||||||
|
}
|
||||||
|
lep.showAll();
|
||||||
|
}
|
||||||
|
onUnselected(g: DisplayObject): void {
|
||||||
|
const rect = g as Rect;
|
||||||
|
const lep = rect.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (lep) {
|
||||||
|
lep.hideAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
src/graphics/signal/Signal.ts
Normal file
0
src/graphics/signal/Signal.ts
Normal file
101
src/graphics/station/Station.ts
Normal file
101
src/graphics/station/Station.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { Color, Graphics, IPointData, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface IStationData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get hasCircle(): boolean; // 是否有圆圈--线网图
|
||||||
|
set hasCircle(v: boolean);
|
||||||
|
get radius(): number; // 半径
|
||||||
|
set radius(v: number);
|
||||||
|
get borderWidth(): number; // 线宽
|
||||||
|
set borderWidth(v: number);
|
||||||
|
get borderColor(): string; // 圆边框线色
|
||||||
|
set borderColor(v: string);
|
||||||
|
get fillColor(): string; // 圆填充颜色
|
||||||
|
set fillColor(v: string);
|
||||||
|
get codeColor(): string; // 车站字体颜色
|
||||||
|
set codeColor(v: string);
|
||||||
|
get codeFontSize(): number; // 车站字体大小
|
||||||
|
set codeFontSize(v: number);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
get circlePoint(): IPointData; // 位置坐标
|
||||||
|
set circlePoint(point: IPointData);
|
||||||
|
clone(): IStationData;
|
||||||
|
copyFrom(data: IStationData): void;
|
||||||
|
eq(other: IStationData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Station extends JlGraphic {
|
||||||
|
static Type = 'station';
|
||||||
|
codeGraph: VectorText = new VectorText(''); //站台旁数字、字符
|
||||||
|
circleGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super(Station.Type);
|
||||||
|
this.circleGraphic = new Graphics();
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
this.addChild(this.circleGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IStationData {
|
||||||
|
return this.getDatas<IStationData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
this.circleGraphic.clear();
|
||||||
|
this.position.set(this.datas.point.x, this.datas.point.y);
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
if (this.datas.code == '') {
|
||||||
|
codeGraph.text = '车站Station';
|
||||||
|
} else {
|
||||||
|
codeGraph.text = this.datas.code;
|
||||||
|
}
|
||||||
|
codeGraph.style.fill = this.datas.codeColor;
|
||||||
|
codeGraph.setVectorFontSize(this.datas.codeFontSize);
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
if (this.datas.hasCircle) {
|
||||||
|
const circleGraphic = this.circleGraphic;
|
||||||
|
circleGraphic.lineStyle(
|
||||||
|
this.datas.borderWidth,
|
||||||
|
new Color(this.datas.borderColor)
|
||||||
|
);
|
||||||
|
circleGraphic.beginFill(this.datas.fillColor, 1);
|
||||||
|
circleGraphic.drawCircle(0, 0, this.datas.radius);
|
||||||
|
circleGraphic.endFill;
|
||||||
|
circleGraphic.position.set(
|
||||||
|
this.datas.circlePoint.x,
|
||||||
|
this.datas.circlePoint.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StationTemplate extends JlGraphicTemplate<Station> {
|
||||||
|
hasCircle: boolean;
|
||||||
|
radius: number;
|
||||||
|
borderWidth: number;
|
||||||
|
borderColor: string;
|
||||||
|
fillColor: string;
|
||||||
|
codeColor: string;
|
||||||
|
codeFontSize: number;
|
||||||
|
circlePoint: IPointData;
|
||||||
|
constructor() {
|
||||||
|
super(Station.Type);
|
||||||
|
this.hasCircle = false;
|
||||||
|
this.radius = 5;
|
||||||
|
this.borderWidth = 1;
|
||||||
|
this.borderColor = '0xff0000';
|
||||||
|
this.fillColor = '0xff0000';
|
||||||
|
this.codeColor = '0xF48815';
|
||||||
|
this.codeFontSize = 22;
|
||||||
|
this.circlePoint = new Point(0, -20);
|
||||||
|
}
|
||||||
|
new(): Station {
|
||||||
|
return new Station();
|
||||||
|
}
|
||||||
|
}
|
96
src/graphics/station/StationDrawAssistant.ts
Normal file
96
src/graphics/station/StationDrawAssistant.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { FederatedPointerEvent, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
import { IStationData, Station, StationTemplate } from './Station';
|
||||||
|
|
||||||
|
export interface IStationDrawOptions {
|
||||||
|
newData: () => IStationData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StationDraw extends GraphicDrawAssistant<
|
||||||
|
StationTemplate,
|
||||||
|
IStationData
|
||||||
|
> {
|
||||||
|
point: Point = new Point(0, 0);
|
||||||
|
codeGraph: VectorText = new VectorText('');
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => IStationData) {
|
||||||
|
super(app, new StationTemplate(), createData, Station.Type, '车站Station');
|
||||||
|
this.container.addChild(this.codeGraph);
|
||||||
|
stationInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
//this.codeGraph.clear();
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
this.point = p;
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
codeGraph.text = '车站Station';
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
codeGraph.style.fill = '0xf48815';
|
||||||
|
codeGraph.position.set(p.x, p.y);
|
||||||
|
codeGraph.setVectorFontSize(22);
|
||||||
|
}
|
||||||
|
prepareData(data: IStationData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.point = this.point;
|
||||||
|
data.hasCircle = template.hasCircle;
|
||||||
|
data.radius = template.radius;
|
||||||
|
data.borderWidth = template.borderWidth;
|
||||||
|
data.borderColor = template.borderColor;
|
||||||
|
data.fillColor = template.fillColor;
|
||||||
|
data.codeColor = template.codeColor;
|
||||||
|
data.codeFontSize = template.codeFontSize;
|
||||||
|
data.circlePoint = template.circlePoint;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class stationInteraction extends GraphicInteractionPlugin<Station> {
|
||||||
|
static Name = 'station_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(stationInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
unbind(g: Station): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
185
src/graphics/train/Train.ts
Normal file
185
src/graphics/train/Train.ts
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import { Color, Graphics, IPointData } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface ITrainData extends GraphicData {
|
||||||
|
get code(): string; // 车号
|
||||||
|
set code(v: string);
|
||||||
|
get codeColor(): string; // 车号颜色
|
||||||
|
set codeColor(v: string);
|
||||||
|
get codeFontSize(): number; // 车号字体大小
|
||||||
|
set codeFontSize(v: number);
|
||||||
|
get trainDirection(): string; // 行驶方向
|
||||||
|
set trainDirection(v: string);
|
||||||
|
get hasBorder(): boolean; // 是否有边框
|
||||||
|
set hasBorder(v: boolean);
|
||||||
|
get borderWidth(): number; // 边框线宽
|
||||||
|
set borderWidth(v: number);
|
||||||
|
get borderColor(): string; // 边框颜色
|
||||||
|
set borderColor(v: string);
|
||||||
|
get headColor(): string; // 箭头颜色
|
||||||
|
set headColor(v: string);
|
||||||
|
get bodyColor(): string; // 背景色
|
||||||
|
set bodyColor(v: string);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
clone(): ITrainData;
|
||||||
|
copyFrom(data: ITrainData): void;
|
||||||
|
eq(other: ITrainData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列车颜色
|
||||||
|
export enum TrainColorEnum {
|
||||||
|
headColor = '0x00FF00', // 箭头颜色
|
||||||
|
bodyColor = '0xA388B1', // 背景色
|
||||||
|
codeColor = '0xffffff', // 车号颜色
|
||||||
|
borderColor = '0xA3E198', // 边框的颜色
|
||||||
|
}
|
||||||
|
|
||||||
|
export const trainConsts = {
|
||||||
|
borderWidth: 1,
|
||||||
|
codeFontSize: 22,
|
||||||
|
marginX: 2, // 图形x轴边距
|
||||||
|
pauseW: 2, // 停止框宽度
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Train extends JlGraphic {
|
||||||
|
static Type = 'Train';
|
||||||
|
|
||||||
|
arrowLeft: Graphics;
|
||||||
|
pauseLeft: Graphics;
|
||||||
|
arrowRight: Graphics;
|
||||||
|
pauseRight: Graphics;
|
||||||
|
codeRact: Graphics;
|
||||||
|
codeGraph: VectorText = new VectorText(''); //车号
|
||||||
|
constructor() {
|
||||||
|
super(Train.Type);
|
||||||
|
this.arrowLeft = new Graphics();
|
||||||
|
this.pauseLeft = new Graphics();
|
||||||
|
this.arrowRight = new Graphics();
|
||||||
|
this.pauseRight = new Graphics();
|
||||||
|
this.codeRact = new Graphics();
|
||||||
|
this.addChild(this.arrowLeft);
|
||||||
|
this.addChild(this.pauseLeft);
|
||||||
|
this.addChild(this.arrowRight);
|
||||||
|
this.addChild(this.pauseRight);
|
||||||
|
this.addChild(this.codeRact);
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
this.codeGraph.setVectorFontSize(trainConsts.codeFontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): ITrainData {
|
||||||
|
return this.getDatas<ITrainData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
this.position.set(this.datas.point.x, this.datas.point.y);
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
const codeRact = this.codeRact;
|
||||||
|
if (this.datas.code == '') {
|
||||||
|
codeGraph.text = '01110111';
|
||||||
|
} else {
|
||||||
|
codeGraph.text = this.datas.code;
|
||||||
|
}
|
||||||
|
codeGraph.setVectorFontSize(this.datas.codeFontSize);
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
const style = {
|
||||||
|
fill: this.datas.codeColor,
|
||||||
|
padding: 5,
|
||||||
|
};
|
||||||
|
codeGraph.style = style;
|
||||||
|
const {
|
||||||
|
x: codeX,
|
||||||
|
y: codeY,
|
||||||
|
width: codeWidth,
|
||||||
|
height: codeHeight,
|
||||||
|
} = codeGraph.getLocalBounds();
|
||||||
|
const marginX = trainConsts.marginX;
|
||||||
|
const pauseW = trainConsts.pauseW;
|
||||||
|
const arrowLeft = this.arrowLeft;
|
||||||
|
arrowLeft.beginFill(this.datas.headColor, 1);
|
||||||
|
arrowLeft.drawPolygon([
|
||||||
|
-codeHeight * 0.4 - marginX - pauseW - marginX - codeWidth / 2,
|
||||||
|
0,
|
||||||
|
-marginX - pauseW - marginX - codeWidth / 2,
|
||||||
|
codeHeight / 2,
|
||||||
|
-marginX - pauseW - marginX - codeWidth / 2,
|
||||||
|
-codeHeight / 2,
|
||||||
|
]);
|
||||||
|
arrowLeft.endFill();
|
||||||
|
this.pauseLeft.beginFill(this.datas.headColor, 1);
|
||||||
|
this.pauseLeft.drawRect(0, 0, pauseW, codeHeight);
|
||||||
|
this.pauseLeft.endFill();
|
||||||
|
this.pauseLeft.position.set(
|
||||||
|
-marginX - pauseW - codeWidth / 2,
|
||||||
|
-codeHeight / 2
|
||||||
|
);
|
||||||
|
this.pauseRight.beginFill(this.datas.headColor, 1);
|
||||||
|
this.pauseRight.drawRect(0, 0, pauseW, codeHeight);
|
||||||
|
this.pauseRight.endFill();
|
||||||
|
this.pauseRight.position.set(marginX + codeWidth / 2, -codeHeight / 2);
|
||||||
|
const arrowRight = this.arrowRight;
|
||||||
|
arrowRight.beginFill(this.datas.headColor, 1);
|
||||||
|
arrowRight.drawPolygon([
|
||||||
|
codeWidth / 2 + marginX + pauseW + marginX + codeHeight * 0.4,
|
||||||
|
0,
|
||||||
|
codeWidth / 2 + marginX + pauseW + marginX,
|
||||||
|
codeHeight / 2,
|
||||||
|
codeWidth / 2 + marginX + pauseW + marginX,
|
||||||
|
-codeHeight / 2,
|
||||||
|
]);
|
||||||
|
arrowRight.endFill();
|
||||||
|
if (this.datas.hasBorder) {
|
||||||
|
codeRact.visible = true;
|
||||||
|
codeRact.lineStyle(
|
||||||
|
this.datas.borderWidth,
|
||||||
|
new Color(this.datas.borderColor)
|
||||||
|
);
|
||||||
|
codeRact.beginFill(new Color(this.datas.bodyColor));
|
||||||
|
codeRact.drawRect(codeX, codeY, codeWidth, codeHeight);
|
||||||
|
codeRact.endFill();
|
||||||
|
} else {
|
||||||
|
codeRact.visible = false;
|
||||||
|
}
|
||||||
|
// 运行方向控制箭头停止显隐
|
||||||
|
if (this.datas.trainDirection == 'right') {
|
||||||
|
this.arrowLeft.visible = false;
|
||||||
|
this.arrowRight.visible = true;
|
||||||
|
this.pauseLeft.visible = false;
|
||||||
|
this.pauseRight.visible = true;
|
||||||
|
} else {
|
||||||
|
this.arrowLeft.visible = true;
|
||||||
|
this.arrowRight.visible = false;
|
||||||
|
this.pauseLeft.visible = true;
|
||||||
|
this.pauseRight.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrainTemplate extends JlGraphicTemplate<Train> {
|
||||||
|
trainDirection: string;
|
||||||
|
codeFontSize: number;
|
||||||
|
hasBorder: boolean;
|
||||||
|
borderWidth: number;
|
||||||
|
borderColor: string;
|
||||||
|
headColor: string;
|
||||||
|
codeColor: string;
|
||||||
|
bodyColor: string;
|
||||||
|
constructor() {
|
||||||
|
super(Train.Type);
|
||||||
|
this.trainDirection = 'left';
|
||||||
|
this.codeFontSize = trainConsts.codeFontSize;
|
||||||
|
this.hasBorder = true;
|
||||||
|
this.borderWidth = trainConsts.borderWidth;
|
||||||
|
this.borderColor = TrainColorEnum.borderColor;
|
||||||
|
this.headColor = TrainColorEnum.headColor;
|
||||||
|
this.codeColor = TrainColorEnum.codeColor;
|
||||||
|
this.bodyColor = TrainColorEnum.bodyColor;
|
||||||
|
}
|
||||||
|
new(): Train {
|
||||||
|
return new Train();
|
||||||
|
}
|
||||||
|
}
|
141
src/graphics/train/TrainDrawAssistant.ts
Normal file
141
src/graphics/train/TrainDrawAssistant.ts
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import { Color, FederatedPointerEvent, Graphics, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
import { ITrainData, Train, TrainTemplate, trainConsts } from './Train';
|
||||||
|
|
||||||
|
export interface ITrainDrawOptions {
|
||||||
|
newData: () => ITrainData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrainDraw extends GraphicDrawAssistant<TrainTemplate, ITrainData> {
|
||||||
|
point: Point = new Point(0, 0);
|
||||||
|
arrowLeft: Graphics = new Graphics();
|
||||||
|
pauseLeft: Graphics = new Graphics();
|
||||||
|
codeRact: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => ITrainData) {
|
||||||
|
super(app, new TrainTemplate(), createData, Train.Type, '列车Train');
|
||||||
|
this.container.addChild(this.arrowLeft);
|
||||||
|
this.container.addChild(this.pauseLeft);
|
||||||
|
this.container.addChild(this.codeRact);
|
||||||
|
this.graphicTemplate.hasBorder = true;
|
||||||
|
trainInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.arrowLeft.clear();
|
||||||
|
this.pauseLeft.clear();
|
||||||
|
this.codeRact.clear();
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
this.point = p;
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
const codeGraph = new VectorText(''); // 车号
|
||||||
|
codeGraph.setVectorFontSize(22);
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
codeGraph.text = '01110111';
|
||||||
|
const style = { padding: 5 };
|
||||||
|
codeGraph.style = style;
|
||||||
|
const { width, height } = codeGraph.getLocalBounds();
|
||||||
|
codeGraph.destroy();
|
||||||
|
const marginX = trainConsts.marginX;
|
||||||
|
const pauseW = trainConsts.pauseW;
|
||||||
|
// 边框
|
||||||
|
if (template.hasBorder) {
|
||||||
|
const codeRact = this.codeRact;
|
||||||
|
codeRact.clear();
|
||||||
|
codeRact.lineStyle(template.borderWidth, new Color(template.borderColor));
|
||||||
|
codeRact.beginFill(new Color(template.bodyColor));
|
||||||
|
codeRact.drawRect(-width / 2, -height / 2, width, height);
|
||||||
|
codeRact.endFill();
|
||||||
|
codeRact.position.set(p.x, p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 箭头
|
||||||
|
const arrowLeft = this.arrowLeft;
|
||||||
|
arrowLeft.clear();
|
||||||
|
this.point.set(p.x, p.y);
|
||||||
|
arrowLeft.beginFill(template.headColor, 1);
|
||||||
|
arrowLeft.drawPolygon([
|
||||||
|
-height * 0.4 - marginX - pauseW - marginX - width / 2,
|
||||||
|
0,
|
||||||
|
-marginX - pauseW - marginX - width / 2,
|
||||||
|
height / 2,
|
||||||
|
-marginX - pauseW - marginX - width / 2,
|
||||||
|
-height / 2,
|
||||||
|
]);
|
||||||
|
arrowLeft.endFill();
|
||||||
|
arrowLeft.position.set(this.point.x, this.point.y);
|
||||||
|
|
||||||
|
// 停止框
|
||||||
|
const pauseLeft = this.pauseLeft;
|
||||||
|
pauseLeft.clear();
|
||||||
|
pauseLeft.beginFill(template.headColor, 1);
|
||||||
|
pauseLeft.drawRect(0, 0, pauseW, height);
|
||||||
|
pauseLeft.endFill();
|
||||||
|
pauseLeft.position.set(
|
||||||
|
this.point.x - marginX - pauseW - width / 2,
|
||||||
|
this.point.y - height / 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
prepareData(data: ITrainData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.code = '01110111';
|
||||||
|
data.codeColor = template.codeColor;
|
||||||
|
data.codeFontSize = template.codeFontSize;
|
||||||
|
data.hasBorder = template.hasBorder;
|
||||||
|
data.trainDirection = template.trainDirection;
|
||||||
|
data.point = this.point;
|
||||||
|
data.borderWidth = template.borderWidth;
|
||||||
|
data.borderColor = template.borderColor;
|
||||||
|
data.headColor = template.headColor;
|
||||||
|
data.bodyColor = template.bodyColor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class trainInteraction extends GraphicInteractionPlugin<Train> {
|
||||||
|
static Name = 'train_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(trainInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new trainInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Train[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type === Train.Type).map((g) => g as Train);
|
||||||
|
}
|
||||||
|
bind(g: Train): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
}
|
||||||
|
unbind(g: Train): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
53
src/graphics/turnout/Turnout.ts
Normal file
53
src/graphics/turnout/Turnout.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { Color, Graphics, IPointData } from 'pixi.js';
|
||||||
|
import { GraphicData, JlGraphic } from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface ITrunoutData extends GraphicData {
|
||||||
|
get code(): string;
|
||||||
|
set code(code: string);
|
||||||
|
get lineWidth(): number;
|
||||||
|
set lineWidth(code: number);
|
||||||
|
get lineColor(): string;
|
||||||
|
set lineColor(code: string);
|
||||||
|
get pointA(): IPointData;
|
||||||
|
set pointA(code: IPointData);
|
||||||
|
get pointB(): IPointData;
|
||||||
|
set pointB(code: IPointData);
|
||||||
|
get pointC(): IPointData;
|
||||||
|
set pointC(code: IPointData);
|
||||||
|
clone(): ITrunoutData;
|
||||||
|
copyFrom(data: ITrunoutData): void;
|
||||||
|
eq(other: ITrunoutData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Turnout extends JlGraphic {
|
||||||
|
static Type = 'Turnout';
|
||||||
|
graphics: {
|
||||||
|
forkPoint: {
|
||||||
|
AB: Graphics;
|
||||||
|
AC: Graphics;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Turnout.Type);
|
||||||
|
this.graphics = {
|
||||||
|
forkPoint: { AB: new Graphics(), AC: new Graphics() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): ITrunoutData {
|
||||||
|
return this.getDatas<ITrunoutData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
doRepaint(): void {
|
||||||
|
const { pointA, pointB, pointC } = this.datas;
|
||||||
|
|
||||||
|
const forkPoint: IPointData = {
|
||||||
|
x: pointA.x + (pointB.x - pointA.x),
|
||||||
|
y: pointA.y + (pointB.y - pointA.y),
|
||||||
|
};
|
||||||
|
|
||||||
|
// this.lineGraphic.clear();
|
||||||
|
// this.lineGraphic.lineStyle(this.datas.lineWidth, new Color());
|
||||||
|
}
|
||||||
|
}
|
0
src/graphics/turnout/TurnoutDrawAssistant.ts
Normal file
0
src/graphics/turnout/TurnoutDrawAssistant.ts
Normal file
@ -19,6 +19,8 @@ import {
|
|||||||
distance2,
|
distance2,
|
||||||
linePoint,
|
linePoint,
|
||||||
pointPolygon,
|
pointPolygon,
|
||||||
|
calculateLineSegmentingPoint,
|
||||||
|
calculateDistanceFromPointToLine,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { GraphicTransformEvent, ShiftData } from './GraphicTransformPlugin';
|
import { GraphicTransformEvent, ShiftData } from './GraphicTransformPlugin';
|
||||||
|
|
||||||
@ -57,6 +59,9 @@ export abstract class GraphicEditPlugin<
|
|||||||
export const addWaypointConfig: MenuItemOptions = {
|
export const addWaypointConfig: MenuItemOptions = {
|
||||||
name: '添加路径点',
|
name: '添加路径点',
|
||||||
};
|
};
|
||||||
|
export const addWaySegmentingConfig: MenuItemOptions = {
|
||||||
|
name: '细分',
|
||||||
|
};
|
||||||
export const removeWaypointConfig: MenuItemOptions = {
|
export const removeWaypointConfig: MenuItemOptions = {
|
||||||
name: '移除路径点',
|
name: '移除路径点',
|
||||||
};
|
};
|
||||||
@ -103,6 +108,45 @@ export abstract class LineEditPlugin extends GraphicEditPlugin<ILineGraphic> {
|
|||||||
abstract initEditPoints(): void;
|
abstract initEditPoints(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getWayLineIndex(
|
||||||
|
points: IPointData[],
|
||||||
|
p: IPointData
|
||||||
|
): { start: number; end: number } {
|
||||||
|
let start = 0;
|
||||||
|
let end = 0;
|
||||||
|
let minDistance = 0;
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const sp = points[i - 1];
|
||||||
|
const ep = points[i];
|
||||||
|
let distance = calculateDistanceFromPointToLine(sp, ep, p);
|
||||||
|
distance = Math.round(distance * 100) / 100;
|
||||||
|
if (i == 1) {
|
||||||
|
minDistance = distance;
|
||||||
|
}
|
||||||
|
if (distance == minDistance) {
|
||||||
|
const minX = Math.min(sp.x, ep.x);
|
||||||
|
const maxX = Math.max(sp.x, ep.x);
|
||||||
|
const minY = Math.min(sp.y, ep.y);
|
||||||
|
const maxY = Math.max(sp.y, ep.y);
|
||||||
|
const point = calculateFootPointFromPointToLine(sp, ep, p);
|
||||||
|
if (
|
||||||
|
point.x >= minX &&
|
||||||
|
point.x <= maxX &&
|
||||||
|
point.y >= minY &&
|
||||||
|
point.y <= maxY
|
||||||
|
) {
|
||||||
|
start = i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (distance < minDistance) {
|
||||||
|
minDistance = distance;
|
||||||
|
start = i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end = start + 1;
|
||||||
|
return { start, end };
|
||||||
|
}
|
||||||
|
|
||||||
export function getWaypointRangeIndex(
|
export function getWaypointRangeIndex(
|
||||||
points: IPointData[],
|
points: IPointData[],
|
||||||
curve: boolean,
|
curve: boolean,
|
||||||
@ -251,6 +295,21 @@ export function addLineWayPoint(
|
|||||||
graphic.linePoints = points;
|
graphic.linePoints = points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addPolygonSegmentingPoint(
|
||||||
|
graphic: ILineGraphic,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
knife = 2
|
||||||
|
) {
|
||||||
|
const linePoints = graphic.linePoints;
|
||||||
|
const points = linePoints.slice(0, start + 1);
|
||||||
|
points.push(
|
||||||
|
...calculateLineSegmentingPoint(linePoints[start], linePoints[end], knife)
|
||||||
|
);
|
||||||
|
points.push(...linePoints.slice(end));
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
|
||||||
function assertBezierWayPoint(i: number) {
|
function assertBezierWayPoint(i: number) {
|
||||||
const c = i % 3;
|
const c = i % 3;
|
||||||
if (c !== 0) {
|
if (c !== 0) {
|
||||||
|
@ -287,6 +287,31 @@ export function calculateLineMidpoint(p1: IPointData, p2: IPointData): Point {
|
|||||||
return new Point(x, y);
|
return new Point(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算线段细分坐标--线段分成几份
|
||||||
|
* @param p1
|
||||||
|
* @param p2
|
||||||
|
* @param knife
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function calculateLineSegmentingPoint(
|
||||||
|
p1: IPointData,
|
||||||
|
p2: IPointData,
|
||||||
|
knife: number
|
||||||
|
): IPointData[] {
|
||||||
|
const segmentingPoints: IPointData[] = [];
|
||||||
|
const x = p1.x < p2.x ? p1.x : p2.x;
|
||||||
|
const y = p1.y < p2.y ? p1.y : p2.y;
|
||||||
|
const w = Math.abs(p1.x - p2.x);
|
||||||
|
const h = Math.abs(p1.y - p2.y);
|
||||||
|
for (let i = 0; i < knife - 1; i++) {
|
||||||
|
const pointX = x + (w * (i + 1)) / knife;
|
||||||
|
const pointy = y + (h * (i + 1)) / knife;
|
||||||
|
segmentingPoints.push(new Point(pointX, pointy));
|
||||||
|
}
|
||||||
|
return segmentingPoints;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算点到直线距离
|
* 计算点到直线距离
|
||||||
* @param p1
|
* @param p1
|
||||||
|
184
src/layouts/DrawLayout.vue
Normal file
184
src/layouts/DrawLayout.vue
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout view="hHh LpR fFf">
|
||||||
|
<q-header reveal class="bg-primary text-white">
|
||||||
|
<q-toolbar>
|
||||||
|
<q-btn dense flat round icon="menu" @click="toggleLeftDrawer" />
|
||||||
|
|
||||||
|
<q-toolbar-title>
|
||||||
|
<q-avatar>
|
||||||
|
<img src="https://cdn.quasar.dev/logo-v2/svg/logo-mono-white.svg" />
|
||||||
|
</q-avatar>
|
||||||
|
Title
|
||||||
|
</q-toolbar-title>
|
||||||
|
|
||||||
|
<q-btn dense flat round icon="menu" @click="toggleRightDrawer" />
|
||||||
|
</q-toolbar>
|
||||||
|
<q-resize-observer @resize="onHeaderResize" />
|
||||||
|
</q-header>
|
||||||
|
|
||||||
|
<q-drawer v-model="leftDrawerOpen" bordered side="left">
|
||||||
|
<q-resize-observer @resize="onLeftResize" />
|
||||||
|
<!-- drawer content -->
|
||||||
|
<q-list bordered padding class="rounded-borders text-primary">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:active="link === 'inbox'"
|
||||||
|
@click="link = 'inbox'"
|
||||||
|
active-class="my-menu-link"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="inbox" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>Inbox</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:active="link === 'outbox'"
|
||||||
|
@click="link = 'outbox'"
|
||||||
|
active-class="my-menu-link"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="send" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>Outbox</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:active="link === 'trash'"
|
||||||
|
@click="link = 'trash'"
|
||||||
|
active-class="my-menu-link"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="delete" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>Trash</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-separator spaced />
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:active="link === 'settings'"
|
||||||
|
@click="link = 'settings'"
|
||||||
|
active-class="my-menu-link"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="settings" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>Settings</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:active="link === 'help'"
|
||||||
|
@click="link = 'help'"
|
||||||
|
active-class="my-menu-link"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="help" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>Help</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-drawer>
|
||||||
|
|
||||||
|
<q-drawer show-if-above bordered v-model="rightDrawerOpen" side="right">
|
||||||
|
<q-resize-observer @resize="onRightResize" />
|
||||||
|
<!-- drawer content -->
|
||||||
|
<draw-properties></draw-properties>
|
||||||
|
</q-drawer>
|
||||||
|
|
||||||
|
<q-page-container>
|
||||||
|
<div id="draw-app-container"></div>
|
||||||
|
</q-page-container>
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import DrawProperties from 'src/components/draw-app/DrawProperties.vue';
|
||||||
|
import { getDrawApp, loadDrawDatas } from 'src/drawApp';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
console.log(useRoute().fullPath);
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
|
||||||
|
const leftDrawerOpen = ref(false);
|
||||||
|
const rightDrawerOpen = ref(false);
|
||||||
|
function toggleLeftDrawer() {
|
||||||
|
leftDrawerOpen.value = !leftDrawerOpen.value;
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
function toggleRightDrawer() {
|
||||||
|
rightDrawerOpen.value = !rightDrawerOpen.value;
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = ref('outbox');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('绘制应用layout mounted');
|
||||||
|
const dom = document.getElementById('draw-app-container');
|
||||||
|
if (dom) {
|
||||||
|
const drawApp = drawStore.initDrawApp(dom);
|
||||||
|
loadDrawDatas(drawApp);
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const canvasWidth = ref(0);
|
||||||
|
const canvasHeight = ref(0);
|
||||||
|
const headerHeight = ref(0);
|
||||||
|
const leftWidth = ref(0);
|
||||||
|
const rightWidth = ref(0);
|
||||||
|
|
||||||
|
function onHeaderResize(size: { height: number; width: number }) {
|
||||||
|
headerHeight.value = size.height;
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
function onLeftResize(size: { height: number; width: number }) {
|
||||||
|
leftWidth.value = size.width;
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
function onRightResize(size: { height: number; width: number }) {
|
||||||
|
rightWidth.value = size.width;
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onResize() {
|
||||||
|
const clientWidth = document.body.clientWidth;
|
||||||
|
const clientHeight = document.body.clientHeight;
|
||||||
|
canvasWidth.value =
|
||||||
|
clientWidth -
|
||||||
|
(leftDrawerOpen.value ? leftWidth.value : 0) -
|
||||||
|
(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';
|
||||||
|
}
|
||||||
|
const drawApp = getDrawApp();
|
||||||
|
if (drawApp) {
|
||||||
|
drawApp.onDomResize(canvasWidth.value, canvasHeight.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
drawStore.destroy();
|
||||||
|
});
|
||||||
|
</script>
|
2309
src/protos/stationLayoutGraphics.ts
Normal file
2309
src/protos/stationLayoutGraphics.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,11 @@ const routes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/painting/:id',
|
||||||
|
name: 'painting',
|
||||||
|
component: () => import('layouts/DrawLayout.vue'),
|
||||||
|
},
|
||||||
|
|
||||||
// Always leave this as last one,
|
// Always leave this as last one,
|
||||||
// but you can also remove it
|
// but you can also remove it
|
||||||
|
78
src/stores/draw-store.ts
Normal file
78
src/stores/draw-store.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { destroyDrawApp, getDrawApp, initDrawApp } from 'src/drawApp';
|
||||||
|
import { DrawAssistant, JlCanvas, JlDrawApp, JlGraphic } from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export const useDrawStore = defineStore('draw', {
|
||||||
|
state: () => ({
|
||||||
|
drawAssistant: null as DrawAssistant | null,
|
||||||
|
selectedGraphics: null as JlGraphic[] | null,
|
||||||
|
}),
|
||||||
|
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) => {
|
||||||
|
if (state.selectedGraphics) {
|
||||||
|
if (state.selectedGraphics.length == 0) {
|
||||||
|
return '画布';
|
||||||
|
} else if (state.selectedGraphics.length == 1) {
|
||||||
|
return state.selectedGraphics[0].type;
|
||||||
|
}
|
||||||
|
return '多选';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
selectedGraphic: (state) => {
|
||||||
|
if (state.selectedGraphics) {
|
||||||
|
if (state.selectedGraphics.length === 1) {
|
||||||
|
return state.selectedGraphics[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
getDrawApp(): JlDrawApp {
|
||||||
|
const app = getDrawApp();
|
||||||
|
if (app == null) {
|
||||||
|
throw new Error('未初始化app');
|
||||||
|
}
|
||||||
|
return app;
|
||||||
|
},
|
||||||
|
getJlCanvas(): JlCanvas {
|
||||||
|
return this.getDrawApp().canvas;
|
||||||
|
},
|
||||||
|
initDrawApp(dom: HTMLElement) {
|
||||||
|
const app = initDrawApp(dom);
|
||||||
|
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('graphicselectedchange', () => {
|
||||||
|
this.selectedGraphics = app.selectedGraphics;
|
||||||
|
});
|
||||||
|
this.selectedGraphics = [];
|
||||||
|
return app;
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
// console.log('绘制状态清空,绘制应用销毁');
|
||||||
|
this.drawAssistant = null;
|
||||||
|
this.selectedGraphics = null;
|
||||||
|
destroyDrawApp();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -1 +1 @@
|
|||||||
Subproject commit 9aa54608cd592ff96fc506e310fb19caff67f809
|
Subproject commit d3082562ac7e602c4993aea9c8c19561ef5cd449
|
@ -2699,6 +2699,11 @@ process-nextick-args@~2.0.0:
|
|||||||
resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||||
|
|
||||||
|
protoc-gen-ts@^0.8.6:
|
||||||
|
version "0.8.6"
|
||||||
|
resolved "https://registry.npmmirror.com/protoc-gen-ts/-/protoc-gen-ts-0.8.6.tgz#e789a6fc3fbe09bdc119acecc349b9554ec5940e"
|
||||||
|
integrity sha512-66oeorGy4QBvYjQGd/gaeOYyFqKyRmRgTpofmnw8buMG0P7A0jQjoKSvKJz5h5tNUaVkIzvGBUTRVGakrhhwpA==
|
||||||
|
|
||||||
proxy-addr@~2.0.7:
|
proxy-addr@~2.0.7:
|
||||||
version "2.0.7"
|
version "2.0.7"
|
||||||
resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
||||||
|
Loading…
Reference in New Issue
Block a user