@logicflow/extension
Version:
LogicFlow Extensions
349 lines (348 loc) • 13.6 kB
JavaScript
"use strict";
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLabelPositionOfLine = exports.calcLabelPositionOnPolyline = exports.getNewPointAtDistance = exports.calcClosestPointOnBezierEdge = exports.pointPositionRatio = exports.calcPolylineTotalLength = exports.pointToSegmentDistance = exports.getPointOffsetOfElementOutline = exports.rotatePointAroundCenter = exports.calcPointAfterResize = void 0;
var core_1 = require("@logicflow/core");
var lodash_es_1 = require("lodash-es");
var algorithm_1 = require("./algorithm");
// 工具函数:计算「缩放」后 某坐标点 相对中心位置比例不变的 新坐标点
// 前提条件: 当缩放一个矩形时,如果你希望矩形中的某个点的位置相对于矩形保持不变
//
// 1. 原始矩形的左上角坐标为 (x1, y1),宽度为 w1,高度为 h1。
// 2. 缩放后的矩形的左上角坐标为 (x2, y2),宽度为 w2,高度为 h2。
// 3. 矩形中的某个点在原始矩形中的坐标为 (px1, py1)。
//
// 目标
// 计算该点在缩放后矩形中的新坐标 (px2, py2)。
//
// 步骤
// 1. 计算相对位置:首先计算点 (px1, py1) 在原始矩形中的相对位置。
// relativeX = (px1 - x1) / w1
// relativeY = (py1 - y1) / h1
//
// 2. 计算新坐标:然后,使用相对位置计算该点在缩放后矩形中的新坐标。
// px2 = x2 + relativeX * w2
// py2 = y2 + relativeY * h2
function calcPointAfterResize(origin, scaled, point) {
var x1 = origin.x, y1 = origin.y, w1 = origin.width, h1 = origin.height;
var x2 = scaled.x, y2 = scaled.y, w2 = scaled.width, h2 = scaled.height;
var px1 = point.x, py1 = point.y;
// 计算点在原始矩形中的相对位置
var relativeX = (px1 - x1) / w1;
var relativeY = (py1 - y1) / h1;
// 计算点在缩放后矩形中的新坐标
var px2 = x2 + relativeX * w2;
var py2 = y2 + relativeY * h2;
return { x: px2, y: py2 };
}
exports.calcPointAfterResize = calcPointAfterResize;
// 工具函数:计算「旋转」后 某坐标点 相对中心位置比例不变的 新坐标点
// 要计算以点 x1 = (x1, y1) 为中心,点 x2 = (x2, y2) 旋转 θ 度后的坐标位置,可以使用旋转矩阵进行计算。
//
// 旋转公式如下:
// 1. 首先将点 x2 平移到以 x1 为原点的坐标系:
// x' = x2 - x1
// y' = y2 - y1
// 2. 然后应用旋转矩阵进行旋转:
// x'' = x' * cos(θ) - y' * sin(θ)
// y'' = x' * sin(θ) + y' * cos(θ)
// 3. 最后将结果平移回原来的坐标系:
// x_new = x'' + x1
// y_new = y'' + y1
//
// 综合起来,旋转后的新坐标 (x_new, y_new) 计算公式如下:
//
// x_new = (x2 - x1) * cos(θ) - (y2 - y1) * sin(θ) + x1
// y_new = (x2 - x1) * sin(θ) + (y2 - y1) * cos(θ) + y1
//
// 其中,θ 需要用弧度表示,如果你有的是角度,可以用以下公式转换为弧度:
//
// rad = deg * π / 180
function rotatePointAroundCenter(target, center, radian) {
// Rotate point (x2, y2) around point (x1, y1) by theta degrees.
//
// Parameters:
// x1, y1: Coordinates of the center point.
// x2, y2: Coordinates of the point to rotate.
// theta_degrees: Angle in degrees to rotate the point.
//
// Returns:
// Tuple of new coordinates (x_new, y_new) after rotation.
var x1 = center.x, y1 = center.y;
var x2 = target.x, y2 = target.y;
// Translate point to origin
var xPrime = x2 - x1;
var yPrime = y2 - y1;
// Rotate point
var xDoublePrime = xPrime * Math.cos(radian) - yPrime * Math.sin(radian);
var yDoublePrime = xPrime * Math.sin(radian) + yPrime * Math.cos(radian);
// Translate point back
var xNew = xDoublePrime + x1;
var yNew = yDoublePrime + y1;
return {
x: xNew,
y: yNew,
};
}
exports.rotatePointAroundCenter = rotatePointAroundCenter;
/** Edge 相关工具方法 */
/**
* 获取某点在一个矩形图形(节点 or 边的 outline)内的偏移量
* @param point 目标点(此处即 Label 的坐标信息)
* @param element 目标元素
*/
var getPointOffsetOfElementOutline = function (point, element) {
var baseType = element.BaseType;
var bboxInfo = baseType === 'node' ? (0, core_1.getNodeOutline)(element) : (0, core_1.getEdgeOutline)(element);
if (bboxInfo) {
var x = point.x, y = point.y;
var minX = bboxInfo.x, minY = bboxInfo.y, maxX = bboxInfo.x1, maxY = bboxInfo.y1;
var xDeltaPercent = 0.5;
var yDeltaPercent = 0.5;
var xDeltaDistance = x - maxX;
var yDeltaDistance = y - maxY;
/**
* 文本在由路径点组成的凸包内,就记录偏移比例
* 文本在凸包外,记录绝对距离
* 用于边路径变化时计算文本新位置
*/
if (minX && maxX && minX < x && x < maxX) {
xDeltaPercent = (0, lodash_es_1.min)([(x - minX) / (maxX - minX), 1]);
}
else if (x <= minX) {
xDeltaDistance = x - minX;
}
else {
xDeltaDistance = x - maxX;
}
if (minY && maxY && minY < y && y < maxY) {
yDeltaPercent = (0, lodash_es_1.min)([(y - minY) / (maxY - minY), 1]);
}
else if (y <= minY) {
yDeltaDistance = y - minY;
}
else {
yDeltaDistance = y - maxY;
}
return {
xDeltaPercent: xDeltaPercent,
yDeltaPercent: yDeltaPercent,
xDeltaDistance: xDeltaDistance,
yDeltaDistance: yDeltaDistance,
};
}
};
exports.getPointOffsetOfElementOutline = getPointOffsetOfElementOutline;
/**
* 判断节点是否在折线上
* @param point 目标点坐标
* @param points 折线上的点坐标
*/
var isPointOnPolyline = function (point, points) {
for (var i = 0; i < points.length - 1; i++) {
var start = points[i];
var end = points[i + 1];
if ((0, core_1.isInSegment)(point, start, end)) {
return true;
}
}
return false;
};
/**
* 给定一个点 P = (x_0, y_0) 和线段的两个端点 A = (x_1, y_1) 和 B = (x_2, y_2) ,可以使用以下步骤计算点到线段的距离:
* 1. 计算向量 AB 和 AP 。
* 2. 计算 AB 的平方长度。
* 3. 计算点 P 在直线 AB 上的投影点 Q 。
* 4. 判断 Q 是否在线段 AB 上。
* 5. 根据 Q 是否在线段上,计算点到线段的距离。
*
* 计算点到线段质检的距离
* @param point
* @param start
* @param end
*/
var pointToSegmentDistance = function (point, start, end) {
var px = point.x, py = point.y;
var sx = start.x, sy = start.y;
var ex = end.x, ey = end.y;
var SEx = ex - sx;
var SEy = ey - sy;
var SPx = px - sx;
var SPy = py - sy;
var SE_SE = Math.pow(SEx, 2) + Math.pow(SEy, 2);
var SP_SE = SPx * SEx + SPy * SEy;
var t = SP_SE / SE_SE;
if (t < 0)
t = 0;
if (t > 1)
t = 1;
var qx = sx + t * SEx;
var qy = sy + t * SEy;
return Math.sqrt(Math.pow((px - qx), 2) + Math.pow((py - qy), 2));
};
exports.pointToSegmentDistance = pointToSegmentDistance;
var calcPolylineTotalLength = function (points) {
var length = 0;
for (var i = 0; i < points.length - 1; i++) {
var start = points[i];
var end = points[i + 1];
length += (0, algorithm_1.calcTwoPointsDistance)(start, end);
}
return length;
};
exports.calcPolylineTotalLength = calcPolylineTotalLength;
/**
* TODO: 确认该函数的意义,写完还是没看懂什么意思
* @param point
* @param points
*/
var pointPositionRatio = function (point, points) {
var length = 0;
for (var i = 0; i < points.length - 1; i++) {
var start = points[i];
var end = points[i + 1];
var segmentLength = (0, algorithm_1.calcTwoPointsDistance)(start, end);
if ((0, exports.pointToSegmentDistance)(point, start, end) <= 20) {
var d1 = (0, algorithm_1.calcTwoPointsDistance)(point, start);
length += d1;
var totalLength = (0, exports.calcPolylineTotalLength)(points);
// 小数点后保留一位(四舍五入)
return Math.round((length / totalLength) * 10) / 10;
}
else {
length += segmentLength;
}
}
return 0;
};
exports.pointPositionRatio = pointPositionRatio;
/**
* 计算一个坐标在贝塞尔曲线上最近的一个点
* @param point
* @param edge
* @param step
*/
var calcClosestPointOnBezierEdge = function (point, edge, step) {
if (step === void 0) { step = 5; }
var minDistance = Infinity;
var closestPoint = point;
var pointsList = (0, core_1.getBezierPoints)(edge.path);
if ((0, lodash_es_1.isEmpty)(pointsList))
return closestPoint;
var _a = __read(pointsList, 4), start = _a[0], sNext = _a[1], ePre = _a[2], end = _a[3];
for (var i = 0; i <= step; i++) {
var t = i / step;
var bezierPoint = (0, algorithm_1.getPointOnBezier)(t, start, sNext, ePre, end);
var distance = (0, algorithm_1.calcTwoPointsDistance)(point, bezierPoint);
if (distance < minDistance) {
minDistance = distance;
closestPoint = bezierPoint;
}
}
return closestPoint;
};
exports.calcClosestPointOnBezierEdge = calcClosestPointOnBezierEdge;
var getNewPointAtDistance = function (points, ratio) {
var totalLength = (0, exports.calcPolylineTotalLength)(points);
var targetLength = totalLength * ratio;
var length = 0;
for (var i = 0; i < points.length - 1; i++) {
var start = points[i];
var end = points[i + 1];
var segmentLength = (0, algorithm_1.calcTwoPointsDistance)(start, end);
if (length + segmentLength >= targetLength) {
var ratio_1 = (targetLength - length) / segmentLength;
return {
x: start.x + (end.x - start.x) * ratio_1,
y: start.y + (end.y - start.y) * ratio_1,
};
}
else {
length += segmentLength;
}
}
return (0, lodash_es_1.last)(points);
};
exports.getNewPointAtDistance = getNewPointAtDistance;
/**
* 计算一个坐标离折线(包括 PolylineEdge 和 LineEdge 直线)最近的一个点
* @param point
* @param edge
*/
var calcLabelPositionOnPolyline = function (point, edge) {
var _a, _b;
var points = (0, core_1.points2PointsList)(edge.points);
if (points.length === 0) {
points = [edge.startPoint, edge.endPoint];
}
var _c = (_a = (0, exports.getPointOffsetOfElementOutline)(point, edge)) !== null && _a !== void 0 ? _a : {}, xDeltaPercent = _c.xDeltaPercent, yDeltaPercent = _c.yDeltaPercent, yDeltaDistance = _c.yDeltaDistance, xDeltaDistance = _c.xDeltaDistance;
var isPointOnEdge = isPointOnPolyline(point, points);
var ratio = (0, exports.pointPositionRatio)(point, points);
var start = (0, lodash_es_1.head)(points);
var end = (0, lodash_es_1.last)(points);
// 分别取路径中,x轴 和 y轴上的最大最小坐标值组合成一个矩形
var _d = (0, core_1.getBBoxOfPoints)(points, 10), minX = _d.minX, minY = _d.minY, maxX = _d.maxX, maxY = _d.maxY;
if (!start || !end)
return point;
if (xDeltaPercent && yDeltaPercent) {
var positByPercent = {
x: minX + (maxX - minX) * xDeltaPercent,
y: minY + (maxY - minY) * yDeltaPercent,
};
return isPointOnEdge
? ((_b = (0, exports.getNewPointAtDistance)(points, ratio)) !== null && _b !== void 0 ? _b : point) // 函数什么意思
: positByPercent;
}
// 如果文本在凸包的上方或者下方
if (xDeltaPercent && yDeltaDistance) {
return {
x: minX + (maxX - minX) * xDeltaPercent,
y: yDeltaDistance < 0 ? minY + yDeltaDistance : maxY + yDeltaDistance,
};
}
// 如果文本在凸包的左边或者右边
if (yDeltaPercent && xDeltaDistance) {
return {
x: xDeltaDistance < 0 ? minX + xDeltaDistance : maxX + xDeltaDistance,
y: minY + (maxY - minY) * yDeltaPercent,
};
}
// 如果文本在凸包左上/左下/右上/右下
if (xDeltaDistance && yDeltaDistance) {
return {
x: xDeltaDistance < 0 ? minX + xDeltaDistance : maxX + xDeltaDistance,
y: yDeltaDistance < 0 ? minY + yDeltaDistance : maxY + yDeltaDistance,
};
}
// 兜底
return point;
};
exports.calcLabelPositionOnPolyline = calcLabelPositionOnPolyline;
/**
* 计算 Label 离边最近的点的坐标,用于更新为 Label 的坐标
* @param label LabelConfig -> 当前 Label 的配置项
* @param edge
*/
var getLabelPositionOfLine = function (label, edge) {
var x = label.x, y = label.y;
if (edge.modelType === core_1.ModelType.BEZIER_EDGE) {
return (0, exports.calcClosestPointOnBezierEdge)({ x: x, y: y }, edge);
}
return (0, exports.calcLabelPositionOnPolyline)({ x: x, y: y }, edge);
};
exports.getLabelPositionOfLine = getLabelPositionOfLine;