Skip to content

Commit

Permalink
feat(grid-snapping): add grid snapping
Browse files Browse the repository at this point in the history
Related to #973
  • Loading branch information
philippfromme authored and merge-me[bot] committed Apr 12, 2019
1 parent 95bef2f commit f987baf
Show file tree
Hide file tree
Showing 4 changed files with 1,029 additions and 0 deletions.
110 changes: 110 additions & 0 deletions lib/features/grid-snapping/Grid.js
@@ -0,0 +1,110 @@
import {
append as svgAppend,
attr as svgAttr,
clear as svgClear,
create as svgCreate
} from 'tiny-svg';

import { query as domQuery } from 'min-dom';

var SPACING = 10,
GRID_COLOR = '#ccc',
LAYER_NAME = 'djs-grid';

var GRID_DIMENSIONS = {
width : 100000,
height: 100000
};


export default function Grid(canvas, config, eventBus) {
this._canvas = canvas;

this.hasGrid = false;

if (config) {
this.visible = config.visible === false ? false : true;
} else {
this.visible = true;
}

var self = this;

eventBus.on('diagram.init', function() {
self._init();

self.setVisible(self.visible);
});
}

Grid.prototype._init = function() {
var defs = domQuery('defs', this._canvas._svg);

if (!defs) {
defs = svgCreate('defs');

svgAppend(this._canvas._svg, defs);
}

var pattern = this.pattern = svgCreate('pattern');

svgAttr(pattern, {
id: 'djs-grid-pattern',
width: SPACING,
height: SPACING,
patternUnits: 'userSpaceOnUse'
});

var circle = this.circle = svgCreate('circle');

svgAttr(circle, {
cx: 0.5,
cy: 0.5,
r: 0.5,
fill: GRID_COLOR
});

svgAppend(pattern, circle);

svgAppend(defs, pattern);

var grid = this.grid = svgCreate('rect');

svgAttr(grid, {
x: -(GRID_DIMENSIONS.width / 2),
y: -(GRID_DIMENSIONS.height / 2),
width: GRID_DIMENSIONS.width,
height: GRID_DIMENSIONS.height,
fill: 'url(#djs-grid-pattern)'
});
};

Grid.prototype.isVisible = function() {
return this.visible;
};

Grid.prototype.setVisible = function(visible) {
this.visible = visible;

var parent = this._getParent();

if (visible) {
svgAppend(parent, this.grid);
} else {
svgClear(parent);
}
};

Grid.prototype.toggleVisible = function() {
this.setVisible(!this.visible);
};

Grid.prototype._getParent = function() {
return this._canvas.getLayer(LAYER_NAME, -2);
};

Grid.$inject = [
'canvas',
'config.grid',
'eventBus'
];
205 changes: 205 additions & 0 deletions lib/features/grid-snapping/GridSnapping.js
@@ -0,0 +1,205 @@
import {
setSnapped,
isSnapped
} from '../snapping/SnapUtil';

import { isCmd } from '../keyboard/KeyboardUtil';

import { isNumber } from 'min-dash';

var SPACING = 10,
LOWER_PRIORITY = 1200;


export default function GridSnapping(
eventBus,
config,
grid
) {
this._grid = grid;

if (config) {
this.active = config.active === false ? false : true;
} else {
this.active = true;
}

var self = this;

eventBus.on('diagram.init', LOWER_PRIORITY, function() {
self.setActive(self.active);

if (!self.active) {
grid.setVisible(false);
}
});

eventBus.on([
'shape.move.move',
'shape.move.end',
'create.move',
'create.end',
'connect.move',
'connect.end',
'resize.move',
'resize.end',
'bendpoint.move.move',
'bendpoint.move.end',
'connectionSegment.move.move',
'connectionSegment.move.end'
], LOWER_PRIORITY, function(event) {
var originalEvent = event.originalEvent;

if (!self.active || (originalEvent && isCmd(originalEvent))) {
return;
}

[ 'x', 'y' ].forEach(function(axis) {
if (!isSnapped(event, axis)) {
self.snap(event, axis);
}
});
});
}

GridSnapping.prototype.snap = function(event, axis) {
var snapConstraints = getSnapConstraints(event, axis);

var snappedValue = this._getSnappedValue(event[ axis ], snapConstraints);

setSnapped(event, axis, snappedValue);
};

GridSnapping.prototype._getSnappedValue = function(value, snapConstraints) {
value = quantize(value, SPACING);

var min, max;

if (snapConstraints) {
min = snapConstraints.min;
max = snapConstraints.max;

if (isNumber(min)) {
min = quantize(min, SPACING, 'ceil');

value = Math.max(value, min);
}

if (isNumber(max)) {
max = quantize(max, SPACING, 'floor');

value = Math.min(value, max);
}
}

return value;
};

GridSnapping.prototype.isActive = function() {
return this.active;
};

GridSnapping.prototype.setActive = function(active) {
this.active = active;
};

GridSnapping.prototype.toggleActive = function() {
this.setActive(!this.active);
};

GridSnapping.$inject = [
'eventBus',
'config.gridSnapping',
'grid'
];

// helpers //////////

/**
* Get minimum and maximum snap constraints.
*
* @param {Object} event - Event.
* @param {String} axis - Axis.
*
* @returns {Object}
*/
function getSnapConstraints(event, axis) {
var context = event.context,
resizeConstraints = context.resizeConstraints;

if (!resizeConstraints) {
return null;
}

var direction = context.direction;

var minResizeConstraints = resizeConstraints.min,
maxResizeConstraints = resizeConstraints.max;

var snapConstraints = {};

// resize
if (minResizeConstraints) {

if (isHorizontal(axis)) {

if (isWest(direction)) {
snapConstraints.max = minResizeConstraints.left;
} else {
snapConstraints.min = minResizeConstraints.right;
}

} else {

if (isNorth(direction)) {
snapConstraints.max = minResizeConstraints.top;
} else {
snapConstraints.min = minResizeConstraints.bottom;
}

}
}

if (maxResizeConstraints) {

if (isHorizontal(axis)) {

if (isWest(direction)) {
snapConstraints.min = maxResizeConstraints.left;
} else {
snapConstraints.max = maxResizeConstraints.right;
}

} else {

if (isNorth(direction)) {
snapConstraints.min = maxResizeConstraints.top;
} else {
snapConstraints.max = maxResizeConstraints.bottom;
}

}
}

return snapConstraints;
}

function isHorizontal(axis) {
return axis === 'x';
}

function isNorth(direction) {
return direction.charAt(0) === 'n';
}

function isWest(direction) {
return direction.charAt(1) === 'w';
}

function quantize(value, quantum, fn) {
if (!fn) {
fn = 'round';
}

return Math[ fn ](value / quantum) * quantum;
}
8 changes: 8 additions & 0 deletions lib/features/grid-snapping/index.js
@@ -0,0 +1,8 @@
import Grid from './Grid';
import GridSnapping from './GridSnapping';

export default {
__init__: [ 'grid', 'gridSnapping' ],
grid: [ 'type', Grid ],
gridSnapping: [ 'type', GridSnapping ]
};

0 comments on commit f987baf

Please sign in to comment.