diff --git a/package.json b/package.json index 2ebe69b..560d58c 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,11 @@ "build": "quasar build" }, "dependencies": { + "@pixi/graphics-extras": "^7.2.4", "@quasar/extras": "^1.0.0", "@stomp/stompjs": "^7.0.0", + "alova": "^2.7.1", + "axios": "^1.4.0", "google-protobuf": "^3.21.2", "js-base64": "^3.7.5", "pinia": "^2.0.11", @@ -37,6 +40,7 @@ "eslint-plugin-vue": "^9.0.0", "prettier": "^2.5.1", "protoc-gen-ts": "^0.8.6", + "ts-md5": "^1.3.1", "typescript": "^4.5.4" }, "engines": { diff --git a/quasar.config.js b/quasar.config.js index 6d7aa33..c5be092 100644 --- a/quasar.config.js +++ b/quasar.config.js @@ -28,7 +28,7 @@ module.exports = configure(function (/* ctx */) { // app boot file (/src/boot) // --> boot files are part of "main.js" // https://v2.quasar.dev/quasar-cli-vite/boot-files - boot: [], + boot: ['axios', '@pixi/graphics-extras'], // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css css: ['app.scss'], diff --git a/src/boot/@pixi/graphics-extras.ts b/src/boot/@pixi/graphics-extras.ts new file mode 100644 index 0000000..4a70eb8 --- /dev/null +++ b/src/boot/@pixi/graphics-extras.ts @@ -0,0 +1,7 @@ +import { boot } from 'quasar/wrappers'; +import * as GraphicsExtras from '@pixi/graphics-extras'; +// "async" is optional; +// more info on params: https://v2.quasar.dev/quasar-cli/boot-files +export default boot(async (/* { app, router, ... } */) => { + GraphicsExtras; +}); diff --git a/src/boot/axios.ts b/src/boot/axios.ts new file mode 100644 index 0000000..d9a9d34 --- /dev/null +++ b/src/boot/axios.ts @@ -0,0 +1,111 @@ +import axios, { AxiosInstance } from 'axios'; +import { AxiosError } from 'axios'; +import { Dialog } from 'quasar'; +import { boot } from 'quasar/wrappers'; +import { getJwtToken } from 'src/examples/app/configs/TokenManage'; +import { getHttpBase } from 'src/examples/app/configs/UrlManage'; + +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { + $axios: AxiosInstance; + } +} + +interface ErrorData { + status: number; + title: string; + detail: string; + code: number; +} + +export class ApiError { + origin: AxiosError; + /** + * 业务错误代码 + */ + code: number; + /** + * 错误信息 + */ + title: string; + /** + * 相关问题描述 + */ + detail?: string; + constructor(origin: AxiosError) { + this.origin = origin; + const response = origin.response; + if (response) { + const err = response.data as ErrorData; + this.code = err.code; + this.title = err.title; + this.detail = err.detail; + } else { + this.code = origin.status || -1; + this.title = origin.message; + } + } + + static from(err: AxiosError): ApiError { + return new ApiError(err); + } + + /** + * 是否认证失败(登录过期) + * @returns + */ + isAuthError(): boolean { + return this.origin.response?.status === 401; + } +} + +// Be careful when using SSR for cross-request state pollution +// due to creating a Singleton instance here; +// If any client changes this (global) instance, it might be a +// good idea to move this instance creation inside of the +// "export default () => {}" function below (which runs individually +// for each client) +const api = axios.create({ baseURL: getHttpBase() }); + +export default boot(({ app, router }) => { + // for use inside Vue files (Options API) through this.$axios and this.$api + + // 拦截请求,添加 + api.interceptors.request.use( + (config) => { + config.headers.Authorization = getJwtToken(); + return config; + }, + (err: AxiosError) => { + return Promise.reject(ApiError.from(err)); + } + ); + + api.interceptors.response.use( + (response) => { + return response; + }, + (err) => { + if (err.response && err.response.status === 401) { + Dialog.create({ + title: '认证失败', + message: '认证失败或登录超时,请重新登录', + persistent: true, + }).onOk(() => { + router.push({ name: 'login' }); + }); + } + return Promise.reject(ApiError.from(err)); + } + ); + + app.config.globalProperties.$axios = axios; + // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form) + // so you won't necessarily have to import axios in each vue file + + app.config.globalProperties.$api = api; + // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form) + // so you can easily perform requests against your app's API +}); + +export { api }; diff --git a/src/components/draw-app/DrawProperties.vue b/src/components/draw-app/DrawProperties.vue index 578796d..6cf6434 100644 --- a/src/components/draw-app/DrawProperties.vue +++ b/src/components/draw-app/DrawProperties.vue @@ -10,9 +10,18 @@ + + + @@ -33,12 +42,24 @@ + + + + @@ -47,15 +68,25 @@ diff --git a/src/components/draw-app/properties/CanvasProperty.vue b/src/components/draw-app/properties/CanvasProperty.vue index e5d4f11..74ac95b 100644 --- a/src/components/draw-app/properties/CanvasProperty.vue +++ b/src/components/draw-app/properties/CanvasProperty.vue @@ -58,19 +58,13 @@ const canvas = reactive({ }); 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, diff --git a/src/components/draw-app/properties/PlatformProperty.vue b/src/components/draw-app/properties/PlatformProperty.vue index 8f60b52..147eeeb 100644 --- a/src/components/draw-app/properties/PlatformProperty.vue +++ b/src/components/draw-app/properties/PlatformProperty.vue @@ -1,19 +1,18 @@ - + @@ -49,37 +55,58 @@ import { PlatformData } from 'src/examples/app/graphics/PlatformInteraction'; import { Platform } from 'src/graphics/platform/Platform'; import { useDrawStore } from 'src/stores/draw-store'; -import { onMounted, reactive, watch } from 'vue'; +import { onMounted, reactive, ref, watch } from 'vue'; const drawStore = useDrawStore(); -const stationModel = reactive(new PlatformData()); -const options = [true, false]; +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) { - // console.log('station变更'); - stationModel.copyFrom(val.saveData() as PlatformData); + platformModel.copyFrom(val.saveData() as PlatformData); + hasDoor.value = (showSelectData as never)[platformModel.hasdoor + '']; + trainDirection.value = (showSelectData as never)[ + platformModel.trainDirection + ]; } } ); onMounted(() => { - //console.log('station 属性表单 mounted'); - const station = drawStore.selectedGraphic as Platform; - - if (station) { - stationModel.copyFrom(station.saveData()); + 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() { - //console.log(stationModel, 'station 属性更新'); - const station = drawStore.selectedGraphic as Platform; - if (station) { - drawStore.getDrawApp().updateGraphicAndRecord(station, stationModel); + 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); } } diff --git a/src/components/draw-app/properties/RectProperty.vue b/src/components/draw-app/properties/RectProperty.vue new file mode 100644 index 0000000..981baba --- /dev/null +++ b/src/components/draw-app/properties/RectProperty.vue @@ -0,0 +1,74 @@ + + + diff --git a/src/components/draw-app/properties/SignalProperty.vue b/src/components/draw-app/properties/SignalProperty.vue new file mode 100644 index 0000000..f2688bd --- /dev/null +++ b/src/components/draw-app/properties/SignalProperty.vue @@ -0,0 +1,57 @@ + + + diff --git a/src/components/draw-app/properties/StationProperty.vue b/src/components/draw-app/properties/StationProperty.vue new file mode 100644 index 0000000..3699cf0 --- /dev/null +++ b/src/components/draw-app/properties/StationProperty.vue @@ -0,0 +1,200 @@ + + + diff --git a/src/components/draw-app/properties/TrainProperty.vue b/src/components/draw-app/properties/TrainProperty.vue new file mode 100644 index 0000000..9e6360c --- /dev/null +++ b/src/components/draw-app/properties/TrainProperty.vue @@ -0,0 +1,198 @@ + + + diff --git a/src/components/draw-app/templates/RectTemplate.vue b/src/components/draw-app/templates/RectTemplate.vue new file mode 100644 index 0000000..75c2ee2 --- /dev/null +++ b/src/components/draw-app/templates/RectTemplate.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/components/draw-app/templates/StationTemplate.vue b/src/components/draw-app/templates/StationTemplate.vue new file mode 100644 index 0000000..fe433bf --- /dev/null +++ b/src/components/draw-app/templates/StationTemplate.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/components/draw-app/templates/TrainTemplate.vue b/src/components/draw-app/templates/TrainTemplate.vue new file mode 100644 index 0000000..84c3cd2 --- /dev/null +++ b/src/components/draw-app/templates/TrainTemplate.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/examples/app/api/ApiCommon.ts b/src/examples/app/api/ApiCommon.ts new file mode 100644 index 0000000..18494ce --- /dev/null +++ b/src/examples/app/api/ApiCommon.ts @@ -0,0 +1,42 @@ +export class PageQueryDto { + current: number; + size: number; + orders?: OrderItemDto[]; + constructor(current: number, size: number, orders?: OrderItemDto[]) { + this.current = current; + this.size = size; + this.orders = orders; + } +} + +export class OrderItemDto { + column: string; + asc: boolean; + constructor(column: string, asc: boolean) { + this.column = column; + this.asc = asc; + } + + static asc(column: string): OrderItemDto { + return new OrderItemDto(column, true); + } + static desc(column: string): OrderItemDto { + return new OrderItemDto(column, false); + } +} + +export interface PageDto { + records: T[]; + /** + * 记录总数 + */ + total: number; + /** + * 第几页 + */ + current: number; + /** + * 每页数量 + */ + size: number; +} diff --git a/src/examples/app/api/UserApi.ts b/src/examples/app/api/UserApi.ts new file mode 100644 index 0000000..085fe3f --- /dev/null +++ b/src/examples/app/api/UserApi.ts @@ -0,0 +1,77 @@ +import { api } from 'src/boot/axios'; +import { PageDto, PageQueryDto } from './ApiCommon'; +import { Md5 } from 'ts-md5'; + +const UserUriBase = '/api/user'; + +interface RegisterInfo { + name: string; + mobile: string; + password: string; +} + +interface User { + id: string; + name: string; + mobile: string; + password: string; + registerTime: string; +} + +const PasswordSult = '4a6d74126bfd06d69406fcccb7e7d5d9'; // 密码加盐 +function encryptPassword(password: string): string { + const md5 = new Md5(); + return md5.appendStr(`${password}${PasswordSult}`).end() as string; +} + +/** + * 用户注册 + * @param info + * @returns + */ +export async function register(info: RegisterInfo): Promise { + const response = await api.post(`${UserUriBase}/register`, { + ...info, + password: encryptPassword(info.password), + }); + return response.data as User; +} + +interface LoginInfo { + account: string; + password: string; +} + +/** + * 用户登录 + * @param loginInfo + * @returns + */ +export async function login(loginInfo: LoginInfo): Promise { + const info = { ...loginInfo, password: encryptPassword(loginInfo.password) }; + const response = await api.post(`${UserUriBase}/login`, info); + return response.data; +} + +export class PagingQueryParams extends PageQueryDto { + name?: string; + roleId?: number; +} + +/** + * 分页查询用户信息 + * @param params + * @returns + */ +export async function pageQuery( + params: PagingQueryParams +): Promise> { + const response = await api.get(`${UserUriBase}/paging`, { + params: params, + }); + return response.data; +} + +export function distributeRole(query: { userId: number; roleIds: number[] }) { + return api.post('/api/role/distribute', query); +} diff --git a/src/examples/app/app_message/protos/draw_data_storage.proto b/src/examples/app/app_message/protos/draw_data_storage.proto index da44bc9..3ded330 100644 --- a/src/examples/app/app_message/protos/draw_data_storage.proto +++ b/src/examples/app/app_message/protos/draw_data_storage.proto @@ -7,6 +7,10 @@ message RtssGraphicStorage { repeated Link links = 2; repeated IscsFan iscsFans = 3; repeated Platform Platforms = 4; + repeated Station stations = 5; + repeated Rect Rects = 6; + repeated Train train = 7; + repeated Signal signals = 8; } message Canvas { @@ -64,17 +68,57 @@ message Link { repeated Point points = 7; // 点坐标列表 } +message Rect { + CommonInfo common = 1; + string code = 2; + int32 lineWidth = 3; // 线宽 + string lineColor = 4; // 线色 + Point point = 5; // 位置坐标 + float width = 6;//宽度 + float height = 7; //高度 + repeated Point points = 8; // 点坐标列表 +} + message Platform { CommonInfo common = 1; string code = 2; bool hasdoor = 3; // 是否有屏蔽门 - int32 lineWidth = 4; // 线宽 - string lineColor = 5; // 站台线色 - string lineColorDoor = 6; // 屏蔽门线色 - Point point = 7; // 位置坐标 - float width = 8;//宽度 - float height = 9; //高度 - repeated string orbitCode = 10;//站台轨 + string trainDirection = 4; // 行驶方向--屏蔽门上下 + int32 lineWidth = 5; // 线宽 + string lineColor = 6; // 站台线色 + string lineColorDoor = 7; // 屏蔽门线色 + Point point = 8; // 位置坐标 + float width = 9;//宽度 + float height = 10; //高度 + repeated string orbitCode = 11;//站台轨 +} + +message Station { + CommonInfo common = 1; + string code = 2; + bool hasCircle = 3; // 是否有圆圈--线网图 + int32 radius = 4; // 半径 + int32 borderWidth = 5; // 边框线宽 + string borderColor = 6; // 边框颜色 + string fillColor = 7; // 圆填充颜色 + string codeColor = 8; // 车站字体颜色 + int32 codeFontSize = 9; // 车站字体大小 + Point point = 10; // 位置坐标 + Point circlePoint = 11; // 位置坐标 +} + +message Train { + CommonInfo common = 1; + string code = 2; + string codeColor = 3; // 车号颜色 + int32 codeFontSize = 4; // 车号字体大小 + Point point = 5; // 位置坐标 + string trainDirection = 6; // 行驶方向 + bool hasBorder = 7; // 是否有边框 + int32 borderWidth = 8; // 边框线宽 + string borderColor = 9; // 边框颜色 + string headColor = 10; // 箭头颜色 + string bodyColor = 11; // 背景色 } message IscsFan { @@ -83,3 +127,8 @@ message IscsFan { } message Turnout {} + +message Signal { + CommonInfo common = 1; + string code = 2; +} diff --git a/src/examples/app/app_message/protos/graphic_states.proto b/src/examples/app/app_message/protos/graphic_states.proto new file mode 100644 index 0000000..6c9a057 --- /dev/null +++ b/src/examples/app/app_message/protos/graphic_states.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package graphicStates; + +// 公共属性 +message CommonState { + string code = 1; + string graphicType = 2; +} + +message IscsFan { + CommonState common = 1; + int32 state = 2; +} diff --git a/src/examples/app/configs/TokenManage.ts b/src/examples/app/configs/TokenManage.ts new file mode 100644 index 0000000..11dddf9 --- /dev/null +++ b/src/examples/app/configs/TokenManage.ts @@ -0,0 +1,13 @@ +const JwtTokenKey = 'jwttoken'; + +export function saveJwtToken(token: string) { + sessionStorage.setItem(JwtTokenKey, `Bearer ${token}`); +} + +export function getJwtToken(): string | null { + return sessionStorage.getItem(JwtTokenKey); +} + +export function clearJwtToken(): void { + sessionStorage.removeItem(JwtTokenKey); +} diff --git a/src/examples/app/configs/UrlManage.ts b/src/examples/app/configs/UrlManage.ts new file mode 100644 index 0000000..7f7a422 --- /dev/null +++ b/src/examples/app/configs/UrlManage.ts @@ -0,0 +1,13 @@ +function getHost(): string { + // return '192.168.3.7:9081'; + return '192.168.3.233:9081'; + // return '192.168.3.7:9081'; +} + +export function getHttpBase() { + return `http://${getHost()}`; +} + +export function getWebsocketUrl() { + return `ws://${getHost()}/ws-default`; +} diff --git a/src/examples/app/graphics/GraphicDataBase.ts b/src/examples/app/graphics/GraphicDataBase.ts index e004380..263cb87 100644 --- a/src/examples/app/graphics/GraphicDataBase.ts +++ b/src/examples/app/graphics/GraphicDataBase.ts @@ -2,12 +2,14 @@ import * as pb_1 from 'google-protobuf'; import { ChildTransform, GraphicData, + GraphicState, GraphicTransform, IChildTransform, IGraphicTransform, } from 'src/jlgraphic'; import { toStorageTransform } from '..'; import { graphicData } from '../protos/draw_data_storage'; +import { graphicStates } from '../protos/graphic_states'; export interface ICommonInfo { id: string; @@ -27,10 +29,10 @@ export abstract class GraphicDataBase implements GraphicData { this._data = data; } - static defaultCommonInfo(): graphicData.CommonInfo { + static defaultCommonInfo(graphicType: string): graphicData.CommonInfo { return new graphicData.CommonInfo({ id: '', - graphicType: '', + graphicType: graphicType, transform: new graphicData.Transform({ position: new graphicData.Point({ x: 0, y: 0 }), scale: new graphicData.Point({ x: 1, y: 1 }), @@ -99,3 +101,41 @@ export abstract class GraphicDataBase implements GraphicData { return pb_1.Message.equals(this._data, other._data); } } + +export interface IProtoGraphicState extends pb_1.Message { + common: graphicStates.CommonState; +} + +export abstract class GraphicStateBase implements GraphicState { + _state: IProtoGraphicState; + constructor(state: IProtoGraphicState) { + this._state = state; + } + + static defaultCommonState(graphicType: string): graphicStates.CommonState { + return new graphicStates.CommonState({ + code: '', + graphicType: graphicType, + }); + } + + getState(): S { + return this._state as S; + } + + get code(): string { + return this._state.common.code; + } + get graphicType(): string { + return this._state.common.graphicType; + } + clone(): GraphicState { + throw new Error('Method not implemented.'); + } + copyFrom(data: GraphicStateBase): void { + pb_1.Message.copyInto(data._state, this._state); + } + eq(data: GraphicStateBase): boolean { + return pb_1.Message.equals(this._state, data._state); + } +} diff --git a/src/examples/app/graphics/IscsFanInteraction.ts b/src/examples/app/graphics/IscsFanInteraction.ts index 1eb2f5f..255af32 100644 --- a/src/examples/app/graphics/IscsFanInteraction.ts +++ b/src/examples/app/graphics/IscsFanInteraction.ts @@ -1,7 +1,12 @@ import * as pb_1 from 'google-protobuf'; -import { IIscsFanData } from 'src/graphics/iscs-fan/IscsFan'; +import { + IIscsFanData, + IIscsFanState, + IscsFan, +} from 'src/graphics/iscs-fan/IscsFan'; import { graphicData } from '../protos/draw_data_storage'; -import { GraphicDataBase } from './GraphicDataBase'; +import { graphicStates } from '../protos/graphic_states'; +import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase'; export class IscsFanData extends GraphicDataBase implements IIscsFanData { constructor(data?: graphicData.IscsFan) { @@ -10,7 +15,7 @@ export class IscsFanData extends GraphicDataBase implements IIscsFanData { fan = data; } else { fan = new graphicData.IscsFan({ - common: GraphicDataBase.defaultCommonInfo(), + common: GraphicDataBase.defaultCommonInfo(IscsFan.Type), }); } super(fan); @@ -36,3 +41,32 @@ export class IscsFanData extends GraphicDataBase implements IIscsFanData { return pb_1.Message.equals(this.data, other.data); } } + +export class IscsFanState extends GraphicStateBase implements IIscsFanState { + constructor(proto?: graphicStates.IscsFan) { + let states; + if (proto) { + states = proto; + states.common.graphicType = IscsFan.Type; + } else { + states = new graphicStates.IscsFan({ + common: GraphicStateBase.defaultCommonState(IscsFan.Type), + }); + } + super(states); + } + + get states(): graphicStates.IscsFan { + return this.getState(); + } + + get state(): number { + return this.states.state; + } + set state(v: number) { + this.states.state = v; + } + clone(): IscsFanState { + return new IscsFanState(this.states.cloneMessage()); + } +} diff --git a/src/examples/app/graphics/PlatformInteraction.ts b/src/examples/app/graphics/PlatformInteraction.ts index eaf61ed..b438c83 100644 --- a/src/examples/app/graphics/PlatformInteraction.ts +++ b/src/examples/app/graphics/PlatformInteraction.ts @@ -33,6 +33,12 @@ export class PlatformData extends GraphicDataBase implements IPlatformData { 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; } diff --git a/src/examples/app/graphics/RectInteraction.ts b/src/examples/app/graphics/RectInteraction.ts new file mode 100644 index 0000000..9951752 --- /dev/null +++ b/src/examples/app/graphics/RectInteraction.ts @@ -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 '../protos/draw_data_storage'; +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(); + } + + 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); + } +} diff --git a/src/examples/app/graphics/SignalInteraction.ts b/src/examples/app/graphics/SignalInteraction.ts new file mode 100644 index 0000000..78fb97f --- /dev/null +++ b/src/examples/app/graphics/SignalInteraction.ts @@ -0,0 +1,37 @@ +import * as pb_1 from 'google-protobuf'; +import { ISignalData, Signal } from 'src/graphics/signal/Signal'; +import { graphicData } from '../protos/draw_data_storage'; +import { GraphicDataBase } from './GraphicDataBase'; + +export class SignalData extends GraphicDataBase implements ISignalData { + constructor(data?: graphicData.Signal) { + let signal; + if (!data) { + signal = new graphicData.Signal({ + common: GraphicDataBase.defaultCommonInfo(Signal.Type), + }); + } else { + signal = data; + } + super(signal); + } + + public get data(): graphicData.Signal { + return this.getData(); + } + get code(): string { + return this.data.code; + } + set code(v: string) { + this.data.code = v; + } + clone(): SignalData { + return new SignalData(this.data.cloneMessage()); + } + copyFrom(data: SignalData): void { + pb_1.Message.copyInto(data.data, this.data); + } + eq(other: SignalData): boolean { + return pb_1.Message.equals(this.data, other.data); + } +} diff --git a/src/examples/app/graphics/StationInteraction.ts b/src/examples/app/graphics/StationInteraction.ts new file mode 100644 index 0000000..9022621 --- /dev/null +++ b/src/examples/app/graphics/StationInteraction.ts @@ -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 '../protos/draw_data_storage'; +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(); + } + 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); + } +} diff --git a/src/examples/app/graphics/TrainInteraction.ts b/src/examples/app/graphics/TrainInteraction.ts new file mode 100644 index 0000000..db770cc --- /dev/null +++ b/src/examples/app/graphics/TrainInteraction.ts @@ -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 '../protos/draw_data_storage'; +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(); + } + 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); + } +} diff --git a/src/examples/app/index.ts b/src/examples/app/index.ts index 80fef97..5327f78 100644 --- a/src/examples/app/index.ts +++ b/src/examples/app/index.ts @@ -1,11 +1,19 @@ import { fromUint8Array, toUint8Array } from 'js-base64'; import { IPointData, Point } from 'pixi.js'; -import { IscsFan } from 'src/graphics/iscs-fan/IscsFan'; +import { IscsFan, IscsFanTemplate } from 'src/graphics/iscs-fan/IscsFan'; import { IscsFanDraw } from 'src/graphics/iscs-fan/IscsFanDrawAssistant'; -import { Link } from 'src/graphics/link/Link'; +import { Link, LinkTemplate } from 'src/graphics/link/Link'; import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant'; +import { Rect } from 'src/graphics/rect/Rect'; +import { RectDraw } from 'src/graphics/rect/RectDrawAssistant'; import { Platform } from 'src/graphics/platform/Platform'; import { PlatformDraw } from 'src/graphics/platform/PlatformDrawAssistant'; +import { Station } from 'src/graphics/station/Station'; +import { Train } from 'src/graphics/train/Train'; +import { StationDraw } from 'src/graphics/station/StationDrawAssistant'; +import { Signal, SignalTemplate } from 'src/graphics/signal/Signal'; +import { SignalDraw } from 'src/graphics/signal/SignalDrawAssistant'; +import { TrainDraw } from 'src/graphics/train/TrainDrawAssistant'; import { CombinationKey, GraphicApp, @@ -16,10 +24,15 @@ import { } from 'src/jlgraphic'; import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu'; import { MenuItemOptions } from 'src/jlgraphic/ui/Menu'; -import { IscsFanData } from './graphics/IscsFanInteraction'; +import { IscsFanData, IscsFanState } from './graphics/IscsFanInteraction'; import { LinkData } from './graphics/LinkInteraction'; +import { RectData } from './graphics/RectInteraction'; import { PlatformData } from './graphics/PlatformInteraction'; +import { StationData } from './graphics/StationInteraction'; +import { SignalData } from './graphics/SignalInteraction'; +import { TrainData } from './graphics/TrainInteraction'; import { graphicData } from './protos/draw_data_storage'; +import { Notify } from 'quasar'; export function fromStoragePoint(p: graphicData.Point): Point { return new Point(p.x, p.y); @@ -91,16 +104,27 @@ export function initDrawApp(dom: HTMLElement): JlDrawApp { const app = drawApp; app.setOptions({ drawAssistants: [ - new LinkDraw(app, () => { - return new LinkData(); - }), - new IscsFanDraw(app, () => { - return new IscsFanData(); - }), - new PlatformDraw(app, () => { - return new PlatformData(); + new LinkDraw(app, new LinkTemplate(new LinkData())), + new IscsFanDraw( + app, + new IscsFanTemplate(new IscsFanData(), new IscsFanState()) + ), + new SignalDraw(app, new SignalTemplate(new SignalData())), + new TrainDraw(app, () => { + return new TrainData(); }), ], + // isSupportDeletion: (g): boolean => { + // if (g.type === Signal.Type) { + // Notify.create({ + // type: 'warning', + // message: '信号机不支持删除', + // timeout: 1000, + // }); + // return false; + // } + // return true; + // }, }); // 画布右键菜单 @@ -129,6 +153,14 @@ export function initDrawApp(dom: HTMLElement): JlDrawApp { }, }) ); + app.addKeyboardListener( + new KeyListener({ + value: 'KeyR', + onPress: () => { + app.interactionPlugin(Rect.Type).resume(); + }, + }) + ); app.addKeyboardListener( new KeyListener({ value: 'KeyF', @@ -145,13 +177,37 @@ export function initDrawApp(dom: HTMLElement): JlDrawApp { }, }) ); + app.addKeyboardListener( + new KeyListener({ + value: 'KeyO', + onPress: () => { + app.interactionPlugin(Station.Type).resume(); + }, + }) + ); + app.addKeyboardListener( + new KeyListener({ + value: 'KeyH', + onPress: () => { + app.interactionPlugin(Signal.Type).resume(); + }, + }) + ); + app.addKeyboardListener( + new KeyListener({ + value: 'KeyT', + onPress: () => { + app.interactionPlugin(Train.Type).resume(); + }, + }) + ); app.addKeyboardListener( new KeyListener({ value: '1', onPress: () => { app.queryStore.queryByType(IscsFan.Type).forEach((fan) => { - fan.__state = fan.__state + 1; - fan.__state = fan.__state % 5; + fan.states.state = fan.states.state + 1; + fan.states.state = fan.states.state % 5; fan.repaint(); }); }, @@ -183,12 +239,24 @@ export function saveDrawDatas(app: JlDrawApp) { if (Link.Type === g.type) { const linkData = (g as Link).saveData(); storage.links.push((linkData as LinkData).data); + } else if (Rect.Type === g.type) { + const rectData = (g as Rect).saveData(); + storage.Rects.push((rectData as RectData).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); + } else if (Station.Type === g.type) { + const stationData = (g as Station).saveData(); + storage.stations.push((stationData as StationData).data); + } else if (Signal.Type === g.type) { + const signalData = (g as Signal).saveData(); + storage.signals.push((signalData as SignalData).data); + } else if (Train.Type === g.type) { + const trainData = (g as Train).saveData(); + storage.train.push((trainData as TrainData).data); } }); const base64 = fromUint8Array(storage.serialize()); @@ -210,12 +278,27 @@ export function loadDrawDatas(app: GraphicApp) { storage.links.forEach((link) => { datas.push(new LinkData(link)); }); + storage.Rects.forEach((rect) => { + datas.push(new RectData(rect)); + }); storage.iscsFans.forEach((fan) => { datas.push(new IscsFanData(fan)); }); storage.Platforms.forEach((platform) => { datas.push(new PlatformData(platform)); }); + storage.stations.forEach((station) => { + datas.push(new StationData(station)); + }); + storage.signals.forEach((data) => { + datas.push(new SignalData(data)); + }); + storage.stations.forEach((signal) => { + datas.push(new StationData(signal)); + }); + storage.stations.forEach((train) => { + datas.push(new StationData(train)); + }); app.loadGraphic(datas); } else { app.loadGraphic([]); diff --git a/src/examples/app/protos/draw_data_storage.ts b/src/examples/app/protos/draw_data_storage.ts index 6f7ba94..5467429 100644 --- a/src/examples/app/protos/draw_data_storage.ts +++ b/src/examples/app/protos/draw_data_storage.ts @@ -12,9 +12,13 @@ export namespace graphicData { links?: Link[]; iscsFans?: IscsFan[]; Platforms?: Platform[]; + stations?: Station[]; + Rects?: Rect[]; + train?: Train[]; + signals?: Signal[]; }) { super(); - pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [2, 3, 4], this.#one_of_decls); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [2, 3, 4, 5, 6, 7, 8], this.#one_of_decls); if (!Array.isArray(data) && typeof data == "object") { if ("canvas" in data && data.canvas != undefined) { this.canvas = data.canvas; @@ -28,6 +32,18 @@ export namespace graphicData { if ("Platforms" in data && data.Platforms != undefined) { this.Platforms = data.Platforms; } + if ("stations" in data && data.stations != undefined) { + this.stations = data.stations; + } + if ("Rects" in data && data.Rects != undefined) { + this.Rects = data.Rects; + } + if ("train" in data && data.train != undefined) { + this.train = data.train; + } + if ("signals" in data && data.signals != undefined) { + this.signals = data.signals; + } } } get canvas() { @@ -57,11 +73,39 @@ export namespace graphicData { set Platforms(value: Platform[]) { pb_1.Message.setRepeatedWrapperField(this, 4, value); } + get stations() { + return pb_1.Message.getRepeatedWrapperField(this, Station, 5) as Station[]; + } + set stations(value: Station[]) { + pb_1.Message.setRepeatedWrapperField(this, 5, value); + } + get Rects() { + return pb_1.Message.getRepeatedWrapperField(this, Rect, 6) as Rect[]; + } + set Rects(value: Rect[]) { + pb_1.Message.setRepeatedWrapperField(this, 6, value); + } + get train() { + return pb_1.Message.getRepeatedWrapperField(this, Train, 7) as Train[]; + } + set train(value: Train[]) { + pb_1.Message.setRepeatedWrapperField(this, 7, value); + } + get signals() { + return pb_1.Message.getRepeatedWrapperField(this, Signal, 8) as Signal[]; + } + set signals(value: Signal[]) { + pb_1.Message.setRepeatedWrapperField(this, 8, value); + } static fromObject(data: { canvas?: ReturnType; links?: ReturnType[]; iscsFans?: ReturnType[]; Platforms?: ReturnType[]; + stations?: ReturnType[]; + Rects?: ReturnType[]; + train?: ReturnType[]; + signals?: ReturnType[]; }): RtssGraphicStorage { const message = new RtssGraphicStorage({}); if (data.canvas != null) { @@ -76,6 +120,18 @@ export namespace graphicData { if (data.Platforms != null) { message.Platforms = data.Platforms.map(item => Platform.fromObject(item)); } + if (data.stations != null) { + message.stations = data.stations.map(item => Station.fromObject(item)); + } + if (data.Rects != null) { + message.Rects = data.Rects.map(item => Rect.fromObject(item)); + } + if (data.train != null) { + message.train = data.train.map(item => Train.fromObject(item)); + } + if (data.signals != null) { + message.signals = data.signals.map(item => Signal.fromObject(item)); + } return message; } toObject() { @@ -84,6 +140,10 @@ export namespace graphicData { links?: ReturnType[]; iscsFans?: ReturnType[]; Platforms?: ReturnType[]; + stations?: ReturnType[]; + Rects?: ReturnType[]; + train?: ReturnType[]; + signals?: ReturnType[]; } = {}; if (this.canvas != null) { data.canvas = this.canvas.toObject(); @@ -97,6 +157,18 @@ export namespace graphicData { if (this.Platforms != null) { data.Platforms = this.Platforms.map((item: Platform) => item.toObject()); } + if (this.stations != null) { + data.stations = this.stations.map((item: Station) => item.toObject()); + } + if (this.Rects != null) { + data.Rects = this.Rects.map((item: Rect) => item.toObject()); + } + if (this.train != null) { + data.train = this.train.map((item: Train) => item.toObject()); + } + if (this.signals != null) { + data.signals = this.signals.map((item: Signal) => item.toObject()); + } return data; } serialize(): Uint8Array; @@ -111,6 +183,14 @@ export namespace graphicData { writer.writeRepeatedMessage(3, this.iscsFans, (item: IscsFan) => item.serialize(writer)); if (this.Platforms.length) writer.writeRepeatedMessage(4, this.Platforms, (item: Platform) => item.serialize(writer)); + if (this.stations.length) + writer.writeRepeatedMessage(5, this.stations, (item: Station) => item.serialize(writer)); + if (this.Rects.length) + writer.writeRepeatedMessage(6, this.Rects, (item: Rect) => item.serialize(writer)); + if (this.train.length) + writer.writeRepeatedMessage(7, this.train, (item: Train) => item.serialize(writer)); + if (this.signals.length) + writer.writeRepeatedMessage(8, this.signals, (item: Signal) => item.serialize(writer)); if (!w) return writer.getResultBuffer(); } @@ -132,6 +212,18 @@ export namespace graphicData { case 4: reader.readMessage(message.Platforms, () => pb_1.Message.addToRepeatedWrapperField(message, 4, Platform.deserialize(reader), Platform)); break; + case 5: + reader.readMessage(message.stations, () => pb_1.Message.addToRepeatedWrapperField(message, 5, Station.deserialize(reader), Station)); + break; + case 6: + reader.readMessage(message.Rects, () => pb_1.Message.addToRepeatedWrapperField(message, 6, Rect.deserialize(reader), Rect)); + break; + case 7: + reader.readMessage(message.train, () => pb_1.Message.addToRepeatedWrapperField(message, 7, Train.deserialize(reader), Train)); + break; + case 8: + reader.readMessage(message.signals, () => pb_1.Message.addToRepeatedWrapperField(message, 8, Signal.deserialize(reader), Signal)); + break; default: reader.skipField(); } } @@ -958,12 +1050,247 @@ export namespace graphicData { return Link.deserialize(bytes); } } + export class Rect extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + common?: CommonInfo; + code?: string; + lineWidth?: number; + lineColor?: string; + point?: Point; + width?: number; + height?: number; + points?: Point[]; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [8], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("common" in data && data.common != undefined) { + this.common = data.common; + } + if ("code" in data && data.code != undefined) { + this.code = data.code; + } + if ("lineWidth" in data && data.lineWidth != undefined) { + this.lineWidth = data.lineWidth; + } + if ("lineColor" in data && data.lineColor != undefined) { + this.lineColor = data.lineColor; + } + if ("point" in data && data.point != undefined) { + this.point = data.point; + } + if ("width" in data && data.width != undefined) { + this.width = data.width; + } + if ("height" in data && data.height != undefined) { + this.height = data.height; + } + if ("points" in data && data.points != undefined) { + this.points = data.points; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, CommonInfo, 1) as CommonInfo; + } + set common(value: CommonInfo) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set code(value: string) { + pb_1.Message.setField(this, 2, value); + } + get lineWidth() { + return pb_1.Message.getFieldWithDefault(this, 3, 0) as number; + } + set lineWidth(value: number) { + pb_1.Message.setField(this, 3, value); + } + get lineColor() { + return pb_1.Message.getFieldWithDefault(this, 4, "") as string; + } + set lineColor(value: string) { + pb_1.Message.setField(this, 4, value); + } + get point() { + return pb_1.Message.getWrapperField(this, Point, 5) as Point; + } + set point(value: Point) { + pb_1.Message.setWrapperField(this, 5, value); + } + get has_point() { + return pb_1.Message.getField(this, 5) != null; + } + get width() { + return pb_1.Message.getFieldWithDefault(this, 6, 0) as number; + } + set width(value: number) { + pb_1.Message.setField(this, 6, value); + } + get height() { + return pb_1.Message.getFieldWithDefault(this, 7, 0) as number; + } + set height(value: number) { + pb_1.Message.setField(this, 7, value); + } + get points() { + return pb_1.Message.getRepeatedWrapperField(this, Point, 8) as Point[]; + } + set points(value: Point[]) { + pb_1.Message.setRepeatedWrapperField(this, 8, value); + } + static fromObject(data: { + common?: ReturnType; + code?: string; + lineWidth?: number; + lineColor?: string; + point?: ReturnType; + width?: number; + height?: number; + points?: ReturnType[]; + }): Rect { + const message = new Rect({}); + if (data.common != null) { + message.common = CommonInfo.fromObject(data.common); + } + if (data.code != null) { + message.code = data.code; + } + if (data.lineWidth != null) { + message.lineWidth = data.lineWidth; + } + if (data.lineColor != null) { + message.lineColor = data.lineColor; + } + if (data.point != null) { + message.point = Point.fromObject(data.point); + } + if (data.width != null) { + message.width = data.width; + } + if (data.height != null) { + message.height = data.height; + } + if (data.points != null) { + message.points = data.points.map(item => Point.fromObject(item)); + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + code?: string; + lineWidth?: number; + lineColor?: string; + point?: ReturnType; + width?: number; + height?: number; + points?: ReturnType[]; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.code != null) { + data.code = this.code; + } + if (this.lineWidth != null) { + data.lineWidth = this.lineWidth; + } + if (this.lineColor != null) { + data.lineColor = this.lineColor; + } + if (this.point != null) { + data.point = this.point.toObject(); + } + if (this.width != null) { + data.width = this.width; + } + if (this.height != null) { + data.height = this.height; + } + if (this.points != null) { + data.points = this.points.map((item: Point) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_common) + writer.writeMessage(1, this.common, () => this.common.serialize(writer)); + if (this.code.length) + writer.writeString(2, this.code); + if (this.lineWidth != 0) + writer.writeInt32(3, this.lineWidth); + if (this.lineColor.length) + writer.writeString(4, this.lineColor); + if (this.has_point) + writer.writeMessage(5, this.point, () => this.point.serialize(writer)); + if (this.width != 0) + writer.writeFloat(6, this.width); + if (this.height != 0) + writer.writeFloat(7, this.height); + if (this.points.length) + writer.writeRepeatedMessage(8, this.points, (item: Point) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Rect { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Rect(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.common, () => message.common = CommonInfo.deserialize(reader)); + break; + case 2: + message.code = reader.readString(); + break; + case 3: + message.lineWidth = reader.readInt32(); + break; + case 4: + message.lineColor = reader.readString(); + break; + case 5: + reader.readMessage(message.point, () => message.point = Point.deserialize(reader)); + break; + case 6: + message.width = reader.readFloat(); + break; + case 7: + message.height = reader.readFloat(); + break; + case 8: + reader.readMessage(message.points, () => pb_1.Message.addToRepeatedWrapperField(message, 8, Point.deserialize(reader), Point)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Rect { + return Rect.deserialize(bytes); + } + } export class Platform extends pb_1.Message { #one_of_decls: number[][] = []; constructor(data?: any[] | { common?: CommonInfo; code?: string; hasdoor?: boolean; + trainDirection?: string; lineWidth?: number; lineColor?: string; lineColorDoor?: string; @@ -973,7 +1300,7 @@ export namespace graphicData { orbitCode?: string[]; }) { super(); - pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [10], this.#one_of_decls); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [11], this.#one_of_decls); if (!Array.isArray(data) && typeof data == "object") { if ("common" in data && data.common != undefined) { this.common = data.common; @@ -984,6 +1311,9 @@ export namespace graphicData { if ("hasdoor" in data && data.hasdoor != undefined) { this.hasdoor = data.hasdoor; } + if ("trainDirection" in data && data.trainDirection != undefined) { + this.trainDirection = data.trainDirection; + } if ("lineWidth" in data && data.lineWidth != undefined) { this.lineWidth = data.lineWidth; } @@ -1028,55 +1358,62 @@ export namespace graphicData { set hasdoor(value: boolean) { pb_1.Message.setField(this, 3, value); } - get lineWidth() { - return pb_1.Message.getFieldWithDefault(this, 4, 0) as number; + get trainDirection() { + return pb_1.Message.getFieldWithDefault(this, 4, "") as string; } - set lineWidth(value: number) { + set trainDirection(value: string) { pb_1.Message.setField(this, 4, value); } - get lineColor() { - return pb_1.Message.getFieldWithDefault(this, 5, "") as string; + get lineWidth() { + return pb_1.Message.getFieldWithDefault(this, 5, 0) as number; } - set lineColor(value: string) { + set lineWidth(value: number) { pb_1.Message.setField(this, 5, value); } - get lineColorDoor() { + get lineColor() { return pb_1.Message.getFieldWithDefault(this, 6, "") as string; } - set lineColorDoor(value: string) { + set lineColor(value: string) { pb_1.Message.setField(this, 6, value); } + get lineColorDoor() { + return pb_1.Message.getFieldWithDefault(this, 7, "") as string; + } + set lineColorDoor(value: string) { + pb_1.Message.setField(this, 7, value); + } get point() { - return pb_1.Message.getWrapperField(this, Point, 7) as Point; + return pb_1.Message.getWrapperField(this, Point, 8) as Point; } set point(value: Point) { - pb_1.Message.setWrapperField(this, 7, value); + pb_1.Message.setWrapperField(this, 8, value); } get has_point() { - return pb_1.Message.getField(this, 7) != null; + return pb_1.Message.getField(this, 8) != null; } get width() { - return pb_1.Message.getFieldWithDefault(this, 8, 0) as number; - } - set width(value: number) { - pb_1.Message.setField(this, 8, value); - } - get height() { return pb_1.Message.getFieldWithDefault(this, 9, 0) as number; } - set height(value: number) { + set width(value: number) { pb_1.Message.setField(this, 9, value); } + get height() { + return pb_1.Message.getFieldWithDefault(this, 10, 0) as number; + } + set height(value: number) { + pb_1.Message.setField(this, 10, value); + } get orbitCode() { - return pb_1.Message.getFieldWithDefault(this, 10, []) as string[]; + return pb_1.Message.getFieldWithDefault(this, 11, []) as string[]; } set orbitCode(value: string[]) { - pb_1.Message.setField(this, 10, value); + pb_1.Message.setField(this, 11, value); } static fromObject(data: { common?: ReturnType; code?: string; hasdoor?: boolean; + trainDirection?: string; lineWidth?: number; lineColor?: string; lineColorDoor?: string; @@ -1095,6 +1432,9 @@ export namespace graphicData { if (data.hasdoor != null) { message.hasdoor = data.hasdoor; } + if (data.trainDirection != null) { + message.trainDirection = data.trainDirection; + } if (data.lineWidth != null) { message.lineWidth = data.lineWidth; } @@ -1123,6 +1463,7 @@ export namespace graphicData { common?: ReturnType; code?: string; hasdoor?: boolean; + trainDirection?: string; lineWidth?: number; lineColor?: string; lineColorDoor?: string; @@ -1140,6 +1481,9 @@ export namespace graphicData { if (this.hasdoor != null) { data.hasdoor = this.hasdoor; } + if (this.trainDirection != null) { + data.trainDirection = this.trainDirection; + } if (this.lineWidth != null) { data.lineWidth = this.lineWidth; } @@ -1173,20 +1517,22 @@ export namespace graphicData { writer.writeString(2, this.code); if (this.hasdoor != false) writer.writeBool(3, this.hasdoor); + if (this.trainDirection.length) + writer.writeString(4, this.trainDirection); if (this.lineWidth != 0) - writer.writeInt32(4, this.lineWidth); + writer.writeInt32(5, this.lineWidth); if (this.lineColor.length) - writer.writeString(5, this.lineColor); + writer.writeString(6, this.lineColor); if (this.lineColorDoor.length) - writer.writeString(6, this.lineColorDoor); + writer.writeString(7, this.lineColorDoor); if (this.has_point) - writer.writeMessage(7, this.point, () => this.point.serialize(writer)); + writer.writeMessage(8, this.point, () => this.point.serialize(writer)); if (this.width != 0) - writer.writeFloat(8, this.width); + writer.writeFloat(9, this.width); if (this.height != 0) - writer.writeFloat(9, this.height); + writer.writeFloat(10, this.height); if (this.orbitCode.length) - writer.writeRepeatedString(10, this.orbitCode); + writer.writeRepeatedString(11, this.orbitCode); if (!w) return writer.getResultBuffer(); } @@ -1206,25 +1552,28 @@ export namespace graphicData { message.hasdoor = reader.readBool(); break; case 4: - message.lineWidth = reader.readInt32(); + message.trainDirection = reader.readString(); break; case 5: - message.lineColor = reader.readString(); + message.lineWidth = reader.readInt32(); break; case 6: - message.lineColorDoor = reader.readString(); + message.lineColor = reader.readString(); break; case 7: - reader.readMessage(message.point, () => message.point = Point.deserialize(reader)); + message.lineColorDoor = reader.readString(); break; case 8: - message.width = reader.readFloat(); + reader.readMessage(message.point, () => message.point = Point.deserialize(reader)); break; case 9: - message.height = reader.readFloat(); + message.width = reader.readFloat(); break; case 10: - pb_1.Message.addToRepeatedField(message, 10, reader.readString()); + message.height = reader.readFloat(); + break; + case 11: + pb_1.Message.addToRepeatedField(message, 11, reader.readString()); break; default: reader.skipField(); } @@ -1238,6 +1587,615 @@ export namespace graphicData { return Platform.deserialize(bytes); } } + export class Station extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + common?: CommonInfo; + code?: string; + hasCircle?: boolean; + radius?: number; + borderWidth?: number; + borderColor?: string; + fillColor?: string; + codeColor?: string; + codeFontSize?: number; + point?: Point; + circlePoint?: Point; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("common" in data && data.common != undefined) { + this.common = data.common; + } + if ("code" in data && data.code != undefined) { + this.code = data.code; + } + if ("hasCircle" in data && data.hasCircle != undefined) { + this.hasCircle = data.hasCircle; + } + if ("radius" in data && data.radius != undefined) { + this.radius = data.radius; + } + if ("borderWidth" in data && data.borderWidth != undefined) { + this.borderWidth = data.borderWidth; + } + if ("borderColor" in data && data.borderColor != undefined) { + this.borderColor = data.borderColor; + } + if ("fillColor" in data && data.fillColor != undefined) { + this.fillColor = data.fillColor; + } + if ("codeColor" in data && data.codeColor != undefined) { + this.codeColor = data.codeColor; + } + if ("codeFontSize" in data && data.codeFontSize != undefined) { + this.codeFontSize = data.codeFontSize; + } + if ("point" in data && data.point != undefined) { + this.point = data.point; + } + if ("circlePoint" in data && data.circlePoint != undefined) { + this.circlePoint = data.circlePoint; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, CommonInfo, 1) as CommonInfo; + } + set common(value: CommonInfo) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set code(value: string) { + pb_1.Message.setField(this, 2, value); + } + get hasCircle() { + return pb_1.Message.getFieldWithDefault(this, 3, false) as boolean; + } + set hasCircle(value: boolean) { + pb_1.Message.setField(this, 3, value); + } + get radius() { + return pb_1.Message.getFieldWithDefault(this, 4, 0) as number; + } + set radius(value: number) { + pb_1.Message.setField(this, 4, value); + } + get borderWidth() { + return pb_1.Message.getFieldWithDefault(this, 5, 0) as number; + } + set borderWidth(value: number) { + pb_1.Message.setField(this, 5, value); + } + get borderColor() { + return pb_1.Message.getFieldWithDefault(this, 6, "") as string; + } + set borderColor(value: string) { + pb_1.Message.setField(this, 6, value); + } + get fillColor() { + return pb_1.Message.getFieldWithDefault(this, 7, "") as string; + } + set fillColor(value: string) { + pb_1.Message.setField(this, 7, value); + } + get codeColor() { + return pb_1.Message.getFieldWithDefault(this, 8, "") as string; + } + set codeColor(value: string) { + pb_1.Message.setField(this, 8, value); + } + get codeFontSize() { + return pb_1.Message.getFieldWithDefault(this, 9, 0) as number; + } + set codeFontSize(value: number) { + pb_1.Message.setField(this, 9, value); + } + get point() { + return pb_1.Message.getWrapperField(this, Point, 10) as Point; + } + set point(value: Point) { + pb_1.Message.setWrapperField(this, 10, value); + } + get has_point() { + return pb_1.Message.getField(this, 10) != null; + } + get circlePoint() { + return pb_1.Message.getWrapperField(this, Point, 11) as Point; + } + set circlePoint(value: Point) { + pb_1.Message.setWrapperField(this, 11, value); + } + get has_circlePoint() { + return pb_1.Message.getField(this, 11) != null; + } + static fromObject(data: { + common?: ReturnType; + code?: string; + hasCircle?: boolean; + radius?: number; + borderWidth?: number; + borderColor?: string; + fillColor?: string; + codeColor?: string; + codeFontSize?: number; + point?: ReturnType; + circlePoint?: ReturnType; + }): Station { + const message = new Station({}); + if (data.common != null) { + message.common = CommonInfo.fromObject(data.common); + } + if (data.code != null) { + message.code = data.code; + } + if (data.hasCircle != null) { + message.hasCircle = data.hasCircle; + } + if (data.radius != null) { + message.radius = data.radius; + } + if (data.borderWidth != null) { + message.borderWidth = data.borderWidth; + } + if (data.borderColor != null) { + message.borderColor = data.borderColor; + } + if (data.fillColor != null) { + message.fillColor = data.fillColor; + } + if (data.codeColor != null) { + message.codeColor = data.codeColor; + } + if (data.codeFontSize != null) { + message.codeFontSize = data.codeFontSize; + } + if (data.point != null) { + message.point = Point.fromObject(data.point); + } + if (data.circlePoint != null) { + message.circlePoint = Point.fromObject(data.circlePoint); + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + code?: string; + hasCircle?: boolean; + radius?: number; + borderWidth?: number; + borderColor?: string; + fillColor?: string; + codeColor?: string; + codeFontSize?: number; + point?: ReturnType; + circlePoint?: ReturnType; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.code != null) { + data.code = this.code; + } + if (this.hasCircle != null) { + data.hasCircle = this.hasCircle; + } + if (this.radius != null) { + data.radius = this.radius; + } + if (this.borderWidth != null) { + data.borderWidth = this.borderWidth; + } + if (this.borderColor != null) { + data.borderColor = this.borderColor; + } + if (this.fillColor != null) { + data.fillColor = this.fillColor; + } + if (this.codeColor != null) { + data.codeColor = this.codeColor; + } + if (this.codeFontSize != null) { + data.codeFontSize = this.codeFontSize; + } + if (this.point != null) { + data.point = this.point.toObject(); + } + if (this.circlePoint != null) { + data.circlePoint = this.circlePoint.toObject(); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_common) + writer.writeMessage(1, this.common, () => this.common.serialize(writer)); + if (this.code.length) + writer.writeString(2, this.code); + if (this.hasCircle != false) + writer.writeBool(3, this.hasCircle); + if (this.radius != 0) + writer.writeInt32(4, this.radius); + if (this.borderWidth != 0) + writer.writeInt32(5, this.borderWidth); + if (this.borderColor.length) + writer.writeString(6, this.borderColor); + if (this.fillColor.length) + writer.writeString(7, this.fillColor); + if (this.codeColor.length) + writer.writeString(8, this.codeColor); + if (this.codeFontSize != 0) + writer.writeInt32(9, this.codeFontSize); + if (this.has_point) + writer.writeMessage(10, this.point, () => this.point.serialize(writer)); + if (this.has_circlePoint) + writer.writeMessage(11, this.circlePoint, () => this.circlePoint.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Station { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Station(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.common, () => message.common = CommonInfo.deserialize(reader)); + break; + case 2: + message.code = reader.readString(); + break; + case 3: + message.hasCircle = reader.readBool(); + break; + case 4: + message.radius = reader.readInt32(); + break; + case 5: + message.borderWidth = reader.readInt32(); + break; + case 6: + message.borderColor = reader.readString(); + break; + case 7: + message.fillColor = reader.readString(); + break; + case 8: + message.codeColor = reader.readString(); + break; + case 9: + message.codeFontSize = reader.readInt32(); + break; + case 10: + reader.readMessage(message.point, () => message.point = Point.deserialize(reader)); + break; + case 11: + reader.readMessage(message.circlePoint, () => message.circlePoint = Point.deserialize(reader)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Station { + return Station.deserialize(bytes); + } + } + export class Train extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + common?: CommonInfo; + code?: string; + codeColor?: string; + codeFontSize?: number; + point?: Point; + trainDirection?: string; + hasBorder?: boolean; + borderWidth?: number; + borderColor?: string; + headColor?: string; + bodyColor?: string; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("common" in data && data.common != undefined) { + this.common = data.common; + } + if ("code" in data && data.code != undefined) { + this.code = data.code; + } + if ("codeColor" in data && data.codeColor != undefined) { + this.codeColor = data.codeColor; + } + if ("codeFontSize" in data && data.codeFontSize != undefined) { + this.codeFontSize = data.codeFontSize; + } + if ("point" in data && data.point != undefined) { + this.point = data.point; + } + if ("trainDirection" in data && data.trainDirection != undefined) { + this.trainDirection = data.trainDirection; + } + if ("hasBorder" in data && data.hasBorder != undefined) { + this.hasBorder = data.hasBorder; + } + if ("borderWidth" in data && data.borderWidth != undefined) { + this.borderWidth = data.borderWidth; + } + if ("borderColor" in data && data.borderColor != undefined) { + this.borderColor = data.borderColor; + } + if ("headColor" in data && data.headColor != undefined) { + this.headColor = data.headColor; + } + if ("bodyColor" in data && data.bodyColor != undefined) { + this.bodyColor = data.bodyColor; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, CommonInfo, 1) as CommonInfo; + } + set common(value: CommonInfo) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set code(value: string) { + pb_1.Message.setField(this, 2, value); + } + get codeColor() { + return pb_1.Message.getFieldWithDefault(this, 3, "") as string; + } + set codeColor(value: string) { + pb_1.Message.setField(this, 3, value); + } + get codeFontSize() { + return pb_1.Message.getFieldWithDefault(this, 4, 0) as number; + } + set codeFontSize(value: number) { + pb_1.Message.setField(this, 4, value); + } + get point() { + return pb_1.Message.getWrapperField(this, Point, 5) as Point; + } + set point(value: Point) { + pb_1.Message.setWrapperField(this, 5, value); + } + get has_point() { + return pb_1.Message.getField(this, 5) != null; + } + get trainDirection() { + return pb_1.Message.getFieldWithDefault(this, 6, "") as string; + } + set trainDirection(value: string) { + pb_1.Message.setField(this, 6, value); + } + get hasBorder() { + return pb_1.Message.getFieldWithDefault(this, 7, false) as boolean; + } + set hasBorder(value: boolean) { + pb_1.Message.setField(this, 7, value); + } + get borderWidth() { + return pb_1.Message.getFieldWithDefault(this, 8, 0) as number; + } + set borderWidth(value: number) { + pb_1.Message.setField(this, 8, value); + } + get borderColor() { + return pb_1.Message.getFieldWithDefault(this, 9, "") as string; + } + set borderColor(value: string) { + pb_1.Message.setField(this, 9, value); + } + get headColor() { + return pb_1.Message.getFieldWithDefault(this, 10, "") as string; + } + set headColor(value: string) { + pb_1.Message.setField(this, 10, value); + } + get bodyColor() { + return pb_1.Message.getFieldWithDefault(this, 11, "") as string; + } + set bodyColor(value: string) { + pb_1.Message.setField(this, 11, value); + } + static fromObject(data: { + common?: ReturnType; + code?: string; + codeColor?: string; + codeFontSize?: number; + point?: ReturnType; + trainDirection?: string; + hasBorder?: boolean; + borderWidth?: number; + borderColor?: string; + headColor?: string; + bodyColor?: string; + }): Train { + const message = new Train({}); + if (data.common != null) { + message.common = CommonInfo.fromObject(data.common); + } + if (data.code != null) { + message.code = data.code; + } + if (data.codeColor != null) { + message.codeColor = data.codeColor; + } + if (data.codeFontSize != null) { + message.codeFontSize = data.codeFontSize; + } + if (data.point != null) { + message.point = Point.fromObject(data.point); + } + if (data.trainDirection != null) { + message.trainDirection = data.trainDirection; + } + if (data.hasBorder != null) { + message.hasBorder = data.hasBorder; + } + if (data.borderWidth != null) { + message.borderWidth = data.borderWidth; + } + if (data.borderColor != null) { + message.borderColor = data.borderColor; + } + if (data.headColor != null) { + message.headColor = data.headColor; + } + if (data.bodyColor != null) { + message.bodyColor = data.bodyColor; + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + code?: string; + codeColor?: string; + codeFontSize?: number; + point?: ReturnType; + trainDirection?: string; + hasBorder?: boolean; + borderWidth?: number; + borderColor?: string; + headColor?: string; + bodyColor?: string; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.code != null) { + data.code = this.code; + } + if (this.codeColor != null) { + data.codeColor = this.codeColor; + } + if (this.codeFontSize != null) { + data.codeFontSize = this.codeFontSize; + } + if (this.point != null) { + data.point = this.point.toObject(); + } + if (this.trainDirection != null) { + data.trainDirection = this.trainDirection; + } + if (this.hasBorder != null) { + data.hasBorder = this.hasBorder; + } + if (this.borderWidth != null) { + data.borderWidth = this.borderWidth; + } + if (this.borderColor != null) { + data.borderColor = this.borderColor; + } + if (this.headColor != null) { + data.headColor = this.headColor; + } + if (this.bodyColor != null) { + data.bodyColor = this.bodyColor; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_common) + writer.writeMessage(1, this.common, () => this.common.serialize(writer)); + if (this.code.length) + writer.writeString(2, this.code); + if (this.codeColor.length) + writer.writeString(3, this.codeColor); + if (this.codeFontSize != 0) + writer.writeInt32(4, this.codeFontSize); + if (this.has_point) + writer.writeMessage(5, this.point, () => this.point.serialize(writer)); + if (this.trainDirection.length) + writer.writeString(6, this.trainDirection); + if (this.hasBorder != false) + writer.writeBool(7, this.hasBorder); + if (this.borderWidth != 0) + writer.writeInt32(8, this.borderWidth); + if (this.borderColor.length) + writer.writeString(9, this.borderColor); + if (this.headColor.length) + writer.writeString(10, this.headColor); + if (this.bodyColor.length) + writer.writeString(11, this.bodyColor); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Train { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Train(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.common, () => message.common = CommonInfo.deserialize(reader)); + break; + case 2: + message.code = reader.readString(); + break; + case 3: + message.codeColor = reader.readString(); + break; + case 4: + message.codeFontSize = reader.readInt32(); + break; + case 5: + reader.readMessage(message.point, () => message.point = Point.deserialize(reader)); + break; + case 6: + message.trainDirection = reader.readString(); + break; + case 7: + message.hasBorder = reader.readBool(); + break; + case 8: + message.borderWidth = reader.readInt32(); + break; + case 9: + message.borderColor = reader.readString(); + break; + case 10: + message.headColor = reader.readString(); + break; + case 11: + message.bodyColor = reader.readString(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Train { + return Train.deserialize(bytes); + } + } export class IscsFan extends pb_1.Message { #one_of_decls: number[][] = []; constructor(data?: any[] | { @@ -1331,4 +2289,137 @@ export namespace graphicData { return IscsFan.deserialize(bytes); } } + export class Turnout extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | {}) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { } + } + static fromObject(data: {}): Turnout { + const message = new Turnout({}); + return message; + } + toObject() { + const data: {} = {}; + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Turnout { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Turnout(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Turnout { + return Turnout.deserialize(bytes); + } + } + export class Signal extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + common?: CommonInfo; + code?: string; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("common" in data && data.common != undefined) { + this.common = data.common; + } + if ("code" in data && data.code != undefined) { + this.code = data.code; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, CommonInfo, 1) as CommonInfo; + } + set common(value: CommonInfo) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set code(value: string) { + pb_1.Message.setField(this, 2, value); + } + static fromObject(data: { + common?: ReturnType; + code?: string; + }): Signal { + const message = new Signal({}); + if (data.common != null) { + message.common = CommonInfo.fromObject(data.common); + } + if (data.code != null) { + message.code = data.code; + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + code?: string; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.code != null) { + data.code = this.code; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_common) + writer.writeMessage(1, this.common, () => this.common.serialize(writer)); + if (this.code.length) + writer.writeString(2, this.code); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Signal { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Signal(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.common, () => message.common = CommonInfo.deserialize(reader)); + break; + case 2: + message.code = reader.readString(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Signal { + return Signal.deserialize(bytes); + } + } } diff --git a/src/examples/app/protos/graphic_states.ts b/src/examples/app/protos/graphic_states.ts new file mode 100644 index 0000000..a44ccea --- /dev/null +++ b/src/examples/app/protos/graphic_states.ts @@ -0,0 +1,191 @@ +/** + * Generated by the protoc-gen-ts. DO NOT EDIT! + * compiler version: 4.22.2 + * source: graphic_states.proto + * git: https://github.com/thesayyn/protoc-gen-ts */ +import * as pb_1 from "google-protobuf"; +export namespace graphicStates { + export class CommonState extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + code?: string; + graphicType?: string; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("code" in data && data.code != undefined) { + this.code = data.code; + } + if ("graphicType" in data && data.graphicType != undefined) { + this.graphicType = data.graphicType; + } + } + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 1, "") as string; + } + set code(value: string) { + pb_1.Message.setField(this, 1, value); + } + get graphicType() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set graphicType(value: string) { + pb_1.Message.setField(this, 2, value); + } + static fromObject(data: { + code?: string; + graphicType?: string; + }): CommonState { + const message = new CommonState({}); + if (data.code != null) { + message.code = data.code; + } + if (data.graphicType != null) { + message.graphicType = data.graphicType; + } + return message; + } + toObject() { + const data: { + code?: string; + graphicType?: string; + } = {}; + if (this.code != null) { + data.code = this.code; + } + if (this.graphicType != null) { + data.graphicType = this.graphicType; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.code.length) + writer.writeString(1, this.code); + if (this.graphicType.length) + writer.writeString(2, this.graphicType); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): CommonState { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new CommonState(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + message.code = reader.readString(); + break; + case 2: + message.graphicType = reader.readString(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): CommonState { + return CommonState.deserialize(bytes); + } + } + export class IscsFan extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + common?: CommonState; + state?: number; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("common" in data && data.common != undefined) { + this.common = data.common; + } + if ("state" in data && data.state != undefined) { + this.state = data.state; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, CommonState, 1) as CommonState; + } + set common(value: CommonState) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get state() { + return pb_1.Message.getFieldWithDefault(this, 2, 0) as number; + } + set state(value: number) { + pb_1.Message.setField(this, 2, value); + } + static fromObject(data: { + common?: ReturnType; + state?: number; + }): IscsFan { + const message = new IscsFan({}); + if (data.common != null) { + message.common = CommonState.fromObject(data.common); + } + if (data.state != null) { + message.state = data.state; + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + state?: number; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.state != null) { + data.state = this.state; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_common) + writer.writeMessage(1, this.common, () => this.common.serialize(writer)); + if (this.state != 0) + writer.writeInt32(2, this.state); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): IscsFan { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new IscsFan(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.common, () => message.common = CommonState.deserialize(reader)); + break; + case 2: + message.state = reader.readInt32(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): IscsFan { + return IscsFan.deserialize(bytes); + } + } +} diff --git a/src/graphics/CommonGraphics.ts b/src/graphics/CommonGraphics.ts new file mode 100644 index 0000000..60e935f --- /dev/null +++ b/src/graphics/CommonGraphics.ts @@ -0,0 +1,51 @@ +import { Graphics } from 'pixi.js'; +export function drawArrow( + polygon: Graphics, + x: number, + y: number, + length: number, + radius: number, + lineWidth: number +) { + const trianglAcme = { x, y }; + const triangleP1 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y + Math.cos(Math.PI / 6) * radius, + }; + const triangleP2 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y - Math.cos(Math.PI / 6) * radius, + }; + const lineP1 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y + lineWidth / 2, + }; + const lineP2 = { + x: x - length, + y: y + lineWidth / 2, + }; + const lineP3 = { + x: x - length, + y: y - lineWidth / 2, + }; + const lineP4 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y - lineWidth / 2, + }; + polygon.drawPolygon( + trianglAcme.x, + trianglAcme.y, + triangleP1.x, + triangleP1.y, + lineP1.x, + lineP1.y, + lineP2.x, + lineP2.y, + lineP3.x, + lineP3.y, + lineP4.x, + lineP4.y, + triangleP2.x, + triangleP2.y + ); +} diff --git a/src/graphics/iscs-fan/IscsFan.ts b/src/graphics/iscs-fan/IscsFan.ts index fdc71d5..184f3b3 100644 --- a/src/graphics/iscs-fan/IscsFan.ts +++ b/src/graphics/iscs-fan/IscsFan.ts @@ -1,6 +1,7 @@ import { GraphicAnimation, GraphicData, + GraphicState, JlGraphic, JlGraphicTemplate, } from 'src/jlgraphic'; @@ -23,12 +24,16 @@ export interface IIscsFanData extends GraphicData { set code(v: string); } +export interface IIscsFanState extends GraphicState { + get state(): number; + set state(v: number); +} + export class IscsFan extends JlGraphic { static Type = 'IscsFan'; _border: Sprite; _fan: Sprite; fanTextures: FanTextures; - __state = 0; constructor(fanTextures: FanTextures) { super(IscsFan.Type); @@ -42,28 +47,31 @@ export class IscsFan extends JlGraphic { this.addChild(this._border); this.addChild(this._fan); } + get states(): IIscsFanState { + return this.getStates(); + } doRepaint(): void { - if (this.__state === 0) { + if (this.states.state === 0) { // 停止 this.stopFanRun(); this._fan.rotation = 0; this._fan.texture = this.fanTextures.gray; - } else if (this.__state === 1) { + } else if (this.states.state === 1) { // 正常运行 this._fan.texture = this.fanTextures.green; // 动画 this.initFanRun(); - } else if (this.__state === 2) { + } else if (this.states.state === 2) { // 报警运行 this._fan.texture = this.fanTextures.yellow; // 动画 this.initFanRun(); - } else if (this.__state === 3) { + } else if (this.states.state === 3) { // 故障 this.stopFanRun(); this._fan.rotation = 0; this._fan.texture = this.fanTextures.red; - } else if (this.__state === 4) { + } else if (this.states.state === 4) { // 通信故障 // 停止 this.stopFanRun(); @@ -99,12 +107,18 @@ export class IscsFan extends JlGraphic { export class IscsFanTemplate extends JlGraphicTemplate { fanTextures?: FanTextures; - constructor() { - super(IscsFan.Type); + constructor(dataTemplate: IIscsFanData, stateTemplate: IIscsFanState) { + super(IscsFan.Type, { + dataTemplate, + stateTemplate, + }); } new(): IscsFan { if (this.fanTextures) { - return new IscsFan(this.fanTextures); + const g = new IscsFan(this.fanTextures); + g.loadData(this.datas); + g.loadState(this.states); + return g; } throw new Error('资源未加载/加载失败'); } diff --git a/src/graphics/iscs-fan/IscsFanDrawAssistant.ts b/src/graphics/iscs-fan/IscsFanDrawAssistant.ts index 71ae98f..4c7bc1e 100644 --- a/src/graphics/iscs-fan/IscsFanDrawAssistant.ts +++ b/src/graphics/iscs-fan/IscsFanDrawAssistant.ts @@ -1,7 +1,10 @@ import { FederatedMouseEvent, Point } from 'pixi.js'; import { + AbsorbableLine, + AbsorbablePosition, GraphicDrawAssistant, GraphicInteractionPlugin, + GraphicTransformEvent, JlDrawApp, JlGraphic, } from 'src/jlgraphic'; @@ -13,9 +16,8 @@ export class IscsFanDraw extends GraphicDrawAssistant< > { _iscsFan: IscsFan | null = null; - constructor(app: JlDrawApp, createData: () => IIscsFanData) { - const template = new IscsFanTemplate(); - super(app, template, createData, IscsFan.Type, '风机'); + constructor(app: JlDrawApp, template: IscsFanTemplate) { + super(app, template, IscsFan.Type, '风机'); IscsFanInteraction.init(app); } @@ -66,12 +68,42 @@ export class IscsFanInteraction extends GraphicInteractionPlugin { bind(g: IscsFan): void { g.eventMode = 'static'; g.cursor = 'pointer'; - g.scalable = true; + // g.scalable = true; g.rotatable = true; + g.on('drag-start', () => { + console.log('风机拖拽'); + }); + g.on('transformstart', (e: GraphicTransformEvent) => { + if (e.isShift()) { + g.getGraphicApp().setOptions({ + absorbablePositions: buildAbsorbablePositions(g), + }); + } + }); } unbind(g: IscsFan): void { g.eventMode = 'none'; - g.scalable = false; + // g.scalable = false; g.rotatable = false; } } + +function buildAbsorbablePositions( + g: IscsFan +): AbsorbablePosition[] | undefined { + const app = g.getGraphicApp(); + const canvas = app.canvas; + const store = app.queryStore; + const aps: AbsorbablePosition[] = []; + store.queryByType(IscsFan.Type).forEach((fan) => { + if (fan.id === g.id) return; + const p = fan.position; + aps.push( + new AbsorbableLine(new Point(0, p.y), new Point(canvas.width, p.y)) + ); + aps.push( + new AbsorbableLine(new Point(p.x, 0), new Point(p.x, canvas.height)) + ); + }); + return aps; +} diff --git a/src/graphics/link/Link.ts b/src/graphics/link/Link.ts index 04285df..6dc36e0 100644 --- a/src/graphics/link/Link.ts +++ b/src/graphics/link/Link.ts @@ -89,8 +89,10 @@ export class LinkTemplate extends JlGraphicTemplate { lineWidth: number; lineColor: string; segmentsCount: number; - constructor() { - super(Link.Type); + constructor(dataTemplate: ILinkData) { + super(Link.Type, { + dataTemplate, + }); this.lineWidth = 2; this.lineColor = '#000000'; this.curve = false; diff --git a/src/graphics/link/LinkDrawAssistant.ts b/src/graphics/link/LinkDrawAssistant.ts index fb798f3..779c69c 100644 --- a/src/graphics/link/LinkDrawAssistant.ts +++ b/src/graphics/link/LinkDrawAssistant.ts @@ -30,12 +30,13 @@ import { ILineGraphic, PolylineEditPlugin, addWayPoint, - addWaypointConfig, clearWayPoint, - clearWaypointsConfig, getWaypointRangeIndex, + removeBezierWayPoint, + removeLineWayPoint, } from 'src/jlgraphic/plugins/GraphicEditPlugin'; import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu'; +import { MenuItemOptions, MenuOptions } from 'src/jlgraphic/ui/Menu'; import { ILinkData, Link, LinkTemplate } from './Link'; export interface ILinkDrawOptions { @@ -67,8 +68,8 @@ export class LinkDraw extends GraphicDrawAssistant { }, }); - constructor(app: JlDrawApp, createData: () => ILinkData) { - super(app, new LinkTemplate(), createData, Link.Type, '轨道Link'); + constructor(app: JlDrawApp, template: LinkTemplate) { + super(app, template, Link.Type, '轨道Link'); this.container.addChild(this.graphic); this.graphicTemplate.curve = true; @@ -251,7 +252,7 @@ function buildAbsorbablePositions(link: Link): AbsorbablePosition[] { * @param dp * @param index */ -function onEditPointCreate( +function onPolylineEditPointCreate( g: ILineGraphic, dp: DraggablePoint, index: number @@ -267,8 +268,78 @@ function onEditPointCreate( } }); } + dp.on('rightclick', (e: FederatedMouseEvent) => { + // dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu); + // 路径中的点 + const app = dp.getGraphicApp(); + app.registerMenu(EditPointContextMenu); + removeWaypointConfig.handler = () => { + removeLineWayPoint(link, index); + }; + clearWaypointsConfig.handler = () => { + clearWayPoint(link, false); + }; + EditPointContextMenu.open(e.global); + }); } +function onCurveEditPointCreate( + 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 c = index % 3; + dp.on('rightclick', (e: FederatedMouseEvent) => { + // dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu); + if (c === 0) { + // 路径中的点 + const app = dp.getGraphicApp(); + app.registerMenu(EditPointContextMenu); + removeWaypointConfig.handler = () => { + removeBezierWayPoint(link, index); + }; + clearWaypointsConfig.handler = () => { + clearWayPoint(link, true); + }; + EditPointContextMenu.open(e.global); + } + }); +} + +export const addWaypointConfig: MenuItemOptions = { + name: '添加路径点', +}; +export const addWaySegmentingConfig: MenuItemOptions = { + name: '细分', +}; +export const removeWaypointConfig: MenuItemOptions = { + name: '移除路径点', +}; +export const clearWaypointsConfig: MenuItemOptions = { + name: '清除所有路径点', +}; +const menuOptions: MenuOptions = { + name: '图形编辑点菜单', + groups: [ + { + items: [removeWaypointConfig, clearWaypointsConfig], + }, + ], +}; + +const EditPointContextMenu = ContextMenu.init(menuOptions); + const LinkEditMenu: ContextMenu = ContextMenu.init({ name: '轨道编辑菜单', groups: [ @@ -311,14 +382,17 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin { const target = e.target as DisplayObject; const link = target.getGraphic() as Link; this.app.updateSelected(link); + const p = link.getGraphicApp().toCanvasCoordinates(e.global); + console.log('右键坐标', p); addWaypointConfig.handler = () => { const linePoints = link.linePoints; - const p = link.screenToLocalPoint(e.global); + console.log('添加路径点', linePoints, p); const { start, end } = getWaypointRangeIndex( linePoints, link.datas.curve, - p + p, + link.datas.lineWidth ); addWayPoint(link, link.datas.curve, start, end, p); }; @@ -338,7 +412,7 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin { ); if (!lep) { lep = new BezierCurveEditPlugin(link, { - onEditPointCreate, + onEditPointCreate: onCurveEditPointCreate, }); link.addAssistantAppend(lep); } @@ -348,7 +422,9 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin { PolylineEditPlugin.Name ); if (!lep) { - lep = new PolylineEditPlugin(link, { onEditPointCreate }); + lep = new PolylineEditPlugin(link, { + onEditPointCreate: onPolylineEditPointCreate, + }); link.addAssistantAppend(lep); } } diff --git a/src/graphics/platform/Platform.ts b/src/graphics/platform/Platform.ts index 3f74ab5..ba46c2e 100644 --- a/src/graphics/platform/Platform.ts +++ b/src/graphics/platform/Platform.ts @@ -3,6 +3,7 @@ import { GraphicData, JlGraphic, JlGraphicTemplate, + VectorText, getRectangleCenter, } from 'src/jlgraphic'; @@ -11,6 +12,8 @@ export interface IPlatformData extends GraphicData { 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; // 站台线色 @@ -28,43 +31,90 @@ export interface IPlatformData extends GraphicData { 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(); } 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.clear(); doorGraphic.lineStyle( this.datas.lineWidth, new Color(this.datas.lineColorDoor) ); - const width = this.datas.width; - const height = this.datas.height; - doorGraphic.moveTo( - -width / 2 - this.datas.lineWidth / 2, - -height / 2 - 10 + 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) ); - doorGraphic.lineTo( - width / 2 + this.datas.lineWidth / 2, - -height / 2 - 10 + 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( @@ -74,14 +124,64 @@ export class Platform extends JlGraphic { platformGraphic.beginFill(this.datas.lineColor, 1); platformGraphic.drawRect(0, 0, this.datas.width, this.datas.height); platformGraphic.endFill; - const rect = new Rectangle(0, 0, this.datas.width, this.datas.height); - platformGraphic.pivot = getRectangleCenter(rect); + 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 { hasdoor: boolean; + trainDirection: string; lineWidth: number; lineColor: string; lineColorDoor: string; @@ -89,12 +189,13 @@ export class PlatformTemplate extends JlGraphicTemplate { height: number; constructor() { super(Platform.Type); - this.lineWidth = 2; - this.lineColor = '#000000'; - this.lineColorDoor = '0x008000'; this.hasdoor = true; - this.width = 100; - this.height = 30; + 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(); diff --git a/src/graphics/platform/PlatformDrawAssistant.ts b/src/graphics/platform/PlatformDrawAssistant.ts index 634dead..f7987a9 100644 --- a/src/graphics/platform/PlatformDrawAssistant.ts +++ b/src/graphics/platform/PlatformDrawAssistant.ts @@ -7,14 +7,15 @@ import { } from 'pixi.js'; import { GraphicDrawAssistant, + GraphicInteractionPlugin, JlDrawApp, - KeyListener, + JlGraphic, getRectangleCenter, } from 'src/jlgraphic'; import { IPlatformData, Platform, PlatformTemplate } from './Platform'; -export interface ILinkDrawOptions { +export interface IPlatformDrawOptions { newData: () => IPlatformData; } @@ -26,15 +27,6 @@ export class PlatformDraw extends GraphicDrawAssistant< platformGraphic: Graphics = new Graphics(); doorGraphic: Graphics = new Graphics(); - // 快捷绘制 - keypListener: KeyListener = new KeyListener({ - value: 'KeyP', - global: true, - onPress: () => { - this.graphicTemplate.hasdoor = true; - }, - }); - constructor(app: JlDrawApp, createData: () => IPlatformData) { super( app, @@ -46,24 +38,20 @@ export class PlatformDraw extends GraphicDrawAssistant< this.container.addChild(this.platformGraphic); this.container.addChild(this.doorGraphic); this.graphicTemplate.hasdoor = true; + platformInteraction.init(app); } bind(): void { super.bind(); - this.app.addKeyboardListener(this.keypListener); } unbind(): void { super.unbind(); - this.app.removeKeyboardListener(this.keypListener); } 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); @@ -85,6 +73,7 @@ export class PlatformDraw extends GraphicDrawAssistant< 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); } //站台 @@ -102,6 +91,7 @@ export class PlatformDraw extends GraphicDrawAssistant< 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; @@ -111,3 +101,29 @@ export class PlatformDraw extends GraphicDrawAssistant< return true; } } + +export class platformInteraction extends GraphicInteractionPlugin { + 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; + } +} diff --git a/src/graphics/rect/Rect.ts b/src/graphics/rect/Rect.ts new file mode 100644 index 0000000..5cde77a --- /dev/null +++ b/src/graphics/rect/Rect.ts @@ -0,0 +1,86 @@ +import { Color, Graphics, IPointData, Point } from 'pixi.js'; +import { GraphicData, JlGraphic, JlGraphicTemplate } from 'src/jlgraphic'; + +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(); + } + 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 { + 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(); + } +} diff --git a/src/graphics/rect/RectDrawAssistant.ts b/src/graphics/rect/RectDrawAssistant.ts new file mode 100644 index 0000000..306b2f0 --- /dev/null +++ b/src/graphics/rect/RectDrawAssistant.ts @@ -0,0 +1,261 @@ +import { + FederatedPointerEvent, + Graphics, + Point, + IHitArea, + DisplayObject, + FederatedMouseEvent, +} from 'pixi.js'; +import { + DraggablePoint, + GraphicApp, + GraphicDrawAssistant, + GraphicInteractionPlugin, + GraphicTransformEvent, + JlDrawApp, + JlGraphic, + linePoint, +} from 'src/jlgraphic'; + +import AbsorbablePoint, { + AbsorbablePosition, +} from 'src/jlgraphic/graphic/AbsorbablePosition'; +import { + ILineGraphic, + PolylineEditPlugin, + addWaySegmentingConfig, + addPolygonSegmentingPoint, + clearWayPoint, + clearWaypointsConfig, + getWayLineIndex, +} from 'src/jlgraphic/plugins/GraphicEditPlugin'; +import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu'; + +import { IRectData, Rect, RectTemplate } from './Rect'; +import { Link } from '../link/Link'; + +export interface IRectDrawOptions { + newData: () => IRectData; +} + +export class RectDraw extends GraphicDrawAssistant { + 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(); + } + 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.Type); + const links = rect.queryStore.queryByType(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( + other.localToCanvasPoint(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 { + 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.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.Name + ); + if (lep) { + lep.hideAll(); + } + } +} diff --git a/src/graphics/signal/Lamp.ts b/src/graphics/signal/Lamp.ts new file mode 100644 index 0000000..1b0d564 --- /dev/null +++ b/src/graphics/signal/Lamp.ts @@ -0,0 +1,52 @@ +import { Container } from '@pixi/display'; +import { Graphics } from 'pixi.js'; + +export enum LampEnum { + lampPostColor = '0xc0c0c0', + lampDefaultColor = '0xff0000', + logicModeColor = '0x000000', +} + +const lampConsts = { + lampRadius: 10, + logicModeLineWidth: 2, + logicModeDistance: 7, +}; + +export class Lamp extends Container { + circleLamp: Graphics = new Graphics(); + logicMode: Graphics = new Graphics(); + + constructor() { + super(); + this.addChild(this.circleLamp); + this.addChild(this.logicMode); + } + paint(radiusX: number, radiusY: number) { + this.circleLamp.clear(); + this.circleLamp + .beginFill(LampEnum.lampDefaultColor, 1) + .drawCircle(radiusX, radiusY, lampConsts.lampRadius) + .endFill(); + + this.logicMode + .clear() + .lineStyle(lampConsts.logicModeLineWidth, LampEnum.logicModeColor) + .moveTo( + radiusX - lampConsts.logicModeDistance, + radiusY + lampConsts.logicModeDistance + ) + .lineTo( + radiusX + lampConsts.logicModeDistance, + radiusY - lampConsts.logicModeDistance + ) + .moveTo( + radiusX - lampConsts.logicModeDistance, + radiusY - lampConsts.logicModeDistance + ) + .lineTo( + radiusX + lampConsts.logicModeDistance, + radiusY + lampConsts.logicModeDistance + ); + } +} diff --git a/src/graphics/signal/LampMainBody.ts b/src/graphics/signal/LampMainBody.ts new file mode 100644 index 0000000..7436303 --- /dev/null +++ b/src/graphics/signal/LampMainBody.ts @@ -0,0 +1,51 @@ +import { Container } from '@pixi/display'; +import { Graphics } from 'pixi.js'; +import { Lamp } from './Lamp'; + +export enum LampEnum { + lampPostColor = '0xc0c0c0', +} + +const lampConsts = { + verticalLampPostLength: 20, + levelLampPostLength: 5, + postLineWidth: 3, + lampRadius: 10, +}; + +export class LampMainBody extends Container { + lampNum = 1; + lampPost: Graphics = new Graphics(); + lamps: Lamp[] = []; + + constructor() { + super(); + } + paint(lampNum: number) { + if (lampNum < 1) { + throw new Error('信号机灯数量异常'); + } + this.lampNum = lampNum; + this.removeChildren(0); + this.lampPost = new Graphics(); + this.lampPost + .lineStyle(lampConsts.postLineWidth, LampEnum.lampPostColor) + .moveTo(0, -lampConsts.verticalLampPostLength / 2) + .lineTo(0, lampConsts.verticalLampPostLength / 2) + .moveTo(0, 0) + .lineTo(lampConsts.levelLampPostLength, 0); + this.addChild(this.lampPost); + + this.lamps = []; + for (let i = 0; i < this.lampNum; i++) { + const lamp = new Lamp(); + const radiusX = + (1 + i * 2) * lampConsts.lampRadius + lampConsts.levelLampPostLength; + lamp.paint(radiusX, 0); + this.addChild(lamp); + this.lamps.push(lamp); + } + } + + // setState() {} +} diff --git a/src/graphics/signal/Signal.ts b/src/graphics/signal/Signal.ts index e69de29..27c385c 100644 --- a/src/graphics/signal/Signal.ts +++ b/src/graphics/signal/Signal.ts @@ -0,0 +1,102 @@ +import { Graphics } from 'pixi.js'; +import { + GraphicData, + JlGraphic, + JlGraphicTemplate, + VectorText, +} from 'src/jlgraphic'; +import { LampMainBody } from './LampMainBody'; +import { drawArrow } from '../CommonGraphics'; + +export interface ISignalData extends GraphicData { + get code(): string; // 编号 + set code(v: string); + clone(): ISignalData; + copyFrom(data: ISignalData): void; + eq(other: ISignalData): boolean; +} + +export enum SignalColorEnum { + humanControlColor = '0xffff00', + fleetModeColor = '0x00ff00', + codeColor = '0X000000', +} + +const signalConsts = { + lampNum: 1, + codeFontSize: 11, + nameOffsetY: 20, + fleetModeLength: 33, + fleetModeRadius: 10, + fleetModeLineWidth: 6, +}; +export class Signal extends JlGraphic { + static Type = 'signal'; + codeGraph: VectorText = new VectorText(''); + humanControl: Graphics = new Graphics(); + fleetMode: Graphics = new Graphics(); + lampMainBody: LampMainBody = new LampMainBody(); + + constructor() { + super(Signal.Type); + this.codeGraph.name = 'signalCode'; + this.addChild(this.codeGraph); + this.addChild(this.humanControl); + this.addChild(this.fleetMode); + this.addChild(this.lampMainBody); + } + + get datas(): ISignalData { + return this.getDatas(); + } + + paint(): void { + this.lampMainBody.paint(signalConsts.lampNum); + this.humanControl.beginFill(SignalColorEnum.humanControlColor, 1); + if (this.humanControl.drawRegularPolygon) { + this.humanControl.drawRegularPolygon(-10, 0, 10, 3, Math.PI / 2); + } + this.humanControl.endFill(); + this.fleetMode.beginFill(SignalColorEnum.fleetModeColor, 1); + drawArrow( + this.fleetMode, + this.lampMainBody.width + signalConsts.fleetModeLength, + 0, + signalConsts.fleetModeLength, + signalConsts.fleetModeRadius, + signalConsts.fleetModeLineWidth + ); + this.fleetMode.endFill(); + this.codeGraph.text = this.datas?.code || '信号机编号'; + this.codeGraph.style.fill = SignalColorEnum.codeColor; + this.codeGraph.setVectorFontSize(signalConsts.codeFontSize); + this.codeGraph.anchor.set(0.5); + this.codeGraph.style.fill = SignalColorEnum.codeColor; + const codeTransform = this.datas?.childTransforms?.find( + (item) => item.name === 'signalCode' + ); + if (codeTransform) { + const position = codeTransform?.transform.position; + const rotation = codeTransform?.transform?.rotation; + this.codeGraph.position.set(position?.x, position?.y); + this.codeGraph.rotation = rotation || 0; + } else { + this.codeGraph.position.set(0, signalConsts.nameOffsetY); + } + } + + doRepaint(): void { + this.paint(); + } +} + +export class SignalTemplate extends JlGraphicTemplate { + constructor(dataTemplate: ISignalData) { + super(Signal.Type, { + dataTemplate, + }); + } + new(): Signal { + return new Signal(); + } +} diff --git a/src/graphics/signal/SignalDrawAssistant.ts b/src/graphics/signal/SignalDrawAssistant.ts new file mode 100644 index 0000000..c119cdc --- /dev/null +++ b/src/graphics/signal/SignalDrawAssistant.ts @@ -0,0 +1,133 @@ +import { FederatedPointerEvent, Point } from 'pixi.js'; +import { + AbsorbableLine, + AbsorbablePosition, + GraphicApp, + GraphicDrawAssistant, + GraphicInteractionPlugin, + GraphicTransformEvent, + JlDrawApp, + JlGraphic, +} from 'src/jlgraphic'; +import { ISignalData, Signal, SignalTemplate } from './Signal'; +import { IscsFan } from '../iscs-fan/IscsFan'; + +export interface ISignalDrawOptions { + newData: () => ISignalData; +} + +export class SignalDraw extends GraphicDrawAssistant< + SignalTemplate, + ISignalData +> { + _signal: Signal | null = null; + + constructor(app: JlDrawApp, template: SignalTemplate) { + super( + app, + template, + 'svguse: ../../drawIcon.svg#icon-signal', + '信号机Signal' + ); + + signalInteraction.init(app); + } + + public get signal(): Signal { + if (!this._signal) { + this._signal = this.graphicTemplate.new(); + this.signal.loadData(this.graphicTemplate.datas); + this.container.addChild(this.signal); + } + return this._signal; + } + + clearCache(): void { + //this.codeGraph.clear(); + } + onRightClick(): void { + this.createAndStore(true); + } + onLeftUp(e: FederatedPointerEvent): void { + this.container.position.copyFrom(this.toCanvasCoordinates(e.global)); + this.createAndStore(true); + } + + redraw(p: Point): void { + this.signal.paint(); + this.container.position.set(p.x, p.y); + } + prepareData(data: ISignalData): boolean { + data.transform = this.container.saveTransform(); + return true; + } +} + +export class signalInteraction extends GraphicInteractionPlugin { + static Name = 'signal_transform'; + constructor(app: JlDrawApp) { + super(signalInteraction.Name, app); + } + static init(app: JlDrawApp) { + return new signalInteraction(app); + } + filter(...grahpics: JlGraphic[]): Signal[] | undefined { + return grahpics + .filter((g) => g.type === Signal.Type) + .map((g) => g as Signal); + } + bind(g: Signal): void { + g.eventMode = 'static'; + g.cursor = 'pointer'; + g.scalable = true; + g.rotatable = true; + g.codeGraph.draggable = true; + g.codeGraph.selectable = true; + g.codeGraph.rotatable = true; + g.codeGraph.scalable = true; + g.codeGraph.transformSave = true; + g.codeGraph.eventMode = 'static'; + g.codeGraph.on('transformstart', (e: GraphicTransformEvent) => { + if (e.isShift()) { + console.log( + '信号机画布坐标', + e.target.position, + e.target.getPositionOnCanvas() + ); + const app = g.getGraphicApp(); + app.setOptions({ + absorbablePositions: buildAbsorbablePositions(app), + }); + } + }); + } + // onScaleDragEnd() { + // console.log('-----------------'); + // } + unbind(g: Signal): void { + g.eventMode = 'none'; + g.scalable = false; + g.rotatable = false; + g.codeGraph.draggable = false; + g.codeGraph.selectable = false; + g.codeGraph.rotatable = false; + g.codeGraph.scalable = false; + g.codeGraph.transformSave = false; + g.codeGraph.eventMode = 'none'; + } +} +function buildAbsorbablePositions(app: GraphicApp): AbsorbablePosition[] { + const canvas = app.canvas; + const store = app.queryStore; + const aps: AbsorbablePosition[] = []; + store.queryByType(IscsFan.Type).forEach((fan) => { + const p = fan.position; + aps.push( + new AbsorbableLine(new Point(0, p.y), new Point(canvas.width, p.y)) + ); + aps.push( + new AbsorbableLine(new Point(p.x, 0), new Point(p.x, canvas.height)) + ); + }); + return aps; +} diff --git a/src/graphics/station/Station.ts b/src/graphics/station/Station.ts new file mode 100644 index 0000000..8e94ae7 --- /dev/null +++ b/src/graphics/station/Station.ts @@ -0,0 +1,101 @@ +import { Color, Graphics, IPointData, Point } from 'pixi.js'; +import { + GraphicData, + JlGraphic, + JlGraphicTemplate, + VectorText, +} from 'src/jlgraphic'; + +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(); + } + 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 { + 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(); + } +} diff --git a/src/graphics/station/StationDrawAssistant.ts b/src/graphics/station/StationDrawAssistant.ts new file mode 100644 index 0000000..a29669d --- /dev/null +++ b/src/graphics/station/StationDrawAssistant.ts @@ -0,0 +1,93 @@ +import { FederatedPointerEvent, Point } from 'pixi.js'; +import { + GraphicDrawAssistant, + GraphicInteractionPlugin, + JlDrawApp, + JlGraphic, + VectorText, +} from 'src/jlgraphic'; + +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(); + } + 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 { + 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; + } +} diff --git a/src/graphics/train/Train.ts b/src/graphics/train/Train.ts new file mode 100644 index 0000000..217ac3c --- /dev/null +++ b/src/graphics/train/Train.ts @@ -0,0 +1,185 @@ +import { Color, Graphics, IPointData } from 'pixi.js'; +import { + GraphicData, + JlGraphic, + JlGraphicTemplate, + VectorText, +} from 'src/jlgraphic'; + +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(); + } + 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 { + 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(); + } +} diff --git a/src/graphics/train/TrainDrawAssistant.ts b/src/graphics/train/TrainDrawAssistant.ts new file mode 100644 index 0000000..767a383 --- /dev/null +++ b/src/graphics/train/TrainDrawAssistant.ts @@ -0,0 +1,138 @@ +import { Color, FederatedPointerEvent, Graphics, Point } from 'pixi.js'; +import { + GraphicDrawAssistant, + GraphicInteractionPlugin, + JlDrawApp, + JlGraphic, + VectorText, +} from 'src/jlgraphic'; + +import { ITrainData, Train, TrainTemplate, trainConsts } from './Train'; + +export interface ITrainDrawOptions { + newData: () => ITrainData; +} + +export class TrainDraw extends GraphicDrawAssistant { + 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(); + } + 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 { + 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; + } +} diff --git a/src/jlgraphic/app/JlDrawApp.ts b/src/jlgraphic/app/JlDrawApp.ts index cf49b2e..79f1071 100644 --- a/src/jlgraphic/app/JlDrawApp.ts +++ b/src/jlgraphic/app/JlDrawApp.ts @@ -45,7 +45,6 @@ export abstract class GraphicDrawAssistant< icon: string; // 界面显示的图标 container: Container = new Container(); graphicTemplate: GT; - createGraphicData: () => GD; escListener: KeyListener = new KeyListener({ value: 'Escape', @@ -61,7 +60,6 @@ export abstract class GraphicDrawAssistant< constructor( graphicApp: JlDrawApp, graphicTemplate: GT, - createGraphicData: () => GD, icon: string, description: string ) { @@ -69,7 +67,6 @@ export abstract class GraphicDrawAssistant< this.app = graphicApp; this.type = graphicTemplate.type; this.graphicTemplate = graphicTemplate; - this.createGraphicData = createGraphicData; this.icon = icon; this.description = description; this.app.registerGraphicTemplates(this.graphicTemplate); @@ -102,7 +99,9 @@ export abstract class GraphicDrawAssistant< unbind(): void { this.clearCache(); const canvas = this.canvas; - canvas.removeChild(this.container); + if (this.container?.parent) { + canvas.removeChild(this.container); + } canvas.off('mousedown', this.onLeftDown, this); canvas.off('mousemove', this.onMouseMove, this); canvas.off('mouseup', this.onLeftUp, this); @@ -161,7 +160,7 @@ export abstract class GraphicDrawAssistant< * 创建并添加到图形App */ createAndStore(finish: boolean): JlGraphic | null { - const data = this.createGraphicData(); + const data = this.graphicTemplate.datas as GD; data.id = this.nextId(); data.graphicType = this.graphicTemplate.type; if (!this.prepareData(data)) { @@ -457,13 +456,13 @@ export class JlDrawApp extends GraphicApp { * 删除选中图形对象 */ deleteSelectedGraphics() { - const deletes = this.selectedGraphics.slice( - 0, - this.selectedGraphics.length - ); - this.deleteGraphics(...this.selectedGraphics); - // 删除图形对象操作记录 - this.opRecord.record(new GraphicDeleteOperation(this, deletes)); + const deletes = this.deleteGraphics(...this.selectedGraphics); + if (deletes.length > 0) { + // 删除图形对象操作记录 + this.opRecord.record(new GraphicDeleteOperation(this, deletes)); + } else { + console.debug('没有删除元素,不记录'); + } } updateCanvasAndRecord(data: ICanvasProperties) { diff --git a/src/jlgraphic/app/JlGraphicApp.ts b/src/jlgraphic/app/JlGraphicApp.ts index 529f67d..5cf08f3 100644 --- a/src/jlgraphic/app/JlGraphicApp.ts +++ b/src/jlgraphic/app/JlGraphicApp.ts @@ -288,6 +288,7 @@ export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents { drag_op_end: [event: AppDragEvent]; 'pre-menu-handle': [menu: MenuItemOptions]; 'post-menu-handle': [menu: MenuItemOptions]; + 'websocket-state-change': [app: GraphicApp, connected: boolean]; destroy: [app: GraphicApp]; } @@ -319,6 +320,11 @@ export interface IGraphicAppConfig { * 超出屏幕显示范围是否剪裁,默认true */ cullable?: boolean; + + /** + * 是否支持删除操作 + */ + isSupportDeletion?: (g: JlGraphic) => boolean; } /** @@ -527,7 +533,10 @@ export class GraphicApp extends EventEmitter { */ handleGraphicStates(graphicStates: GraphicState[]) { graphicStates.forEach((state) => { - const list = this.queryStore.queryByIdOrCode(state.code); + const list = this.queryStore.queryByIdOrCodeAndType( + state.code, + state.graphicType + ); if (list.length == 0) { const template = this.getGraphicTemplatesByType(state.graphicType); const g = template.new(); @@ -630,7 +639,7 @@ export class GraphicApp extends EventEmitter { this.addGraphics(g); }); // 加载数据关系 - this.graphicStore.getAllGraphics().forEach((g) => g.loadRealtions()); + this.graphicStore.getAllGraphics().forEach((g) => g.loadRelations()); // 更新id生成器 const max = this.graphicStore @@ -725,8 +734,19 @@ export class GraphicApp extends EventEmitter { * 删除图形 * @param graphics 图形对象 */ - deleteGraphics(...graphics: JlGraphic[]) { - graphics.forEach((g) => this.doDeleteGraphics(g)); + deleteGraphics(...graphics: JlGraphic[]): JlGraphic[] { + const dels = graphics.filter((g) => { + if ( + this._options?.isSupportDeletion == undefined || + this._options.isSupportDeletion(g) + ) { + this.doDeleteGraphics(g); + return true; + } + console.debug(`type=${g.type},id=${g.id}的图形不支持删除`); + return false; + }); + return dels; } /** @@ -888,10 +908,10 @@ export class GraphicApp extends EventEmitter { this.emit('destroy', this); if (this.wsMsgBroker) { this.wsMsgBroker.close(); - if (!StompCli.hasAppMsgBroker()) { - // 如果没有其他消息代理,关闭websocket Stomp客户端 - StompCli.close(); - } + // if (!StompCli.hasAppMsgBroker()) { + // // 如果没有其他消息代理,关闭websocket Stomp客户端 + // StompCli.close(); + // } } this.interactionPluginMap.forEach((plugin) => { plugin.pause(); diff --git a/src/jlgraphic/core/GraphicStore.ts b/src/jlgraphic/core/GraphicStore.ts index 979bd20..23e8889 100644 --- a/src/jlgraphic/core/GraphicStore.ts +++ b/src/jlgraphic/core/GraphicStore.ts @@ -37,6 +37,12 @@ export interface GraphicQueryStore { * @param v */ queryByIdOrCode(v: string): JlGraphic[]; + /** + * 根据id或code及类型查询图形 + * @param v + * @param type + */ + queryByIdOrCodeAndType(v: string, type: string): JlGraphic[]; /** * 根据code和类型获取图形 * @param code @@ -69,6 +75,7 @@ export class GraphicStore implements GraphicQueryStore { this.store = new Map(); this.relationManage = new RelationManage(app); } + /** * 获取所有图形对象 */ @@ -125,6 +132,15 @@ export class GraphicStore implements GraphicQueryStore { }); return list; } + queryByIdOrCodeAndType(s: string, type: string): JlGraphic[] { + const list: JlGraphic[] = []; + this.store.forEach((g) => { + if (g.isIdOrCode(s) && g.type === type) { + list.push(g); + } + }); + return list; + } queryByCodeAndType( code: string, type: string diff --git a/src/jlgraphic/core/JlGraphic.ts b/src/jlgraphic/core/JlGraphic.ts index 640b6fd..7ed967a 100644 --- a/src/jlgraphic/core/JlGraphic.ts +++ b/src/jlgraphic/core/JlGraphic.ts @@ -148,6 +148,23 @@ DisplayObject.prototype.getAllParentScaled = }); return scaled; }; +DisplayObject.prototype.getPositionOnCanvas = + function getPositionOnCanvas(): Point { + if (this.parent.isCanvas()) { + return this.position; + } else { + return this.parent.localToCanvasPoint(this.position); + } + }; +DisplayObject.prototype.updatePositionByCanvasPosition = + function updatePositionByCanvasPosition(p: IPointData): void { + if (this.parent.isCanvas()) { + this.position.copyFrom(p); + } else { + const localPosition = this.parent.canvasToLocalPoint(p); + this.position.copyFrom(localPosition); + } + }; DisplayObject.prototype.saveTransform = function saveTransform() { return GraphicTransform.fromObject(this); }; @@ -755,7 +772,7 @@ export abstract class JlGraphic extends Container { /** * 从数据加载恢复图形关系 */ - loadRealtions() {} + loadRelations() {} /** * 获取当前图形的所有图形关系 @@ -923,15 +940,47 @@ export abstract class JlGraphic extends Container { } } +export type CreateData = () => GraphicData; +export type CreateState = () => GraphicState; + +export interface IGraphicTemplateOptions { + dataTemplate?: GraphicData; + stateTemplate?: GraphicState; +} + /** * 图形对象模板 */ export abstract class JlGraphicTemplate { readonly type: string; + options: IGraphicTemplateOptions; - constructor(type: string) { + constructor(type: string, options: IGraphicTemplateOptions) { this.type = type; + this.options = options; } + + get datas(): GraphicData { + if (this.options.dataTemplate) { + return this.options.dataTemplate.clone(); + } + throw new Error(`type=${this.type}的图形模板没有数据模板`); + } + + get states(): GraphicState { + if (this.options.stateTemplate) { + return this.options.stateTemplate.clone(); + } + throw new Error(`type=${this.type}的图形模板没有状态模板`); + } + + // getDataCreator(): T { + // return this.options.dataCreator as T; + // } + // getStateCreator(): T { + // return this.options.stateCreator as T; + // } + /** * 初始化一个新的图形对象 */ diff --git a/src/jlgraphic/global.d.ts b/src/jlgraphic/global.d.ts index df5fa49..a70a5f9 100644 --- a/src/jlgraphic/global.d.ts +++ b/src/jlgraphic/global.d.ts @@ -60,9 +60,28 @@ declare namespace GlobalMixins { _rotatable: boolean; // 是否可旋转 rotatable: boolean; worldAngle: number; // 世界角度,(-180, 180] + /** + * 获取所有父级元素叠加缩放 + */ getAllParentScaled(): PointType; - saveTransform(): GraphicTransform; // 保存变换 - loadTransform(transform: GraphicTransform): void; // 加载变换 + /** + * 获取位置在画布的坐标 + */ + getPositionOnCanvas(): PointType; + /** + * 通过画布坐标更新位置 + * @param p 画布坐标 + */ + updatePositionByCanvasPosition(p: IPointData): void; + /** + * 保存变换数据 + */ + saveTransform(): GraphicTransform; + /** + * 加载变换 + * @param transform 变换数据 + */ + loadTransform(transform: GraphicTransform): void; isChild(obj: DisplayObject): boolean; // 是否子元素 isParent(obj: DisplayObject): boolean; // 是否父元素 isAssistantAppend(): boolean; // 是否辅助附加图形 @@ -83,14 +102,45 @@ declare namespace GlobalMixins { isCanvas(): boolean; // 是否画布对象 getViewport(): Viewport; // 获取视口 getGraphicApp(): GraphicApp; // 获取图形app - localToCanvasPoint(p: IPointData): PointType; // 图形本地坐标转为画布坐标 - localToCanvasPoints(...points: IPointData[]): PointType[]; // 批量转换 - canvasToLocalPoint(p: IPointData): PointType; // 画布坐标转为图形本地坐标 - canvasToLocalPoints(...points: IPointData[]): PointType[]; // 批量转换 - - localToScreenPoint(p: IPointData): PointType; // 本地坐标转为屏幕坐标 - localToScreenPoints(...points: IPointData[]): PointType[]; // 批量 - screenToLocalPoint(p: IPointData): PointType; // 屏幕坐标转为本地坐标 + /** + * 图形本地坐标转为画布坐标 + * @param p 图形本地坐标 + */ + localToCanvasPoint(p: IPointData): PointType; + /** + * 批量转换图形本地坐标为画布坐标 + * @param points 图形本地坐标 + */ + localToCanvasPoints(...points: IPointData[]): PointType[]; + /** + * 画布坐标转为图形本地坐标 + * @param p 画布坐标 + */ + canvasToLocalPoint(p: IPointData): PointType; + /** + * 批量转换画布坐标为图形本地坐标 + * @param points 画布坐标 + */ + canvasToLocalPoints(...points: IPointData[]): PointType[]; + /** + * 本地坐标转为屏幕坐标 + * @param p 本地坐标 + */ + localToScreenPoint(p: IPointData): PointType; + /** + * 批量转换本地坐标为屏幕坐标 + * @param points 本地坐标 + */ + localToScreenPoints(...points: IPointData[]): PointType[]; + /** + * 屏幕坐标转为本地坐标 + * @param p 屏幕坐标 + */ + screenToLocalPoint(p: IPointData): PointType; + /** + * 批量转换屏幕坐标为本地坐标 + * @param points 屏幕坐标 + */ screenToLocalPoints(...points: IPointData[]): PointType[]; // 批量 localBoundsToCanvasPoints(): PointType[]; // 本地包围框转为多边形点坐标 diff --git a/src/jlgraphic/graphic/AbsorbablePosition.ts b/src/jlgraphic/graphic/AbsorbablePosition.ts index ec379a1..b3a7acf 100644 --- a/src/jlgraphic/graphic/AbsorbablePosition.ts +++ b/src/jlgraphic/graphic/AbsorbablePosition.ts @@ -11,6 +11,8 @@ import { calculateFootPointFromPointToLine, calculateIntersectionPointOfCircleAndPoint, distance, + distance2, + isLineContainOther, linePoint, } from '../utils'; import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic'; @@ -19,12 +21,25 @@ import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic'; * 抽象可吸附位置 */ export interface AbsorbablePosition extends Container { + /** + * 是否与另一个可吸附位置重叠(相似,但可能范围不同) + * @param other + */ + isOverlapping(other: AbsorbablePosition): boolean; + + /** + * 与另一个相似的吸附位置比较范围大小 + * @param other + * @returns >0此吸附范围大,<0另一个吸附范围大,=0两个吸附范围一样大 + */ + compareTo(other: AbsorbablePosition): number; + /** * 尝试吸附图形对象 * @param objs 图形对象列表 * @returns 如果吸附成功,返回true,否则false */ - tryAbsorb(...objs: DisplayObject[]): boolean; + tryAbsorb(...objs: DisplayObject[]): void; } /** @@ -57,6 +72,11 @@ export default class AbsorbablePoint absorbRange: number; scaledListenerOn = false; + /** + * + * @param point 画布坐标 + * @param absorbRange + */ constructor(point: IPointData, absorbRange = 10) { super(AbsorbablePointGraphic.geometry); this._point = new Point(point.x, point.y); @@ -65,19 +85,37 @@ export default class AbsorbablePoint this.interactive; VectorGraphicUtil.handle(this); } - tryAbsorb(...objs: DisplayObject[]): boolean { - for (let i = 0; i < objs.length; i++) { - const obj = objs[i]; - if ( - distance(this._point.x, this._point.y, obj.position.x, obj.position.y) < - this.absorbRange - ) { - obj.position.copyFrom(this._point); - return true; - } + compareTo(other: AbsorbablePosition): number { + if (other instanceof AbsorbablePoint) { + return this.absorbRange - other.absorbRange; + } + throw new Error('非可吸附点'); + } + isOverlapping(other: AbsorbablePosition): boolean { + if (other instanceof AbsorbablePoint) { + return ( + this._point.equals(other._point) && + this.absorbRange === other.absorbRange + ); } return false; } + tryAbsorb(...objs: DisplayObject[]): void { + for (let i = 0; i < objs.length; i++) { + const obj = objs[i]; + const canvasPosition = obj.getPositionOnCanvas(); + if ( + distance( + this._point.x, + this._point.y, + canvasPosition.x, + canvasPosition.y + ) < this.absorbRange + ) { + obj.updatePositionByCanvasPosition(this._point); + } + } + } updateOnScaled() { const scaled = this.getAllParentScaled(); const scale = Math.max(scaled.x, scaled.y); @@ -94,6 +132,12 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition { absorbRange: number; _color = '#E77E0E'; + /** + * + * @param p1 画布坐标 + * @param p2 画布坐标 + * @param absorbRange + */ constructor(p1: IPointData, p2: IPointData, absorbRange = 20) { super(); this.p1 = new Point(p1.x, p1.y); @@ -101,6 +145,22 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition { this.absorbRange = absorbRange; this.redraw(); } + isOverlapping(other: AbsorbablePosition): boolean { + if (other instanceof AbsorbableLine) { + const contain = isLineContainOther( + { p1: this.p1, p2: this.p2 }, + { p1: other.p1, p2: other.p2 } + ); + return contain; + } + return false; + } + compareTo(other: AbsorbablePosition): number { + if (other instanceof AbsorbableLine) { + return distance2(this.p1, this.p2) - distance2(other.p1, other.p2); + } + throw new Error('非可吸附线'); + } redraw() { this.clear(); this.lineStyle(1, new Color(this._color)); @@ -108,17 +168,19 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition { this.lineTo(this.p2.x, this.p2.y); } - tryAbsorb(...objs: DisplayObject[]): boolean { + tryAbsorb(...objs: DisplayObject[]): void { for (let i = 0; i < objs.length; i++) { const obj = objs[i]; - const p = obj.position.clone(); - if (linePoint(this.p1, this.p2, p, this.absorbRange, true)) { - const fp = calculateFootPointFromPointToLine(this.p1, this.p2, p); - obj.position.copyFrom(fp); - return true; + const canvasPosition = obj.getPositionOnCanvas(); + if (linePoint(this.p1, this.p2, canvasPosition, this.absorbRange, true)) { + const fp = calculateFootPointFromPointToLine( + this.p1, + this.p2, + canvasPosition + ); + obj.updatePositionByCanvasPosition(fp); } } - return false; } } @@ -131,6 +193,12 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition { radius: number; _color = '#E77E0E'; + /** + * + * @param p 画布坐标 + * @param radius + * @param absorbRange + */ constructor(p: IPointData, radius: number, absorbRange = 10) { super(); this.p0 = new Point(p.x, p.y); @@ -138,6 +206,18 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition { this.absorbRange = absorbRange; this.redraw(); } + isOverlapping(other: AbsorbablePosition): boolean { + if (other instanceof AbsorbableCircle) { + return this.p0.equals(other.p0) && this.radius === other.radius; + } + return false; + } + compareTo(other: AbsorbablePosition): number { + if (other instanceof AbsorbableCircle) { + return this.absorbRange - other.absorbRange; + } + throw new Error('非可吸附圆'); + } redraw() { this.clear(); @@ -145,14 +225,15 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition { this.drawCircle(this.p0.x, this.p0.y, this.radius); } - tryAbsorb(...objs: DisplayObject[]): boolean { + tryAbsorb(...objs: DisplayObject[]): void { for (let i = 0; i < objs.length; i++) { const obj = objs[i]; + const canvasPosition = obj.getPositionOnCanvas(); const len = distance( this.p0.x, this.p0.y, - obj.position.x, - obj.position.y + canvasPosition.x, + canvasPosition.y ); if ( len > this.radius - this.absorbRange && @@ -162,12 +243,10 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition { const p = calculateIntersectionPointOfCircleAndPoint( this.p0, this.radius, - obj.position + canvasPosition ); - obj.position.copyFrom(p); - return true; + obj.updatePositionByCanvasPosition(p); } } - return false; } } diff --git a/src/jlgraphic/message/WsMsgBroker.ts b/src/jlgraphic/message/WsMsgBroker.ts index dc172f8..52d561a 100644 --- a/src/jlgraphic/message/WsMsgBroker.ts +++ b/src/jlgraphic/message/WsMsgBroker.ts @@ -9,9 +9,20 @@ import type { GraphicApp } from '../app/JlGraphicApp'; import { GraphicState } from '../core/JlGraphic'; export interface StompCliOption { - wsUrl: string; // websocket url - token: string; // 认证token - reconnectDelay?: number; // 重连延时,默认3秒 + /** + * websocket url地址 + */ + wsUrl: string; + /** + * 认证token + */ + token?: string; + /** + * 认证失败处理 + * @returns + */ + onAuthenticationFailed?: () => void; + reconnectDelay?: number; // 重连延时,默认3秒,设置为0不重连. heartbeatIncoming?: number; // 服务端过来的心跳间隔,默认30秒 heartbeatOutgoing?: number; // 到服务端的心跳间隔,默认30秒 } @@ -25,28 +36,28 @@ const DefaultStompOption: StompCliOption = { }; export class StompCli { - private static enabled = false; - private static options: StompCliOption; private static client: StompClient; + private static options: StompCliOption; private static appMsgBroker: AppWsMsgBroker[] = []; + /** + * key-订阅路径 + */ + subscriptions: Map = new Map< + string, + AppStateSubscription + >(); private static connected = false; static new(options: StompCliOption) { - if (StompCli.enabled) { - // 以及启用 + if (StompCli.client) { + // 已经创建 return; - // throw new Error('websocket 已连接,若确实需要重新连接,请先断开StompCli.close再重新StompCli.new') } - StompCli.enabled = true; StompCli.options = Object.assign({}, DefaultStompOption, options); StompCli.client = new StompClient({ brokerURL: StompCli.options.wsUrl, connectHeaders: { - Authorization: StompCli.options.token, - // Authorization: '' + Authorization: StompCli.options.token ? StompCli.options.token : '', }, - // debug: (str) => { - // console.log(str) - // } reconnectDelay: StompCli.options.reconnectDelay, heartbeatIncoming: StompCli.options.heartbeatIncoming, heartbeatOutgoing: StompCli.options.heartbeatOutgoing, @@ -55,43 +66,47 @@ export class StompCli { StompCli.client.onConnect = () => { // console.log('websocket连接(重连),重新订阅', StompCli.appMsgBroker.length) StompCli.connected = true; + StompCli.emitConnectStateChangeEvent(); StompCli.appMsgBroker.forEach((broker) => { broker.resubscribe(); }); }; StompCli.client.onStompError = (frame: Frame) => { - console.error( - 'Stomp收到error消息,可能是认证失败(暂时没有判断具体错误类型,后需添加判断),关闭Stomp客户端', - frame - ); - StompCli.close(); + const errMsg = frame.headers['message']; + if (errMsg === '401') { + console.warn('认证失败,断开WebSocket连接'); + StompCli.close(); + if (StompCli.options.onAuthenticationFailed) { + StompCli.options.onAuthenticationFailed(); + } + } else { + console.error('收到Stomp错误消息', frame); + } }; StompCli.client.onDisconnect = (frame: Frame) => { console.log('Stomp 断开连接', frame); StompCli.connected = false; - // StompCli.appMsgBroker.forEach(broker => { - // broker.close(); - // }); + StompCli.emitConnectStateChangeEvent(); }; StompCli.client.onWebSocketClose = (evt: CloseEvent) => { console.log('websocket 关闭', evt); StompCli.connected = false; + StompCli.emitConnectStateChangeEvent(); }; // websocket错误处理 StompCli.client.onWebSocketError = (err: Event) => { console.log('websocket错误', err); - // StompCli.appMsgBroker.forEach(broker => { - // broker.unsbuscribeAll(); - // }); }; StompCli.client.activate(); } - static isEnabled(): boolean { - return StompCli.enabled; + static emitConnectStateChangeEvent() { + StompCli.appMsgBroker.forEach((broker) => { + broker.app.emit('websocket-state-change', broker.app, StompCli.connected); + }); } static isConnected(): boolean { @@ -129,7 +144,6 @@ export class StompCli { * 关闭websocket连接 */ static close() { - StompCli.enabled = false; StompCli.connected = false; if (StompCli.client) { StompCli.client.deactivate(); @@ -138,21 +152,32 @@ export class StompCli { } // 状态订阅消息转换器 -export type MessageConverter = (message: Uint8Array) => GraphicState[]; +export type GraphicStateMessageConvert = ( + message: Uint8Array +) => GraphicState[]; + +// 订阅消息处理器 +export type SubscriptionMessageHandle = (message: Uint8Array) => void; + // 图形app状态订阅 -export class AppStateSubscription { +export interface AppStateSubscription { + /** + * 订阅路径 + */ destination: string; - messageConverter: MessageConverter; - subscription?: StompSubscription; // 订阅成功对象,用于取消订阅 - constructor( - destination: string, - messageConverter: MessageConverter, - subscription?: StompSubscription - ) { - this.destination = destination; - this.messageConverter = messageConverter; - this.subscription = subscription; - } + /** + * 图形状态消息转换 + */ + messageConverter?: GraphicStateMessageConvert; + /** + * 订阅消息处理 + */ + messageHandle?: SubscriptionMessageHandle; + /** + * 订阅成功对象,用于取消订阅 + * 非客户端使用 + */ + subscription?: StompSubscription; } /** @@ -175,8 +200,16 @@ export class AppWsMsgBroker { sub.subscription = StompCli.trySubscribe( sub.destination, (message: Message) => { - const graphicStates = sub.messageConverter(message.binaryBody); - this.app.handleGraphicStates(graphicStates); + if (sub.messageConverter) { + const graphicStates = sub.messageConverter(message.binaryBody); + this.app.handleGraphicStates(graphicStates); + } else if (sub.messageHandle) { + sub.messageHandle(message.binaryBody); + } else { + console.error( + `订阅destination:${sub.destination}没有消息处理器或图形状态消息转换器` + ); + } } ); // console.log('代理订阅结果', sub.subscription) diff --git a/src/jlgraphic/plugins/CommonMousePlugin.ts b/src/jlgraphic/plugins/CommonMousePlugin.ts index 5160f41..b7a33f1 100644 --- a/src/jlgraphic/plugins/CommonMousePlugin.ts +++ b/src/jlgraphic/plugins/CommonMousePlugin.ts @@ -194,18 +194,17 @@ export class CommonMouseTool extends AppInteractionPlugin { const graphic = this.leftDownTarget.getGraphic(); if (graphic) { const app = this.app; + // console.log(this.leftDownTarget.isGraphic()); // 图形选中 if (!e.ctrlKey && !graphic.selected && graphic.selectable) { app.updateSelected(graphic); graphic.childEdit = false; this.graphicSelect = true; - } else if ( - !e.ctrlKey && - graphic.selected && - graphic.childEdit && - this.leftDownTarget.isGraphicChild() - ) { - if (this.leftDownTarget.selectable) { + } else if (!e.ctrlKey && graphic.selected && graphic.childEdit) { + if ( + this.leftDownTarget.isGraphicChild() && + this.leftDownTarget.selectable + ) { graphic.setChildSelected(this.leftDownTarget); } else { graphic.exitChildEdit(); diff --git a/src/jlgraphic/plugins/GraphicEditPlugin.ts b/src/jlgraphic/plugins/GraphicEditPlugin.ts index 276b519..c93631a 100644 --- a/src/jlgraphic/plugins/GraphicEditPlugin.ts +++ b/src/jlgraphic/plugins/GraphicEditPlugin.ts @@ -2,7 +2,6 @@ import { Color, Container, DisplayObject, - FederatedMouseEvent, Graphics, IDestroyOptions, IPointData, @@ -10,12 +9,12 @@ import { } from 'pixi.js'; import { JlGraphic } from '../core'; import { DraggablePoint } from '../graphic'; -import { ContextMenu } from '../ui/ContextMenu'; -import { MenuItemOptions, MenuOptions } from '../ui/Menu'; import { + calculateDistanceFromPointToLine, calculateFootPointFromPointToLine, + calculateLineSegmentingPoint, calculateMirrorPoint, - assertBezierPoints, + convertToBezierParams, distance2, linePoint, pointPolygon, @@ -33,13 +32,13 @@ export abstract class GraphicEditPlugin< this.sortableChildren = true; this.graphic.on('transformstart', this.hideAll, this); this.graphic.on('transformend', this.showAll, this); - this.graphic.on('repaint', this.showAll, this); + this.graphic.on('repaint', this.updateEditedPointsPosition, this); } destroy(options?: boolean | IDestroyOptions | undefined): void { this.graphic.off('transformstart', this.hideAll, this); this.graphic.off('transformend', this.showAll, this); - this.graphic.off('repaint', this.showAll, this); + this.graphic.off('repaint', this.updateEditedPointsPosition, this); super.destroy(options); } @@ -54,26 +53,6 @@ export abstract class GraphicEditPlugin< } } -export const addWaypointConfig: MenuItemOptions = { - name: '添加路径点', -}; -export const removeWaypointConfig: MenuItemOptions = { - name: '移除路径点', -}; -export const clearWaypointsConfig: MenuItemOptions = { - name: '清除所有路径点', -}; -const menuOptions: MenuOptions = { - name: '图形编辑点菜单', - groups: [ - { - items: [removeWaypointConfig, clearWaypointsConfig], - }, - ], -}; - -const EditPointContextMenu = ContextMenu.init(menuOptions); - export interface ILineGraphic extends JlGraphic { get linePoints(): IPointData[]; set linePoints(points: IPointData[]); @@ -103,10 +82,50 @@ export abstract class LineEditPlugin extends GraphicEditPlugin { 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( points: IPointData[], curve: boolean, - p: IPointData + p: IPointData, + lineWidth: number ): { start: number; end: number } { let start = 0; let end = 0; @@ -124,17 +143,25 @@ export function getWaypointRangeIndex( } } else { // 贝塞尔曲线 - assertBezierPoints(points); - for (let i = 0; i < points.length - 3; i += 3) { - const p1 = points[i]; - const cp1 = points[i + 1]; - const cp2 = points[i + 2]; - const p2 = points[i + 3]; - if (pointPolygon(p, [p1, cp1, cp2, p2], 1)) { - start = i; - end = i + 3; + const bps = convertToBezierParams(points); + for (let i = 0; i < bps.length; i++) { + const bp = bps[i]; + if (pointPolygon(p, [bp.p1, bp.cp1, bp.cp2, bp.p2], lineWidth)) { + start = i * 3; + end = start + 3; } } + // assertBezierPoints(points); + // for (let i = 0; i < points.length - 3; i += 3) { + // const p1 = points[i]; + // const cp1 = points[i + 1]; + // const cp2 = points[i + 2]; + // const p2 = points[i + 3]; + // if (pointPolygon(p, [p1, cp1, cp2, p2], lineWidth)) { + // start = i; + // end = i + 3; + // } + // } } return { start, end }; } @@ -170,19 +197,7 @@ export class PolylineEditPlugin extends LineEditPlugin { for (let i = 0; i < cps.length; i++) { const p = cps[i]; const dp = new DraggablePoint(p); - dp.on('rightclick', (e: FederatedMouseEvent) => { - // dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu); - // 路径中的点 - const app = dp.getGraphicApp(); - app.registerMenu(EditPointContextMenu); - removeWaypointConfig.handler = () => { - removeLineWayPoint(this.graphic, i); - }; - clearWaypointsConfig.handler = () => { - clearWayPoint(this.graphic, false); - }; - EditPointContextMenu.open(e.global); - }); + dp.on('transforming', () => { const tlp = this.graphic.canvasToLocalPoint(dp.position); const cp = this.linePoints[i]; @@ -251,6 +266,21 @@ export function addLineWayPoint( 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) { const c = i % 3; if (c !== 0) { @@ -265,6 +295,7 @@ export function addBezierWayPoint( p: IPointData ) { if (start === end) { + console.error('添加贝塞尔曲线路径点开始结束点相等', start); throw new Error('开始结束点不能一致'); } assertBezierWayPoint(start); @@ -394,21 +425,6 @@ export class BezierCurveEditPlugin extends LineEditPlugin { this.drawAuxiliaryLine(line, p, np); this.auxiliaryLines.push(line); } - dp.on('rightclick', (e: FederatedMouseEvent) => { - // dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu); - if (c === 0) { - // 路径中的点 - const app = dp.getGraphicApp(); - app.registerMenu(EditPointContextMenu); - removeWaypointConfig.handler = () => { - removeBezierWayPoint(this.graphic, i); - }; - clearWaypointsConfig.handler = () => { - clearWayPoint(this.graphic, true); - }; - EditPointContextMenu.open(e.global); - } - }); dp.on('transforming', (e: GraphicTransformEvent) => { const tlp = this.graphic.canvasToLocalPoint(dp.position); const cp = this.linePoints[i]; diff --git a/src/jlgraphic/plugins/GraphicTransformPlugin.ts b/src/jlgraphic/plugins/GraphicTransformPlugin.ts index d277651..e80025d 100644 --- a/src/jlgraphic/plugins/GraphicTransformPlugin.ts +++ b/src/jlgraphic/plugins/GraphicTransformPlugin.ts @@ -14,7 +14,7 @@ import { } from '.'; import { GraphicApp } from '../app'; import { JlGraphic } from '../core'; -import { AbsorbablePosition } from '../graphic'; +import { AbsorbablePosition, VectorText } from '../graphic'; import { DraggablePoint } from '../graphic/DraggablePoint'; import { angleToAxisx, @@ -171,11 +171,40 @@ export class GraphicTransformPlugin extends InteractionPluginBase { this.app.canvas.addAssistantAppend(this.apContainer); app.on('options-update', (options) => { if (options.absorbablePositions) { - this.absorbablePositions = options.absorbablePositions; + this.absorbablePositions = this.filterAbsorbablePositions( + options.absorbablePositions + ); } }); } + /** + * 过滤重复的吸附位置 + * @param positions + * @returns + */ + filterAbsorbablePositions( + positions: AbsorbablePosition[] + ): AbsorbablePosition[] { + const aps: AbsorbablePosition[] = []; + for (let i = 0; i < positions.length; i++) { + const ap1 = positions[i]; + let ap: AbsorbablePosition | null = ap1; + for (let j = positions.length - 1; j > i; j--) { + const ap2 = positions[j]; + if (ap.isOverlapping(ap2) && ap.compareTo(ap2) <= 0) { + ap = null; + break; + } + } + if (ap != null) { + aps.push(ap); + } + } + // console.log(positions, aps); + return aps; + } + static new(app: GraphicApp) { return new GraphicTransformPlugin(app); } @@ -187,7 +216,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase { this.app.on('graphicselectedchange', this.onGraphicSelectedChange, this); this.app.on( 'graphicchildselectedchange', - this.onGraphicChildSelectedChange, + this.onGraphicSelectedChange, this ); } @@ -198,7 +227,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase { this.app.off('graphicselectedchange', this.onGraphicSelectedChange, this); this.app.off( 'graphicchildselectedchange', - this.onGraphicChildSelectedChange, + this.onGraphicSelectedChange, this ); } @@ -255,9 +284,10 @@ export class GraphicTransformPlugin extends InteractionPluginBase { targets.forEach((target) => { if (target.shiftStartPoint) { target.shiftLastPoint = target.position.clone(); + const { dx, dy } = e.toTargetShiftLen(target.parent); target.position.set( - target.shiftStartPoint.x + e.dsx, - target.shiftStartPoint.y + e.dsy + target.shiftStartPoint.x + dx, + target.shiftStartPoint.y + dy ); } }); @@ -265,9 +295,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase { if (this.absorbablePositions) { for (let i = 0; i < this.absorbablePositions.length; i++) { const ap = this.absorbablePositions[i]; - if (ap.tryAbsorb(...targets)) { - break; - } + ap.tryAbsorb(...targets); } } // 事件发布 @@ -335,7 +363,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase { br.visible = false; } } - if (g.scalable) { + if (g.scalable || g.rotatable) { // 缩放点 let sp = g.getAssistantAppend(TransformPoints.Name); if (!sp) { @@ -350,19 +378,19 @@ export class GraphicTransformPlugin extends InteractionPluginBase { } } - onGraphicChildSelectedChange(child: DisplayObject, selected: boolean) { - let br = child.getAssistantAppend(BoundsGraphic.Name); - if (!br) { - // 绘制辅助包围框 - br = new BoundsGraphic(child); - } - if (selected) { - br.redraw(); - br.visible = true; - } else { - br.visible = false; - } - } + // onGraphicChildSelectedChange(child: DisplayObject, selected: boolean) { + // let br = child.getAssistantAppend(BoundsGraphic.Name); + // if (!br) { + // // 绘制辅助包围框 + // br = new BoundsGraphic(child); + // } + // if (selected) { + // br.redraw(); + // br.visible = true; + // } else { + // br.visible = false; + // } + // } } /** @@ -419,6 +447,10 @@ export class TransformPoints extends Container { * 起始图形角度 */ startAngle = 0; + /** + * 当前角度信息文本辅助 + */ + angleAssistantText: VectorText; /** * 旋转角度步长 */ @@ -433,6 +465,10 @@ export class TransformPoints extends Container { this.obj = obj; this.name = TransformPoints.Name; + this.angleAssistantText = new VectorText(''); + this.angleAssistantText.setVectorFontSize(16); + this.angleAssistantText.anchor.set(0.5); + // 创建缩放拖拽点 this.ltScalePoint = new DraggablePoint(new Point()); this.ltScalePoint.name = TransformPoints.LeftTopName; @@ -460,6 +496,19 @@ export class TransformPoints extends Container { this.addChild(this.lScalePoint); this.obj.on('transformstart', this.onObjTransformStart, this); this.obj.on('transformend', this.onObjTransformEnd, this); + + if (this.obj.children && this.obj.children.length > 0) { + recursiveChildren(this.obj as Container, (child) => { + child.on('transformstart', this.onObjTransformStart, this); + child.on('transformend', this.onObjTransformEnd, this); + }); + } + const pg = this.obj.getGraphic(); + if (pg != null) { + pg.on('transformstart', this.onObjTransformStart, this); + pg.on('transformend', this.onObjTransformEnd, this); + } + this.obj.on('repaint', this.onGraphicRepaint, this); this.children.forEach((dp) => { dp.on('transformstart', this.onScaleDragStart, this); @@ -520,6 +569,19 @@ export class TransformPoints extends Container { ); this.obj.emit('transformstart', GraphicTransformEvent.rotate(this.obj)); // app.emit('transformstart', app.selectedGraphics); + this.obj.getCanvas().addAssistantAppends(this.angleAssistantText); + this.updateAngleAssistantText(de); + } + updateAngleAssistantText(de: GraphicTransformEvent) { + this.angleAssistantText.text = this.obj.angle + '°'; + let cursorPoint = de.data?.startPosition; + if (de.data?.currentPosition) { + cursorPoint = de.data?.currentPosition; + } + if (cursorPoint) { + this.angleAssistantText.position.x = cursorPoint.x; + this.angleAssistantText.position.y = cursorPoint.y - 10; + } } /** * 旋转移动 @@ -529,11 +591,13 @@ export class TransformPoints extends Container { // 旋转角度计算逻辑:取锚点y负方向一点作为旋转点,求旋转点和锚点所形成的直线与x轴角度,此角度+90°即为最终旋转角度,再将旋转角度限制到(-180,180]之间 let angle = angleToAxisx(this.rotatePivot, de.target.position); angle = Math.floor(angle / this.angleStep) * this.angleStep; - angle = (angle + 90) % 360; + const parentAngle = this.obj.parent.worldAngle; + angle = (angle + 90 - parentAngle) % 360; if (angle > 180) { angle = angle - 360; } this.obj.angle = angle; + this.updateAngleAssistantText(de); // this.obj.emit('rotatemove', this.obj); } /** @@ -542,6 +606,7 @@ export class TransformPoints extends Container { */ onRotateEnd() { this.showAll(); + this.obj.getCanvas().removeAssistantAppends(this.angleAssistantText); this.rotateAngleStepKeyListeners.forEach((listener) => this.obj.getGraphicApp().removeKeyboardListener(listener) ); @@ -789,6 +854,17 @@ export class BoundsGraphic extends Graphics { this.visible = false; this.obj.on('transformstart', this.onObjTransformStart, this); this.obj.on('transformend', this.onObjTransformEnd, this); + if (this.obj.children && this.obj.children.length > 0) { + recursiveChildren(this.obj as Container, (child) => { + child.on('transformstart', this.onObjTransformStart, this); + child.on('transformend', this.onObjTransformEnd, this); + }); + } + const pg = this.obj.getGraphic(); + if (pg != null) { + pg.on('transformstart', this.onObjTransformStart, this); + pg.on('transformend', this.onObjTransformEnd, this); + } this.obj.on('repaint', this.onGraphicRepaint, this); graphic.addAssistantAppend(this); } diff --git a/src/jlgraphic/plugins/InteractionPlugin.ts b/src/jlgraphic/plugins/InteractionPlugin.ts index 34a5f5e..70226d6 100644 --- a/src/jlgraphic/plugins/InteractionPlugin.ts +++ b/src/jlgraphic/plugins/InteractionPlugin.ts @@ -99,7 +99,7 @@ export class AppDragEvent { type: 'start' | 'move' | 'end'; target: DisplayObject; original: FederatedPointerEvent; - start: Point; + start: Point; // 画布坐标 constructor( app: GraphicApp, type: 'start' | 'move' | 'end', @@ -146,6 +146,9 @@ export class AppDragEvent { return this.original.pointerType === 'touch'; } + /** + * 终点坐标(画布坐标) + */ public get end(): Point { return this.app.toCanvasCoordinates(this.original.global); } @@ -167,6 +170,15 @@ export class AppDragEvent { public get dsy(): number { return this.end.y - this.start.y; } + + /** + * 转换为目标对象的位移距离 + */ + toTargetShiftLen(target: DisplayObject): { dx: number; dy: number } { + const sl = target.canvasToLocalPoint(this.start); + const el = target.canvasToLocalPoint(this.end); + return { dx: el.x - sl.x, dy: el.y - sl.y }; + } } /** diff --git a/src/jlgraphic/utils/GraphicUtils.ts b/src/jlgraphic/utils/GraphicUtils.ts index 362935e..b8e5109 100644 --- a/src/jlgraphic/utils/GraphicUtils.ts +++ b/src/jlgraphic/utils/GraphicUtils.ts @@ -67,15 +67,16 @@ export function recursiveFindChild( container: Container, finder: (child: DisplayObject) => boolean ): DisplayObject | null { + let result = null; for (let i = 0; i < container.children.length; i++) { const child = container.children[i]; if (finder(child)) { return child; } else if (child.children) { - return recursiveFindChild(child as Container, finder); + result = recursiveFindChild(child as Container, finder); } } - return null; + return result; } export interface BezierParam { @@ -287,6 +288,31 @@ export function calculateLineMidpoint(p1: IPointData, p2: IPointData): Point { 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 @@ -517,3 +543,153 @@ export function angleOfIncludedAngle( } return angle; } + +/** + * 计算两点连线的法向量 + * @param point1 + * @param point2 + * @returns 单位法向量 + */ +export function getNormalVector( + point1: IPointData, + point2: IPointData +): number[] { + const x1 = point1.x, + y1 = point1.y; + const x2 = point2.x, + y2 = point2.y; + const length = Math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2); + return [(y2 - y1) / length, (x1 - x2) / length]; +} + +/** + * 点延向量方向移动 + * @param point + * @param normal 单位向量 + * @param length 平移长度 + * @returns 移动后的点 + */ +export function movePointAlongNormal( + point: IPointData, + normal: number[], + length: number +): Point { + const newPoint = new Point( + point.x + length * normal[0], + point.y + length * normal[1] + ); + return newPoint; +} + +/** + * 计算两组点各自组成直线的相交点(若两线平行 返回第一组坐标第一个点) + * @param line1 两点坐标列表 + * @param line2 两点坐标列表 + * @returns 相交点 + */ +export function getIntersectionPoint(line1: number[], line2: number[]) { + const a1 = line1[0], + b1 = line1[1]; + const a2 = line1[2], + b2 = line1[3]; + const a3 = line2[0], + b3 = line2[1]; + const a4 = line2[2], + b4 = line2[3]; + const denominator = (a3 - a4) * (b1 - b2) - (a1 - a2) * (b3 - b4); + if (denominator === 0) { + return new Point(a1, b1); + } + const x = + ((a3 - a4) * (a2 * b1 - a1 * b2) - (a1 - a2) * (a4 * b3 - a3 * b4)) / + denominator; + const y = + ((b3 - b4) * (b2 * a1 - b1 * a2) - (b1 - b2) * (b4 * a3 - b3 * a4)) / + -denominator; + return new Point(x, y); +} + +/** + * 是否平行线 + * @param p1 + * @param p2 + * @param pa + * @param pb + * @returns + */ +export function isParallelLines( + p1: IPointData, + p2: IPointData, + pa: IPointData, + pb: IPointData +): boolean { + const vle1 = Vector2.direction(Vector2.from(p1), Vector2.from(p2)); + const vle2 = Vector2.direction(Vector2.from(pa), Vector2.from(pb)); + if (vle2.equals(vle1)) { + return true; + } + return vle1.equals(Vector2.direction(Vector2.from(pb), Vector2.from(pa))); +} + +/** + * 点是否在线段上 + * @param p1 + * @param p2 + * @param p + * @returns + */ +export function isPointOnLine( + p1: IPointData, + p2: IPointData, + p: IPointData +): boolean { + const vp1 = Vector2.from(p1); + const vp2 = Vector2.from(p2); + const vp = Vector2.from(p); + if (vp1.equals(vp) || vp2.equals(vp)) { + return true; + } + const vle = Vector2.direction(vp1, Vector2.from(p2)); + const vpe = Vector2.direction(vp1, vp); + if (vle.equals(vpe)) { + return ( + Vector2.difference(vp1, vp2).squaredLength() >= + Vector2.difference(vp1, vp).squaredLength() + ); + } + return false; +} +/** + * 两条线段是否存在包含关系 + * @param line1 + * @param line2 + * @returns + */ +export function isLineContainOther( + line1: { p1: IPointData; p2: IPointData }, + line2: { p1: IPointData; p2: IPointData } +): boolean { + return ( + (isPointOnLine(line1.p1, line1.p2, line2.p1) && + isPointOnLine(line1.p1, line1.p2, line2.p2)) || + (isPointOnLine(line2.p1, line2.p2, line1.p1) && + isPointOnLine(line2.p1, line2.p2, line1.p2)) + ); +} + +/** 均分线段, 返回各线段端点 */ +export function splitLineEvenly( + p1: IPointData, + p2: IPointData, + count: number +): IPointData[][] { + const [stepX, stepY] = [(p2.x - p1.x) / count, (p2.y - p1.y) / count]; + return Array(count) + .fill(1) + .map((_, i) => { + return [ + { x: p1.x + stepX * i, y: p1.y + stepY * i }, + { x: p1.x + stepX * (i + 1), y: p1.y + stepY * (i + 1) }, + ]; + }); +} diff --git a/src/layouts/DrawLayout.vue b/src/layouts/DrawLayout.vue index 8d23ae4..fa1c35a 100644 --- a/src/layouts/DrawLayout.vue +++ b/src/layouts/DrawLayout.vue @@ -11,6 +11,19 @@ Title +
+ + +
+ @@ -100,19 +113,27 @@ - +
+ + diff --git a/src/pages/UserRegister.vue b/src/pages/UserRegister.vue new file mode 100644 index 0000000..211a173 --- /dev/null +++ b/src/pages/UserRegister.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/router/routes.ts b/src/router/routes.ts index 57f61e2..8997b3e 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -3,9 +3,22 @@ import { RouteRecordRaw } from 'vue-router'; const routes: RouteRecordRaw[] = [ { path: '/', + name: 'home', component: () => import('layouts/DrawLayout.vue'), }, + { + path: '/login', + name: 'login', + component: () => import('pages/UserLogin.vue'), + }, + + { + path: '/register', + name: 'register', + component: () => import('pages/UserRegister.vue'), + }, + // Always leave this as last one, // but you can also remove it { diff --git a/yarn.lock b/yarn.lock index c14e795..d43e0b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -183,6 +183,11 @@ resolved "https://registry.npmmirror.com/@pixi/filter-noise/-/filter-noise-7.2.4.tgz#0586a00381ec0e63f6c00d49cd58b781eaf07f37" integrity sha512-QAU9Ybj2ZQrWM9ZEjTTC0iLnQcuyNoZNRinxSbg1G0yacpmsSb9wvV5ltIZ66+hfY+90+u2Nudt/v9g6pvOdGg== +"@pixi/graphics-extras@^7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/graphics-extras/-/graphics-extras-7.2.4.tgz#72ac967992f239d3671d6e680ac891471619fe07" + integrity sha512-0yT91yqF3KLiZI/iLRcfcYlTVpkVyWsfGtWEIorZs0eX+/zYx7um7EJ2h7tFORI/1FxA2maR4td5vpgCwOLJAQ== + "@pixi/graphics@7.2.4": version "7.2.4" resolved "https://registry.npmmirror.com/@pixi/graphics/-/graphics-7.2.4.tgz#8500b604c36184736926393cb0ca9b9de9afef86" @@ -729,6 +734,11 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" +alova@^2.7.1: + version "2.7.1" + resolved "https://registry.npmmirror.com/alova/-/alova-2.7.1.tgz#b91b7c80137a44b57792badda9e7ab09acf93f55" + integrity sha512-/+lbPt+u/c4rBx4fq83795Xuh8Ohj5ZB4tJ7pxL2BxLgRlLNCT9/I1yGrgJEz/jXTqIZHMI2EeBAIb/B0u76tw== + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -810,6 +820,11 @@ async@^3.2.3: resolved "https://registry.npmmirror.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.4.2: version "10.4.14" resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" @@ -822,6 +837,15 @@ autoprefixer@^10.4.2: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +axios@^1.4.0: + version "1.4.0" + resolved "https://registry.npmmirror.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -1047,6 +1071,13 @@ colord@^2.9.3: resolved "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^2.19.0: version "2.20.3" resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -1177,6 +1208,11 @@ define-lazy-prop@^2.0.0: resolved "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + depd@2.0.0: version "2.0.0" resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -1806,6 +1842,20 @@ flatted@^3.1.0: resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -2322,7 +2372,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -2681,6 +2731,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@1.3.2: version "1.3.2" resolved "https://registry.npmmirror.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -3106,6 +3161,11 @@ toidentifier@1.0.1: resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +ts-md5@^1.3.1: + version "1.3.1" + resolved "https://registry.npmmirror.com/ts-md5/-/ts-md5-1.3.1.tgz#f5b860c0d5241dd9bb4e909dd73991166403f511" + integrity sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg== + tslib@^1.8.1: version "1.14.1" resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"