Hey,
I have successfully implemented a new custom shape into the modeler. My solution is similar to the custom shapes example but it’s different in some point. For example my custom shape is serialized into XML as a regular bpmn2:callActivity like this:
<bpmn2:callActivity id=“CallActivity_00lzzem” c365ns:custom=“callActivity”>
So it has a custom attribute. This way I can differentiate between the regular Call Activity and my custom one. The shape itself is rendered by a custom renderer and I also created custom element factory, context pad provider, palette and rules for it. However I judged I will not need a custom updater. This is because its not really a new shape, its still a Call Activity and the default update is just fine for me. The custom renderer also contain only the part responsible to draw the custom shape and path and doesn’t contain the part which is responsible for the red connection like in the custom shape example.
This is because the default connection would be okay for me. But here is my problem. This default connection(its path) is calculated wrongly. Not always but most of the time. So for example when I create a connection to my custom shape first time, its okay. Then I move my shape and its okay again, then I move again and the end arrow is now not properly pointing to the border of my custom shape. See attached picture.
I see no browser console log error during this.
I think I am missing a function somewhere or missing something totally in the concept. I don’t know. I copy here my custom renderer. Tell me if you need more source code.
'use strict';
var inherits = require('inherits');
var BpmnRenderer = require('bpmn-js/lib/draw/BpmnRenderer');
var TextUtil = require('diagram-js/lib/util/Text');
var is = require('bpmn-js/lib/util/ModelUtil').is;
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var componentsToPath = require('diagram-js/lib/util/RenderUtil').componentsToPath;
var svgAppend = require('tiny-svg/lib/append'),
svgAttr = require('tiny-svg/lib/attr'),
svgCreate = require('tiny-svg/lib/create'),
svgClasses = require('tiny-svg/lib/classes');
var LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: 12
};
/**
* A renderer that knows how to render custom elements.
*/
function CustomRenderer(eventBus, styles, pathMap, canvas) {
BpmnRenderer.call(this, eventBus, styles, pathMap, canvas, 2000);
var textUtil = new TextUtil({
style: LABEL_STYLE,
size: { width: 100 }
});
var computeStyle = styles.computeStyle;
function getSemantic(element) {
return element.businessObject;
}
function renderLabel(p, label, options) {
var text = textUtil.createText(label || '', options);
svgClasses(text).add('djs-label');
svgAppend(p, text);
return text;
}
function renderEmbeddedLabel(p, element, align) {
var semantic = getSemantic(element);
return renderLabel(p, semantic.name, {
box: element,
align: align,
padding: 5,
style: {
fill: '#000000'
}
});
}
this.drawCustomShape = function(p, element, fillColor) {
var width = element.width,
height = element.height,
indent = width / 5,
points,
attrs;
points = [ 0, 0, width - indent, 0, width, height / 2, width - indent, height, 0, height, indent, height / 2 ];
attrs = computeStyle(attrs, {
stroke: '#000000',
strokeWidth: 2,
fill: fillColor
});
var polygon = svgCreate('polygon');
svgAttr(polygon, {
points: points
});
svgAttr(polygon, attrs);
svgAppend(p, polygon);
renderEmbeddedLabel(p, element, 'center-middle');
return polygon;
};
this.getCustomShapePath = function(element) {
var x = element.x,
y = element.y,
width = element.width,
height = element.height,
indent = width / 5;
var callActivityPath = [
['M', x, y],
['l', width - indent, y],
['l', indent, height / 2 ],
['l', -indent, height / 2],
['l', -width, 0 ],
['l', indent, - height / 2 ],
['z']
];
return componentsToPath(callActivityPath);
};
}
inherits(CustomRenderer, BpmnRenderer);
module.exports = CustomRenderer;
CustomRenderer.$inject = [ 'eventBus', 'styles', 'pathMap', 'canvas' ];
CustomRenderer.prototype.canRender = function(element) {
var bo = getBusinessObject(element);
return (is(element, 'bpmn:CallActivity') || is(element, 'bpmn:SubProcess')) && bo && typeof bo.get('c365ns:custom') != 'undefined';
};
CustomRenderer.prototype.drawShape = function(p, element) {
var bo, custom;
bo = getBusinessObject(element);
custom = bo.get('custom');
if (custom == "callActivity") {
return this.drawCustomShape(p, element, '#ffff00');
} else if (custom == "subProcess") {
return this.drawCustomShape(p, element, '#00aeff');
}
};
CustomRenderer.prototype.getShapePath = function(shape) {
var bo, custom;
bo = getBusinessObject(shape);
custom = bo.get('custom');
if (custom == "callActivity") {
return this.getCustomShapePath(shape);
} else if (custom == "subProcess") {
return this.getCustomShapePath(shape);
}
};