import * as PIXI from 'pixi.js'; 
 | 
import * as TWEEDLE from 'tweedle.js'; 
 | 
import { 
 | 
    DEVICE_TYPE, 
 | 
    DEVICE_Z_INDEX, 
 | 
    AGV_STATUS_MODE, 
 | 
    DEVICE_SPRITE_TINT, 
 | 
    DEVICE_SELECTED_EFFECT_PADDING, 
 | 
    DEVICE_SELECTED_EFFECT_COLOR, 
 | 
    POINT_ROUTE_DIRECTION, 
 | 
    DEVICE_SPRITE_TINT_DARK, 
 | 
    ANIMATE_DURING_TIME, 
 | 
} from './constants'; 
 | 
import { getRouteList } from './http'; 
 | 
import PointRoute from "./PointRoute"; 
 | 
  
 | 
import shelf from '/map/shelf.svg'; 
 | 
import charge from '/map/charge.svg'; 
 | 
import station from '/map/station.svg'; 
 | 
import direction from '/map/direction.svg'; 
 | 
import point from '/map/point.svg'; 
 | 
// agv 
 | 
import ctuEmptyNoBattery from '/map/agv/ctuEmptyNoBattery.svg'; 
 | 
import ctuEmptyHalfBattery from '/map/agv/ctuEmptyHalfBattery.svg'; 
 | 
import ctuEmptyFullBattery from '/map/agv/ctuEmptyFullBattery.svg'; 
 | 
import ctuLoadedNoBattery from '/map/agv/ctuLoadedNoBattery.svg'; 
 | 
import ctuLoadedHalfBattery from '/map/agv/ctuLoadedHalfBattery.svg'; 
 | 
import ctuLoadedFullBattery from '/map/agv/ctuLoadedFullBattery.svg'; 
 | 
  
 | 
let app, mapContainer, themeMode; 
 | 
let selectedSprite, effectCircle, effectTicker; 
 | 
let tooltip; 
 | 
  
 | 
export function getApp() { 
 | 
    return app; 
 | 
} 
 | 
  
 | 
export function setApp(param) { 
 | 
    app = param; 
 | 
} 
 | 
  
 | 
export function getMapContainer() { 
 | 
    return mapContainer; 
 | 
} 
 | 
  
 | 
export function setMapContainer(param) { 
 | 
    mapContainer = param; 
 | 
} 
 | 
  
 | 
export function getThemeMode() { 
 | 
    return themeMode; 
 | 
} 
 | 
  
 | 
export function setThemeMode(param) { 
 | 
    themeMode = param; 
 | 
    if (mapContainer) { 
 | 
        // mapContainer.children.forEach(child => { 
 | 
        //     const deviceType = child.data?.type; 
 | 
        //     if (deviceType) { 
 | 
        //         if (themeMode === 'dark') { 
 | 
        //             const tint = DEVICE_SPRITE_TINT_DARK[deviceType] 
 | 
        //             if (tint) { 
 | 
        //                 child.tint = tint; 
 | 
        //             } 
 | 
        //         } else { 
 | 
        //             DEVICE_SPRITE_TINT[deviceType] != null && (child.tint = DEVICE_SPRITE_TINT[deviceType]); 
 | 
        //         } 
 | 
        //     } 
 | 
        // }) 
 | 
    } 
 | 
} 
 | 
  
 | 
  
 | 
export const getRealPosition = (x, y) => { 
 | 
    const globalPoint = new PIXI.Point(); 
 | 
    app.renderer.plugins.interaction.mapPositionToPoint(globalPoint, x, y); 
 | 
    const mapPoint = mapContainer.toLocal(globalPoint); 
 | 
    return { mapX: mapPoint.x, mapY: mapPoint.y }; 
 | 
} 
 | 
  
 | 
export const generateSprite = (deviceType, texture) => { 
 | 
    let sprite; 
 | 
    switch (deviceType) { 
 | 
        case DEVICE_TYPE.SHELF: 
 | 
            sprite = new PIXI.Sprite(PIXI.Texture.from(shelf, { resourceOptions: { scale: 1 } })); 
 | 
            // sprite.width = 50; 
 | 
            // sprite.height = 50; 
 | 
            sprite.zIndex = DEVICE_Z_INDEX.SHELF; 
 | 
            break; 
 | 
        case DEVICE_TYPE.CHARGE: 
 | 
            sprite = new PIXI.Sprite(PIXI.Texture.from(charge, { resourceOptions: { scale: 1 } })); 
 | 
            // sprite.width = 60; 
 | 
            // sprite.height = 60; 
 | 
            sprite.zIndex = DEVICE_Z_INDEX.CHARGE; 
 | 
            break; 
 | 
        case DEVICE_TYPE.STATION: 
 | 
            sprite = new PIXI.Sprite(PIXI.Texture.from(station, { resourceOptions: { scale: 1 } })); 
 | 
            // sprite.width = 112; 
 | 
            // sprite.height = 63; 
 | 
            sprite.zIndex = DEVICE_Z_INDEX.STATION; 
 | 
            break; 
 | 
        case DEVICE_TYPE.DIRECTION: 
 | 
            sprite = new PIXI.Sprite(PIXI.Texture.from(direction, { resourceOptions: { scale: 1 } })); 
 | 
            // sprite.width = 112; 
 | 
            // sprite.height = 63; 
 | 
            sprite.zIndex = DEVICE_Z_INDEX.DIRECTION; 
 | 
            break; 
 | 
  
 | 
  
 | 
        case DEVICE_TYPE.AGV: 
 | 
            if (texture) { 
 | 
                sprite = new PIXI.Sprite(PIXI.Texture.from(texture, { resourceOptions: { scale: 1 } })); 
 | 
            } 
 | 
            sprite.width = 750; 
 | 
            sprite.height = 1300; 
 | 
            sprite.zIndex = DEVICE_Z_INDEX.AGV; 
 | 
            break 
 | 
        case DEVICE_TYPE.POINT: 
 | 
            sprite = new PIXI.Sprite(PIXI.Texture.from(point, { resourceOptions: { scale: 1 } })); 
 | 
            // sprite.width = 112; 
 | 
            // sprite.height = 63; 
 | 
            sprite.zIndex = DEVICE_Z_INDEX.POINT; 
 | 
            break 
 | 
        default: 
 | 
            break; 
 | 
    } 
 | 
    const tintType = themeMode === 'dark' ? DEVICE_SPRITE_TINT_DARK : DEVICE_SPRITE_TINT; 
 | 
    tintType[deviceType] != null && (sprite.tint = tintType[deviceType]); 
 | 
  
 | 
    if (sprite && deviceType !== DEVICE_TYPE.AGV) { 
 | 
        sprite.blendMode = PIXI.BLEND_MODES.MULTIPLY; 
 | 
    } 
 | 
    return sprite; 
 | 
} 
 | 
  
 | 
export const initSprite = (sprite, type) => { 
 | 
    sprite.anchor.set(0.5); 
 | 
    // sprite.alpha = 1; 
 | 
    sprite.cursor = 'pointer'; 
 | 
    sprite.eventMode = 'static'; 
 | 
    sprite.data = { 
 | 
        type: type, 
 | 
        uuid: generateID() 
 | 
    }; 
 | 
} 
 | 
  
 | 
export const querySprite = (type, no) => { 
 | 
    if (!mapContainer) { 
 | 
        return; 
 | 
    } 
 | 
    for (const sprite of mapContainer.children) { 
 | 
        if (sprite.data?.type === type && sprite.data?.no === no) { 
 | 
            return sprite; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
export const findSpriteByUuid = (uuid) => { 
 | 
    return mapContainer?.children?.find(child => child?.data?.uuid === uuid); 
 | 
} 
 | 
  
 | 
export const markSprite = (sprite) => { 
 | 
    sprite.alpha = 0.5; 
 | 
} 
 | 
  
 | 
export const unMarkSprite = (sprite) => { 
 | 
    sprite.alpha = 1; 
 | 
} 
 | 
  
 | 
export const spriteListBeMovable = (selectedSprites, endFn) => { 
 | 
    if (selectedSprites && selectedSprites.length > 0) { 
 | 
        let batchMove = false; 
 | 
        let batchMoveStartPos = null; 
 | 
  
 | 
        const batchMoving = (event) => { 
 | 
            const scale = mapContainer.scale.x; 
 | 
            if (batchMove && batchMoveStartPos) { 
 | 
                // offset move val 
 | 
                var mouseMovement = { 
 | 
                    x: (event.global.x - batchMoveStartPos.x) / scale, 
 | 
                    y: (event.global.y - batchMoveStartPos.y) / scale 
 | 
                }; 
 | 
                for (let sprite of selectedSprites) { 
 | 
                    sprite.position.x = sprite.batchMoveStartPos.x + mouseMovement.x; 
 | 
                    sprite.position.y = sprite.batchMoveStartPos.y + mouseMovement.y; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        const batchMoveEnd = (event) => { 
 | 
            batchMove = false; 
 | 
            batchMoveStartPos = null; 
 | 
            selectedSprites.forEach(child => { 
 | 
                unMarkSprite(child); 
 | 
            }) 
 | 
            selectedSprites = []; 
 | 
            mapContainer.parent.off('mousedown'); 
 | 
            mapContainer.parent.off('mousemove'); 
 | 
            mapContainer.parent.off('mouseup'); 
 | 
  
 | 
            if (endFn) { 
 | 
                endFn(); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        const batchMoveStart = (event) => { 
 | 
            batchMoveStartPos = { x: event.data.global.clone().x, y: event.data.global.clone().y }; 
 | 
            selectedSprites.forEach(child => { 
 | 
                child.batchMoveStartPos = { x: child.position.x, y: child.position.y }; 
 | 
            }) 
 | 
  
 | 
            batchMove = true; 
 | 
            mapContainer.parent.off('mousemove'); 
 | 
            mapContainer.parent.on('mousemove', batchMoving); 
 | 
  
 | 
            mapContainer.parent.off('mouseup'); 
 | 
            mapContainer.parent.on('mouseup', batchMoveEnd); 
 | 
        } 
 | 
  
 | 
        mapContainer.parent.off('mousedown') 
 | 
        mapContainer.parent.on('mousedown', batchMoveStart); 
 | 
    } 
 | 
} 
 | 
  
 | 
export const copySprite = (sprite) => { 
 | 
    const copiedSprite = generateSprite(sprite.data.type) 
 | 
    initSprite(copiedSprite); 
 | 
    copiedSprite.position.set(sprite.position.x, sprite.position.y); 
 | 
    copiedSprite.scale.set(sprite.scale.x, sprite.scale.y); 
 | 
    copiedSprite.rotation = sprite.rotation; 
 | 
    copiedSprite.data = deepCopy(sprite.data); 
 | 
    copiedSprite.data.uuid = generateID(); 
 | 
    return copiedSprite; 
 | 
} 
 | 
  
 | 
export const isSpriteInSelectionBox = (sprite, selectionBox) => { 
 | 
    const spriteBounds = sprite.getBounds(); 
 | 
    const boxBounds = selectionBox.getBounds(); 
 | 
  
 | 
    return spriteBounds.x + spriteBounds.width > boxBounds.x 
 | 
        && spriteBounds.x < boxBounds.x + boxBounds.width 
 | 
        && spriteBounds.y + spriteBounds.height > boxBounds.y 
 | 
        && spriteBounds.y < boxBounds.y + boxBounds.height; 
 | 
} 
 | 
  
 | 
export const beInsight = (sprite, setCurSprite) => { 
 | 
    if (!sprite?.data?.type) { return } 
 | 
  
 | 
    sprite.off('pointerup'); 
 | 
    sprite.off('pointermove'); 
 | 
    sprite.off('pointerdown'); 
 | 
    sprite.off('pointerover'); 
 | 
    sprite.off('pointerout'); 
 | 
    sprite.off('click'); 
 | 
  
 | 
    sprite.on("click", (event) => { 
 | 
        setCurSprite(sprite); 
 | 
    }); 
 | 
  
 | 
    sprite.on('pointerover', onSpriteMouseOver); 
 | 
    sprite.on('pointermove', onSpriteMouseMove); 
 | 
    sprite.on('pointerout', onSpriteMouseOut); 
 | 
} 
 | 
  
 | 
export const beMovable = (sprite) => { 
 | 
    sprite.off('pointerup'); 
 | 
    sprite.off('pointermove'); 
 | 
    sprite.off('pointerdown'); 
 | 
    sprite.off('pointerover'); 
 | 
    sprite.off('pointerout'); 
 | 
    sprite.off('click'); 
 | 
  
 | 
    sprite.on("pointerdown", onDragStart); 
 | 
  
 | 
    let dragTarget; 
 | 
    function onDragStart(event) { 
 | 
        if (event.button === 0) { 
 | 
            dragTarget = event.currentTarget; 
 | 
            mapContainer.parent.off('pointermove'); 
 | 
            mapContainer.parent.on('pointermove', onDragMove, dragTarget); 
 | 
  
 | 
            mapContainer.parent.off('pointerup'); 
 | 
            mapContainer.parent.on('pointerup', onDragEnd.bind(mapContainer)); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    function onDragMove(event) { 
 | 
        if (this) { 
 | 
            this.parent.toLocal(event.global, null, this.position); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    function onDragEnd() { 
 | 
        if (dragTarget) { 
 | 
            this.parent.off('pointermove'); 
 | 
            this.parent.off('pointerup'); 
 | 
            dragTarget.alpha = 1; 
 | 
            dragTarget = null; 
 | 
        } 
 | 
    } 
 | 
  
 | 
} 
 | 
  
 | 
export const beSettings = (sprite, setSpriteSettings) => { 
 | 
    if (!sprite?.data?.type) { return } 
 | 
  
 | 
    sprite.off('pointerup'); 
 | 
    sprite.off('pointermove'); 
 | 
    sprite.off('pointerdown'); 
 | 
    sprite.off('pointerover'); 
 | 
    sprite.off('pointerout'); 
 | 
    sprite.off('click'); 
 | 
  
 | 
    sprite.on("click", (event) => { 
 | 
        setSpriteSettings(sprite); 
 | 
    }); 
 | 
  
 | 
    sprite.on('pointerover', onSpriteMouseOver); 
 | 
    sprite.on('pointermove', onSpriteMouseMove); 
 | 
    sprite.on('pointerout', onSpriteMouseOut); 
 | 
} 
 | 
  
 | 
const createSpriteTooltip = (sprite) => { 
 | 
    const style = new PIXI.TextStyle({ 
 | 
        fontFamily: 'Roboto', 
 | 
        fontSize: 12, 
 | 
        fill: themeMode === 'light' ? '#000' : '#eee', 
 | 
    }); 
 | 
    const text = new PIXI.Text(`${sprite.data?.type} ${sprite.data?.no}`, style); 
 | 
    const background = new PIXI.Graphics(); 
 | 
    // shadow 
 | 
    background.beginFill(themeMode === 'light' ? '#000' : '#eee', 0.1); 
 | 
    background.drawRoundedRect(4, 4, text.width + 6, text.height + 6, 4); 
 | 
    background.endFill(); 
 | 
    // background 
 | 
    background.beginFill(themeMode === 'light' ? '#fff' : '#333', 1); 
 | 
    background.drawRoundedRect(0, 0, text.width + 8, text.height + 8, 4); 
 | 
    background.endFill(); 
 | 
  
 | 
    text.x = 5; 
 | 
    text.y = 3; 
 | 
    background.addChild(text); 
 | 
    tooltip = new PIXI.Container(); 
 | 
    tooltip.name = "tooltip"; 
 | 
    tooltip.addChild(background); 
 | 
    return tooltip; 
 | 
} 
 | 
  
 | 
function onSpriteMouseOver(event) { 
 | 
    if (tooltip) { 
 | 
        app?.stage.removeChild(tooltip); 
 | 
    } 
 | 
    tooltip = createSpriteTooltip(this);// this => sprite 
 | 
    tooltip.x = event.data.global.x + 10; 
 | 
    tooltip.y = event.data.global.y + 10; 
 | 
    app.stage.addChild(tooltip); 
 | 
} 
 | 
  
 | 
function onSpriteMouseMove(event) { 
 | 
    if (tooltip) { 
 | 
        tooltip.x = event.data.global.x + 10; 
 | 
        tooltip.y = event.data.global.y + 10; 
 | 
    } 
 | 
} 
 | 
  
 | 
function onSpriteMouseOut() { 
 | 
    if (tooltip && tooltip.parent) { 
 | 
        tooltip.parent?.removeChild(tooltip); 
 | 
        tooltip = null; 
 | 
    } 
 | 
} 
 | 
  
 | 
export const focusSprite = (sprite) => { 
 | 
    if (!sprite || !app || !mapContainer) { 
 | 
        return; 
 | 
    } 
 | 
  
 | 
    mapContainer.scale.set(0.3); 
 | 
    mapContainer.position.set(0, 0); 
 | 
  
 | 
    let bounds = sprite.getBounds(); 
 | 
    let focusPoint = { 
 | 
        x: bounds.x + bounds.width / 2, 
 | 
        y: bounds.y + bounds.height / 2 
 | 
    }; 
 | 
  
 | 
    let targetPos = { 
 | 
        x: app.renderer.width / 4 - focusPoint.x, 
 | 
        y: app.renderer.height / 2 - focusPoint.y 
 | 
    }; 
 | 
  
 | 
    new TWEEDLE.Tween(mapContainer.position) 
 | 
        .easing(TWEEDLE.Easing.Quadratic.Out) 
 | 
        .to(targetPos, 500) 
 | 
        .onComplete(() => { 
 | 
        }) 
 | 
        .start(); 
 | 
} 
 | 
  
 | 
export const clearMapData = () => { 
 | 
    if (!mapContainer) { 
 | 
        return; 
 | 
    } 
 | 
    let childList = []; 
 | 
    mapContainer.children.forEach(child => { 
 | 
        if (child.data?.uuid) { 
 | 
            childList.push(child); 
 | 
        } 
 | 
    }) 
 | 
    if (childList.length > 0) { 
 | 
        childList.forEach(child => { 
 | 
            mapContainer.removeChild(child); 
 | 
            child.destroy({ children: true, texture: false, baseTexture: false }); 
 | 
        }) 
 | 
        childList.forEach((child, index) => { 
 | 
            childList[index] = null; 
 | 
        }); 
 | 
        childList = []; 
 | 
    } 
 | 
} 
 | 
  
 | 
export const showSelectedEffect = (sprite) => { 
 | 
    if (!sprite?.texture || !sprite?.texture?.valid) { 
 | 
        return; 
 | 
    } 
 | 
  
 | 
    const { width, height } = sprite; 
 | 
    const maxDimension = Math.max(width, height); 
 | 
    const radius = (maxDimension / 2) + (DEVICE_SELECTED_EFFECT_PADDING[sprite.data?.type] || 10); 
 | 
  
 | 
    let color; 
 | 
    const spriteEffectColor = DEVICE_SELECTED_EFFECT_COLOR[sprite.data?.type]; 
 | 
    if (spriteEffectColor) { 
 | 
        color = spriteEffectColor; 
 | 
    } else { 
 | 
        color = themeMode === 'light' ? '#747d8c' : '#718093'; 
 | 
    } 
 | 
  
 | 
    const alpha = 1; 
 | 
  
 | 
    effectCircle = new PIXI.Graphics(); 
 | 
    effectCircle.beginFill(color, alpha); 
 | 
    effectCircle.drawCircle(0, 0, radius); 
 | 
    effectCircle.endFill(); 
 | 
    effectCircle.position.set(sprite.x, sprite.y); 
 | 
    effectCircle.zIndex = -1; 
 | 
    effectCircle.blendMode = PIXI.BLEND_MODES.NORMAL; 
 | 
  
 | 
    mapContainer.addChild(effectCircle); 
 | 
  
 | 
    selectedSprite = sprite; 
 | 
  
 | 
    let pulseScale = 1; 
 | 
    let scalingUp = true; 
 | 
    effectTicker = (delta) => { 
 | 
        const pulseSpeed = 0.003 * delta; 
 | 
        if (scalingUp) { 
 | 
            pulseScale += pulseSpeed; 
 | 
            if (pulseScale >= 1.1) { 
 | 
                scalingUp = false; 
 | 
            } 
 | 
        } else { 
 | 
            pulseScale -= pulseSpeed; 
 | 
            if (pulseScale <= 1) { 
 | 
                scalingUp = true; 
 | 
            } 
 | 
        } 
 | 
        effectCircle.scale.set(pulseScale); 
 | 
        effectCircle.position.set(sprite.x, sprite.y); 
 | 
    }; 
 | 
  
 | 
    app.ticker.add(effectTicker); 
 | 
}; 
 | 
  
 | 
export const removeSelectedEffect = () => { 
 | 
    if (effectTicker) { 
 | 
        app.ticker.remove(effectTicker); 
 | 
        effectTicker = null; 
 | 
    } 
 | 
    if (effectCircle) { 
 | 
        mapContainer?.removeChild(effectCircle); 
 | 
        effectCircle = null; 
 | 
    } 
 | 
    selectedSprite = null; 
 | 
}; 
 | 
  
 | 
export const updateEffect = () => { 
 | 
    if (!selectedSprite || !effectCircle) { 
 | 
        return; 
 | 
    } 
 | 
    effectCircle.position.set(selectedSprite.x, selectedSprite.y); 
 | 
}; 
 | 
  
 | 
export const showRoutes = (curZone, setShowRoutes) => { 
 | 
    setShowRoutes(true); 
 | 
    getRouteList(curZone, (routeList) => { 
 | 
        routeList.forEach(route => { 
 | 
            const startPoint = querySprite(DEVICE_TYPE.POINT, route.startCodeStr); 
 | 
            const endPoint = querySprite(DEVICE_TYPE.POINT, route.endCodeStr); 
 | 
            const pointRoute = new PointRoute(POINT_ROUTE_DIRECTION[route.direction]); 
 | 
            pointRoute.setPoint(startPoint, endPoint); 
 | 
            pointRoute.clear(); 
 | 
            pointRoute.lineStyle(Math.max(7, 2 * (1 / mapContainer.scale.x)), themeMode === 'light' ? '#ced6e0' : '#535c68'); 
 | 
            pointRoute.moveTo(startPoint.position.x, startPoint.position.y); 
 | 
            pointRoute.lineTo(endPoint.position.x, endPoint.position.y); 
 | 
            pointRoute.alpha = 1; 
 | 
            mapContainer.addChild(pointRoute); 
 | 
        }) 
 | 
    }) 
 | 
}; 
 | 
  
 | 
export const hideRoutes = (curZone, setShowRoutes) => { 
 | 
    setShowRoutes(false); 
 | 
    for (let i = mapContainer.children.length - 1; i >= 0; i--) { 
 | 
        const child = mapContainer.children[i]; 
 | 
        if (child?.type === DEVICE_TYPE.ROUTE) { 
 | 
            mapContainer.removeChild(child); 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
export const multipleSelectEnhancer = (selectedSprites, setCurSprite, setBatchSprites) => { 
 | 
    selectedSprites = selectedSprites.filter(sprite => sprite.data?.type); 
 | 
  
 | 
    if (selectedSprites.length === 1) { 
 | 
        unMarkSprite(selectedSprites[0]); 
 | 
        setCurSprite(selectedSprites[0]); 
 | 
    } else { 
 | 
        setBatchSprites(selectedSprites); 
 | 
    } 
 | 
} 
 | 
  
 | 
export const rotationToNum = (rotation) => { 
 | 
    let res = rotation * 180 / Math.PI; 
 | 
    if (res < 0) { 
 | 
        res += 360; 
 | 
    } else if (res > 360) { 
 | 
        res -= 360; 
 | 
    } 
 | 
    return res; 
 | 
} 
 | 
  
 | 
export const rotationParseNum = (num) => { 
 | 
    return num * (Math.PI / 180); 
 | 
} 
 | 
  
 | 
const animateRotation = (sprite, targetRotation, rotationOffset = 0) => { 
 | 
    if (!mapContainer || !sprite) { 
 | 
        return; 
 | 
    } 
 | 
    // origin 
 | 
    let currentRotation = sprite.rotation + rotationOffset; 
 | 
    // target 
 | 
    targetRotation += rotationOffset; 
 | 
  
 | 
    // diff 
 | 
    let rotationDifference = targetRotation - currentRotation; 
 | 
    if (rotationDifference === 0) { 
 | 
        return; 
 | 
    } 
 | 
    rotationDifference = ((rotationDifference + Math.PI) % (2 * Math.PI)) - Math.PI; 
 | 
  
 | 
    // destination 
 | 
    const endRotation = sprite.rotation + rotationDifference; 
 | 
  
 | 
    new TWEEDLE.Tween(sprite) 
 | 
        .to({ rotation: endRotation }, ANIMATE_DURING_TIME) 
 | 
        .easing(TWEEDLE.Easing.Linear.None) 
 | 
        .onUpdate(() => { 
 | 
            // agv 
 | 
            if (sprite.updateTextRotation) { 
 | 
                sprite.updateTextRotation(); 
 | 
            } 
 | 
        }) 
 | 
        .start(); 
 | 
}; 
 | 
  
 | 
export const incrementSpriteNo = (str, incrementValue) => { 
 | 
    const match = str.match(/(\D*)(\d+)/); 
 | 
    if (match) { 
 | 
        const prefix = match[1]; 
 | 
        const numberPart = match[2]; 
 | 
        const newNumber = parseInt(numberPart, 10) + incrementValue; 
 | 
        const paddedNumber = newNumber.toString().padStart(numberPart.length, '0'); 
 | 
        return `${prefix}${paddedNumber}`; 
 | 
    } else { 
 | 
        return str; 
 | 
    } 
 | 
} 
 | 
  
 | 
export const generateID = () => { 
 | 
    return Date.now().toString(36) + Math.random().toString(36).substring(2); 
 | 
} 
 | 
  
 | 
export const deepCopy = (data) => { 
 | 
    return JSON.parse(JSON.stringify(data)); 
 | 
} 
 | 
  
 | 
export const patchRaLayout = (param) => { 
 | 
    const parentElement = document.getElementById('main-content'); 
 | 
    if (parentElement && parentElement.classList.contains('RaLayout-content')) { 
 | 
        parentElement.style.paddingLeft = param; 
 | 
        parentElement.style.paddingRight = param; 
 | 
    } 
 | 
} 
 | 
  
 | 
  
 | 
// service ------------------------ 
 | 
  
 | 
export const getAgvStatusMode = (backpack, battery) => { 
 | 
    if (backpack === 0) { 
 | 
        if (battery < 10) { 
 | 
            return AGV_STATUS_MODE.EMPTY_NO_BATTERY; 
 | 
        } 
 | 
        if (battery < 80) { 
 | 
            return AGV_STATUS_MODE.EMPTY_HALF_BATTERY; 
 | 
        } 
 | 
        return AGV_STATUS_MODE.EMPTY_FULL_BATTERY; 
 | 
    } else { 
 | 
        if (battery < 10) { 
 | 
            return AGV_STATUS_MODE.LOADED_NO_BATTERY; 
 | 
        } 
 | 
        if (battery < 80) { 
 | 
            return AGV_STATUS_MODE.LOADED_HALF_BATTERY; 
 | 
        } 
 | 
        return AGV_STATUS_MODE.LOADED_FULL_BATTERY; 
 | 
    } 
 | 
} 
 | 
  
 | 
export const generateAgvSpriteTexture = (agvStatusMode) => { 
 | 
    switch (agvStatusMode) { 
 | 
        case AGV_STATUS_MODE.EMPTY_NO_BATTERY: 
 | 
            return ctuEmptyNoBattery; 
 | 
        case AGV_STATUS_MODE.EMPTY_HALF_BATTERY: 
 | 
            return ctuEmptyHalfBattery; 
 | 
        case AGV_STATUS_MODE.EMPTY_FULL_BATTERY: 
 | 
            return ctuEmptyFullBattery; 
 | 
        case AGV_STATUS_MODE.LOADED_NO_BATTERY: 
 | 
            return ctuLoadedNoBattery; 
 | 
        case AGV_STATUS_MODE.LOADED_HALF_BATTERY: 
 | 
            return ctuLoadedHalfBattery; 
 | 
        case AGV_STATUS_MODE.LOADED_FULL_BATTERY: 
 | 
            return ctuLoadedFullBattery; 
 | 
        default: 
 | 
            return; 
 | 
    } 
 | 
} 
 | 
  
 | 
  
 | 
// dynamic graphic ---------------- 
 | 
  
 | 
export const generateDynamicGraphic = (curZone, data, setCurSprite) => { 
 | 
    for (const agvVo of data.agvVos) { 
 | 
        showAgvSprite(curZone, agvVo, setCurSprite); 
 | 
        drawerAgvPath(curZone, agvVo); 
 | 
    } 
 | 
} 
 | 
  
 | 
const agvRotationOffsetDegrees = 90; 
 | 
const agvRotationOffset = rotationParseNum(agvRotationOffsetDegrees); 
 | 
  
 | 
const showAgvSprite = (curZone, agvVo, setCurSprite) => { 
 | 
    const { agvNo, code, direction, battery, backpack } = agvVo; 
 | 
    if (!code) { return; } 
 | 
    const codeSprite = querySprite(DEVICE_TYPE.POINT, code); 
 | 
    if (!codeSprite) { return; } 
 | 
  
 | 
    const targetRotation = rotationParseNum(direction); 
 | 
    const backpackCount = backpack?.filter(item => item.loaded === true).length || 0; 
 | 
  
 | 
    let agvSprite = querySprite(DEVICE_TYPE.AGV, agvNo); 
 | 
    if (!agvSprite) { 
 | 
        const agvStatusMode = getAgvStatusMode(backpackCount, battery); 
 | 
        const agvTexture = generateAgvSpriteTexture(agvStatusMode); 
 | 
        agvSprite = generateSprite(DEVICE_TYPE.AGV, agvTexture); 
 | 
  
 | 
        initSprite(agvSprite, DEVICE_TYPE.AGV); 
 | 
  
 | 
        agvSprite.data.no = agvNo; 
 | 
        agvSprite.data.backpackCount = backpackCount; 
 | 
        agvSprite.data.battery = battery; 
 | 
  
 | 
        agvSprite.position.set(codeSprite.position.x, codeSprite.position.y); 
 | 
        agvSprite.rotation = targetRotation + agvRotationOffset; 
 | 
        mapContainer.addChild(agvSprite); 
 | 
  
 | 
        // agvNo sprite 
 | 
        const agvText = new PIXI.Text(agvNo.toString(), { 
 | 
            fontSize: 50, 
 | 
            fill: 0x000000, 
 | 
        }); 
 | 
        agvText.anchor.set(0.5, 0.5); 
 | 
        agvText.position.set(0, 5); 
 | 
        agvSprite.addChild(agvText); 
 | 
  
 | 
        agvSprite.updateTextRotation = () => { 
 | 
            if (agvText && agvSprite) { 
 | 
                agvText.rotation = -agvSprite.rotation; 
 | 
            } 
 | 
        }; 
 | 
        agvSprite.updateTextRotation(); 
 | 
  
 | 
    } else { 
 | 
        const prevBackpackCount = agvSprite.data.backpackCount; 
 | 
        const prevBattery = agvSprite.data.battery; 
 | 
  
 | 
        if (backpackCount !== prevBackpackCount || battery !== prevBattery) { 
 | 
            const agvStatusMode = getAgvStatusMode(backpackCount, battery); 
 | 
            const agvTexture = generateAgvSpriteTexture(agvStatusMode); 
 | 
            agvSprite.texture = PIXI.Texture.from(agvTexture, { resourceOptions: { scale: 1 } }); 
 | 
            // update backpackCount and battery 
 | 
            agvSprite.data.backpackCount = backpackCount; 
 | 
            agvSprite.data.battery = battery; 
 | 
        } 
 | 
  
 | 
        animateRotation(agvSprite, targetRotation + agvRotationOffset, agvRotationOffset); 
 | 
    } 
 | 
  
 | 
    beInsight(agvSprite, setCurSprite); 
 | 
  
 | 
    new TWEEDLE.Tween(agvSprite.position) 
 | 
        .to({ 
 | 
            x: codeSprite.position.x, 
 | 
            y: codeSprite.position.y 
 | 
        }, ANIMATE_DURING_TIME) 
 | 
        .easing(TWEEDLE.Easing.Linear.None) 
 | 
        .start(); 
 | 
} 
 | 
  
 | 
const drawerAgvPath = (curZone, agvVo) => { 
 | 
    if (!mapContainer) { 
 | 
        return; 
 | 
    } 
 | 
    const { agvNo, code: curCode, dynamicRoute } = agvVo; 
 | 
  
 | 
    const agvPathName = 'agvPath-' + agvNo; 
 | 
    let agvPath = mapContainer.getChildByName(agvPathName); 
 | 
    if (agvPath) { 
 | 
        mapContainer.removeChild(agvPath); 
 | 
    } 
 | 
    agvPath = new PIXI.Graphics(); 
 | 
    agvPath.name = agvPathName; 
 | 
    agvPath.lineStyle(Math.max(20, 4 * (1 / mapContainer?.scale.x || 1)), 0x2f68ac, 0.8); 
 | 
    agvPath.zIndex = DEVICE_Z_INDEX.DYNAMIC_ROUTE; 
 | 
    // agvPath.blendMode = PIXI.BLEND_MODES.NORMAL; 
 | 
  
 | 
    let firstNode = true; 
 | 
    for (let i = Math.max(0, dynamicRoute.indexOf(curCode)); i < dynamicRoute.length; i++) { 
 | 
        const node = dynamicRoute[i]; 
 | 
        const codeSprite = querySprite(DEVICE_TYPE.POINT, node); 
 | 
        if (!codeSprite) { continue }; 
 | 
        const { x, y } = codeSprite.position; 
 | 
        if (firstNode) { 
 | 
            agvPath.moveTo(x, y); 
 | 
            firstNode = false; 
 | 
        } else { 
 | 
            agvPath.lineTo(x, y); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    mapContainer.addChild(agvPath); 
 | 
} 
 |