control/drawing/LineStringDrawingScene.tstypescript
import { Entity } from "../../entity/Entity";
import { Vec2 } from "../../math/Vec2";
import { Vec3 } from "../../math/Vec3";
import { NUM_SEGMENTS, OUTLINE_ALT, PolygonDrawingScene, type IPolygonDrawingSceneParams } from "./PolygonDrawingScene";

class LineStringDrawingScene extends PolygonDrawingScene {
    constructor(props: IPolygonDrawingSceneParams) {
        super(props);
        // LineString has no closing edge, so polygon "base" outline entity is redundant here.
        this._outlineLayer.setEntities([]);
    }

    public override get geometryType(): string {
        return "LineString";
    }

    protected override _addNew(cart: Vec3) {
        this._appendCart(cart);
    }

    protected override _appendCart(cart: Vec3) {
        let corners = this._cornerLayer.getEntities();

        let segNum = corners.length - 1;
        let prevCorn = corners[segNum];

        let corner = new Entity({
            geoObject: this._cornerStyle
        });

        corner.setCartesian3v(cart);
        corner.addTo(this._cornerLayer);
        this._checkTerrainCollision(corner);

        if (prevCorn) {
            let prevCart = prevCorn.getCartesian();

            let vecPrev = corner.getCartesian().sub(prevCart);

            let distPrev = vecPrev.length();

            vecPrev.normalize();

            let prevPath = [];

            for (let i = 0; i <= NUM_SEGMENTS; i++) {
                let p = vecPrev.scaleTo((i * distPrev) / NUM_SEGMENTS).addA(prevCart);
                prevPath.push(p);
            }

            let entity = new Entity({
                polyline: {
                    path3v: [prevPath],
                    ...this._outlineStyle
                }
            });
            entity.polyline!.altitude = OUTLINE_ALT;
            this._outlineLayer.add(entity);

            let prevCenterCart = vecPrev.scaleTo(distPrev * 0.5).addA(prevCart);

            let center = new Entity({
                geoObject: this._centerStyle
            });
            center.setCartesian3v(prevCenterCart);
            center.addTo(this._centerLayer);
            this._checkTerrainCollision(center);
        }
    }

    protected override _clearGhostPointer() {
        this._ghostOutlineLayer.getEntities()[0].polyline!.clear();
    }

    protected _moveCorner(indexCurrent: number, indexPrev: number, indexCenter: number) {
        let corners = this._cornerLayer.getEntities();
        if (corners.length == 0) return;
        if (corners.length == 1) {
            indexCurrent = indexPrev = indexCenter = 0;
        }
        let cartCurr = corners[indexCurrent].getCartesian();
        let vecCurr = this._pickedCorner!.getCartesian().sub(cartCurr);
        let distCurr = vecCurr.length();
        vecCurr.normalize();

        let path = [];
        for (let i = 0; i <= NUM_SEGMENTS; i++) {
            let p = vecCurr.scaleTo((i * distCurr) / NUM_SEGMENTS).addA(cartCurr);
            path.push(p);
        }

        let entities = this._outlineLayer.getEntities();

        let prevPolyline = entities[indexPrev].polyline;

        prevPolyline?.setPath3v([path]);

        //
        // Move center points
        let centers = this._centerLayer.getEntities();
        let center = centers[indexCenter];

        if (center) {
            let centerCart = vecCurr.scaleTo(distCurr * 0.5).addA(cartCurr);
            center.setCartesian3v(centerCart);
            this._checkTerrainCollision(center);
        }
    }

    protected override _moveCornerPoint(e: Vec2) {
        let d = new Vec2(e.x, e.y).sub(this._startClick),
            p = this._startPos!.add(d);

        let groundCoords = this._planet!.getCartesianFromPixelTerrain(p);

        if (groundCoords) {
            this._pickedCorner!.setCartesian3v(groundCoords);

            let corners = this._cornerLayer.getEntities();

            if (corners.length) {
                let ind = this._pickedCorner!.layerIndex;

                if (ind === 0) {
                    this._moveCorner(ind + 1, ind, ind);
                } else if (ind === corners.length - 1) {
                    this._moveCorner(ind - 1, ind - 1, ind - 1);
                } else {
                    this._moveCorner(ind + 1, ind, ind);
                    this._moveCorner(ind - 1, ind - 1, ind - 1);
                }
            }
        }
    }

    public override clear() {
        this._geometryLayer.clear();

        let corners = this._cornerLayer.getEntities();
        for (let i = 0; i < corners.length; i++) {
            corners[i].remove();
        }

        let centers = this._centerLayer.getEntities();
        for (let i = 0; i < centers.length; i++) {
            centers[i].remove();
        }

        let entities = this._outlineLayer.getEntities();
        for (let i = 0; i < entities.length; i++) {
            entities[i].polyline!.clear();
            entities[i].remove();
        }

        this._clearGhostPointer();
    }

    public override _updateGhostOutlinePointer(groundPos: Vec3) {
        let corners = this._cornerLayer.getEntities();
        let size = corners.length;

        if (size > 0) {
            let ind = size - 1;

            this._insertCornerIndex = ind;

            let cartPrev = corners[ind].getCartesian();

            let vecPrev = this._ghostCorner.getCartesian().sub(cartPrev);

            let distPrev = vecPrev.length();

            vecPrev.normalize();

            let pathPrev = [];

            for (let i = 0; i <= NUM_SEGMENTS; i++) {
                let p = vecPrev.scaleTo((i * distPrev) / NUM_SEGMENTS).addA(cartPrev);
                pathPrev.push(p);
            }

            let entities = this._ghostOutlineLayer.getEntities();

            let prevPolyline = entities[0].polyline!;

            prevPolyline.setPath3v([pathPrev]);
        }
    }

    protected override _initGhostLayerPointer() {
        this._ghostOutlineLayer.setEntities([
            new Entity({
                polyline: {
                    path3v: [],
                    ...this._outlineStyle
                }
            }),
            this._ghostCorner
        ]);

        this._ghostOutlineLayer.getEntities()[0].polyline!.altitude = OUTLINE_ALT;
    }
}

export { LineStringDrawingScene };