All files / src/elements/line PolylineElement.ts

78.43% Statements 80/102
47.36% Branches 9/19
44.44% Functions 4/9
78.43% Lines 80/102

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 1031x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 6x 6x 6x 6x 3x           3x 1x 1x 1x 1x 1x           1x 1x                         1x 1x 1x 1x 1x  
/*----------------------------------------------------------------------+
|    Title:	PolylineElement.ts                                          |
|    Part of the geomlib port of David E. Joyce's Geometry Applet.       |
|    TypeScript: 2026, Nelson Brown, brownnrl@gmail.com                  |
+----------------------------------------------------------------------*/
 
import {PointElement} from "../point/PointElement";
import {GeomElement} from "../GeomElement";
import {SlateCanvas} from "../../Slate";
 
// An open path (polyline): an ordered run of connected segments through
// the given points (A → B → C → …) that, unlike a polygon, does NOT close
// back to the first point. It is a one-dimensional route — the "bent line"
// of, e.g., Heron's minimum-distance problem (the path A→E→B) — drawn and
// highlightable as ONE element, so a prose ref to the whole route lights
// every segment together.
//
// Mirrors PolygonElement's edge trace (drawProgress-aware), but open and
// with no face. Its points are independent slate elements that move by
// their own handlers, so translate/rotate are no-ops.
export class PolylineElement extends GeomElement {
 
    public V : PointElement[];
 
    constructor(ps?: PointElement[]) {
        super();
        this.dimension = 1;
        this.V = ps == null ? [] : ps;
    }
 
    public defined() : boolean {
        if (this.V.length < 2) return false;
        for (let v of this.V) if (!v.defined()) return false;
        return true;
    }
 
    public drawEdge(c: SlateCanvas) : void {
        if (!this.visible && !this.shouldHighlight && this.emphasisAmount <= 0) return;
        const color = (this.emphasized || this.shouldHighlight)
            ? this.edgeHighlightColor
            : this.edgeColor;
        if (color == null || this.V.length < 2) return;
 
        let ctx = c.getContext("2d") as CanvasRenderingContext2D;
        ctx.beginPath();
        ctx.strokeStyle = color;
        {
            const baseW = this.shouldHighlight ? 3 : 1;
            ctx.lineWidth = (baseW + this.emphasisAmount * (6 - baseW)) * GeomElement.styleScale;
        }
        // Open path: V.length - 1 edges (no closing edge). Each integer
        // step of drawProgress · edgeCount advances one full segment; the
        // fractional remainder partially traces the next. drawProgress = 1
        // (default) traces the whole route.
        const n = this.V.length;
        const edgeCount = n - 1;
        const total = this.drawProgress * edgeCount;
        const fullEdges = Math.floor(total);
        const frac = total - fullEdges;
 
        ctx.moveTo(this.V[0].x, this.V[0].y);
        for (let i = 0; i < fullEdges; i++) {
            const to = this.V[i + 1];
            ctx.lineTo(to.x, to.y);
            ctx.stroke();
        }
        if (frac > 0 && fullEdges < edgeCount) {
            const from = this.V[fullEdges];
            const to = this.V[fullEdges + 1];
            ctx.lineTo(from.x + (to.x - from.x) * frac, from.y + (to.y - from.y) * frac);
            ctx.stroke();
        }
    }
 
    // Open route: no enclosed area to fill.
    public drawFace(c: SlateCanvas) : void {}
 
    public drawVertex(c: SlateCanvas) : void {
        if (!this.visible && !this.shouldHighlight && this.emphasisAmount <= 0) return;
        if (this.vertexColor != null && this.defined()) {
            for (let v of this.V) v.drawVertex(c, this.vertexColor);
        }
    }
 
    public drawName(c: SlateCanvas) : void {
        if (!this.visible && !this.shouldHighlight && this.emphasisAmount <= 0) return;
        if (this.nameColor != null && this.name != null && this.defined()) {
            let x = 0, y = 0;
            for (let v of this.V) { x += v.x; y += v.y; }
            const k = this.V.length;
            let ctx = c.getContext("2d") as CanvasRenderingContext2D;
            let [w, h] = this._getTextMetrics(ctx, this._name);
            ctx.beginPath();
            ctx.fillStyle = this.nameColor;
            ctx.fillText(this._name, x / k - w / 2, y / k + h / 4);
        }
    }
 
    public rotate(pivot: PointElement, ac: number, as: number) : void {}
    public translate(dx: number, dy: number) : void {}
    public update() : void {}
}