UNPKG

@logicflow/extension

Version:
349 lines (348 loc) 13.6 kB
"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;