UNPKG

@logicflow/core

Version:

LogicFlow, help you quickly create flowcharts

437 lines (436 loc) 15 kB
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; }; import { pick } from 'lodash-es'; import { getBytesLength } from './edge'; import { SegmentDirection } from '../constant'; import { isInSegment } from '../algorithm/edge'; import { Matrix } from './matrix'; /* 获取所有锚点 */ export var getAnchors = function (data) { var anchors = data.anchors; return anchors; }; /* 手动边时获取目标节点的信息:目标节点,目标节点的锚点index以及坐标 */ export var targetNodeInfo = function (position, graphModel) { var nodes = graphModel.nodes; var nodeInfo; for (var i = nodes.length - 1; i >= 0; i--) { var targetNode = nodes[i]; var inNode = isInNodeBbox(position, targetNode); if (inNode) { var anchorInfo = targetNode.getTargetAnchor(position); if (anchorInfo) { // 不能连接到没有锚点的节点 var currentNodeInfo = { node: targetNode, anchorIndex: anchorInfo.index, anchor: anchorInfo.anchor, }; // fix: 489 多个节点重合时,连线连接上面的那一个。 if (!nodeInfo || isNodeHigher(targetNode, nodeInfo.node, graphModel)) { nodeInfo = currentNodeInfo; } } } } return nodeInfo; }; /** * 比较两个节点 */ var isNodeHigher = function (node1, node2, graphModel) { if (node1.zIndex > node2.zIndex) { return true; } return (graphModel.nodesMap[node1.id].index > graphModel.nodesMap[node2.id].index); }; /* 手动边时获取目标节点上,距离目标位置最近的锚点 */ export var getClosestAnchor = function (position, node) { var anchors = getAnchors(node); var closest; var minDistance = Number.MAX_SAFE_INTEGER; for (var i = 0; i < anchors.length; i++) { var len = distance(position.x, position.y, anchors[i].x, anchors[i].y); if (len < minDistance) { minDistance = len; closest = { index: i, anchor: __assign(__assign({}, anchors[i]), { x: anchors[i].x, y: anchors[i].y, id: anchors[i].id }), }; } } return closest; }; /* 两点之间距离 */ export var distance = function (x1, y1, x2, y2) { return Math.hypot(x1 - x2, y1 - y2); }; /* 是否在某个节点内,手否进行连接,有offset控制粒度,与outline有关,可以优化 */ export var isInNode = function (position, node, offset) { if (offset === void 0) { offset = 0; } var inNode = false; var bBox = getNodeBBox(node); var _a = __read(new Matrix([position.x, position.y, 1]) .translate(-node.x, -node.y) .rotate(-node.rotate) .translate(node.x, node.y)[0], 2), x = _a[0], y = _a[1]; var reverseRotatedPosition = { x: x, y: y, }; if (reverseRotatedPosition.x >= bBox.minX - offset && reverseRotatedPosition.x <= bBox.maxX + offset && reverseRotatedPosition.y >= bBox.minY - offset && reverseRotatedPosition.y <= bBox.maxY + offset) { inNode = true; } return inNode; }; export var isInNodeBbox = function (position, node) { return isInNode(position, node, 5); }; /* 获取节点bbox */ export var getNodeBBox = function (node) { var x = node.x, y = node.y, width = node.width, height = node.height; return { minX: x - width / 2, minY: y - height / 2, maxX: x + width / 2, maxY: y + height / 2, x: x, y: y, width: width, height: height, centerX: x, centerY: y, }; }; export var getRectRadiusCircle = function (node) { var _a = node, x = _a.x, y = _a.y, width = _a.width, height = _a.height, radius = _a.radius; return [ { x: x - width / 2 + radius, y: y - height / 2 + radius, r: radius, }, { x: x + width / 2 - radius, y: y - height / 2 + radius, r: radius, }, { x: x - width / 2 + radius, y: y + height / 2 - radius, r: radius, }, { x: x + width / 2 - radius, y: y + height / 2 - radius, r: radius, }, ]; }; export var getClosestRadiusCenter = function (point, direction, node) { var radiusCenter = getRectRadiusCircle(node); var closestRadiusPoint; var minDistance = Number.MAX_SAFE_INTEGER; radiusCenter.forEach(function (item) { var radiusDistance = distance(point.x, point.y, item.x, item.y); if (radiusDistance < minDistance) { minDistance = radiusDistance; closestRadiusPoint = item; } }); return getCrossPointWithCircle(point, direction, closestRadiusPoint); }; /* 求点在垂直或者水平方向上与圆形的交点 */ export var getCrossPointWithCircle = function (point, direction, node) { var crossPoint; var x = node.x, y = node.y, r = node.r; if (direction === SegmentDirection.HORIZONTAL) { // 水平,x轴 var crossLeft = x - Math.sqrt(r * r - (point.y - y) * (point.y - y)); var crossRight = x + Math.sqrt(r * r - (point.y - y) * (point.y - y)); var crossX = Math.abs(crossLeft - point.x) < Math.abs(crossRight - point.x) ? crossLeft : crossRight; crossPoint = { x: crossX, y: point.y, }; } else if (direction === SegmentDirection.VERTICAL) { // 垂直,y轴 var crossTop = y - Math.sqrt(r * r - (point.x - x) * (point.x - x)); var crossBottom = y + Math.sqrt(r * r - (point.x - x) * (point.x - x)); var crossY = Math.abs(crossTop - point.y) < Math.abs(crossBottom - point.y) ? crossTop : crossBottom; crossPoint = { x: point.x, y: crossY, }; } return crossPoint; }; /* 判断点所在边的方向 */ export var pointEdgeDirection = function (point, node) { var dx = Math.abs(point.x - node.x); var dy = Math.abs(point.y - node.y); return dx / node.width > dy / node.height ? SegmentDirection.VERTICAL : SegmentDirection.HORIZONTAL; }; // 判断矩形外框上一点是否在矩形直行线上 export var inStraightLineOfRect = function (point, node) { var rect = node; var isInStraight = false; var x = rect.x, y = rect.y, width = rect.width, height = rect.height, radius = rect.radius; var rectBox = { minX: x - width / 2 + radius, maxX: x + width / 2 - radius, minY: y - height / 2 + radius, maxY: y + height / 2 - radius, }; if (point.y === y + height / 2 || point.y === y - height / 2) { isInStraight = point.x > rectBox.minX && point.x < rectBox.maxX; } else if (point.x === x + width / 2 || point.x === x - width / 2) { isInStraight = point.y > rectBox.minY && point.y < rectBox.maxY; } return isInStraight; }; /* 求点在垂直或者水平方向上与椭圆的交点 */ export var getCrossPointWithEllipse = function (point, direction, node) { var crossPoint; var _a = node, x = _a.x, y = _a.y, rx = _a.rx, ry = _a.ry; if (direction === SegmentDirection.HORIZONTAL) { // 水平 var crossLeft = x - Math.sqrt(rx * rx - ((point.y - y) * (point.y - y) * rx * rx) / (ry * ry)); var crossRight = x + Math.sqrt(rx * rx - ((point.y - y) * (point.y - y) * rx * rx) / (ry * ry)); var crossX = Math.abs(crossLeft - point.x) < Math.abs(crossRight - point.x) ? crossLeft : crossRight; crossPoint = { x: crossX, y: point.y, }; } else if (direction === SegmentDirection.VERTICAL) { // 垂直 var crossTop = y - Math.sqrt(ry * ry - ((point.x - x) * (point.x - x) * ry * ry) / (rx * rx)); var crossBottom = y + Math.sqrt(ry * ry - ((point.x - x) * (point.x - x) * ry * ry) / (rx * rx)); var crossY = Math.abs(crossTop - point.y) < Math.abs(crossBottom - point.y) ? crossTop : crossBottom; crossPoint = { x: point.x, y: crossY, }; } return crossPoint; }; /* 求点在垂直或者水平方向上与多边形的交点 */ export var getCrossPointWithPolygon = function (point, direction, node) { var pointsPosition = node.pointsPosition; 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; var a = start; var b = end; if (start.x > end.x) { a = end; b = start; } var pointXY = { x: point.x, y: point.y, }; // 如果多边形当前线段是垂直,求交点 if (a.x === b.x && direction === SegmentDirection.HORIZONTAL) { pointXY = { x: a.x, y: point.y, }; } // 如果多边形当前线段是水平,求交点 if (a.y === b.y && direction === SegmentDirection.VERTICAL) { pointXY = { x: point.x, y: a.y, }; } // 如果线段不是水平或者垂直, 使用向量方程进行计算 if (a.x !== b.x && a.y !== b.y) { var k = (b.y - a.y) / (b.x - a.x); var m = (a.x * b.y - b.x * a.y) / (a.x - b.x); if (direction === SegmentDirection.HORIZONTAL) { pointXY = { x: (point.y - m) / k, y: point.y, }; } else if (direction === SegmentDirection.VERTICAL) { pointXY = { x: point.x, y: k * point.x + m, }; } } // 如果交点在线段上 var inSegment = isInSegment(pointXY, start, end); if (inSegment) { var currentDistance = distance(pointXY.x, pointXY.y, point.x, point.y); if (currentDistance < minDistance) { minDistance = currentDistance; crossPoint = pointXY; } } }); return crossPoint; }; // 规范节点初始化数据 export var pickNodeConfig = function (data) { var nodeData = pick(data, [ 'id', 'type', 'x', 'y', 'text', 'label', 'properties', 'virtual', // 区域节点是否为dnd添加的虚拟节点 'rotate', // TODO: 确认是否可以加到此处 'resizable', 'rotatable', ]); return nodeData; }; /** * 基于节点的边,重新获取新的节点 */ export var getNodeAnchorPosition = function (center, point, width, height) { var x = center.x, y = center.y; if (point.x > center.x) { x = center.x + width / 2; } else if (point.x < center.x) { x = center.x - width / 2; } if (point.y > center.y) { y = center.y + height / 2; } else if (point.y < center.y) { y = center.y - height / 2; } return { x: x, y: y, }; }; /********************************************************* * Text 节点文本相关工具函数 ********************************************************/ // Text 相关节点工具函数 // TODO: 获取文案高度,设置自动换行,利用 dom 计算高度 // function getTextHeight(text: string, font: string): number { // const span = document.createElement('span'); // span.textContent = text; // span.style.font = font; // const range = document.createRange(); // range.selectNodeContents(span); // const rect = range.getBoundingClientRect(); // const height = rect.height; // return height; // } // 获取文案高度,自动换行,利用 dom 计算高度 export var getHtmlTextHeight = function (_a) { var rows = _a.rows, style = _a.style, rowsLength = _a.rowsLength, className = _a.className; var dom = document.createElement('div'); dom.className = className; dom.style.fontSize = "".concat(style.fontSize); dom.style.width = "".concat(style.width); dom.style.lineHeight = "".concat(style.lineHeight); dom.style.padding = "".concat(style.padding); if (style.fontFamily) { dom.style.fontFamily = "".concat(style.fontFamily); } if (rowsLength > 1) { rows.forEach(function (row) { var rowDom = document.createElement('div'); rowDom.textContent = row; dom.appendChild(rowDom); }); } else { dom.textContent = rows[0]; } document.body.appendChild(dom); var height = dom.clientHeight; document.body.removeChild(dom); return height; }; // 获取文案高度,自动换行,利用dom计算高度 export var getSvgTextWidthHeight = function (_a) { var rows = _a.rows, rowsLength = _a.rowsLength, fontSize = _a.fontSize; var longestBytes = 0; rows && rows.forEach(function (item) { var rowByteLength = getBytesLength(item); longestBytes = rowByteLength > longestBytes ? rowByteLength : longestBytes; }); // 背景框宽度,最长一行字节数/2 * fontsize + 2 // 背景框宽度, 行数 * fontsize + 2 return { width: Math.ceil(longestBytes / 2) * fontSize + fontSize / 4, height: rowsLength * (fontSize + 2) + fontSize / 4, }; }; /** * @description 格式化边校验信息 */ export var formatAnchorConnectValidateData = function (data) { if (typeof data !== 'object') { return { isAllPass: !!data, msg: data ? '' : '不允许连接', }; } return data; };