| | |
| | | Vue.component('map-canvas', { |
| | | template: ` |
| | | <div style="width: 100%; height: 100%; position: relative;"> |
| | | <div ref="pixiView"></div> |
| | | <div ref="pixiView" style="position: absolute; inset: 0;"></div> |
| | | <div style="position: absolute; top: 12px; left: 14px; z-index: 30; pointer-events: none; max-width: 52%;"> |
| | | <div style="display: flex; flex-direction: column; gap: 6px; align-items: flex-start;"> |
| | | <div v-for="item in cycleCapacity.loopList" |
| | | :key="'loop-' + item.loopNo" |
| | | @mouseenter="handleLoopCardEnter(item)" |
| | | @mouseleave="handleLoopCardLeave(item)" |
| | | style="padding: 6px 10px; border-radius: 4px; background: rgba(11, 35, 58, 0.72); color: #fff; font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: auto;"> |
| | | 圈{{ item.loopNo }} | |
| | | 站点: {{ item.stationCount || 0 }} | |
| | | 任务: {{ item.taskCount || 0 }} | |
| | | 承载: {{ formatLoadPercent(item.currentLoad) }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-show="shelfTooltip.visible" |
| | | :style="shelfTooltipStyle()"> |
| | | {{ shelfTooltip.text }} |
| | | </div> |
| | | <div style="position: absolute; top: 20px; right: 50px; text-align: right;"> |
| | | <div>FPS:{{mapFps}}</div> |
| | | <div style="margin-top: 6px; display: flex; gap: 6px; justify-content: flex-end;"> |
| | | <button type="button" @click="rotateMap" style="padding: 2px 8px; font-size: 12px; cursor: pointer;">旋转</button> |
| | | <button type="button" @click="toggleMirror" style="padding: 2px 8px; font-size: 12px; cursor: pointer;">{{ mapMirrorX ? '取消镜像' : '镜像' }}</button> |
| | | <div style="position: absolute; top: 18px; right: 34px; z-index: 30; display: flex; flex-direction: column; align-items: flex-end; gap: 8px;"> |
| | | <div :style="mapToolFpsStyle()">FPS {{ mapFps }}</div> |
| | | <button type="button" @click="toggleMapToolPanel" :style="mapToolToggleStyle(showMapToolPanel)">{{ showMapToolPanel ? '收起操作' : '地图操作' }}</button> |
| | | <div v-show="showMapToolPanel" :style="mapToolBarStyle()"> |
| | | <button type="button" @click="toggleStationDirection" :style="mapToolButtonStyle(showStationDirection)">{{ showStationDirection ? '隐藏站点方向' : '显示站点方向' }}</button> |
| | | <button type="button" @click="rotateMap" :style="mapToolButtonStyle(false)">旋转</button> |
| | | <button type="button" @click="toggleMirror" :style="mapToolButtonStyle(mapMirrorX)">{{ mapMirrorX ? '取消镜像' : '镜像' }}</button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | item: null |
| | | }, |
| | | shelfTooltipMinScale: 0.4, |
| | | containerResizeObserver: null, |
| | | timer: null, |
| | | adjustLabelTimer: null, |
| | | isSwitchingFloor: false |
| | | isSwitchingFloor: false, |
| | | cycleCapacity: { |
| | | loopList: [], |
| | | totalStationCount: 0, |
| | | taskStationCount: 0, |
| | | currentLoad: 0 |
| | | }, |
| | | showMapToolPanel: false, |
| | | showStationDirection: false, |
| | | hoverLoopNo: null, |
| | | hoverLoopStationIdSet: new Set(), |
| | | loopHighlightColor: 0xfff34d, |
| | | stationDirectionColor: 0xff5a36 |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.currentLev = this.lev || 1; |
| | | this.createMap(); |
| | | this.startContainerResizeObserve(); |
| | | this.loadMapTransformConfig(); |
| | | this.loadLocList(); |
| | | this.connectWs(); |
| | |
| | | this.getCrnInfo(); |
| | | this.getDualCrnInfo(); |
| | | this.getSiteInfo(); |
| | | this.getCycleCapacityInfo(); |
| | | this.getRgvInfo(); |
| | | }, 1000); |
| | | }, |
| | |
| | | |
| | | if (this.hoverRaf) { cancelAnimationFrame(this.hoverRaf); this.hoverRaf = null; } |
| | | if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); } |
| | | if (this.containerResizeObserver) { this.containerResizeObserver.disconnect(); this.containerResizeObserver = null; } |
| | | window.removeEventListener('resize', this.resizeToContainer); |
| | | if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; } |
| | | if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { try { this.ws.close(); } catch (e) {} } |
| | |
| | | } |
| | | }, |
| | | methods: { |
| | | mapToolBarStyle() { |
| | | return { |
| | | display: 'flex', |
| | | gap: '8px', |
| | | alignItems: 'center', |
| | | padding: '7px', |
| | | borderRadius: '14px', |
| | | background: 'rgba(255, 255, 255, 0.72)', |
| | | border: '1px solid rgba(160, 180, 205, 0.3)', |
| | | boxShadow: '0 8px 20px rgba(37, 64, 97, 0.08)', |
| | | backdropFilter: 'blur(4px)' |
| | | }; |
| | | }, |
| | | mapToolFpsStyle() { |
| | | return { |
| | | padding: '4px 10px', |
| | | borderRadius: '999px', |
| | | background: 'rgba(255, 255, 255, 0.7)', |
| | | border: '1px solid rgba(160, 180, 205, 0.28)', |
| | | color: '#48617c', |
| | | fontSize: '12px', |
| | | lineHeight: '18px', |
| | | letterSpacing: '0.04em', |
| | | boxShadow: '0 6px 16px rgba(37, 64, 97, 0.06)', |
| | | userSelect: 'none' |
| | | }; |
| | | }, |
| | | mapToolToggleStyle(active) { |
| | | return { |
| | | appearance: 'none', |
| | | border: '1px solid ' + (active ? 'rgba(96, 132, 170, 0.36)' : 'rgba(160, 180, 205, 0.3)'), |
| | | background: active ? 'rgba(235, 243, 251, 0.96)' : 'rgba(255, 255, 255, 0.82)', |
| | | color: '#46617b', |
| | | height: '30px', |
| | | padding: '0 12px', |
| | | borderRadius: '999px', |
| | | fontSize: '12px', |
| | | lineHeight: '30px', |
| | | cursor: 'pointer', |
| | | whiteSpace: 'nowrap', |
| | | boxShadow: '0 6px 16px rgba(37, 64, 97, 0.06)' |
| | | }; |
| | | }, |
| | | mapToolButtonStyle(active) { |
| | | return { |
| | | appearance: 'none', |
| | | border: '1px solid ' + (active ? 'rgba(255, 136, 93, 0.38)' : 'rgba(160, 180, 205, 0.3)'), |
| | | background: active ? 'rgba(255, 119, 77, 0.16)' : 'rgba(255, 255, 255, 0.88)', |
| | | color: active ? '#d85a31' : '#4d647d', |
| | | height: '30px', |
| | | padding: '0 12px', |
| | | borderRadius: '10px', |
| | | fontSize: '12px', |
| | | lineHeight: '30px', |
| | | cursor: 'pointer', |
| | | transition: 'all 0.2s ease', |
| | | boxShadow: active ? '0 4px 10px rgba(255, 119, 77, 0.12)' : 'none', |
| | | whiteSpace: 'nowrap' |
| | | }; |
| | | }, |
| | | toggleMapToolPanel() { |
| | | this.showMapToolPanel = !this.showMapToolPanel; |
| | | }, |
| | | createMap() { |
| | | this.pixiApp = new PIXI.Application({ backgroundColor: 0xF5F7F9, antialias: false, powerPreference: 'high-performance', autoDensity: true, resolution: Math.min(window.devicePixelRatio || 1, 2) }); |
| | | PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.LINEAR; |
| | |
| | | }); |
| | | //*******************FPS******************* |
| | | }, |
| | | startContainerResizeObserve() { |
| | | if (typeof ResizeObserver === 'undefined' || !this.$el) { return; } |
| | | this.containerResizeObserver = new ResizeObserver(() => { |
| | | this.resizeToContainer(); |
| | | }); |
| | | this.containerResizeObserver.observe(this.$el); |
| | | }, |
| | | getViewportSize() { |
| | | if (!this.pixiApp || !this.pixiApp.renderer) { return { width: 0, height: 0 }; } |
| | | const screen = this.pixiApp.renderer.screen; |
| | | if (screen && screen.width > 0 && screen.height > 0) { |
| | | return { width: screen.width, height: screen.height }; |
| | | } |
| | | const rect = this.pixiApp.view ? this.pixiApp.view.getBoundingClientRect() : null; |
| | | return { width: rect ? rect.width : 0, height: rect ? rect.height : 0 }; |
| | | }, |
| | | resizeToContainer() { |
| | | const w = this.$el.clientWidth || 0; |
| | | const h = this.$el.clientHeight || 0; |
| | | if (w > 0 && h > 0 && this.pixiApp) { |
| | | const vw = this.pixiApp.renderer && this.pixiApp.renderer.screen ? this.pixiApp.renderer.screen.width : 0; |
| | | const vh = this.pixiApp.renderer && this.pixiApp.renderer.screen ? this.pixiApp.renderer.screen.height : 0; |
| | | if (vw === w && vh === h) { return; } |
| | | this.pixiApp.renderer.resize(w, h); |
| | | if (this.mapContentSize && this.mapContentSize.width > 0 && this.mapContentSize.height > 0) { |
| | | this.applyMapTransform(true); |
| | | } |
| | | } |
| | | }, |
| | | getMap() { |
| | |
| | | }, |
| | | changeFloor(lev) { |
| | | this.currentLev = lev; |
| | | this.clearLoopStationHighlight(); |
| | | this.isSwitchingFloor = true; |
| | | this.hideShelfTooltip(); |
| | | this.hoveredShelfCell = null; |
| | |
| | | this.getMap(); |
| | | }, |
| | | createMapData(map) { |
| | | this.clearLoopStationHighlight(); |
| | | this.hideShelfTooltip(); |
| | | this.hoveredShelfCell = null; |
| | | this.mapRowOffsets = []; |
| | |
| | | } |
| | | } |
| | | xCursor += cellWidth; |
| | | } |
| | | }); |
| | | |
| | | map.forEach((row, rowIndex) => { |
| | | for (let colIndex = 0; colIndex < row.length; colIndex++) { |
| | | const val = row[colIndex]; |
| | | if (!val || val.type !== 'devp' || val.type === 'merge') { continue; } |
| | | val.stationDirectionList = this.resolveStationDirectionList(map, rowIndex, colIndex, val); |
| | | } |
| | | }); |
| | | |
| | |
| | | sta.statusObj = null; |
| | | if (sta.textObj.parent !== sta) { sta.addChild(sta.textObj); sta.textObj.position.set(sta.width / 2, sta.height / 2); } |
| | | } |
| | | if (status === "site-auto") { |
| | | this.updateColor(sta, 0x78ff81); |
| | | } else if (status === "site-auto-run" || status === "site-auto-id" || status === "site-auto-run-id") { |
| | | this.updateColor(sta, 0xfa51f6); |
| | | } else if (status === "site-unauto") { |
| | | this.updateColor(sta, 0xb8b8b8); |
| | | } else if (status === "machine-pakin") { |
| | | this.updateColor(sta, 0x30bffc); |
| | | } else if (status === "machine-pakout") { |
| | | this.updateColor(sta, 0x97b400); |
| | | } else if (status === "site-run-block") { |
| | | this.updateColor(sta, 0xe69138); |
| | | } else { |
| | | this.updateColor(sta, 0xb8b8b8); |
| | | } |
| | | this.setStationBaseColor(sta, this.getStationStatusColor(status)); |
| | | }); |
| | | }, |
| | | getCrnInfo() { |
| | |
| | | getRgvInfo() { |
| | | if (this.isSwitchingFloor) { return; } |
| | | this.sendWs(JSON.stringify({ url: "/console/latest/data/rgv", data: {} })); |
| | | }, |
| | | getCycleCapacityInfo() { |
| | | if (this.isSwitchingFloor) { return; } |
| | | this.sendWs(JSON.stringify({ url: "/console/latest/data/station/cycle/capacity", data: {} })); |
| | | }, |
| | | setCycleCapacityInfo(res) { |
| | | const payload = res && res.code === 200 ? res.data : null; |
| | | if (res && res.code === 403) { parent.location.href = baseUrl + "/login"; return; } |
| | | if (!payload) { return; } |
| | | const loopList = Array.isArray(payload.loopList) ? payload.loopList : []; |
| | | this.cycleCapacity = { |
| | | loopList: loopList, |
| | | totalStationCount: payload.totalStationCount || 0, |
| | | taskStationCount: payload.taskStationCount || 0, |
| | | currentLoad: typeof payload.currentLoad === 'number' ? payload.currentLoad : parseFloat(payload.currentLoad || 0) |
| | | }; |
| | | if (this.hoverLoopNo != null) { |
| | | const targetLoop = loopList.find(v => v && v.loopNo === this.hoverLoopNo); |
| | | if (targetLoop) { |
| | | this.hoverLoopStationIdSet = this.buildStationIdSet(targetLoop.stationIdList); |
| | | this.applyLoopStationHighlight(); |
| | | } else { |
| | | this.clearLoopStationHighlight(); |
| | | } |
| | | } |
| | | }, |
| | | formatLoadPercent(load) { |
| | | let value = typeof load === 'number' ? load : parseFloat(load || 0); |
| | | if (!isFinite(value)) { value = 0; } |
| | | if (value < 0) { value = 0; } |
| | | if (value > 1) { value = 1; } |
| | | return (value * 100).toFixed(1) + "%"; |
| | | }, |
| | | setCrnInfo(res) { |
| | | let crns = Array.isArray(res) ? res : (res && res.code === 200 ? res.data : null); |
| | |
| | | if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; } |
| | | this.wsReconnectAttempts = 0; |
| | | this.getMap(this.currentLev); |
| | | this.getCycleCapacityInfo(); |
| | | }, |
| | | webSocketOnError(e) { |
| | | this.scheduleReconnect(); |
| | |
| | | this.setDualCrnInfo(JSON.parse(result.data)); |
| | | } else if (result.url === "/console/latest/data/rgv") { |
| | | this.setRgvInfo(JSON.parse(result.data)); |
| | | } else if (result.url === "/console/latest/data/station/cycle/capacity") { |
| | | this.setCycleCapacityInfo(JSON.parse(result.data)); |
| | | } else if (typeof result.url === "string" && result.url.indexOf("/basMap/lev/") === 0) { |
| | | this.setMap(JSON.parse(result.data)); |
| | | } |
| | |
| | | const brightness = (r * 299 + g * 587 + b * 114) / 1000; |
| | | return brightness > 150 ? '#000000' : '#ffffff'; |
| | | }, |
| | | getStationStatusColor(status) { |
| | | if (status === "site-auto") { return 0x78ff81; } |
| | | if (status === "site-auto-run") { return 0xfa51f6; } |
| | | if (status === "site-auto-id") { return 0xc4c400; } |
| | | if (status === "site-auto-run-id") { return 0x30bffc; } |
| | | if (status === "site-unauto") { return 0xb8b8b8; } |
| | | if (status === "machine-pakin") { return 0x30bffc; } |
| | | if (status === "machine-pakout") { return 0x97b400; } |
| | | if (status === "site-run-block") { return 0xe69138; } |
| | | return 0xb8b8b8; |
| | | }, |
| | | getCrnStatusColor(status) { |
| | | if (status === "machine-auto") { return 0x21BA45; } |
| | | if (status === "machine-un-auto") { return 0xBBBBBB; } |
| | |
| | | } |
| | | sprite = new PIXI.Sprite(texture); |
| | | sprite._kind = 'devp'; |
| | | const directionOverlay = this.createStationDirectionOverlay(item.width, item.height, item.stationDirectionList); |
| | | if (directionOverlay) { |
| | | directionOverlay.visible = this.showStationDirection; |
| | | sprite.addChild(directionOverlay); |
| | | } |
| | | sprite.directionObj = directionOverlay; |
| | | sprite._stationDirectionList = Array.isArray(item.stationDirectionList) ? item.stationDirectionList.slice() : []; |
| | | let siteId = this.getStationId(value); |
| | | if (siteId === -1) { siteId = item.data; } |
| | | const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 10, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 }); |
| | |
| | | text.position.set(sprite.width / 2, sprite.height / 2); |
| | | sprite.addChild(text); |
| | | sprite.textObj = text; |
| | | if (siteId != null && siteId !== -1) { this.pixiStaMap.set(parseInt(siteId), sprite); } |
| | | const stationIdInt = parseInt(siteId, 10); |
| | | if (!isNaN(stationIdInt)) { this.pixiStaMap.set(stationIdInt, sprite); } |
| | | sprite._stationId = isNaN(stationIdInt) ? null : stationIdInt; |
| | | sprite._baseColor = 0x00ff7f; |
| | | sprite._loopHighlighted = false; |
| | | sprite.interactive = true; |
| | | sprite.buttonMode = true; |
| | | sprite.on('pointerdown', () => { |
| | |
| | | } |
| | | sprite.tint = color; |
| | | }, |
| | | setStationBaseColor(sprite, color) { |
| | | if (!sprite) { return; } |
| | | sprite._baseColor = color; |
| | | if (this.isStationInHoverLoop(sprite)) { |
| | | this.applyHighlightColor(sprite); |
| | | } else { |
| | | this.updateColor(sprite, color); |
| | | sprite._loopHighlighted = false; |
| | | } |
| | | }, |
| | | applyHighlightColor(sprite) { |
| | | if (!sprite) { return; } |
| | | this.updateColor(sprite, this.loopHighlightColor); |
| | | sprite._loopHighlighted = true; |
| | | }, |
| | | isStationInHoverLoop(sprite) { |
| | | if (!sprite || sprite._stationId == null || !this.hoverLoopStationIdSet) { return false; } |
| | | return this.hoverLoopStationIdSet.has(sprite._stationId); |
| | | }, |
| | | buildStationIdSet(stationIdList) { |
| | | const set = new Set(); |
| | | if (!Array.isArray(stationIdList)) { return set; } |
| | | stationIdList.forEach((id) => { |
| | | const v = parseInt(id, 10); |
| | | if (!isNaN(v)) { set.add(v); } |
| | | }); |
| | | return set; |
| | | }, |
| | | applyLoopStationHighlight() { |
| | | if (!this.pixiStaMap) { return; } |
| | | this.pixiStaMap.forEach((sprite) => { |
| | | if (!sprite) { return; } |
| | | if (this.isStationInHoverLoop(sprite)) { |
| | | this.applyHighlightColor(sprite); |
| | | } else if (sprite._loopHighlighted) { |
| | | const baseColor = (typeof sprite._baseColor === 'number') ? sprite._baseColor : 0xb8b8b8; |
| | | this.updateColor(sprite, baseColor); |
| | | sprite._loopHighlighted = false; |
| | | } |
| | | }); |
| | | }, |
| | | clearLoopStationHighlight() { |
| | | if (this.pixiStaMap) { |
| | | this.pixiStaMap.forEach((sprite) => { |
| | | if (!sprite || !sprite._loopHighlighted) { return; } |
| | | const baseColor = (typeof sprite._baseColor === 'number') ? sprite._baseColor : 0xb8b8b8; |
| | | this.updateColor(sprite, baseColor); |
| | | sprite._loopHighlighted = false; |
| | | }); |
| | | } |
| | | this.hoverLoopNo = null; |
| | | this.hoverLoopStationIdSet = new Set(); |
| | | }, |
| | | handleLoopCardEnter(loopItem) { |
| | | if (!loopItem) { return; } |
| | | this.hoverLoopNo = loopItem.loopNo; |
| | | this.hoverLoopStationIdSet = this.buildStationIdSet(loopItem.stationIdList); |
| | | this.applyLoopStationHighlight(); |
| | | }, |
| | | handleLoopCardLeave(loopItem) { |
| | | if (!loopItem) { |
| | | this.clearLoopStationHighlight(); |
| | | return; |
| | | } |
| | | if (this.hoverLoopNo === loopItem.loopNo) { |
| | | this.clearLoopStationHighlight(); |
| | | } |
| | | }, |
| | | isJson(str) { |
| | | try { JSON.parse(str); return true; } catch (e) { return false; } |
| | | }, |
| | |
| | | }, |
| | | getStationId(obj) { |
| | | if (this.isJson(obj)) { let data = JSON.parse(obj); if (data.stationId == null || data.stationId == undefined) { return -1; } return data.stationId; } else { return -1; } |
| | | }, |
| | | parseMapValue(obj) { |
| | | if (obj == null) { return null; } |
| | | if (typeof obj === 'object') { return obj; } |
| | | if (!this.isJson(obj)) { return null; } |
| | | try { |
| | | return JSON.parse(obj); |
| | | } catch (e) { |
| | | return null; |
| | | } |
| | | }, |
| | | normalizeDirectionList(direction) { |
| | | const aliasMap = { |
| | | top: 'top', |
| | | up: 'top', |
| | | north: 'top', |
| | | bottom: 'bottom', |
| | | down: 'bottom', |
| | | south: 'bottom', |
| | | left: 'left', |
| | | west: 'left', |
| | | right: 'right', |
| | | east: 'right' |
| | | }; |
| | | let rawList = []; |
| | | if (Array.isArray(direction)) { |
| | | rawList = direction; |
| | | } else if (typeof direction === 'string') { |
| | | rawList = direction.split(/[,\s|/]+/); |
| | | } |
| | | const result = []; |
| | | const seen = new Set(); |
| | | rawList.forEach((item) => { |
| | | const key = aliasMap[String(item || '').trim().toLowerCase()]; |
| | | if (!key || seen.has(key)) { return; } |
| | | seen.add(key); |
| | | result.push(key); |
| | | }); |
| | | return result; |
| | | }, |
| | | resolveStationDirectionList(map, rowIndex, colIndex, item) { |
| | | const valueObj = this.parseMapValue(item && item.value); |
| | | const fromValue = this.normalizeDirectionList(valueObj && valueObj.direction); |
| | | if (fromValue.length > 0) { return fromValue; } |
| | | const rowSpan = item && item.rowSpan ? item.rowSpan : 1; |
| | | const colSpan = item && item.colSpan ? item.colSpan : 1; |
| | | const fallback = []; |
| | | const candidateList = [ |
| | | { key: 'top', cell: this.resolveMergedCell(map, rowIndex - 1, colIndex) }, |
| | | { key: 'right', cell: this.resolveMergedCell(map, rowIndex, colIndex + colSpan) }, |
| | | { key: 'bottom', cell: this.resolveMergedCell(map, rowIndex + rowSpan, colIndex) }, |
| | | { key: 'left', cell: this.resolveMergedCell(map, rowIndex, colIndex - 1) } |
| | | ]; |
| | | candidateList.forEach((candidate) => { |
| | | if (this.isStationDirectionNeighbor(candidate.cell)) { |
| | | fallback.push(candidate.key); |
| | | } |
| | | }); |
| | | return fallback; |
| | | }, |
| | | isStationDirectionNeighbor(cell) { |
| | | if (!cell) { return false; } |
| | | if (cell.type === 'devp') { return true; } |
| | | return this.isTrackType(cell); |
| | | }, |
| | | createStationDirectionOverlay(width, height, directionList) { |
| | | if (!Array.isArray(directionList) || directionList.length === 0) { return null; } |
| | | const container = new PIXI.Container(); |
| | | const arrowSize = Math.max(4, Math.min(width, height) * 0.22); |
| | | const margin = Math.max(2, Math.min(width, height) * 0.12); |
| | | directionList.forEach((direction) => { |
| | | const arrow = new PIXI.Graphics(); |
| | | this.drawStationDirectionArrow(arrow, width, height, direction, arrowSize, margin); |
| | | container.addChild(arrow); |
| | | }); |
| | | return container; |
| | | }, |
| | | drawStationDirectionArrow(graphics, width, height, direction, size, margin) { |
| | | if (!graphics) { return; } |
| | | const halfBase = Math.max(2, size * 0.45); |
| | | const stemLen = Math.max(3, size * 0.7); |
| | | const centerX = width / 2; |
| | | const centerY = height / 2; |
| | | graphics.beginFill(this.stationDirectionColor, 0.95); |
| | | if (direction === 'top') { |
| | | const tipY = margin; |
| | | const baseY = margin + size; |
| | | const stemY = Math.min(centerY - 2, baseY + stemLen); |
| | | if (stemY > baseY) { graphics.moveTo(centerX, stemY); graphics.lineTo(centerX, baseY); } |
| | | graphics.moveTo(centerX, tipY); |
| | | graphics.lineTo(centerX - halfBase, baseY); |
| | | graphics.lineTo(centerX + halfBase, baseY); |
| | | } else if (direction === 'right') { |
| | | const tipX = width - margin; |
| | | const baseX = width - margin - size; |
| | | const stemX = Math.max(centerX + 2, baseX - stemLen); |
| | | if (stemX < baseX) { graphics.moveTo(stemX, centerY); graphics.lineTo(baseX, centerY); } |
| | | graphics.moveTo(tipX, centerY); |
| | | graphics.lineTo(baseX, centerY - halfBase); |
| | | graphics.lineTo(baseX, centerY + halfBase); |
| | | } else if (direction === 'bottom') { |
| | | const tipY = height - margin; |
| | | const baseY = height - margin - size; |
| | | const stemY = Math.max(centerY + 2, baseY - stemLen); |
| | | if (stemY < baseY) { graphics.moveTo(centerX, stemY); graphics.lineTo(centerX, baseY); } |
| | | graphics.moveTo(centerX, tipY); |
| | | graphics.lineTo(centerX - halfBase, baseY); |
| | | graphics.lineTo(centerX + halfBase, baseY); |
| | | } else if (direction === 'left') { |
| | | const tipX = margin; |
| | | const baseX = margin + size; |
| | | const stemX = Math.min(centerX - 2, baseX + stemLen); |
| | | if (stemX > baseX) { graphics.moveTo(stemX, centerY); graphics.lineTo(baseX, centerY); } |
| | | graphics.moveTo(tipX, centerY); |
| | | graphics.lineTo(baseX, centerY - halfBase); |
| | | graphics.lineTo(baseX, centerY + halfBase); |
| | | } |
| | | graphics.closePath(); |
| | | graphics.endFill(); |
| | | }, |
| | | applyStationDirectionVisibility() { |
| | | if (!this.pixiStaMap) { return; } |
| | | this.pixiStaMap.forEach((sprite) => { |
| | | if (!sprite || !sprite.directionObj) { return; } |
| | | sprite.directionObj.visible = this.showStationDirection; |
| | | }); |
| | | }, |
| | | getTrackSiteNo(obj) { |
| | | if (this.isJson(obj)) { let data = JSON.parse(obj); if (data.trackSiteNo == null || data.trackSiteNo == undefined) { return -1; } return data.trackSiteNo; } else { return -1; } |
| | |
| | | adjustLabelScale() { |
| | | const s = this.pixiApp && this.pixiApp.stage ? Math.abs(this.pixiApp.stage.scale.x || 1) : 1; |
| | | const minPx = 14; |
| | | const vw = this.pixiApp.view.width; |
| | | const vh = this.pixiApp.view.height; |
| | | const viewport = this.getViewportSize(); |
| | | const vw = viewport.width; |
| | | const vh = viewport.height; |
| | | const margin = 50; |
| | | const mirrorSign = this.mapMirrorX ? -1 : 1; |
| | | const inverseRotation = -((this.mapRotation % 360) * Math.PI / 180); |
| | |
| | | this.mapRotation = (this.mapRotation + 90) % 360; |
| | | this.applyMapTransform(true); |
| | | this.saveMapTransformConfig(); |
| | | }, |
| | | toggleStationDirection() { |
| | | this.showStationDirection = !this.showStationDirection; |
| | | this.applyStationDirectionVisibility(); |
| | | }, |
| | | toggleMirror() { |
| | | this.mapMirrorX = !this.mapMirrorX; |
| | |
| | | const contentW = size.width || 0; |
| | | const contentH = size.height || 0; |
| | | if (contentW <= 0 || contentH <= 0) { return; } |
| | | const vw = this.pixiApp.view.width; |
| | | const vh = this.pixiApp.view.height; |
| | | const viewport = this.getViewportSize(); |
| | | const vw = viewport.width; |
| | | const vh = viewport.height; |
| | | let scale = Math.min(vw / contentW, vh / contentH) * 0.95; |
| | | if (!isFinite(scale) || scale <= 0) { scale = 1; } |
| | | const baseW = this.mapContentSize.width || contentW; |
| | |
| | | } |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |