How to Create a Custom SVG Path

Hi there,

I wonder how to create SVG paths in a way that they can serve as an entry to the PathMap.

In my research I stumbled upon a forum post that dealt with adding custom images to the renderer but I was missing some explanation on the exact format name of the d-path data.

When I have an SVG image in inkscape, how exactly do I export this as needed? How is this path-specification called?

Thanks in advance and best regards
Felix

1 Like

You may render arbitrary SVG elements in your custom Renderer. What exactly would you like to draw? And why do you think you need the PathMap?

Inkscape allows you to convert any element to a Compound Path via Path > { Object|Stroke } to Path. That path could be the representation of a rendered shape.

Thanks for the reply - especially for the inkscape hint.

I try to create custom elements that appear as tasks/events/gateways. They should have custom “sub”-icons like the bpmn:UserTask element which has the person icon rendered via the PathMap, right?

I’m currently working on enhancing the renderer to support this. I won’t alter the PathMap itself but I’ll mimic its behaviour in a custom, additional PathMap.

I now managed to get a path but it won’t show properly as rendered svg in a little jsFiddle example. I guess there is something I missed out on. Can you throw some buzzwords at me I can do research on? I just don’t know how everything I’m dealing with here is called therefore research is quite complicated…

Can you throw some buzzwords at me I can do research on?

Could you share a little bit of what you’re doing instead (as a GitHub project, JSFiddle, …)? That will help us (helping you !) tremendously :wink:.

I try to create elements that (in this case) visually extend existing bpmn-element-types. I’d like to have something like this:

example-task

This should be done by this code:


const BpmnRenderer = require('bpmn-js/lib/draw/BpmnRenderer');
const inherits = require('inherits');
const is = require('bpmn-js/lib/util/ModelUtil').is;

function Renderer(eventBus, styles, pathMap, canvas, priority) {
    BpmnRenderer.call(this, eventBus, styles, pathMap, canvas, priority);
}

inherits(Renderer, BpmnRenderer);

Renderer.$inject = [ 'eventBus', 'styles', 'pathMap', 'canvas' ];

module.exports = Renderer;

Renderer.prototype.drawShape = function(parentGfx, element) {

    if (element.stepClass && is(element, 'bpmn:Task')) { // set elsewhere; this will determine the icon used
        return this.drawCustomTask(parentGfx, element);
    } else {
        return BpmnRenderer.prototype.drawShape.call(this, parentGfx, element);
    }
};

Renderer.prototype.drawCustomTask = function(parentGfx, element) {
    var task = this.handlers['bpmn:Task'](parentGfx, element);

    // ... draw smiley-path here ...

    return task;
};

And in my understanding, in order to draw the smiley-icon, I’d need to have this smiley represented in a PathMap-manner.

I think I might now be able to phrase my problem more precisely - partially because I was able to solve sub-problems I had.

By the PathMap-entry TASK_TYPE_SEND I guessed that all task-paths are placed on a 100x100 sized layer (?). So I created my own path in inkscape visualizing a cooking-pot which resulted in this path:

m 25.364832,28.669868 c -1.03286,-0.115271 -1.951623,-0.935306 -1.964948,-1.98238 l -0.04158,-7.15195 -2.023919,-0.495021 2.023919,0.495021 v -3.507815 h -1.463373 1.803897 c 0.184201,-0.445579 0.519636,-0.819885 0.780062,-1.139128 0.570409,-0.57041 1.274259,-0.924279 2.107129,-1.059387 0.131752,-0.02137 0.301903,-0.02632 1.080776,-0.03143 l 0.924406,-0.0061 c 0.0037,-0.345463 0.139291,-0.687028 0.229878,-0.969091 0.353687,-0.70717 1.002084,-1.162868 1.774118,-1.246862 0.191055,-0.02079 1.768461,-0.02079 1.959516,0 0.772033,0.08399 1.420431,0.539692 1.774118,1.246862 0.171048,0.347671 0.174662,0.623753 0.229878,0.969091 l 0.924405,0.0061 c 0.778874,0.0051 0.949025,0.01006 1.080776,0.03143 0.832872,0.135108 1.53672,0.488977 2.10713,1.059387 0.357003,0.351583 0.559201,0.740121 0.780061,1.139128 h 1.803896 -1.463179 v 3.508719 L 41.815502,19.040528 39.791794,19.536442 39.75,26.6875 c -0.114301,1.050603 -0.932371,1.868673 -1.982975,1.982975 l -12.402179,-5.96e-4 z

(This path is by no means perfect, for example it’s lacking symmetry but I don’t care for now)

I’m wondering which coordinates of it should be replaced by {e.[xy][0-9]+} entries. The first m-statement must be replaced by by m {mx},{my} right? But how to proceed? How did you guys do it? I can’t imagine you doing this manually.

No one? :slightly_frowning_face:

I have no clue on how to proceed…

I think you only need to replace the coordinates of the first command m since all commands are lowercase and therefore the coordinates are relative .

1 Like

Thanks for the first hint. That sounds reasonable but the original pathmap does not stick to this. For example look at the EVENT_MESSAGE handler has entries for scale altough it’s using lowercase l. I think this is because altough the lines’ positioning might be relative, but their size will be absolute and thus must be scaled.

Have you tried just not giving any values in widthElements and heightElements and just using relative commands. Some of the paths do that and they do scale in the diagram.