import { LEGEND_VEHICLE, Legend } from './features/mcs-status-default' import MCSCarrierHolder from './mcs-carrier-holder' import Node from './node' const DIRECTION_GAP = 3 function findNode(vehicle: MCSVehicle, nodeId: string, nodeMachine: string) { if (!nodeId || !nodeMachine) { return null } const nodes = (vehicle.root.findAllById?.(nodeId) || []).filter(node => node.state.type == 'Node') as Node[] return nodes.find((node: Node) => { return node.nodeMachine === nodeMachine }) } /** * MCS용 Unit > Transport들의 공통 속성을 정의한 오브젝트 */ export default class MCSVehicle extends MCSCarrierHolder { static get properties(): any { return [ ...MCSCarrierHolder.properties, { type: 'select', label: 'direction', name: 'direction', property: { options: ['', 'up', 'down', 'left', 'right'] } } ] } private lastWaypoint: { NodeId: string; NodeMachine: string } | null = null getLegendFallback(): Legend { return LEGEND_VEHICLE } renderDirection(ctx: CanvasRenderingContext2D) { const { direction } = this.state if (direction) { const { left, top, width, height } = this.state ctx.beginPath() ctx.translate(left, top) ctx.strokeStyle = 'black' ctx.fillStyle = 'black' ctx.lineJoin = 'round' ctx.lineWidth = 1 ctx.setLineDash([]) switch (direction) { case 'up': ctx.moveTo((width * 1) / 3, -DIRECTION_GAP) ctx.lineTo(width / 2, -height / 10 - DIRECTION_GAP) ctx.lineTo((width * 2) / 3, -DIRECTION_GAP) break case 'down': ctx.moveTo((width * 1) / 3, height + DIRECTION_GAP) ctx.lineTo(width / 2, height + height / 10 + DIRECTION_GAP) ctx.lineTo((width * 2) / 3, height + DIRECTION_GAP) break case 'left': ctx.moveTo(-DIRECTION_GAP, (height * 1) / 3) ctx.lineTo(-width / 10 - 2, height / 2) ctx.lineTo(-DIRECTION_GAP, (height * 2) / 3) break case 'right': default: ctx.moveTo(DIRECTION_GAP + width, (height * 1) / 3) ctx.lineTo(DIRECTION_GAP + width + width / 10, height / 2) ctx.lineTo(DIRECTION_GAP + width, (height * 2) / 3) break } ctx.translate(-left, -top) ctx.closePath() ctx.fill() ctx.stroke() } } // Performance aware postrender(ctx: CanvasRenderingContext2D): void { super.postrender(ctx) // this.renderDirection(ctx) } onchangeData(after: Record, before: Record): void { super.onchangeData(after, before) const { NODEID: beforeNodeId, NODEMACHINE: beforeNodeMachine } = before.data || {} const { NODEID: NodeId, NODEMACHINE: NodeMachine } = after.data || {} if ( NodeId === undefined || NodeMachine === undefined || (beforeNodeId === NodeId && beforeNodeMachine === NodeMachine) || (this.lastWaypoint && this.lastWaypoint.NodeId === NodeId && this.lastWaypoint.NodeMachine === NodeMachine) ) { return // not changed } const afterNode = findNode(this, NodeId, NodeMachine) if (!afterNode) { console.warn(`Node not found. NodeId: ${NodeId}, NodeMachine: ${NodeMachine}`) return } const beforeNode = this.lastWaypoint ? findNode(this, this.lastWaypoint.NodeId, this.lastWaypoint.NodeMachine) : afterNode // 실제 존재하는 노드 중 가장 최근에 이동한 노드 this.lastWaypoint = { NodeId, NodeMachine } if ((this.root as any).vehicleAnimation) { this.trigger('waypoint', { from: beforeNode || afterNode, to: afterNode, duration: 5000 }) } else if (beforeNode) { const fromCenter = beforeNode.center const toCenter = afterNode.center const { x: startX, y: startY } = beforeNode.transcoordS2T(fromCenter.x, fromCenter.y) const { x: endX, y: endY } = afterNode.transcoordS2T(toCenter.x, toCenter.y) const theta = Math.atan2(endY - startY, endX - startX) + Math.PI / 2 this.location = { x: endX, y: endY } this.setState('rotation', theta) } else { // beforeNode 없음: afterNode 위치로 즉시 이동 const toCenter = afterNode.center const { x, y } = afterNode.transcoordS2T(toCenter.x, toCenter.y) this.location = { x, y } } } }