diff --git a/docs/bpmn-support.adoc b/docs/bpmn-support.adoc index f9fd75e95a..154f3acee4 100644 --- a/docs/bpmn-support.adoc +++ b/docs/bpmn-support.adoc @@ -148,9 +148,9 @@ The default rendering uses `white` as fill color and `black` as stroke color. |Exclusive |icon:check-circle-o[] -| +|Icon may be subject to change + |Parallel -| -| +|icon:check-circle-o[] +|Icon may be subject to change + |=== diff --git a/src/component/mxgraph/ShapeConfigurator.ts b/src/component/mxgraph/ShapeConfigurator.ts index 8425a6675b..645313225d 100644 --- a/src/component/mxgraph/ShapeConfigurator.ts +++ b/src/component/mxgraph/ShapeConfigurator.ts @@ -17,7 +17,7 @@ import { MxGraphFactoryService } from '../../service/MxGraphFactoryService'; import { mxgraph } from 'ts-mxgraph'; import { ShapeBpmnElementKind } from '../../model/bpmn/shape/ShapeBpmnElementKind'; import { EndEventShape, StartEventShape, ThrowIntermediateEventShape } from './shape/event-shapes'; -import { ExclusiveGatewayShape } from './shape/gateway-shapes'; +import { ExclusiveGatewayShape, ParallelGatewayShape } from './shape/gateway-shapes'; import { ServiceTaskShape, TaskShape, UserTaskShape } from './shape/task-shapes'; export default class ShapeConfigurator { @@ -35,6 +35,7 @@ export default class ShapeConfigurator { this.mxCellRenderer.registerShape(ShapeBpmnElementKind.EVENT_START, StartEventShape); this.mxCellRenderer.registerShape(ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW, ThrowIntermediateEventShape); this.mxCellRenderer.registerShape(ShapeBpmnElementKind.GATEWAY_EXCLUSIVE, ExclusiveGatewayShape); + this.mxCellRenderer.registerShape(ShapeBpmnElementKind.GATEWAY_PARALLEL, ParallelGatewayShape); this.mxCellRenderer.registerShape(ShapeBpmnElementKind.TASK, TaskShape); this.mxCellRenderer.registerShape(ShapeBpmnElementKind.TASK_SERVICE, ServiceTaskShape); this.mxCellRenderer.registerShape(ShapeBpmnElementKind.TASK_USER, UserTaskShape); diff --git a/src/component/mxgraph/StyleConfigurator.ts b/src/component/mxgraph/StyleConfigurator.ts index ce62e74176..d00f9ac32a 100644 --- a/src/component/mxgraph/StyleConfigurator.ts +++ b/src/component/mxgraph/StyleConfigurator.ts @@ -44,7 +44,6 @@ export default class StyleConfigurator { this.configureTasksStyle(); // gateways this.configureGatewaysStyle(); - this.configureParallelGatewayStyle(); } private getStylesheet(): any { @@ -117,22 +116,6 @@ export default class StyleConfigurator { }); } - // TODO: to be removed as it will be configured in configureGatewaysStyle - // left just to not break current rendering - private configureParallelGatewayStyle(): void { - const style = this.cloneDefaultVertexStyle(); - style[this.mxConstants.STYLE_SHAPE] = this.mxConstants.SHAPE_RHOMBUS; - style[this.mxConstants.STYLE_PERIMETER] = this.mxPerimeter.RhombusPerimeter; - style[this.mxConstants.STYLE_VERTICAL_ALIGN] = 'top'; - style[this.mxConstants.STYLE_STROKECOLOR] = '#96A826'; - style[this.mxConstants.STYLE_STROKEWIDTH] = 1.7; - - style[this.mxConstants.STYLE_SPACING_TOP] = 55; - style[this.mxConstants.STYLE_SPACING_RIGHT] = 110; - style[this.mxConstants.STYLE_GRADIENTCOLOR] = '#E9ECB1'; - this.putCellStyle(ShapeBpmnElementKind.GATEWAY_PARALLEL, style); - } - private configureGatewaysStyle(): void { ShapeUtil.gatewayKinds().forEach(kind => { const style = this.cloneDefaultVertexStyle(); diff --git a/src/component/mxgraph/shape/gateway-shapes.ts b/src/component/mxgraph/shape/gateway-shapes.ts index 7fe4838cfd..80d0127468 100644 --- a/src/component/mxgraph/shape/gateway-shapes.ts +++ b/src/component/mxgraph/shape/gateway-shapes.ts @@ -17,6 +17,7 @@ import { MxGraphFactoryService } from '../../../service/MxGraphFactoryService'; import { mxgraph } from 'ts-mxgraph'; import { StyleConstant } from '../StyleConfigurator'; +import MxScaleFactorCanvas from '../extension/MxScaleFactorCanvas'; const mxRhombus: typeof mxgraph.mxRhombus = MxGraphFactoryService.getMxGraphProperty('mxRhombus'); const mxUtils: typeof mxgraph.mxUtils = MxGraphFactoryService.getMxGraphProperty('mxUtils'); @@ -27,8 +28,12 @@ abstract class GatewayShape extends mxRhombus { super(bounds, fill, stroke, strokewidth); } + protected abstract paintInnerShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void; + public paintVertexShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void { this.paintOuterShape(c, x, y, w, h); + c.setFillColor(this.stroke); + c.setStrokeWidth(0); this.paintInnerShape(c, x, y, w, h); } @@ -36,9 +41,37 @@ abstract class GatewayShape extends mxRhombus { super.paintVertexShape(c, x, y, w, h); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected paintInnerShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void { - // do nothing by default + // TODO: will be removed when exclusive gateway will use MXScaleFactorCanvas + protected getScaledGeometry(x: number, y: number, w: number, h: number): { xS: number; yS: number; wS: number; hS: number } { + const symbolScale = this.getInnerSymbolScale(w, h); + return { + xS: x + symbolScale, + yS: y + symbolScale, + wS: w - 2 * symbolScale, + hS: h - 2 * symbolScale, + }; + } + + // TODO: will be removed when exclusive gateway will use MXScaleFactorCanvas + private getInnerSymbolScale(w: number, h: number): number { + return 3 * mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5))); + } + + protected configureCanvasForIcon(c: mxgraph.mxXmlCanvas2D, parentWidth: number, parentHeight: number, iconOriginalSize: number): MxScaleFactorCanvas { + // ensure we are not impacted by the configured shape stroke width + c.setStrokeWidth(1); + + const parentSize = Math.min(parentWidth, parentHeight); + const ratioFromParent = 0.25; + const scaleFactor = iconOriginalSize !== 0 ? (parentSize / iconOriginalSize) * ratioFromParent : 0.5; + + return new MxScaleFactorCanvas(c, scaleFactor); + } + + protected translateToStartingIconPosition(c: mxgraph.mxXmlCanvas2D, parentX: number, parentY: number, parentWidth: number, parentHeight: number): void { + const xTranslation = parentX + parentWidth / 4; + const yTranslation = parentY + parentHeight / 4; + c.translate(xTranslation, yTranslation); } } @@ -52,29 +85,55 @@ export class ExclusiveGatewayShape extends GatewayShape { } private addExclusiveGatewaySymbol(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void { - const symbolScale = 3 * mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5))); - x += symbolScale; - y += symbolScale; - w -= 2 * symbolScale; - h -= 2 * symbolScale; - c.setFillColor(this.stroke); - c.setStrokeWidth(0); + const { xS, yS, wS, hS } = this.getScaledGeometry(x, y, w, h); c.begin(); - c.moveTo(x + w * 0.105, y); - c.lineTo(x + w * 0.5, y + h * 0.38); - c.lineTo(x + w * 0.895, y); - c.lineTo(x + w, y + h * 0.11); - c.lineTo(x + w * 0.6172, y + h * 0.5); - c.lineTo(x + w, y + h * 0.89); - c.lineTo(x + w * 0.895, y + h); - c.lineTo(x + w * 0.5, y + h * 0.62); - c.lineTo(x + w * 0.105, y + h); - c.lineTo(x, y + h * 0.89); - c.lineTo(x + w * 0.3808, y + h * 0.5); - c.lineTo(x, y + h * 0.11); + c.moveTo(xS + wS * 0.105, yS); + c.lineTo(xS + wS * 0.5, yS + hS * 0.38); + c.lineTo(xS + wS * 0.895, yS); + c.lineTo(xS + wS, yS + hS * 0.11); + c.lineTo(xS + wS * 0.6172, yS + hS * 0.5); + c.lineTo(xS + wS, yS + hS * 0.89); + c.lineTo(xS + wS * 0.895, yS + hS); + c.lineTo(xS + wS * 0.5, yS + hS * 0.62); + c.lineTo(xS + wS * 0.105, yS + hS); + c.lineTo(xS, yS + hS * 0.89); + c.lineTo(xS + wS * 0.3808, yS + hS * 0.5); + c.lineTo(xS, yS + hS * 0.11); c.close(); c.fillAndStroke(); } } + +export class ParallelGatewayShape extends GatewayShape { + public constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth: number = StyleConstant.STROKE_WIDTH_THIN) { + super(bounds, fill, stroke, strokewidth); + } + + protected paintInnerShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void { + this.addParallelGatewaySymbol(c, x, y, w, h); + } + + private addParallelGatewaySymbol(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void { + const canvas = this.configureCanvasForIcon(c, w, h, 0); + this.translateToStartingIconPosition(c, x, y, w, h); + + canvas.begin(); + canvas.moveTo(w * 0.38, 0); + canvas.lineTo(w * 0.62, 0); + canvas.lineTo(w * 0.62, h * 0.38); + canvas.lineTo(w, h * 0.38); + canvas.lineTo(w, h * 0.62); + canvas.lineTo(w * 0.62, h * 0.62); + canvas.lineTo(w * 0.62, h); + canvas.lineTo(w * 0.38, h); + canvas.lineTo(w * 0.38, h * 0.62); + canvas.lineTo(0, h * 0.62); + canvas.lineTo(0, h * 0.38); + canvas.lineTo(w * 0.38, h * 0.38); + canvas.close(); + + canvas.fillAndStroke(); + } +}