fix(resize): correctly compute cropping box after resize

closes #236
This commit is contained in:
Ricardo Matias 2015-04-17 16:40:45 +02:00 committed by Nico Rehwaldt
parent de648520d5
commit 6011de1c4a
6 changed files with 391 additions and 2 deletions

View File

@ -1501,6 +1501,110 @@ function BpmnRenderer(events, styles, pathMap) {
return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent');
}
/////// cropping path customizations /////////////////////////
function componentsToPath(elements) {
return elements.join(',').replace(/,?([A-z]),?/g, '$1');
}
function getCirclePath(shape) {
var cx = shape.x + shape.width / 2,
cy = shape.y + shape.height / 2,
radius = shape.width / 2;
var circlePath = [
['M', cx, cy],
['m', 0, -radius],
['a', radius, radius, 0, 1, 1, 0, 2 * radius],
['a', radius, radius, 0, 1, 1, 0, -2 * radius],
['z']
];
return componentsToPath(circlePath);
}
function getRoundRectPath(shape) {
var radius = TASK_BORDER_RADIUS,
x = shape.x,
y = shape.y,
width = shape.width,
height = shape.height;
var roundRectPath = [
['M', x + radius, y],
['l', width - radius * 2, 0],
['a', radius, radius, 0, 0, 1, radius, radius],
['l', 0, height - radius * 2],
['a', radius, radius, 0, 0, 1, -radius, radius],
['l', radius * 2 - width, 0],
['a', radius, radius, 0, 0, 1, -radius, -radius],
['l', 0, radius * 2 - height],
['a', radius, radius, 0, 0, 1, radius, -radius],
['z']
];
return componentsToPath(roundRectPath);
}
function getDiamondPath(shape) {
var width = shape.width,
height = shape.height,
x = shape.x,
y = shape.y,
halfWidth = width / 2,
halfHeight = height / 2;
var diamondPath = [
['M', x + halfWidth, y],
['l', halfWidth, halfHeight],
['l', -halfWidth, halfHeight],
['l', -halfWidth, -halfHeight],
['z']
];
return componentsToPath(diamondPath);
}
function getRectPath(shape) {
var x = shape.x,
y = shape.y,
width = shape.width,
height = shape.height;
var rectPath = [
['M', x, y],
['l', width, 0],
['l', 0, height],
['l', -width, 0],
['z']
];
return componentsToPath(rectPath);
}
function getShapePath(element) {
var obj = getSemantic(element);
if (obj.$instanceOf('bpmn:Event')) {
return getCirclePath(element);
}
if (obj.$instanceOf('bpmn:Activity')) {
return getRoundRectPath(element);
}
if (obj.$instanceOf('bpmn:Gateway')) {
return getDiamondPath(element);
}
return getRectPath(element);
}
// hook onto canvas init event to initialize
// connection start/end markers on svg
events.on('canvas.init', function(event) {
@ -1509,6 +1613,8 @@ function BpmnRenderer(events, styles, pathMap) {
this.drawShape = drawShape;
this.drawConnection = drawConnection;
this.getShapePath = getShapePath;
}
inherits(BpmnRenderer, DefaultRenderer);

84
test/fixtures/bpmn/simple-cropping.bpmn vendored Normal file
View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>SequenceFlow_1</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:exclusiveGateway id="ExclusiveGateway_1">
<bpmn:incoming>SequenceFlow_1</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_2</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="StartEvent_1" targetRef="ExclusiveGateway_1" />
<bpmn:subProcess id="SubProcess_1">
<bpmn:incoming>SequenceFlow_2</bpmn:incoming>
<bpmn:task id="Task_1">
<bpmn:incoming>SequenceFlow_3</bpmn:incoming>
</bpmn:task>
<bpmn:startEvent id="StartEvent_2">
<bpmn:outgoing>SequenceFlow_3</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="SequenceFlow_3" sourceRef="StartEvent_2" targetRef="Task_1" />
</bpmn:subProcess>
<bpmn:sequenceFlow id="SequenceFlow_2" sourceRef="ExclusiveGateway_1" targetRef="SubProcess_1" />
<bpmn:textAnnotation id="TextAnnotation_1" />
<bpmn:association id="Association_1" sourceRef="ExclusiveGateway_1" targetRef="TextAnnotation_1" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="229" y="325" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="202" y="361" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ExclusiveGateway_1_di" bpmnElement="ExclusiveGateway_1" isMarkerVisible="true">
<dc:Bounds x="393" y="318" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="373" y="368" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1_di" bpmnElement="SequenceFlow_1">
<di:waypoint xsi:type="dc:Point" x="265" y="343" />
<di:waypoint xsi:type="dc:Point" x="393" y="343" />
<bpmndi:BPMNLabel>
<dc:Bounds x="284" y="333" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
<dc:Bounds x="713" y="303" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SubProcess_1_di" bpmnElement="SubProcess_1" isExpanded="true">
<dc:Bounds x="574" y="243" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_2_di" bpmnElement="SequenceFlow_2">
<di:waypoint xsi:type="dc:Point" x="443" y="343" />
<di:waypoint xsi:type="dc:Point" x="574" y="343" />
<bpmndi:BPMNLabel>
<dc:Bounds x="463.5" y="333" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="StartEvent_2_di" bpmnElement="StartEvent_2">
<dc:Bounds x="613" y="325" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="586" y="361" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_3_di" bpmnElement="SequenceFlow_3">
<di:waypoint xsi:type="dc:Point" x="649" y="343" />
<di:waypoint xsi:type="dc:Point" x="681" y="343" />
<di:waypoint xsi:type="dc:Point" x="681" y="343" />
<di:waypoint xsi:type="dc:Point" x="713" y="343" />
<bpmndi:BPMNLabel>
<dc:Bounds x="636" y="327.5" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="TextAnnotation_1_di" bpmnElement="TextAnnotation_1">
<dc:Bounds x="368" y="156" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Association_1_di" bpmnElement="Association_1">
<di:waypoint xsi:type="dc:Point" x="418" y="318" />
<di:waypoint xsi:type="dc:Point" x="418" y="236" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>SequenceFlow_1</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:subProcess id="SubProcess_1">
<bpmn:incoming>SequenceFlow_1</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_2</bpmn:outgoing>
</bpmn:subProcess>
<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="StartEvent_1" targetRef="SubProcess_1" />
<bpmn:endEvent id="EndEvent_1">
<bpmn:incoming>SequenceFlow_2</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="SequenceFlow_2" sourceRef="SubProcess_1" targetRef="EndEvent_1" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="174" y="224" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="147" y="260" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SubProcess_1_di" bpmnElement="SubProcess_1" isExpanded="true">
<dc:Bounds x="339" y="142" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1_di" bpmnElement="SequenceFlow_1">
<di:waypoint xsi:type="dc:Point" x="210" y="242" />
<di:waypoint xsi:type="dc:Point" x="339" y="242" />
<bpmndi:BPMNLabel>
<dc:Bounds x="229.5" y="232" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
<dc:Bounds x="821" y="224" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="794" y="260" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_2_di" bpmnElement="SequenceFlow_2">
<di:waypoint xsi:type="dc:Point" x="689" y="242" />
<di:waypoint xsi:type="dc:Point" x="821" y="242" />
<bpmndi:BPMNLabel>
<dc:Bounds x="710" y="232" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -1,8 +1,12 @@
'use strict';
var TestHelper = require('../../TestHelper');
var Matchers = require('../../Matchers'),
TestHelper = require('../../TestHelper');
/* global bootstrapViewer */
var coreModule = require('../../../lib/core'),
rendererModule = require('../../../lib/draw');
/* global bootstrapViewer, bootstrapModeler, inject */
describe('draw - bpmn renderer', function() {
@ -146,3 +150,73 @@ describe('draw - bpmn renderer', function() {
});
});
describe('path - bpmn renderer', function () {
beforeEach(Matchers.addDeepEquals);
var diagramXML = require('../../fixtures/bpmn/simple-cropping.bpmn');
var testModules = [ coreModule, rendererModule ];
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
describe('circle', function () {
it('should return a circle path', inject(function(canvas, elementRegistry, renderer) {
// given
var eventElement = elementRegistry.get('StartEvent_1');
// when
var startPath = renderer.getShapePath(eventElement);
// then
expect(startPath).toEqual('M247,343m0,-18a18,18,0,1,1,0,36a18,18,0,1,1,0,-36z');
}));
it('should return a diamond path', inject(function(canvas, elementRegistry, renderer) {
// given
var gatewayElement = elementRegistry.get('ExclusiveGateway_1');
// when
var gatewayPath = renderer.getShapePath(gatewayElement);
// then
expect(gatewayPath).toEqual('M418,318l25,25l-25,25l-25,-25z');
}));
it('should return a rounded rectangular path', inject(function(canvas, elementRegistry, renderer) {
// given
var subProcessElement = elementRegistry.get('SubProcess_1');
// when
var subProcessPath = renderer.getShapePath(subProcessElement);
// then
expect(subProcessPath).toEqual('M584,243l330,0a10,10,0,0,1,10,10l0,180a10,10,0,0,1,-10,10' +
'l-330,0a10,10,0,0,1,-10,-10l0,-180a10,10,0,0,1,10,-10z');
}));
it('should return a rectangular path', inject(function(canvas, elementRegistry, renderer) {
// given
var TextAnnotationElement = elementRegistry.get('TextAnnotation_1');
// when
var TextAnnotationPath = renderer.getShapePath(TextAnnotationElement);
// then
expect(TextAnnotationPath).toEqual('M368,156l100,0l0,80l-100,0z');
}));
});
});

View File

@ -0,0 +1,73 @@
'use strict';
var Matchers = require('../../../Matchers'),
TestHelper = require('../../../TestHelper');
/* global bootstrapModeler, inject */
var modelingModule = require('../../../../lib/features/modeling'),
coreModule = require('../../../../lib/core');
describe('features/modeling - resize shape', function() {
beforeEach(Matchers.addDeepEquals);
var diagramXML = require('../../../fixtures/bpmn/simple-resizable.bpmn');
var testModules = [ coreModule, modelingModule ];
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
describe('shape', function() {
it('should resize', inject(function(elementRegistry, modeling) {
// given
var subProcessElement = elementRegistry.get('SubProcess_1');
var sequenceFlowElement = elementRegistry.get('SequenceFlow_2'),
sequenceFlow = sequenceFlowElement.businessObject;
// when
// Decreasing width by 100px
modeling.resizeShape(subProcessElement, { x: 339, y: 142, width: 250, height: 200 });
// then
// expect flow layout
expect(sequenceFlow.di.waypoint).toDeepEqual([
{ $type: 'dc:Point', x: 589, y: 242 },
{ $type: 'dc:Point', x: 821, y: 242 }
]);
}));
it('should move', inject(function(elementRegistry, modeling) {
// given
var subProcessElement = elementRegistry.get('SubProcess_1');
var sequenceFlowElement = elementRegistry.get('SequenceFlow_2'),
sequenceFlow = sequenceFlowElement.businessObject;
// when
modeling.moveShape(subProcessElement, { x: -50, y: 0 });
// then
// expect flow layout
expect(sequenceFlow.di.waypoint).toDeepEqual([
{ $type: 'dc:Point', x: 639, y: 242 },
{ $type: 'dc:Point', x: 821, y: 242 }
]);
}));
});
});

View File

@ -221,5 +221,7 @@ describe('features/modeling - create/remove space', function() {
expect(task.di.bounds.x).toBe(taskOldPos.x + 50);
expect(task.di.bounds.y).toBe(taskOldPos.y);
}));
});
});