| | |
| | | } |
| | | |
| | | function safeParseJson(text) { |
| | | if (!text || typeof text !== 'string') { |
| | | if (!text) { |
| | | return null; |
| | | } |
| | | if (typeof text === 'object') { |
| | | return text; |
| | | } |
| | | if (typeof text !== 'string') { |
| | | return null; |
| | | } |
| | | try { |
| | |
| | | } catch (e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** @param {string|object|null|undefined} raw */ |
| | | function parseDeviceFormValue(raw) { |
| | | if (!raw) { |
| | | return null; |
| | | } |
| | | if (typeof raw === 'object') { |
| | | return raw; |
| | | } |
| | | return safeParseJson(raw); |
| | | } |
| | | |
| | | /** |
| | | * 设备长宽:有 `deviceLength`/`deviceWidth` 则用像素值,否则用轨道自动尺寸。 |
| | | * @param {object|null|undefined} item |
| | | * @param {number} fallbackAlong |
| | | * @param {number} fallbackAcross |
| | | * @returns {{ along: number, across: number }} |
| | | */ |
| | | function normalizeDeviceSizeOverride(item, fallbackAlong, fallbackAcross) { |
| | | if (!item) { |
| | | return { along: fallbackAlong, across: fallbackAcross }; |
| | | } |
| | | var overrideAlong = Math.round(Number(item.deviceLength)); |
| | | var overrideAcross = Math.round(Number(item.deviceWidth)); |
| | | var along = isFinite(overrideAlong) && overrideAlong > 0 ? overrideAlong : fallbackAlong; |
| | | var across = isFinite(overrideAcross) && overrideAcross > 0 ? overrideAcross : fallbackAcross; |
| | | return { |
| | | along: Math.max(2, along), |
| | | across: Math.max(2, across) |
| | | }; |
| | | } |
| | | |
| | | function getNormalizeAngle(angle, startAngle, endAngle) { |
| | |
| | | var ANNULUS_INSET_MIN_PIXELS = 5; |
| | | |
| | | /** |
| | | * 直线轨道上设备图标像素尺寸(沿轨道方向 × 垂直轨道方向)。 |
| | | * 与 drawCrnDeviceGraphics / drawRgvDeviceGraphics 的 width、height 语义一致,不再在别处做「再乘 2」。 |
| | | * @param {{ width: number, height: number }} rect 轨道外接矩形 |
| | | * @param {"crn"|"dualCrn"|"rgv"} trackType |
| | | * @returns {{ along: number, across: number }} |
| | | */ |
| | | function getDevicePixelBoxForTrack(rect, trackType) { |
| | | var shortLen = Math.min(rect.width, rect.height); |
| | | var across = shortLen; |
| | | var along = shortLen * 2; |
| | | along = Math.round(along * TRACK_DEVICE_BOX_SCALE); |
| | | across = Math.round(across * TRACK_DEVICE_BOX_SCALE); |
| | | return { |
| | | along: Math.max(2, along), |
| | | across: Math.max(2, across) |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * 轨道上设备「自动」像素尺寸(沿轨道方向 × 垂直轨道方向)。 |
| | | * - 直线轨道:来自 getDevicePixelBoxForTrack |
| | | * - 环穿:按历史逻辑缩放到 shortLength 的 15% |
| | | * 直线轨道:短边 × TRACK_DEVICE_BOX_SCALE 为 across,along 为其 2 倍(与 drawCrnDeviceGraphics / drawRgvDeviceGraphics 一致)。 |
| | | * 环穿:先按同上得到基准盒,再缩放到短边的 15%。 |
| | | * @param {{ type: string, width: number, height: number, pathList?: any[] }} rect |
| | | * @returns {{ along: number, across: number } | null} |
| | | */ |
| | |
| | | if (!isFinite(rectW) || !isFinite(rectH) || rectW <= 0 || rectH <= 0) { |
| | | return null; |
| | | } |
| | | var shortLen = Math.min(rectW, rectH); |
| | | var across = Math.round(shortLen * TRACK_DEVICE_BOX_SCALE); |
| | | var along = Math.round(shortLen * 2 * TRACK_DEVICE_BOX_SCALE); |
| | | if (rect.type === 'annulus') { |
| | | var shortLength = Math.min(rectW, rectH); |
| | | var box = getDevicePixelBoxForTrack({ width: shortLength, height: shortLength }, 'rgv'); |
| | | var scale = (shortLength * 0.15) / box.across; |
| | | var scale = (shortLen * 0.15) / across; |
| | | return { |
| | | along: Math.max(2, Math.round(box.along * scale)), |
| | | across: Math.max(2, Math.round(box.across * scale)) |
| | | along: Math.max(2, Math.round(along * scale)), |
| | | across: Math.max(2, Math.round(across * scale)) |
| | | }; |
| | | } |
| | | return getDevicePixelBoxForTrack({ width: rectW, height: rectH }, rect.type); |
| | | return { along, across }; |
| | | } |
| | | |
| | | function getDeviceInfo(rect) { |
| | | var isHorizontal = rect.width > rect.height; |
| | | var longLength = isHorizontal ? rect.width : rect.height; |
| | | var shortLength = isHorizontal ? rect.height : rect.width; |
| | | var deviceForm = parseDeviceFormValue(rect.value); |
| | | var deviceForm = safeParseJson(rect.value); |
| | | if (!deviceForm || !deviceForm.deviceList || deviceForm.deviceList.length === 0) { |
| | | return deviceForm; |
| | | } |
| | |
| | | return deviceForm; |
| | | } |
| | | var allDistance = getAllDistance(pathList); |
| | | var autoBox = getAutoTrackDeviceBox(rect); |
| | | var ab = annulusBandContext(rect, rect.shape || 'rect'); |
| | | deviceForm.deviceList.forEach(function (item) { |
| | | var deltaDistance = (allDistance * item.progress) / 100; |
| | |
| | | ab.inset, |
| | | ab.refInside |
| | | ); |
| | | var fallbackAlong = autoBox ? autoBox.along : Math.max(2, Math.round(shortLength * 0.3)); |
| | | var fallbackAcross = autoBox ? autoBox.across : Math.max(2, Math.round(shortLength * 0.15)); |
| | | var size = normalizeDeviceSizeOverride(item, fallbackAlong, fallbackAcross); |
| | | var width = size.along; |
| | | var height = size.across; |
| | | |
| | | item.x = centered.x; |
| | | item.y = centered.y; |
| | | item.path = moved.path; |
| | | item.width = width; |
| | | item.height = height; |
| | | item.width = item.deviceLength; |
| | | item.height = item.deviceWidth; |
| | | }); |
| | | return deviceForm; |
| | | } |
| | | |
| | | var box = getAutoTrackDeviceBox(rect) || getDevicePixelBoxForTrack(rect, rect.type); |
| | | // 线段轨道 |
| | | deviceForm.deviceList.forEach(function (item) { |
| | | var size = normalizeDeviceSizeOverride(item, box.along, box.across); |
| | | var inset = (shortLength - size.across) / 2; |
| | | var inset = (shortLength - item.deviceLength) / 2; |
| | | var distance = (item.progress * (longLength - 2 * shortLength)) / 100; |
| | | if (isHorizontal) { |
| | | item.x = rect.x + distance; |
| | | item.y = rect.y + inset; |
| | | item.width = size.along; |
| | | item.height = size.across; |
| | | item.width = item.deviceLength; |
| | | item.height = item.deviceWidth; |
| | | } else { |
| | | item.x = rect.x + inset; |
| | | item.y = rect.y + distance; |
| | | item.width = size.across; |
| | | item.height = size.along; |
| | | item.width = item.deviceLength; |
| | | item.height = item.deviceWidth; |
| | | } |
| | | }); |
| | | return deviceForm; |
| | |
| | | // var shortLen = Math.min(Number(sprite.width) || 0, Number(sprite.height) || 0); |
| | | // var fallbackAlong = box ? box.along : Math.max(2, Math.round(shortLen * 0.3)); |
| | | // var fallbackAcross = box ? box.across : Math.max(2, Math.round(shortLen * 0.15)); |
| | | // var form = parseDeviceFormValue(sprite && sprite.value); |
| | | // var form = safeParseJson(sprite && sprite.value); |
| | | // var across = fallbackAcross; |
| | | // console.log('fallbackAcross', across); |
| | | // if (form && form.deviceList && form.deviceList.length) { |
| | |
| | | /** |
| | | * 堆垛机 / 双工位设备图标(与 MapCanvas#createCrnTexture / createCrnTextureColoredDevice 一致) |
| | | * @param {PIXI.Graphics} g |
| | | * @param {number} deviceWidth 图标总宽(像素,与 getDevicePixelBoxForTrack.along 一致) |
| | | * @param {number} deviceHeight 图标总高(与 getDevicePixelBoxForTrack.across 一致) |
| | | * @param {number} deviceWidth 图标总宽(像素,与 getAutoTrackDeviceBox.along 一致) |
| | | * @param {number} deviceHeight 图标总高(与 getAutoTrackDeviceBox.across 一致) |
| | | * @param {number} bodyColor 驾驶舱填充色 |
| | | */ |
| | | function drawCrnDeviceGraphics(g, deviceWidth, deviceHeight, bodyColor) { |
| | |
| | | getPositionAfterMove: getPositionAfterMove, |
| | | getAllDistance: getAllDistance, |
| | | getDeviceInfo: getDeviceInfo, |
| | | getDevicePixelBoxForTrack: getDevicePixelBoxForTrack, |
| | | getAutoTrackDeviceBox: getAutoTrackDeviceBox, |
| | | getLShapePointList: getLShapePointList, |
| | | getIsStillHalf: getIsStillHalf, |