UNPKG

@logicflow/extension

Version:
641 lines (640 loc) 20.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; }; 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)); }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ManhattanLayout = exports.getOrient = exports.PriorityQueue = void 0; var NodeBase = /** @class */ (function () { function NodeBase(x, y) { this.x = x; this.y = y; this.G = 0; this.H = 0; this.isProcessed = false; this.connection = null; this.from = null; } Object.defineProperty(NodeBase.prototype, "F", { get: function () { return this.G + this.H; }, enumerable: false, configurable: true }); NodeBase.prototype.setProcessed = function () { this.isProcessed = true; }; NodeBase.prototype.setConnection = function (connection) { this.connection = connection; }; NodeBase.prototype.setFrom = function (from) { this.from = from; }; NodeBase.prototype.setG = function (g) { this.G = g; }; NodeBase.prototype.setH = function (h) { this.H = h; }; NodeBase.prototype.getManhattanDistanceTo = function (point) { var _a = this, x1 = _a.x, y1 = _a.y; var x2 = point.x, y2 = point.y; return Math.abs(x1 - x2) + Math.abs(y1 - y2); }; return NodeBase; }()); var PriorityQueue = /** @class */ (function () { function PriorityQueue() { this.heap = []; } PriorityQueue.prototype.enqueue = function (node, priority) { this.heap.push({ node: node, priority: priority, }); this.bubbleUp(this.heap.length - 1); }; PriorityQueue.prototype.dequeue = function () { var min = this.heap[0]; var end = this.heap.pop(); if (this.heap.length > 0) { this.heap[0] = end; this.sinkDown(0); } return min; }; PriorityQueue.prototype.bubbleUp = function (index) { var node = this.heap[index]; while (index > 0) { var parentIndex = Math.floor((index - 1) / 2); var parent_1 = this.heap[parentIndex]; if (node.priority >= parent_1.priority) break; this.heap[parentIndex] = node; this.heap[index] = parent_1; index = parentIndex; } }; PriorityQueue.prototype.sinkDown = function (index) { var leftChildIndex = 2 * index + 1; var rightChildIndex = 2 * index + 2; var smallestChildIndex = index; var length = this.heap.length; if (leftChildIndex < length && this.heap[leftChildIndex].priority < this.heap[smallestChildIndex].priority) { smallestChildIndex = leftChildIndex; } if (rightChildIndex < length && this.heap[rightChildIndex].priority < this.heap[smallestChildIndex].priority) { smallestChildIndex = rightChildIndex; } if (smallestChildIndex !== index) { var swapNode = this.heap[smallestChildIndex]; this.heap[smallestChildIndex] = this.heap[index]; this.heap[index] = swapNode; this.sinkDown(smallestChildIndex); } }; PriorityQueue.prototype.isEmpty = function () { return this.heap.length === 0; }; return PriorityQueue; }()); exports.PriorityQueue = PriorityQueue; function expandBBox(bbox, offset) { var minX = bbox.minX, minY = bbox.minY, maxX = bbox.maxX, maxY = bbox.maxY; return { minX: minX - offset, minY: minY - offset, maxX: maxX + offset, maxY: maxY + offset, }; } function getPointsFromBBoxBorder(bbox) { var minX = bbox.minX, minY = bbox.minY, maxX = bbox.maxX, maxY = bbox.maxY; return [ { x: minX, y: minY, }, { x: minX + (maxX - minX) / 2, y: minY, }, { x: maxX, y: minY, }, { x: maxX, y: minY + (maxY - minY) / 2, }, { x: maxX, y: maxY, }, { x: minX + (maxX - minX) / 2, y: maxY, }, { x: minX, y: maxY, }, { x: minX, y: minY + (maxY - minY) / 2, }, ]; } function getHull(points) { var xs = points.map(function (item) { return item.x; }); var ys = points.map(function (item) { return item.y; }); return { minX: Math.min.apply(Math, __spreadArray([], __read(xs), false)), minY: Math.min.apply(Math, __spreadArray([], __read(ys), false)), maxX: Math.max.apply(Math, __spreadArray([], __read(xs), false)), maxY: Math.max.apply(Math, __spreadArray([], __read(ys), false)), }; } function isPointInsideTheBoxes(point, bboxes) { var e_1, _a; var flag = false; try { for (var bboxes_1 = __values(bboxes), bboxes_1_1 = bboxes_1.next(); !bboxes_1_1.done; bboxes_1_1 = bboxes_1.next()) { var bbox = bboxes_1_1.value; if (isBBoxContainThePoint(bbox, point)) { flag = true; break; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (bboxes_1_1 && !bboxes_1_1.done && (_a = bboxes_1.return)) _a.call(bboxes_1); } finally { if (e_1) throw e_1.error; } } return flag; } function isBBoxContainThePoint(bbox, p) { var x = p.x, y = p.y; var minX = bbox.minX, minY = bbox.minY, maxX = bbox.maxX, maxY = bbox.maxY; // ignore the point on the border return x > minX && x < maxX && y > minY && y < maxY; } function isSegmentsIntersected(seg1, seg2) { var _a = __read(seg1, 2), p0 = _a[0], p1 = _a[1]; var _b = __read(seg2, 2), p2 = _b[0], p3 = _b[1]; 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; } function getVerticesFromBBox(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, }, ]; } function isSegmentCrossingBBox(line, bbox) { var _a = __read(line, 2), p1 = _a[0], p2 = _a[1]; var minX = bbox.minX, minY = bbox.minY, maxX = bbox.maxX, maxY = bbox.maxY; var width = Math.abs(maxX - minX); var height = Math.abs(maxY - minY); if (width === 0 && height === 0) { return false; } var _b = __read(getVerticesFromBBox(bbox), 4), pa = _b[0], pb = _b[1], pc = _b[2], pd = _b[3]; var count = 0; if (isSegmentsIntersected([p1, p2], [pa, pb])) { count++; } if (isSegmentsIntersected([p1, p2], [pa, pd])) { count++; } if (isSegmentsIntersected([p1, p2], [pb, pc])) { count++; } if (isSegmentsIntersected([p1, p2], [pc, pd])) { count++; } return count !== 0; } function aStarFindPathByGrid(startNode, endNode, step, bboxes, outside) { var toSearch = [startNode]; var searchSet = new Set(); var _loop_1 = function () { var e_2, _a, e_3, _b; var current = toSearch[0]; try { for (var toSearch_1 = (e_2 = void 0, __values(toSearch)), toSearch_1_1 = toSearch_1.next(); !toSearch_1_1.done; toSearch_1_1 = toSearch_1.next()) { var item = toSearch_1_1.value; if (item.F < current.F || (item.F === current.F && item.H < current.H)) { current = item; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (toSearch_1_1 && !toSearch_1_1.done && (_a = toSearch_1.return)) _a.call(toSearch_1); } finally { if (e_2) throw e_2.error; } } if ("".concat(current.x, "/").concat(current.y) === "".concat(endNode.x, "/").concat(endNode.y)) { var res = [ { x: current.x, y: current.y, }, ]; while (current.connection) { var connection = current.connection; res.push({ x: connection.x, y: connection.y, }); current = current.connection; } return { value: res.reverse() }; } var val = "".concat(current.x, "/").concat(current.y); !searchSet.has(val) && searchSet.add(val); toSearch = toSearch.filter(function (item) { return "".concat(current.x, "/").concat(current.y) !== "".concat(item.x, "/").concat(item.y); }); var neighborsRes = findNeighborsByGridStep(current, step, bboxes, outside).filter(function (item) { var flag = !isPointInsideTheBoxes(item, bboxes); return flag; }); var tmpRes = []; neighborsRes.forEach(function (item) { var key = "".concat(item.x, "/").concat(item.y); if (!searchSet.has(key)) { tmpRes.push(item); tmpRes.push(item); } }); try { for (var tmpRes_1 = (e_3 = void 0, __values(tmpRes)), tmpRes_1_1 = tmpRes_1.next(); !tmpRes_1_1.done; tmpRes_1_1 = tmpRes_1.next()) { var neighbor = tmpRes_1_1.value; if (neighbor.isProcessed) continue; var inSearch = toSearch.includes(current); var costToNeighbor = current.G + current.getManhattanDistanceTo(neighbor); if (!inSearch || costToNeighbor < neighbor.G) { neighbor.setG(costToNeighbor); neighbor.setConnection(current); current.setFrom(neighbor); if (!inSearch) { neighbor.setH(neighbor.getManhattanDistanceTo(endNode)); toSearch.push(neighbor); } } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (tmpRes_1_1 && !tmpRes_1_1.done && (_b = tmpRes_1.return)) _b.call(tmpRes_1); } finally { if (e_3) throw e_3.error; } } }; while (toSearch.length) { var state_1 = _loop_1(); if (typeof state_1 === "object") return state_1.value; } return null; } function findNeighborsByGridStep(cur, step, bboxes, outside) { var neighbors = []; var x = cur.x, y = cur.y; var minX = outside.minX, minY = outside.minY, maxX = outside.maxX, maxY = outside.maxY; var x1 = x - step; var x2 = x + step; var y1 = y - step; var y2 = y + step; // eslint-disable-next-line no-shadow function isValid(cur, neighbor, bboxes) { var e_4, _a; var flag = !isPointInsideTheBoxes(neighbor, bboxes) && !isPointInsideTheBoxes(cur, bboxes); if (!flag) return false; try { for (var bboxes_2 = __values(bboxes), bboxes_2_1 = bboxes_2.next(); !bboxes_2_1.done; bboxes_2_1 = bboxes_2.next()) { var bbox = bboxes_2_1.value; if (isSegmentCrossingBBox([ { x: cur.x, y: cur.y, }, { x: neighbor.x, y: neighbor.y, }, ], bbox)) { flag = false; break; } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (bboxes_2_1 && !bboxes_2_1.done && (_a = bboxes_2.return)) _a.call(bboxes_2); } finally { if (e_4) throw e_4.error; } } return flag; } if (x1 >= minX) { isValid(cur, { x: x1, y: y, }, bboxes) && neighbors.push(new NodeBase(x1, y)); } if (x2 <= maxX) { isValid(cur, { x: x2, y: y, }, bboxes) && neighbors.push(new NodeBase(x2, y)); } if (y1 >= minY) { isValid(cur, { x: x, y: y1, }, bboxes) && neighbors.push(new NodeBase(x, y1)); } if (y2 <= maxY) { isValid(cur, { x: x, y: y2, }, bboxes) && neighbors.push(new NodeBase(x, y2)); } return neighbors; } function getAnchorWithOffset(_a, node, offset) { var bbox = _a.bbox; var minX = bbox.minX, minY = bbox.minY, maxX = bbox.maxX, maxY = bbox.maxY; var x = node.x, y = node.y; if (x === minX) { return { x: x - offset, y: y, }; } if (x === maxX) { return { x: x + offset, y: y, }; } if (y === minY) { return { x: x, y: y - offset, }; } if (y === maxY) { return { x: x, y: y + offset, }; } } function perpendicularDistance(point, lineStart, lineEnd) { var x1 = lineStart.x, y1 = lineStart.y; var x2 = lineEnd.x, y2 = lineEnd.y; var x = point.x, y = point.y; if (x1 === x2) { // 线段是垂直的 return Math.abs(x - x1); } if (y1 === y2) { // 线段是水平的 return Math.abs(y - y1); } // 计算点到线段垂直点的坐标 var px = x1 + ((x2 - x1) * ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1))) / (Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2)); var py = y1 + ((y2 - y1) * ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1))) / (Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2)); // 计算曼哈顿距离 return Math.abs(x - px) + Math.abs(y - py); } function perpendicularToStraight(line) { // Step 1: Convert perpendicular segments to straight lines var straightLine = [line[0]]; for (var i = 0; i < line.length - 2; i++) { var point1 = line[i]; var point2 = line[i + 1]; var point3 = line[i + 2]; if (isVertical(point1, point2, point3) || isHorizontal(point1, point2, point3)) { // Remove point2 to make it a straight line continue; } straightLine.push(point2); } straightLine.push(line[line.length - 1]); // Step 2: Douglas-Peucker algorithm to remove redundant points // return straightLine; var epsilon = 1.0; // Adjust epsilon based on your requirements return douglasPeucker(straightLine, epsilon); } function isVertical(p1, p2, p3) { return p1.x === p2.x && p2.x === p3.x; } function isHorizontal(p1, p2, p3) { return p1.y === p2.y && p2.y === p3.y; } function douglasPeucker(points, epsilon) { var dmax = 0; var index = 0; for (var i = 1; i < points.length - 1; i++) { var d = perpendicularDistance(points[i], points[0], points[points.length - 1]); if (d > dmax) { index = i; dmax = d; } } if (dmax > epsilon) { var left = douglasPeucker(points.slice(0, index + 1), epsilon); var right = douglasPeucker(points.slice(index), epsilon); return left.slice(0, left.length - 1).concat(right); } return [points[0], points[points.length - 1]]; } // 每三个点如果其横坐标或者纵坐标都相同,则取其二 function getSimplePath(path) { // if (path.length < 5) return path; path = circleDetection(path); var res = []; for (var i = 0; i < path.length;) { var point1 = path[i]; var point2 = path[i + 1]; var point3 = path[i + 2]; if (!point3) { res.push(point1); i++; continue; } if ((point1.x === point2.x && point2.x === point3.x) || (point1.y === point2.y && point2.y === point3.y)) { res.push(point1); res.push(point3); i += 3; } else { res.push(point1); i++; } } return res; } // 回环检测 & 处理 function circleDetection(path) { if (path.length < 6) return path; var res = []; for (var i = 0; i < path.length;) { var point1 = path[i]; var point2 = path[i + 1]; var point4 = path[i + 3]; var point5 = path[i + 4]; if (!point5) { res.push(point1); i++; continue; } if (isSegmentsIntersected([point1, point2], [point4, point5])) { var x = 0; var y = 0; if (point1.x === point2.x) { x = point1.x; y = point4.y; } else { x = point4.x; y = point1.y; } res.push({ x: x, y: y, }); res.push(point5); i += 4; continue; } res.push(point1); i++; } return res; } function getOrient(start, end) { var x1 = start.x, y1 = start.y; var x2 = end.x, y2 = end.y; var prefix = ''; var suffix = ''; if (x1 >= x2) { prefix = 'left'; } else { prefix = 'right'; } if (y1 >= y2) { suffix = 'top'; } else { suffix = 'bottom'; } return "".concat(prefix, ":").concat(suffix); } exports.getOrient = getOrient; function ManhattanLayout(startAnchor, endAnchor, startNode, endNode, // obstacles, offset) { // get expanded bbox var startBBox = startNode.bbox; var endBBox = endNode.bbox; var startExpandBBox = expandBBox(startNode.bbox, offset); var endExpandBBox = expandBBox(endNode.bbox, offset); // get points from bbox border var points1 = getPointsFromBBoxBorder(startExpandBBox); var points2 = getPointsFromBBoxBorder(endExpandBBox); // is bbox overlap // const overlap = isBBoxOverlap(startBBox, endBBox); var outsideBBox = getHull(__spreadArray(__spreadArray([], __read(points1), false), __read(points2), false)); var sNode = getAnchorWithOffset(startNode, startAnchor, offset); var eNode = getAnchorWithOffset(endNode, endAnchor, offset); var sNodeBase = new NodeBase(sNode.x, sNode.y); var eNodeBase = new NodeBase(eNode.x, eNode.y); var path = aStarFindPathByGrid(eNodeBase, sNodeBase, 10, // obstacles, [startBBox, endBBox], // [startExpandBBox, endExpandBBox], outsideBBox); if (path) { var simplifiedPath = perpendicularToStraight(path); return getSimplePath(__spreadArray(__spreadArray([endAnchor], __read(simplifiedPath), false), [startAnchor], false).reverse()); } } exports.ManhattanLayout = ManhattanLayout;