fix(grid-snapping): correctly update snapped waypoints

* make side-effect free
* use Modeling#updateWaypoints for actual update
This commit is contained in:
Nico Rehwaldt 2019-06-17 10:23:16 +02:00 committed by merge-me[bot]
parent d0ff81a6e7
commit 06cd481146
3 changed files with 87 additions and 36 deletions

View File

@ -4,6 +4,10 @@ import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import { pointsAligned } from 'diagram-js/lib/util/Geometry'; import { pointsAligned } from 'diagram-js/lib/util/Geometry';
import {
assign
} from 'min-dash';
var HIGH_PRIORITY = 3000; var HIGH_PRIORITY = 3000;
@ -30,11 +34,11 @@ export default function LayoutConnectionBehavior(eventBus, gridSnapping, modelin
return; return;
} }
if (hasMiddleSegments(waypoints)) { if (!hasMiddleSegments(waypoints)) {
modeling.updateProperties(connection, { return;
waypoints: self.snapMiddleSegments(waypoints)
});
} }
modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
}); });
} }
@ -54,34 +58,23 @@ inherits(LayoutConnectionBehavior, CommandInterceptor);
* @returns {Array<Point>} * @returns {Array<Point>}
*/ */
LayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) { LayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) {
var gridSnapping = this._gridSnapping; var gridSnapping = this._gridSnapping,
snapped;
var middleSegments = getMiddleSegments(waypoints); waypoints = waypoints.slice();
middleSegments.forEach(function(middleSegment) { for (var i = 1; i < waypoints.length - 2; i++) {
var segmentStart = middleSegment.start,
segmentEnd = middleSegment.end;
var aligned = pointsAligned(segmentStart, segmentEnd); snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);
if (horizontallyAligned(aligned)) { waypoints[i] = snapped[0];
waypoints[i + 1] = snapped[1];
// snap horizontally }
segmentStart.y = segmentEnd.y = gridSnapping.snapValue(segmentStart.y);
}
if (verticallyAligned(aligned)) {
// snap vertically
segmentStart.x = segmentEnd.x = gridSnapping.snapValue(segmentStart.x);
}
});
return waypoints; return waypoints;
}; };
// helpers ////////// // helpers //////////
/** /**
@ -124,15 +117,28 @@ function verticallyAligned(aligned) {
* *
* @returns {Array} * @returns {Array}
*/ */
function getMiddleSegments(waypoints) { function snapSegment(gridSnapping, segmentStart, segmentEnd) {
var middleSegments = [];
for (var i = 1; i < waypoints.length - 2; i++) { var aligned = pointsAligned(segmentStart, segmentEnd);
middleSegments.push({
start: waypoints[i], var snapped = {};
end: waypoints[i + 1]
}); if (horizontallyAligned(aligned)) {
// snap horizontally
snapped.y = gridSnapping.snapValue(segmentStart.y);
} }
return middleSegments; if (verticallyAligned(aligned)) {
// snap vertically
snapped.x = gridSnapping.snapValue(segmentStart.x);
}
if ('x' in snapped || 'y' in snapped) {
segmentStart = assign({}, segmentStart, snapped);
segmentEnd = assign({}, segmentEnd, snapped);
}
return [ segmentStart, segmentEnd ];
} }

View File

@ -1,11 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?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:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0"> <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="4.0.0-beta.1">
<bpmn:process id="Process_1" isExecutable="true"> <bpmn:process id="Process_1" isExecutable="true">
<bpmn:task id="Task_1" /> <bpmn:task id="Task_1" />
<bpmn:task id="Task_2" /> <bpmn:task id="Task_2">
<bpmn:outgoing>SequenceFlow_1</bpmn:outgoing>
</bpmn:task>
<bpmn:task id="Task_3" /> <bpmn:task id="Task_3" />
<bpmn:task id="Task_4" /> <bpmn:task id="Task_4" />
<bpmn:boundaryEvent id="BoundaryEvent_1" attachedToRef="Task_3" /> <bpmn:boundaryEvent id="BoundaryEvent_1" attachedToRef="Task_3" />
<bpmn:task id="Task_5">
<bpmn:incoming>SequenceFlow_1</bpmn:incoming>
</bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="Task_2" targetRef="Task_5" />
</bpmn:process> </bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
@ -24,6 +30,15 @@
<bpmndi:BPMNShape id="BoundaryEvent_1_di" bpmnElement="BoundaryEvent_1"> <bpmndi:BPMNShape id="BoundaryEvent_1_di" bpmnElement="BoundaryEvent_1">
<dc:Bounds x="132" y="462" width="36" height="36" /> <dc:Bounds x="132" y="462" width="36" height="36" />
</bpmndi:BPMNShape> </bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_5_di" bpmnElement="Task_5">
<dc:Bounds x="546" y="270" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1_di" bpmnElement="SequenceFlow_1">
<di:waypoint x="456" y="242" />
<di:waypoint x="526" y="242" />
<di:waypoint x="526" y="310" />
<di:waypoint x="546" y="310" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane> </bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram> </bpmndi:BPMNDiagram>
</bpmn:definitions> </bpmn:definitions>

View File

@ -76,7 +76,7 @@ describe('features/grid-snapping - layout connection', function() {
})); }));
it('should NOT sap on reconnect start', inject(function(modeling) { it('should NOT snap on reconnect start', inject(function(modeling) {
// when // when
modeling.moveElements([ task1 ], { x: 50, y: 50 }); modeling.moveElements([ task1 ], { x: 50, y: 50 });
@ -87,7 +87,7 @@ describe('features/grid-snapping - layout connection', function() {
})); }));
it('should NOT sap on reconnect end', inject(function(modeling) { it('should NOT snap on reconnect end', inject(function(modeling) {
// when // when
modeling.moveElements([ task2 ], { x: -50, y: -50 }); modeling.moveElements([ task2 ], { x: -50, y: -50 });
@ -97,6 +97,36 @@ describe('features/grid-snapping - layout connection', function() {
expect(connection.waypoints[2]).to.eql({ x: 225, y: 190 }); expect(connection.waypoints[2]).to.eql({ x: 225, y: 190 });
})); }));
it('should snap', inject(function(modeling, elementRegistry) {
// given
var flow = elementRegistry.get('SequenceFlow_1');
// when
modeling.layoutConnection(flow);
// then
expect(flow.waypoints[1]).to.eql({ x: 530, y: 242 });
expect(flow.waypoints[2]).to.eql({ x: 530, y: 310 });
}));
it('should UNDO snap', inject(function(modeling, commandStack, elementRegistry) {
// given
var flow = elementRegistry.get('SequenceFlow_1');
modeling.layoutConnection(flow);
// when
commandStack.undo();
// then
expect(flow.waypoints[1]).to.eql({ x: 526, y: 242 });
expect(flow.waypoints[2]).to.eql({ x: 526, y: 310 });
}));
}); });
}); });