typescript_dist_svg-scene.js

import Scene from "./scene";
/**
 * A scene that renders to a SVG element.
 */
export default class SVGScene extends Scene {
    /**
     * Creates a new SVGScene.
     * @param {number} width - The width of the SVG.
     * @param {number} height - The height of the SVG.
     */
    constructor(width, height) {
        super(width, height);
        this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        this.svg.setAttribute("width", width.toString());
        this.svg.setAttribute("height", height.toString());
        this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
    }
    /**
     * Renders the scene to the SVG. Preferably don't await to keep the animation smooth.
     * @returns {Promise<void>} - A promise that resolves when the scene has been rendered.
     * @async
     */
    async render() {
        this.svg.innerHTML = "";
        const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
        this.svg.appendChild(defs);
        for (const [index, object] of this.objects.entries()) {
            this.renderObject(object, [index], defs, this.svg);
        }
    }
    /**
     * Renders a VectorObject to the SVG. Internal use only.
     * @param {VectorObject} object - The object to render.
     * @param {number[]} index - The index of the object.
     * @param {SVGDefsElement} defs - The defs element.
     * @param {SVGElement} parent - The parent element.
     * @returns {void}
     * @private
     */
    renderObject(object, index, defs, parent) {
        const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
        g.setAttribute("id", `group-${index.join("-")}`);
        if (object.num_curves === 0) {
            const subG = document.createElementNS("http://www.w3.org/2000/svg", "g");
            g.appendChild(subG);
            for (const [child_index, child] of object.children.entries()) {
                this.renderObject(child, [...index, child_index], defs, subG);
            }
            parent.appendChild(g);
            return;
        }
        const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        if (object.name) {
            path.setAttribute("class", object.name);
        }
        path.setAttribute("id", `path-${index.join("-")}`);
        path.setAttribute("d", this.renderPathData(object));
        path.setAttribute("fill", this.renderFill(object, defs, index));
        path.setAttribute("fill-rule", object.fill_rule);
        path.setAttribute("stroke", this.renderStroke(object, defs, index));
        path.setAttribute("stroke-width", object.stroke_width.toString());
        path.setAttribute("stroke-linecap", object.stroke_line_cap);
        path.setAttribute("stroke-linejoin", object.stroke_line_join);
        path.setAttribute("stroke-miterlimit", object.stroke_miter_limit.toString());
        path.setAttribute("stroke-dasharray", object.stroke_dash_array.join(" "));
        path.setAttribute("stroke-dashoffset", object.stroke_dash_offset.toString());
        if (object.name) {
            path.setAttribute("class", object.name);
        }
        path.setAttribute("transform", `matrix(${object.transform.a} ${object.transform.b} ${object.transform.c} ${object.transform.d} ${object.transform.e} ${object.transform.f})`);
        g.appendChild(path);
        if (object.num_children > 0) {
            const subG = document.createElementNS("http://www.w3.org/2000/svg", "g");
            g.appendChild(subG);
            for (const [child_index, child] of object.children.entries()) {
                this.renderObject(child, [...index, child_index], defs, subG);
            }
        }
        parent.appendChild(g);
    }
    /**
     * Renders the path data of a VectorObject. Internal use only.
     * @param {VectorObject} object - The object to render.
     * @returns {string} - The path data.
     * @private
     */
    renderPathData(object) {
        let path_data = "";
        object.subpaths.forEach(subpath => {
            const start = subpath.get(0);
            const end = subpath.get(subpath.len - 1);
            path_data += ` M ${start.x} ${start.y}`;
            subpath.cubic_bezier_tuples.forEach(tuple => {
                path_data += ` C ${tuple.first_control.x} ${tuple.first_control.y} ${tuple.second_control.x} ${tuple.second_control.y} ${tuple.end_anchor.x} ${tuple.end_anchor.y}`;
            });
            if (start.equals(end)) {
                path_data += " Z";
            }
        });
        return path_data.trim();
    }
    /**
     * Renders the fill of a VectorObject. Internal use only.
     * @param {VectorObject} object - The object to render the fill of.
     * @param {SVGDefsElement} defs - The defs element.
     * @param {number[]} index - The index of the object.
     * @returns {string} - The fill.
     * @private
     */
    renderStyle(style, defs, index, prefix) {
        if (style.color) {
            return `rgba(${style.color.red}, ${style.color.green}, ${style.color.blue}, ${style.color.alpha})`;
        }
        if (style.linear_gradient) {
            const linear_gradient = style.linear_gradient;
            const linearGradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
            linearGradient.setAttribute("id", `${prefix}-linear-gradient-${index.join("-")}`);
            linearGradient.setAttribute("x1", linear_gradient.p1.x.toString());
            linearGradient.setAttribute("y1", linear_gradient.p1.y.toString());
            linearGradient.setAttribute("x2", linear_gradient.p2.x.toString());
            linearGradient.setAttribute("y2", linear_gradient.p2.y.toString());
            linear_gradient.color_stops.forEach(stop => {
                const stopElement = document.createElementNS("http://www.w3.org/2000/svg", "stop");
                stopElement.setAttribute("offset", stop.position.toString());
                stopElement.setAttribute("stop-color", `rgba(${stop.color.red}, ${stop.color.green}, ${stop.color.blue}, ${stop.color.alpha})`);
                linearGradient.appendChild(stopElement);
            });
            defs.appendChild(linearGradient);
            return `url(#${prefix}-linear-gradient-${index.join("-")})`;
        }
        if (style.radial_gradient) {
            const radial_gradient = style.radial_gradient;
            const radialGradient = document.createElementNS("http://www.w3.org/2000/svg", "radialGradient");
            radialGradient.setAttribute("id", `${prefix}-radial-gradient-${index.join("-")}`);
            radialGradient.setAttribute("cx", radial_gradient.c.x.toString());
            radialGradient.setAttribute("cy", radial_gradient.c.y.toString());
            radialGradient.setAttribute("r", radial_gradient.r.toString());
            radialGradient.setAttribute("fx", radial_gradient.f.x.toString());
            radialGradient.setAttribute("fy", radial_gradient.f.y.toString());
            radial_gradient.color_stops.forEach(stop => {
                const stopElement = document.createElementNS("http://www.w3.org/2000/svg", "stop");
                stopElement.setAttribute("offset", stop.position.toString());
                stopElement.setAttribute("stop-color", `rgba(${stop.color.red}, ${stop.color.green}, ${stop.color.blue}, ${stop.color.alpha})`);
                radialGradient.appendChild(stopElement);
            });
            defs.appendChild(radialGradient);
            return `url(#${prefix}-radial-gradient-${index.join("-")})`;
        }
        if (style.image) {
            const pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern");
            pattern.setAttribute("id", `${prefix}-pattern-${index.join("-")}`);
            pattern.setAttribute("x", "0");
            pattern.setAttribute("y", "0");
            pattern.setAttribute("width", "1");
            pattern.setAttribute("height", "1");
            const base64 = style.image.base64;
            const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
            image.setAttribute("x", "0");
            image.setAttribute("y", "0");
            image.setAttribute("width", style.image.width.toString());
            image.setAttribute("height", style.image.height.toString());
            image.setAttribute("href", `data:image/png;base64,${base64}`);
            pattern.appendChild(image);
            defs.appendChild(pattern);
            return `url(#${prefix}-pattern-${index.join("-")})`;
        }
        return "none";
    }
    /**
     * Renders the stroke of a VectorObject. Internal use only.
     * @param {VectorObject} object - The object to render the stroke of.
     * @param {SVGDefsElement} defs - The defs element.
     * @param {number[]} index - The index of the object.
     * @returns {string} - The stroke.
     * @private
     */
    renderFill(object, defs, index) {
        return this.renderStyle(object.fill, defs, index, "fill");
    }
    /**
     * Renders the stroke of a VectorObject. Internal use only.
     * @param {VectorObject} object - The object to render the stroke of.
     * @param {SVGDefsElement} defs - The defs element.
     * @param {number[]} index - The index of the object.
     * @returns {string} - The stroke.
     * @private
     */
    renderStroke(object, defs, index) {
        return this.renderStyle(object.stroke, defs, index, "stroke");
    }
}
//# sourceMappingURL=svg-scene.js.map