diff --git a/packages/Signal/Lamp.ts b/packages/Signal/Lamp.ts new file mode 100644 index 0000000..9191f3f --- /dev/null +++ b/packages/Signal/Lamp.ts @@ -0,0 +1,102 @@ +import { Container } from '@pixi/display'; +import { Graphics } from 'pixi.js'; + +const lampConsts = { + lampRadius: 8, + logicModeLineWidth: 2, + logicModeDistance: 5, + logicModeColor: '0x000000', + lampLineWidth: 1, + lampLineColor: '0x3149c3', + lampBadColor: '0xFF0000', + badStart: 10, + badEnd: 15, +}; + +export class Lamp extends Container { + circleLamp: Graphics = new Graphics(); + logicMode: Graphics = new Graphics(); + lampBad: Graphics = new Graphics(); + radiusX = 0; + radiusY = 0; + + constructor() { + super(); + this.addChild(this.circleLamp); + this.addChild(this.logicMode); + this.addChild(this.lampBad); + } + paint(radiusX: number, radiusY: number) { + this.radiusX = radiusX; + this.radiusY = radiusY; + this.createLamp(); + } + createLampBad() { + this.lampBad.clear(); + this.lampBad.lineStyle(lampConsts.lampLineWidth, lampConsts.lampBadColor); + this.lampBad.moveTo(this.radiusX + lampConsts.badStart, this.radiusY); + this.lampBad.lineTo(this.radiusX + lampConsts.badEnd, this.radiusY); + this.lampBad.moveTo(this.radiusX - lampConsts.badStart, this.radiusY); + this.lampBad.lineTo(this.radiusX - lampConsts.badEnd, this.radiusY); + this.lampBad.moveTo(this.radiusX, this.radiusY + lampConsts.badStart); + this.lampBad.lineTo(this.radiusX, this.radiusY + lampConsts.badEnd); + this.lampBad.moveTo(this.radiusX, this.radiusY - lampConsts.badStart); + this.lampBad.lineTo(this.radiusX, this.radiusY - lampConsts.badEnd); + const xieStart = Math.sin(Math.PI / 4) * lampConsts.badStart; + const xieEnd = Math.sin(Math.PI / 4) * lampConsts.badEnd; + this.lampBad.moveTo(this.radiusX + xieStart, this.radiusY + xieStart); + this.lampBad.lineTo(this.radiusX + xieEnd, this.radiusY + xieEnd); + this.lampBad.moveTo(this.radiusX + xieStart, this.radiusY - xieStart); + this.lampBad.lineTo(this.radiusX + xieEnd, this.radiusY - xieEnd); + + this.lampBad.moveTo(this.radiusX - xieStart, this.radiusY - xieStart); + this.lampBad.lineTo(this.radiusX - xieEnd, this.radiusY - xieEnd); + this.lampBad.moveTo(this.radiusX - xieStart, this.radiusY + xieStart); + this.lampBad.lineTo(this.radiusX - xieEnd, this.radiusY + xieEnd); + } + createLamp(color?: string) { + this.circleLamp.clear(); + this.circleLamp.lineStyle( + lampConsts.lampLineWidth, + lampConsts.lampLineColor + ); + if (!color) { + this.circleLamp.beginFill('0XFFFFFF', 0); + } else { + this.circleLamp.beginFill(color, 1); + } + this.circleLamp.drawCircle( + this.radiusX, + this.radiusY, + lampConsts.lampRadius + ); + this.circleLamp.endFill(); + } + createLogicMode() { + this.logicMode + .clear() + .lineStyle(lampConsts.logicModeLineWidth, lampConsts.logicModeColor) + .moveTo( + this.radiusX - lampConsts.logicModeDistance, + this.radiusY + lampConsts.logicModeDistance + ) + .lineTo( + this.radiusX + lampConsts.logicModeDistance, + this.radiusY - lampConsts.logicModeDistance + ) + .moveTo( + this.radiusX - lampConsts.logicModeDistance, + this.radiusY - lampConsts.logicModeDistance + ) + .lineTo( + this.radiusX + lampConsts.logicModeDistance, + this.radiusY + lampConsts.logicModeDistance + ); + } + logicModeClear() { + this.logicMode.clear(); + } + lampClear() { + this.circleLamp.clear(); + } +} diff --git a/packages/Signal/LampMainBody.ts b/packages/Signal/LampMainBody.ts new file mode 100644 index 0000000..fbba26c --- /dev/null +++ b/packages/Signal/LampMainBody.ts @@ -0,0 +1,123 @@ +import { Graphics, Point, Container } from 'pixi.js'; +import { + calculateMirrorPoint, + GraphicAnimation, +} from 'jl-graphic'; +import { Lamp } from './Lamp'; +import { SignalColorEnum, signalConsts, Model } from './Signal'; + +export class LampMainBody extends Container { + static Type = 'LampMainBody'; + lampNum = 1; + lampPost: Graphics = new Graphics(); + lamps: Lamp[] = []; + mirror = false; + deltaTime = 0; + + constructor() { + super(); + } + paint(mt: Model, mirror: boolean) { + this.mirror = mirror; + if ( + mt === Model.HL || + mt === Model.AB + ) { + this.lampNum = 2; + } else { + this.lampNum = 3; + } + this.removeChildren(0); + this.lampPost = new Graphics(); + let lpp = new Point(signalConsts.levelLampPostLength, 0); + if (mirror) { + lpp = calculateMirrorPoint(new Point(0, 0), lpp); + } + this.lampPost + .lineStyle(signalConsts.postLineWidth, SignalColorEnum.lampPostColor) + .moveTo(0, -signalConsts.verticalLampPostLength / 2) + .lineTo(0, signalConsts.verticalLampPostLength / 2) + .moveTo(0, 0) + .lineTo(lpp.x, lpp.y); + this.addChild(this.lampPost); + + this.lamps = []; + for (let i = 0; i < this.lampNum; i++) { + const lamp = new Lamp(); + this.addChild(lamp); + const radiusX = + (1 + i * 2) * signalConsts.lampRadius + + signalConsts.levelLampPostLength; + let lrp = new Point(radiusX, 0); + if (mirror) { + lrp = calculateMirrorPoint(new Point(0, 0), lrp); + } + lamp.paint(lrp.x, lrp.y); + this.lamps.push(lamp); + } + } + setStateH() { + this.lamps[0].createLamp(SignalColorEnum.redLamp); + this.lamps.forEach((lamp, index) => { + if (index !== 0) { + lamp.createLamp(SignalColorEnum.closeLamp); + } + }); + } + setStateL() { + this.lamps[1].createLamp(SignalColorEnum.greenLamp); + this.lamps.forEach((lamp, index) => { + if(index !==1) { + lamp.createLamp(SignalColorEnum.closeLamp); + } + }); + } + setStateU() { + this.lamps[2].createLamp(SignalColorEnum.yellowLamp); + this.lamps.forEach((lamp, index) => { + if (index !== 2) { + lamp.createLamp(SignalColorEnum.closeLamp); + } + }) + } + setStateHu() { + this.lamps[0].createLamp(SignalColorEnum.redLamp); + this.lamps[1].createLamp(SignalColorEnum.closeLamp); + this.lamps[2].createLamp(SignalColorEnum.yellowLamp); + } + setStateA() { + this.lamps[0].createLamp(SignalColorEnum.blueLamp); + this.lamps[1].createLamp(SignalColorEnum.closeLamp); + } + setStateB() { + this.lamps[0].createLamp(SignalColorEnum.whiteLamp); + this.lamps[1].createLamp(SignalColorEnum.closeLamp); + } + setStateOff() { + this.lamps.forEach((lamp) => + lamp.createLamp(SignalColorEnum.closeLamp) + ); + } + createFlashAnmiation( + name: string, + color: string, + lampIndex: number + ): GraphicAnimation { + const bgColor = '0X000000'; + const flashAnmiation = GraphicAnimation.init({ + name: name, + run: (dt: number) => { + this.deltaTime += dt; + if (this.deltaTime > 60) { + this.deltaTime = 0; + this.lamps[lampIndex].createLamp(color); + } else if (this.deltaTime > 30) { + this.lamps.forEach((lamp) => { + lamp.createLamp(bgColor); + }); + } + }, + }); + return flashAnmiation; + } +} diff --git a/packages/Signal/Signal.ts b/packages/Signal/Signal.ts new file mode 100644 index 0000000..98216cd --- /dev/null +++ b/packages/Signal/Signal.ts @@ -0,0 +1,228 @@ +import { GraphicData, calculateMirrorPoint } from 'jl-graphic' +import { Graphics, Point, Container } from 'pixi.js'; +import { SignalCode } from './SignalCode'; +import { LampMainBody } from './LampMainBody'; + +export enum Model { + HL = 0, // 红绿灯 + HLU_FU = 1, // 红绿黄,封黄灯,无引导 + HLU_DU_YY = 2, // 红绿黄,不封灯,有单黄,带引导 + HLU_YY = 3, // 红绿黄,不封灯,无单黄,带引导 + HLU_FL_DU_YY = 4, // 红绿黄,封绿灯,有单黄,带引导 + HLU_DU = 5, // 红绿黄,不封灯,有单黄,无引导 + AB = 6, // 蓝白 + HBU_DU = 7 // 红白黄,不封灯,有单黄,无引导 +} + +export enum SignalColorEnum { + humanControlColor = '0xffff00', + fleetModeColor = '0x00ff00', + blockedColor = '0XFF0000', + defaultCodeColor = '0XFFFFFF', + lampPostColor = '0xFFFFFF', + redLamp = '0XFF0000', + greenLamp = '0X00FF00', + yellowLamp = '0XFFFF00', + whiteLamp = '0XFFFFFF', + blueLamp = '0X0033FF', + closeLamp = '0X000000', + logicModeColor = '0x000000', + lampLineColor = '0x3149c3', +} + +export const signalConsts = { + fleetModeLength: 24, + fleetModeRadius: 8, + fleetModeLineWidth: 6, + humanControlRadius: 8, + codeOffset: 20, + codeFontSize: 11, + blockedLineWidth: 1, + verticalLampPostLength: 16, + levelLampPostLength: 4, + postLineWidth: 3, + lampRadius: 8, + logicModeLineWidth: 2, + logicModeDistance: 5, + lampLineWidth: 1, +}; + +export interface ISignalData extends GraphicData { + code: string + mirror: boolean; + mt: Model +} + +export class Signal extends Container { + static Type = 'signal'; + datas: ISignalData; + signalCode: SignalCode = new SignalCode(); + humanControl: Graphics = new Graphics(); + fleetMode: Graphics = new Graphics(); + lampMainBody: LampMainBody = new LampMainBody(); + blockedMode: Graphics = new Graphics(); + + constructor(datas: ISignalData) { + super(); + this.datas = datas; + this.addChild(this.humanControl); + this.addChild(this.fleetMode); + this.addChild(this.lampMainBody); + this.addChild(this.signalCode); + } + + + doRepaint(): void { + const mirror = this.datas.mirror; + this.lampMainBody.paint(this.datas.mt, mirror); + this.signalCode.paint(this.datas); + const codeTransform = this.datas?.childTransforms?.find( + (item) => item.name === 'signalCode' + ); + if (codeTransform) { + const position = codeTransform?.transform.position; + const rotation = codeTransform?.transform?.rotation; + this.signalCode.position.set(position?.x, position?.y); + this.signalCode.rotation = rotation || 0; + } else { + this.signalCode.position.set(0, signalConsts.codeOffset); + } + } + + createFleetMode(): void { + const mirror = this.datas.mirror; + this.fleetMode.beginFill(SignalColorEnum.fleetModeColor, 1); + let lmp = new Point( + this.lampMainBody.width + signalConsts.fleetModeLength, + 0 + ); + if (mirror) { + lmp = calculateMirrorPoint(new Point(0, 0), lmp); + } + drawArrow( + this.fleetMode, + lmp.x, + 0, + signalConsts.fleetModeLength, + signalConsts.fleetModeRadius, + signalConsts.fleetModeLineWidth, + mirror + ); + this.fleetMode.endFill(); + } + + createHumanControl(): void { + const mirror = this.datas.mirror; + this.humanControl.beginFill(SignalColorEnum.humanControlColor, 1); + if (this.humanControl.drawRegularPolygon) { + let hmp = new Point(-signalConsts.humanControlRadius, 0); + if (mirror) { + hmp = calculateMirrorPoint(new Point(0, 0), hmp); + } + this.humanControl.drawRegularPolygon( + hmp.x, + hmp.y, + signalConsts.humanControlRadius, + 3, + (Math.PI / 2) * (mirror ? -1 : 1) + ); + } + this.humanControl.endFill(); + } + + setStateBlocked() { + this.signalCode.createBlockedMode(); + } + setStateH() { + this.lampMainBody.setStateH(); + } + setStateL() { + this.lampMainBody.setStateL(); + } + setStateU() { + this.lampMainBody.setStateU(); + } + setStateHu() { + this.lampMainBody.setStateHu(); + } + setStateA() { + this.lampMainBody.setStateA(); + } + setStateB() { + this.lampMainBody.setStateB(); + } + setStateOff() { + this.lampMainBody.setStateOff(); + } +} + + +/** + * + * @param polygon + * @param x 箭头顶点x坐标 + * @param y 箭头顶点y坐标 + * @param length 箭头长度 + * @param radius 箭头三角半径 + * @param lineWidth 箭头线宽 + * @param mirror 是否镜像翻转 (基于箭头顶点) + */ +export function drawArrow( + polygon: Graphics, + x: number, + y: number, + length: number, + radius: number, + lineWidth: number, + mirror: boolean +) { + const trianglAcme = { x, y }; + let triangleP1 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y + Math.cos(Math.PI / 6) * radius, + }; + let triangleP2 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y - Math.cos(Math.PI / 6) * radius, + }; + let lineP1 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y + lineWidth / 2, + }; + let lineP2 = { + x: x - length, + y: y + lineWidth / 2, + }; + let lineP3 = { + x: x - length, + y: y - lineWidth / 2, + }; + let lineP4 = { + x: x - radius - Math.sin(Math.PI / 6), + y: y - lineWidth / 2, + }; + if (mirror) { + triangleP1 = calculateMirrorPoint(trianglAcme, triangleP1); + triangleP2 = calculateMirrorPoint(trianglAcme, triangleP2); + lineP1 = calculateMirrorPoint(trianglAcme, lineP1); + lineP2 = calculateMirrorPoint(trianglAcme, lineP2); + lineP3 = calculateMirrorPoint(trianglAcme, lineP3); + lineP4 = calculateMirrorPoint(trianglAcme, lineP4); + } + polygon.drawPolygon( + trianglAcme.x, + trianglAcme.y, + triangleP1.x, + triangleP1.y, + lineP1.x, + lineP1.y, + lineP2.x, + lineP2.y, + lineP3.x, + lineP3.y, + lineP4.x, + lineP4.y, + triangleP2.x, + triangleP2.y + ); +} diff --git a/packages/Signal/SignalCode.ts b/packages/Signal/SignalCode.ts new file mode 100644 index 0000000..bd412c2 --- /dev/null +++ b/packages/Signal/SignalCode.ts @@ -0,0 +1,41 @@ +import { Container, Graphics, Point } from 'pixi.js'; +import { VectorText } from 'jl-graphic'; +import { + ISignalData, + SignalColorEnum, + signalConsts, +} from './Signal'; + +export class SignalCode extends Container { + blockedMode: Graphics = new Graphics(); + codeGraph: VectorText = new VectorText(''); + name = 'signalCode'; + constructor() { + super(); + this.addChild(this.blockedMode); + this.addChild(this.codeGraph); + } + paint(datas: ISignalData) { + this.codeGraph.text = datas?.code || '信号机编号'; + this.codeGraph.style.fill = SignalColorEnum.defaultCodeColor; + this.codeGraph.setVectorFontSize(signalConsts.codeFontSize); + this.codeGraph.anchor.set(0.5); + this.codeGraph.position.set(0, 0); + this.blockedMode.clear(); + } + createBlockedMode() { + const codeRect = this.codeGraph.getBounds(); + const rectP = this.screenToLocalPoint(new Point(codeRect.x, codeRect.y)); + this.blockedMode.clear(); + this.blockedMode.lineStyle( + signalConsts.blockedLineWidth, + SignalColorEnum.blockedColor + ); + this.blockedMode.drawRect( + rectP.x, + rectP.y, + codeRect.width, + codeRect.height + ); + } +}