path.js

import { Vector } from "./vector.js"
import { Box } from "./box.js"
import { Matrix } from "./matrix.js"
import { clipFilter } from "./filter.js"

/**
 *  a path consists of at least two vectors creating a line
 */
export class Path {//
    constructor(verts) {
        this.type = "path"
        this.verts = verts
   


    }
    boundingBox() {
        let box = new Box(this.verts[0], this.verts[1])
        for (let vert in this.verts) {
            box = box.extend(new Box(vert, vert))
        }
    }
    transform(matrix) {
        let p = this
        let result = []
        for (let v of p.verts) {
            v = matrix.mulPosition(v)

            result.push(v)


        }
        return new Path(result)
    }
    chop(step) {
        let result = []
        let p = this.verts


        if (this.type == Paths) { this.chop() }
        for (let i = 0; i < this.verts.length - 1; i++) {

            let a = p[i]
            let b = p[i + 1]

            let v = b.sub(a)
            let l = v.length()
            if (i == 0) {
                result.push(a)
            }
            let d = step
            for (; d < l; d += step) {
                result.push(a.add(v.mulScalar(d / l)))
            }
            result.push(b)

        }
        return new Path(result)

    }
    filter(matrix, eye, step) {
        let result = []
        let path = []
        let p = this.verts
        for (let v of p) {

            let clipV = clipFilter(matrix, eye, step, v)
            let ok = clipV.hit
            // ok = true // show hidden lines

            if (ok) {
                path.push(clipV.w)
            }
            else {
                if (path.length > 1) {
                    result.push(new Path(path))
                }
                path = []
            }

        }
        if (path.length > 1) {
            result.push(new Path(path))


        }


        return new Paths(result)

        // else {return false}

    }
    simplify(threshold) {
        let p = this.verts
        if (p.length < 3) {
            return p
        }
        let a = p[0]
        let b = p[p.length - 1]
        let index = -1
        let distance = 0.0
        // console.log(a,b)

        for (let i = 1; i < p.length - 1; i++) {
        // console.log(a,b,p[i])

            let d = p[i].segmentDistance(a, b)
            if (d > distance) {
                index = i
                distance = d
            }
        }

        if (distance > threshold) {
            // console.log(a,b,p,index)

            let r1 = new Path(p.slice(0, index + 1)).simplify(threshold)
            
            let r2 = new Path(p.slice(index)).simplify(threshold)
            // have to convert back to array to use slice.
            if(r1 instanceof Path){
                r1 = r1.verts
            }
            if(r2 instanceof Path){
                r2 = r2.verts
            }
            return new Path(r1.slice(0, r1.length - 1).concat(r2))
        }
        else {
            return new Path([a, b])
        }

    }
    toSVG(path = this) {

        let coords = ''
        for (let v of this.verts) {

            coords += `${v.x},${v.y} `
        }
       
        const points = coords

        return `<polyline stroke="black" fill="none" points="${points}" />`;
    }
}

/**
 * Paths are a collections of multiple{@link Path} objects
 */
export class Paths {
    constructor(paths = []) {
        this.paths = paths
        this.type = "paths"

    }

    boundingBox() {
        let box = this.paths[0].boundingBox()
        for (let path of this.paths) {
            box = box.extend(path.boundingBox())
        }
        return box
    }
    transform(matrix) {
        let result = []
        for (let path of this.paths) {
           
            result.push(path.transform(matrix))
        }

        return new Paths(result)

    }
    chop(step) {
        let result = []
        for (let path of this.paths) {


            result.push(path.chop(step))
        }
        return new Paths(result)
    }
    filter(matrix, eye, step) {
        let result = []
        for (let path of this.paths) {
            path = path.filter(matrix, eye, step)
            
                result.push(...path.paths)//unpack values here or else we've got Paths inside of Path or some bs 



          
        }
      
        return new Paths(result)

    }
    simplify(threshold) {
        let result = []
        for (let path of this.paths) {
            path = path.simplify(threshold)
            if (!(path instanceof Path)){/// not really a good reason we have to do this that ive found, works sometimes without it but not others
                path = new Path(path)
            }
            result.push(path)/// might need to unpack values here? idk we'll see. <<< i did indeed need to unpack the values
        }
        return new Paths(result)
    }
    /**
     * 
     * @param {Paths} paths 
     * @param {Number} width of SVG File 
     * @param {Number} height of SVG File 
     */
    pathsToSVG(paths = this.paths, width, height) {
        const lines = [];

        // Open the SVG tag
        lines.push(`<svg width="${width}" height="${height}" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">`);

        // Add the coordinate system transformation (flips Y-axis to be bottom-up)
        lines.push(`  <g transform="translate(0,${height}) scale(1,-1)">`);
        // console.log(paths.paths)
        // Iterate through each individual path
        for (const path of paths.paths) {
            // We reuse the toSVG function from the previous step
            if (!(path instanceof Path)) {
                console.log(path)

                path.pathsToSVG()
            }
            // console.log(path)
            lines.push(`${path.toSVG()}`);
        }

        // Close tags
        lines.push("</g>");
        lines.push("</svg>");

        this.saveSVGToFile(lines.join("\n"))
    }
    saveSVGToFile(svgString, fileName = "output.svg") {
        // 1. Create a Blob from the SVG string
        const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });

        // 2. Create an anchor element
        const link = document.createElement("a");

        // 3. Create a URL for the blob and set it as the download target
        link.href = URL.createObjectURL(blob);
        link.download = fileName;

        // 4. Trigger the download
        document.body.appendChild(link);
        link.click();

        // 5. Cleanup
        document.body.removeChild(link);
        URL.revokeObjectURL(link.href);
    }

}