vmes-flowable
Version:
ceshibao
208 lines (163 loc) • 4.88 kB
JavaScript
import {
isObject,
sortBy
} from 'min-dash';
import {
pointDistance,
pointsOnLine
} from '../util/Geometry';
import intersectPaths from 'path-intersection';
export function roundBounds(bounds) {
return {
x: Math.round(bounds.x),
y: Math.round(bounds.y),
width: Math.round(bounds.width),
height: Math.round(bounds.height)
};
}
export function roundPoint(point) {
return {
x: Math.round(point.x),
y: Math.round(point.y)
};
}
/**
* Convert the given bounds to a { top, left, bottom, right } descriptor.
*
* @param {Bounds|Point} bounds
*
* @return {Object}
*/
export function asTRBL(bounds) {
return {
top: bounds.y,
right: bounds.x + (bounds.width || 0),
bottom: bounds.y + (bounds.height || 0),
left: bounds.x
};
}
/**
* Convert a { top, left, bottom, right } to an objects bounds.
*
* @param {Object} trbl
*
* @return {Bounds}
*/
export function asBounds(trbl) {
return {
x: trbl.left,
y: trbl.top,
width: trbl.right - trbl.left,
height: trbl.bottom - trbl.top
};
}
/**
* Get the mid of the given bounds or point.
*
* @param {Bounds|Point} bounds
*
* @return {Point}
*/
export function getMid(bounds) {
return roundPoint({
x: bounds.x + (bounds.width || 0) / 2,
y: bounds.y + (bounds.height || 0) / 2
});
}
// orientation utils //////////////////////
/**
* Get orientation of the given rectangle with respect to
* the reference rectangle.
*
* A padding (positive or negative) may be passed to influence
* horizontal / vertical orientation and intersection.
*
* @param {Bounds} rect
* @param {Bounds} reference
* @param {Point|number} padding
*
* @return {string} the orientation; one of top, top-left, left, ..., bottom, right or intersect.
*/
export function getOrientation(rect, reference, padding) {
padding = padding || 0;
// make sure we can use an object, too
// for individual { x, y } padding
if (!isObject(padding)) {
padding = { x: padding, y: padding };
}
var rectOrientation = asTRBL(rect),
referenceOrientation = asTRBL(reference);
var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
right = rectOrientation.left - padding.x >= referenceOrientation.right,
bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
left = rectOrientation.right + padding.x <= referenceOrientation.left;
var vertical = top ? 'top' : (bottom ? 'bottom' : null),
horizontal = left ? 'left' : (right ? 'right' : null);
if (horizontal && vertical) {
return vertical + '-' + horizontal;
} else {
return horizontal || vertical || 'intersect';
}
}
// intersection utils //////////////////////
/**
* Get intersection between an element and a line path.
*
* @param {PathDef} elementPath
* @param {PathDef} linePath
* @param {boolean} cropStart crop from start or end
*
* @return {Point}
*/
export function getElementLineIntersection(elementPath, linePath, cropStart) {
var intersections = getIntersections(elementPath, linePath);
// recognize intersections
// only one -> choose
// two close together -> choose first
// two or more distinct -> pull out appropriate one
// none -> ok (fallback to point itself)
if (intersections.length === 1) {
return roundPoint(intersections[0]);
} else if (intersections.length === 2 && pointDistance(intersections[0], intersections[1]) < 1) {
return roundPoint(intersections[0]);
} else if (intersections.length > 1) {
// sort by intersections based on connection segment +
// distance from start
intersections = sortBy(intersections, function(i) {
var distance = Math.floor(i.t2 * 100) || 1;
distance = 100 - distance;
distance = (distance < 10 ? '0' : '') + distance;
// create a sort string that makes sure we sort
// line segment ASC + line segment position DESC (for cropStart)
// line segment ASC + line segment position ASC (for cropEnd)
return i.segment2 + '#' + distance;
});
return roundPoint(intersections[cropStart ? 0 : intersections.length - 1]);
}
return null;
}
export function getIntersections(a, b) {
return intersectPaths(a, b);
}
export function filterRedundantWaypoints(waypoints) {
// alter copy of waypoints, not original
waypoints = waypoints.slice();
var idx = 0,
point,
previousPoint,
nextPoint;
while (waypoints[idx]) {
point = waypoints[idx];
previousPoint = waypoints[idx - 1];
nextPoint = waypoints[idx + 1];
if (pointDistance(point, nextPoint) === 0 ||
pointsOnLine(previousPoint, nextPoint, point)) {
// remove point, if overlapping with {nextPoint}
// or on line with {previousPoint} -> {point} -> {nextPoint}
waypoints.splice(idx, 1);
} else {
idx++;
}
}
return waypoints;
}