p5.js-svg icon indicating copy to clipboard operation
p5.js-svg copied to clipboard

Any way to create group inside svg

Open psicomante opened this issue 1 year ago • 6 comments

Hello everybody,

I would like to easily add a SVG Group during drawing, I found the following method:

let g = P5.SVGElement.create("g");
rootGroup.appendChild(g.elt);
ctx.__currentElement = g.elt;

where P5 is the global P5 instance or the P5 imported using Typescript or modules:

import P5 from "p5";

Any better way is available? Thanks

psicomante avatar May 26 '23 13:05 psicomante

@psicomante May I ask if there is any specific reason why this must be done? Typically, the details inside an SVG are managed by the svgcanvas library. Manually modifying them could potentially create issues.

zenozeng avatar Jun 03 '23 11:06 zenozeng

@zenozeng personally I would love to be able to create groups (and specify their id) since that is the way to set a rendering order and visibility when sending plots to my axidraw.

bkuri avatar Jun 05 '23 23:06 bkuri

@zenozeng I share the goal with @bkuri, I need to plot them separately, using different pens; groups is the best method to create this kind of separation inside svg.

psicomante avatar Jun 06 '23 07:06 psicomante

@zenozeng i know this approach could lead to severe issues, but if I use a single group my plot ideas cannot be plotted in any way.

maybe I could create a new method exposing this approach and write some tests? Do you know any specific issues this approach is creating?

thanks

psicomante avatar Jun 06 '23 07:06 psicomante

@psicomante @bkuri I now understand your scenario and there may be a new API in future versions to specify the group. Currently, there is no particularly good and safe way to specify where the next drawing should occur within a group, and it may also require modifications to the implementation details of svgcanvas.

zenozeng avatar Jun 11 '23 08:06 zenozeng

@bkuri @psicomante I share your goal, but solved it a different (maybe bit cumbersome) way: I keep an array of Graphics objects as a sort of layers, and assign parts of my drawing to different layers. When I save my file, I save every Graphics layer separately, and clear the canvas in between. This way I can plot each layer as a separate svg (or as different layers in Inkscape).

I wrote a LayerManager class that does this stuff:


class LayerManager{
    constructor(num_layers) {
        this.N = num_layers;
        this.layers = this.create();
        return new Proxy(this, {
            get(target, prop, receiver) {
                if (!isNaN(prop) && prop in target.layers) {
                    return target.layers[prop];
                }
                return Reflect.get(...arguments);
            }
        });
    }

    create(num_layers = this.N) {
        let layers = [];
        for (let l=0; l<num_layers; l++){
            layers.push(createGraphics(width, height, SVG));
        }
        return layers;
    }

    applyToLayers(fn, layers = this.layers) {
        for (let l of layers) {
            fn(l);
        }
    }

    nofill(layers = this.layers) {
        this.applyToLayers(layer => layer.noFill(), layers);
    }

    paint(layers = this.layers) {
        this.applyToLayers(layer => image(layer, 0, 0), layers);
    }

    clear(layers = this.layers) {
        this.applyToLayers(layer => layer.clear(), layers);
    }

    save(fname, layers = this.layers) {
        for (let [index, layer] of layers.entries()) {
            clear();
            image(layer, 0, 0);
            let filename = `${fname}_${index}.svg`;
            save(filename);
        }
    }
}

I use th layer manager in a sketch like this:

let lm

function setup() {
    createCanvas(200, 300, SVG);
    noLoop();

    lm = new LayerManager(3);
    lm.nofill();
    lm[0].stroke(255, 0, 0);
    lm[1].stroke(0, 255, 0);
    lm[2].stroke(0, 0, 255);
}

function draw() {
    background(255)
    lm[0].rect(width/4, height/4, 25, 25);
    lm[1].rect(width/2, height/2, 35, 25);
    lm[2].rect(width/2, height/4, 25, 45);

    lm.paint()
}

function keyPressed() {
    if (key == "s") {
      lm.save('test_svgs')
    }
  }

Let me know if this works for you.

jakkopieterdejong avatar Aug 09 '23 08:08 jakkopieterdejong