UNPKG

@logicflow/core

Version:

LogicFlow, help you quickly create flowcharts

891 lines (890 loc) 33.6 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; 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; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSvgTextSize = exports.createEdgeGenerator = exports.twoPointDistance = exports.pickEdgeConfig = exports.getClosestPointOfPolyline = exports.getEndTangent = exports.getBezierPoints = exports.getBezierControlPoints = exports.getAppendAttributes = exports.getTextWidth = exports.getBytesLength = exports.getSimplePoints = exports.points2PointsList = exports.segmentDirection = exports.getCrossPointInRect = exports.isSegmentsCrossNode = exports.isSegmentsInNode = exports.getLongestEdge = exports.getPolylinePoints = exports.pointFilter = exports.getBoxByOriginNode = exports.pathFinder = exports.getNextNeighborPoints = exports.isSegmentCrossingBBox = exports.isSegmentsIntersected = exports.removeClosePointFromOpenList = exports.rebuildPath = exports.heuristicCostEstimate = exports.costByPoints = exports.estimateDistance = exports.getBBoxCrossPointsByPoint = exports.getBBoxYCrossPoints = exports.getBBoxXCrossPoints = exports.isPointOutsideBBox = exports.getPointsFromBBox = exports.getBBoxOfPoints = exports.mergeBBox = exports.getExpandedBBoxPoint = exports.pointDirection = exports.getExpandedBBox = exports.getSimplePolyline = exports.filterRepeatPoints = exports.isBboxOverLapping = exports.setupEdgeModel = void 0; var lodash_es_1 = require("lodash-es"); var _1 = require("."); var constant_1 = require("../constant"); var algorithm_1 = require("../algorithm"); var model_1 = require("../model"); /* 手动创建边时 edge -> edgeModel */ var setupEdgeModel = function (edge, graphModel) { var model; switch (edge.type) { case 'line': model = new model_1.LineEdgeModel(edge, graphModel); break; case 'polyline': model = new model_1.PolylineEdgeModel(edge, graphModel); break; default: model = new model_1.LineEdgeModel(edge, graphModel); break; } return model; }; exports.setupEdgeModel = setupEdgeModel; /* 判断两个Bbox是否重合 */ var isBboxOverLapping = function (b1, b2) { return Math.abs(b1.centerX - b2.centerX) * 2 < b1.width + b2.width && Math.abs(b1.centerY - b2.centerY) * 2 < b1.height + b2.height; }; exports.isBboxOverLapping = isBboxOverLapping; /* 连接点去重,去掉x,y位置重复的点 */ var filterRepeatPoints = function (points) { var result = []; var pointsMap = {}; points.forEach(function (p) { var id = "".concat(p.x, "-").concat(p.y); p.id = id; pointsMap[id] = p; }); Object.keys(pointsMap).forEach(function (p) { result.push(pointsMap[p]); }); return result; }; exports.filterRepeatPoints = filterRepeatPoints; /* 获取简单边:边之间除了起始点,只有1个中间点 */ var getSimplePolyline = function (sPoint, tPoint) { var points = [ sPoint, { x: sPoint.x, y: tPoint.y, }, tPoint, ]; return (0, exports.filterRepeatPoints)(points); }; exports.getSimplePolyline = getSimplePolyline; /* 扩展的bbox,保证起始点的下一个点一定在node的垂直方向,不会与线重合, offset是点与线的垂直距离 */ var getExpandedBBox = function (bbox, offset) { if (bbox.width === 0 && bbox.height === 0) { return bbox; } return { x: bbox.x, y: bbox.y, centerX: bbox.centerX, centerY: bbox.centerY, minX: bbox.minX - offset, minY: bbox.minY - offset, maxX: bbox.maxX + offset, maxY: bbox.maxY + offset, height: bbox.height + 2 * offset, width: bbox.width + 2 * offset, }; }; exports.getExpandedBBox = getExpandedBBox; /* 判断点与中心点边的方向:是否水平,true水平,false垂直 */ var pointDirection = function (point, bbox) { var dx = Math.abs(point.x - bbox.centerX); var dy = Math.abs(point.y - bbox.centerY); return dx / bbox.width > dy / bbox.height ? constant_1.SegmentDirection.HORIZONTAL : constant_1.SegmentDirection.VERTICAL; }; exports.pointDirection = pointDirection; /* 获取扩展图形上的点,即起始终点相邻的点,上一个或者下一个节点 */ var getExpandedBBoxPoint = function (expendBBox, bbox, point) { // https://github.com/didi/LogicFlow/issues/817 // 没有修复前传入的参数bbox实际是expendBBox // 由于pointDirection使用的是dx / bbox.width > dy / bbox.height判定方向 // 此时的bbox.width是增加了offset后的宽度,bbox.height同理 // 这导致了部分极端情况(宽度很大或者高度很小),计算出来的方向错误 var direction = (0, exports.pointDirection)(point, bbox); if (direction === constant_1.SegmentDirection.HORIZONTAL) { return { x: point.x > expendBBox.centerX ? expendBBox.maxX : expendBBox.minX, y: point.y, }; } return { x: point.x, y: point.y > expendBBox.centerY ? expendBBox.maxY : expendBBox.minY, }; }; exports.getExpandedBBoxPoint = getExpandedBBoxPoint; /* 获取包含2个图形的外层box */ var mergeBBox = function (b1, b2) { var minX = Math.min(b1.minX, b2.minX); var minY = Math.min(b1.minY, b2.minY); var maxX = Math.max(b1.maxX, b2.maxX); var maxY = Math.max(b1.maxY, b2.maxY); return { x: (minX + maxX) / 2, y: (minY + maxY) / 2, centerX: (minX + maxX) / 2, centerY: (minY + maxY) / 2, minX: minX, minY: minY, maxX: maxX, maxY: maxY, height: maxY - minY, width: maxX - minX, }; }; exports.mergeBBox = mergeBBox; /* 获取多个点的外层bbox * 这个函数的用处 * 1、获取起始终点相邻点(expendBboxPoint)的bbox * 2、polylineEdge, bezierEdge,获取outline边框,这种情况传入offset */ var getBBoxOfPoints = function (points, offset) { if (points === void 0) { points = []; } var xList = []; var yList = []; points.forEach(function (p) { xList.push(p.x); yList.push(p.y); }); var minX = Math.min.apply(Math, __spreadArray([], __read(xList), false)); var maxX = Math.max.apply(Math, __spreadArray([], __read(xList), false)); var minY = Math.min.apply(Math, __spreadArray([], __read(yList), false)); var maxY = Math.max.apply(Math, __spreadArray([], __read(yList), false)); var width = maxX - minX; var height = maxY - minY; if (offset) { width += offset; height += offset; } return { centerX: (minX + maxX) / 2, centerY: (minY + maxY) / 2, maxX: maxX, maxY: maxY, minX: minX, minY: minY, x: (minX + maxX) / 2, y: (minY + maxY) / 2, height: height, width: width, }; }; exports.getBBoxOfPoints = getBBoxOfPoints; /* 获取box四个角上的点 */ var getPointsFromBBox = function (bbox) { var minX = bbox.minX, minY = bbox.minY, maxX = bbox.maxX, maxY = bbox.maxY; return [ { x: minX, y: minY, }, { x: maxX, y: minY, }, { x: maxX, y: maxY, }, { x: minX, y: maxY, }, ]; }; exports.getPointsFromBBox = getPointsFromBBox; /* 判断某一点是否在box中 */ var isPointOutsideBBox = function (point, bbox) { var x = point.x, y = point.y; return x < bbox.minX || x > bbox.maxX || y < bbox.minY || y > bbox.maxY; }; exports.isPointOutsideBBox = isPointOutsideBBox; /* 获取点的x方向上与box的交点 */ var getBBoxXCrossPoints = function (bbox, x) { if (x < bbox.minX || x > bbox.maxX) { return []; } return [ { x: x, y: bbox.minY, }, { x: x, y: bbox.maxY, }, ]; }; exports.getBBoxXCrossPoints = getBBoxXCrossPoints; /* 获取点的y方向上与box的交点 */ var getBBoxYCrossPoints = function (bbox, y) { if (y < bbox.minY || y > bbox.maxY) { return []; } return [ { x: bbox.minX, y: y, }, { x: bbox.maxX, y: y, }, ]; }; exports.getBBoxYCrossPoints = getBBoxYCrossPoints; /* 获取点的x,y方向上与box的交点 */ var getBBoxCrossPointsByPoint = function (bbox, point) { return __spreadArray(__spreadArray([], __read((0, exports.getBBoxXCrossPoints)(bbox, point.x)), false), __read((0, exports.getBBoxYCrossPoints)(bbox, point.y)), false); }; exports.getBBoxCrossPointsByPoint = getBBoxCrossPointsByPoint; /* 计算两点之间的预测距离(非直线距离) */ var estimateDistance = function (p1, p2) { return Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y); }; exports.estimateDistance = estimateDistance; /* 减少点别重复计算进距离的误差 */ var costByPoints = function (p, points) { var offset = -2; var result = 0; points.forEach(function (point) { if (point) { if (p.x === point.x) { result += offset; } if (p.y === point.y) { result += offset; } } }); return result; }; exports.costByPoints = costByPoints; /* 预估距离 */ var heuristicCostEstimate = function (p, ps, pt, source, target) { return (0, exports.estimateDistance)(p, ps) + (0, exports.estimateDistance)(p, pt) + (0, exports.costByPoints)(p, [ps, pt, source, target]); }; exports.heuristicCostEstimate = heuristicCostEstimate; /* 重建路径,根据cameFrom属性计算出从起始到结束的路径 */ var rebuildPath = function (pathPoints, pointById, cameFrom, currentId, iterator) { if (!iterator) { iterator = 0; } pathPoints.unshift(pointById[currentId]); if (cameFrom[currentId] && cameFrom[currentId] !== currentId && iterator <= 100) { (0, exports.rebuildPath)(pathPoints, pointById, cameFrom, cameFrom[currentId], iterator + 1); } }; exports.rebuildPath = rebuildPath; /* 把计算完毕的点从开放列表中删除 */ var removeClosePointFromOpenList = function (arr, item) { var index = arr.indexOf(item); if (index > -1) { arr.splice(index, 1); } }; exports.removeClosePointFromOpenList = removeClosePointFromOpenList; /* 通过向量判断线段之间是否是相交的 */ var isSegmentsIntersected = function (p0, p1, p2, p3) { var s1x = p1.x - p0.x; var s1y = p1.y - p0.y; var s2x = p3.x - p2.x; var s2y = p3.y - p2.y; var s = (-s1y * (p0.x - p2.x) + s1x * (p0.y - p2.y)) / (-s2x * s1y + s1x * s2y); var t = (s2x * (p0.y - p2.y) - s2y * (p0.x - p2.x)) / (-s2x * s1y + s1x * s2y); return s >= 0 && s <= 1 && t >= 0 && t <= 1; }; exports.isSegmentsIntersected = isSegmentsIntersected; /* 判断线段与bbox是否是相交的,保证节点之间的边不会穿过节点自身 */ var isSegmentCrossingBBox = function (p1, p2, bbox) { if (bbox.width === 0 && bbox.height === 0) { return false; } var _a = __read((0, exports.getPointsFromBBox)(bbox), 4), pa = _a[0], pb = _a[1], pc = _a[2], pd = _a[3]; return ((0, exports.isSegmentsIntersected)(p1, p2, pa, pb) || (0, exports.isSegmentsIntersected)(p1, p2, pa, pd) || (0, exports.isSegmentsIntersected)(p1, p2, pb, pc) || (0, exports.isSegmentsIntersected)(p1, p2, pc, pd)); }; exports.isSegmentCrossingBBox = isSegmentCrossingBBox; /* 获取下一个相邻的点 */ var getNextNeighborPoints = function (points, point, bbox1, bbox2) { var neighbors = []; points.forEach(function (p) { if (p !== point) { if (p.x === point.x || p.y === point.y) { if (!(0, exports.isSegmentCrossingBBox)(p, point, bbox1) && !(0, exports.isSegmentCrossingBBox)(p, point, bbox2)) { neighbors.push(p); } } } }); return (0, exports.filterRepeatPoints)(neighbors); }; exports.getNextNeighborPoints = getNextNeighborPoints; /* 路径查找,AStar查找+曼哈顿距离 * 算法wiki:https://zh.wikipedia.org/wiki/A*%E6%90%9C%E5%B0%8B%E6%BC%94%E7%AE%97%E6%B3%95 * 方法无法复用,且调用了很多polyline相关的方法,暂不抽离到src/algorithm中 */ var pathFinder = function (points, start, goal, sBBox, tBBox, os, ot) { // 定义已经遍历过的点 var closedSet = []; // 定义需要遍历的店 var openSet = [start]; // 定义节点的上一个节点 var cameFrom = {}; var gScore = {}; var fScore = {}; if (start.id) { gScore[start.id] = 0; fScore[start.id] = (0, exports.heuristicCostEstimate)(start, goal, start); } var pointById = {}; points.forEach(function (p) { if (p.id) { pointById[p.id] = p; } }); var _loop_1 = function () { var current; var lowestFScore = Infinity; openSet.forEach(function (p) { if (p.id && fScore[p.id] < lowestFScore) { lowestFScore = fScore[p.id]; current = p; } }); if (current === goal && goal.id) { var pathPoints = []; (0, exports.rebuildPath)(pathPoints, pointById, cameFrom, goal.id); return { value: pathPoints }; } if (!current) { return { value: [start, goal] }; } (0, exports.removeClosePointFromOpenList)(openSet, current); closedSet.push(current); (0, exports.getNextNeighborPoints)(points, current, sBBox, tBBox).forEach(function (neighbor) { if (closedSet.indexOf(neighbor) !== -1) { return; } if (openSet.indexOf(neighbor) === -1) { openSet.push(neighbor); } if ((current === null || current === void 0 ? void 0 : current.id) && (neighbor === null || neighbor === void 0 ? void 0 : neighbor.id)) { var tentativeGScore = fScore[current.id] + (0, exports.estimateDistance)(current, neighbor); if (gScore[neighbor.id] && tentativeGScore >= gScore[neighbor.id]) { return; } cameFrom[neighbor.id] = current.id; gScore[neighbor.id] = tentativeGScore; fScore[neighbor.id] = gScore[neighbor.id] + (0, exports.heuristicCostEstimate)(neighbor, goal, start, os, ot); } }); }; while (openSet.length) { var state_1 = _loop_1(); if (typeof state_1 === "object") return state_1.value; } return [start, goal]; }; exports.pathFinder = pathFinder; var getBoxByOriginNode = function (node) { return (0, _1.getNodeBBox)(node); }; exports.getBoxByOriginNode = getBoxByOriginNode; /* 保证一条直线上只有2个节点: 删除x/y相同的中间节点 */ var pointFilter = function (points) { var i = 1; while (i < points.length - 1) { var pre = points[i - 1]; var current = points[i]; var next = points[i + 1]; if ((pre.x === current.x && current.x === next.x) || (pre.y === current.y && current.y === next.y)) { points.splice(i, 1); } else { i++; } } return points; }; exports.pointFilter = pointFilter; /* 计算折线点 */ var getPolylinePoints = function (start, end, sNode, tNode, offset) { var sBBox = (0, exports.getBoxByOriginNode)(sNode); var tBBox = (0, exports.getBoxByOriginNode)(tNode); var sxBBox = (0, exports.getExpandedBBox)(sBBox, offset); var txBBox = (0, exports.getExpandedBBox)(tBBox, offset); var sPoint = (0, exports.getExpandedBBoxPoint)(sxBBox, sBBox, start); var tPoint = (0, exports.getExpandedBBoxPoint)(txBBox, tBBox, end); // 当加上offset后的bbox有重合,直接简单计算节点 if ((0, exports.isBboxOverLapping)(sxBBox, txBBox)) { var points = (0, exports.getSimplePoints)(start, end, sPoint, tPoint); return __spreadArray(__spreadArray([start, sPoint], __read(points), false), [tPoint, end], false); } var lineBBox = (0, exports.getBBoxOfPoints)([sPoint, tPoint]); var sMixBBox = (0, exports.mergeBBox)(sxBBox, lineBBox); var tMixBBox = (0, exports.mergeBBox)(txBBox, lineBBox); var connectPoints = []; connectPoints = connectPoints.concat((0, exports.getPointsFromBBox)(sMixBBox)); connectPoints = connectPoints.concat((0, exports.getPointsFromBBox)(tMixBBox)); // 中心点 var centerPoint = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2, }; [lineBBox, sMixBBox, tMixBBox].forEach(function (bbox) { connectPoints = connectPoints.concat((0, exports.getBBoxCrossPointsByPoint)(bbox, centerPoint).filter(function (p) { return (0, exports.isPointOutsideBBox)(p, sxBBox) && (0, exports.isPointOutsideBBox)(p, txBBox); })); }); [ { x: sPoint.x, y: tPoint.y, }, { x: tPoint.x, y: sPoint.y, }, ].forEach(function (p) { if ((0, exports.isPointOutsideBBox)(p, sxBBox) && (0, exports.isPointOutsideBBox)(p, txBBox)) { connectPoints.push(p); } }); connectPoints.unshift(sPoint); connectPoints.push(tPoint); connectPoints = (0, exports.filterRepeatPoints)(connectPoints); // 路径查找-最关键的步骤 var pathPoints = (0, exports.pathFinder)(connectPoints, sPoint, tPoint, sBBox, tBBox, start, end); pathPoints.unshift(start); pathPoints.push(end); // 删除一条直线上的多余节点 if (pathPoints.length > 2) { pathPoints = (0, exports.pointFilter)(pathPoints); } return (0, exports.filterRepeatPoints)(pathPoints); }; exports.getPolylinePoints = getPolylinePoints; /** * 获取折线中最长的一个线 * @param pointsList 多个点组成的数组 */ var getLongestEdge = function (pointsList) { if (pointsList.length === 1) { var _a = __read(pointsList, 1), point = _a[0]; return [point, point]; } else { var point1 = pointsList[0]; var point2 = pointsList[1]; var edgeLength = (0, _1.distance)(point1.x, point1.y, point2.x, point2.y); for (var i = 1; i < pointsList.length - 1; i++) { var newPoint1 = pointsList[i]; var newPoint2 = pointsList[i + 1]; var newEdgeLength = (0, _1.distance)(newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y); if (newEdgeLength > edgeLength) { edgeLength = newEdgeLength; point1 = newPoint1; point2 = newPoint2; } } return [point1, point2]; } }; exports.getLongestEdge = getLongestEdge; /* 线段是否在节点内部,被包含了 */ var isSegmentsInNode = function (start, end, node) { var startInNode = (0, _1.isInNode)(start, node); var endInNode = (0, _1.isInNode)(end, node); return startInNode && endInNode; }; exports.isSegmentsInNode = isSegmentsInNode; /* 线段是否与节点相交 */ var isSegmentsCrossNode = function (start, end, node) { var startInNode = (0, _1.isInNode)(start, node); var endInNode = (0, _1.isInNode)(end, node); // bothInNode,线段两个端点都在节点内 var bothInNode = startInNode && endInNode; // cross,线段有端点在节点内 var inNode = startInNode || endInNode; // 有且只有一个点在节点内部 return !bothInNode && inNode; }; exports.isSegmentsCrossNode = isSegmentsCrossNode; /* 获取线段在矩形内部的交点 */ var getCrossPointInRect = function (start, end, node) { var crossSegments = undefined; var nodeBox = (0, _1.getNodeBBox)(node); var points = (0, exports.getPointsFromBBox)(nodeBox); for (var i = 0; i < points.length; i++) { var isCross = (0, exports.isSegmentsIntersected)(start, end, points[i], points[(i + 1) % points.length]); if (isCross) { crossSegments = [points[i], points[(i + 1) % points.length]]; } } if (crossSegments) { return (0, algorithm_1.getCrossPointOfLine)(start, end, crossSegments[0], crossSegments[1]); } }; exports.getCrossPointInRect = getCrossPointInRect; /* 判断线段的方向 */ var segmentDirection = function (start, end) { var direction = undefined; if (start.x === end.x) { direction = constant_1.SegmentDirection.VERTICAL; } else if (start.y === end.y) { direction = constant_1.SegmentDirection.HORIZONTAL; } return direction; }; exports.segmentDirection = segmentDirection; var points2PointsList = function (points) { var currentPositionList = points.split(' '); var pointsList = []; currentPositionList && currentPositionList.forEach(function (item) { var _a = __read(item.split(','), 2), x = _a[0], y = _a[1]; pointsList.push({ x: Number(x), y: Number(y), }); }); return pointsList; }; exports.points2PointsList = points2PointsList; var getSimplePoints = function (start, end, sPoint, tPoint) { var points = []; // start,sPoint的方向,水平或者垂直,即路径第一条线段的方向 var startDirection = (0, exports.segmentDirection)(start, sPoint); // end,tPoint的方向,水平或者垂直,即路径最后一条条线段的方向 var endDirection = (0, exports.segmentDirection)(end, tPoint); // 根据两条线段的方向作了计算,调整线段经验所得,非严格最优计算,能保证不出现折线 // 方向相同,添加两个点,两条平行线垂直距离一半的两个端点 if (startDirection === endDirection) { if (start.y === sPoint.y) { points.push({ x: sPoint.x, y: (sPoint.y + tPoint.y) / 2, }); points.push({ x: tPoint.x, y: (sPoint.y + tPoint.y) / 2, }); } else { points.push({ x: (sPoint.x + tPoint.x) / 2, y: sPoint.y, }); points.push({ x: (sPoint.x + tPoint.x) / 2, y: tPoint.y, }); } } else { // 方向不同,添加一个点,保证不在当前线段上(会出现重合),且不能有折线 var point = { x: sPoint.x, y: tPoint.y, }; var inStart = (0, algorithm_1.isInSegment)(point, start, sPoint); var inEnd = (0, algorithm_1.isInSegment)(point, end, tPoint); if (inStart || inEnd) { point = { x: tPoint.x, y: sPoint.y, }; } else { var onStart = isOnLine(point, start, sPoint); var onEnd = isOnLine(point, end, tPoint); if (onStart && onEnd) { point = { x: tPoint.x, y: sPoint.y, }; } } points.push(point); } return points; }; exports.getSimplePoints = getSimplePoints; var isOnLine = function (point, start, end) { return (point.x === start.x && point.x === end.x) || (point.y === start.y && point.y === end.y); }; /* 求字符串的字节长度 */ var getBytesLength = function (word) { if (!word) { return 0; } var totalLength = 0; for (var i = 0; i < word.length; i++) { var c = word.charCodeAt(i); if (word.match(/[A-Z]/)) { totalLength += 1.5; } else if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) { totalLength += 1; } else { totalLength += 2; } } return totalLength; }; exports.getBytesLength = getBytesLength; /** * Uses canvas.measureText to compute * and return the width of the given text of given font in pixels. * @param {String} text The text to be rendered. * @param {String} font The css font descriptor * that text is to be rendered with (e.g. "bold 14px verdana"). * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393 */ var canvas = undefined; var getTextWidth = function (text, font) { if (!canvas) { canvas = document.createElement('canvas'); } var context = canvas.getContext('2d'); context.font = font; var metrics = context.measureText(text); return metrics.width; }; exports.getTextWidth = getTextWidth; // 扩大边可点区域,获取边append的信息 var getAppendAttributes = function (appendInfo) { var start = appendInfo.start, end = appendInfo.end; var d; if (start.x === end.x && start.y === end.y) { // 拖拽过程中会出现起终点重合的情况,这时候append无法计算 d = ''; } else { var config = { start: start, end: end, offset: 10, verticalLength: 5, }; var startPosition = (0, algorithm_1.getVerticalPointOfLine)(__assign(__assign({}, config), { type: 'start' })); var endPosition = (0, algorithm_1.getVerticalPointOfLine)(__assign(__assign({}, config), { type: 'end' })); d = "M".concat(startPosition.leftX, " ").concat(startPosition.leftY, "\n L").concat(startPosition.rightX, " ").concat(startPosition.rightY, "\n L").concat(endPosition.rightX, " ").concat(endPosition.rightY, "\n L").concat(endPosition.leftX, " ").concat(endPosition.leftY, " z"); } return { d: d, fill: 'transparent', stroke: 'transparent', strokeWidth: 1, strokeDasharray: '4, 4', }; }; exports.getAppendAttributes = getAppendAttributes; // bezier曲线 var getBezierControlPoints = function (_a) { var start = _a.start, end = _a.end, sourceNode = _a.sourceNode, targetNode = _a.targetNode, offset = _a.offset; var sBBox = (0, _1.getNodeBBox)(sourceNode); var tBBox = (0, _1.getNodeBBox)(targetNode); var sExpendBBox = (0, exports.getExpandedBBox)(sBBox, offset); var tExpendBBox = (0, exports.getExpandedBBox)(tBBox, offset); var sNext = (0, exports.getExpandedBBoxPoint)(sExpendBBox, sBBox, start); var ePre = (0, exports.getExpandedBBoxPoint)(tExpendBBox, tBBox, end); return { sNext: sNext, ePre: ePre, }; }; exports.getBezierControlPoints = getBezierControlPoints; // 根据bezier曲线path求出Points var getBezierPoints = function (path) { var list = path.replace(/M/g, '').replace(/C/g, ',').split(','); var start = getBezierPoint(list[0]); var sNext = getBezierPoint(list[1]); var ePre = getBezierPoint(list[2]); var end = getBezierPoint(list[3]); return [start, sNext, ePre, end]; }; exports.getBezierPoints = getBezierPoints; // 根据bezier曲线path求出Point坐标 var getBezierPoint = function (positionStr) { var _a = __read(positionStr.replace(/(^\s*)/g, '').split(' '), 2), x = _a[0], y = _a[1]; return { x: +x, y: +y, }; }; // 根据bezier曲线path求出结束切线的两点坐标 var getEndTangent = function (pointsList, offset) { // const bezierPoints = getBezierPoints(path) var _a = __read(pointsList, 4), p1 = _a[0], cp1 = _a[1], cp2 = _a[2], p2 = _a[3]; var start = (0, _1.sampleCubic)(p1, cp1, cp2, p2, offset); return [start, pointsList[3]]; }; exports.getEndTangent = getEndTangent; /** * 获取移动边后,文本位置距离边上的最近的一点 * @param point 边上文本的位置 * @param points 边的各个拐点 * TODO: Label实验没问题后统一改成新的计算方式,把这个方法废弃 */ var getClosestPointOfPolyline = function (point, points) { var x = point.x, y = point.y; var pointsPosition = (0, exports.points2PointsList)(points); var minDistance = Number.MAX_SAFE_INTEGER; var crossPoint; var segments = []; for (var i = 0; i < pointsPosition.length; i++) { segments.push({ start: pointsPosition[i], end: pointsPosition[(i + 1) % pointsPosition.length], }); } segments.forEach(function (item) { var start = item.start, end = item.end; // 若线段垂直,则crossPoint的横坐标与线段一致 if (start.x === end.x) { var pointXY = { x: start.x, y: y, }; var inSegment = (0, algorithm_1.isInSegment)(pointXY, start, end); if (inSegment) { var currentDistance = Math.abs(start.x - x); if (currentDistance < minDistance) { minDistance = currentDistance; crossPoint = pointXY; } } } else if (start.y === end.y) { var pointXY = { x: x, y: start.y, }; var inSegment = (0, algorithm_1.isInSegment)(pointXY, start, end); if (inSegment) { var currentDistance = Math.abs(start.y - y); if (currentDistance < minDistance) { minDistance = currentDistance; crossPoint = pointXY; } } } }); // 边界:只有一条线段时,沿线段移动节点,当文本超出边后,文本没有可供参考的线段 if (!crossPoint) { var _a = segments[0], start = _a.start, end = _a.end; crossPoint = { x: start.x + (end.x - start.x) / 2, y: start.y + (end.y - start.y) / 2, }; } return crossPoint; }; exports.getClosestPointOfPolyline = getClosestPointOfPolyline; // 规范边初始化数据 var pickEdgeConfig = function (data) { return (0, lodash_es_1.pick)(data, [ 'id', 'type', 'sourceNodeId', 'sourceAnchorId', 'targetNodeId', 'targetAnchorId', 'pointsList', 'startPoint', 'endPoint', 'properties', ]); }; exports.pickEdgeConfig = pickEdgeConfig; var twoPointDistance = function (source, target) { // fix: 修复坐标存在负值时计算错误的问题。 // const source = { // x: p1.x, // y: Math.abs(p1.y), // } // const target = { // x: Math.abs(p2.x), // y: Math.abs(p2.y), // } return Math.sqrt(Math.pow((source.x - target.x), 2) + Math.pow((source.y - target.y), 2)); }; exports.twoPointDistance = twoPointDistance; /** * 包装边生成函数 * @param graphModel graph model * @param generator 用户自定义的边生成函数 */ function createEdgeGenerator(graphModel, generator) { // TODO: 定义返回值类型,保证 GraphModel 中类型的正确性 if (typeof generator !== 'function') { return function (_sourceNode, _targetNode, currentEdge) { return Object.assign({ type: graphModel.edgeType }, currentEdge); }; } return function (sourceNode, targetNode, currentEdge) { var result = generator(sourceNode, targetNode, currentEdge); // 无结果使用默认类型 if (!result) return { type: graphModel.edgeType }; if (typeof result === 'string') { return Object.assign({}, currentEdge, { type: result }); } return Object.assign({ type: result }, currentEdge); }; } exports.createEdgeGenerator = createEdgeGenerator; var getSvgTextSize = function (_a) { var rows = _a.rows, rowsLength = _a.rowsLength, fontSize = _a.fontSize; var longestBytes = 0; (0, lodash_es_1.forEach)(rows, function (row) { var rowBytesLength = (0, exports.getBytesLength)(row); longestBytes = rowBytesLength > longestBytes ? rowBytesLength : longestBytes; }); // 背景框宽度,最长一行字节数/2 * fontsize + 2 // 背景框宽度, 行数 * fontsize + 2 return { width: Math.ceil(longestBytes / 2) * fontSize + fontSize / 4, height: rowsLength * (fontSize + 2) + fontSize / 4, }; }; exports.getSvgTextSize = getSvgTextSize;