123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- /* eslint-disable standard/array-bracket-even-spacing */
- import editorStyle from '../util/defaultStyle'
- const uniqBy = (arr, key) => {
- const result = []
- arr.forEach(i => {
- if (!result.find(r => r[key] === i[key])) { result.push(i) }
- })
- return result
- }
- export default function(G6) {
- G6.registerEdge('flow-polyline-round', {
- options: {
- style: {
- ...editorStyle.edgeStyle
- },
- stateStyles: {
- selected: {
- lineWidth: editorStyle.edgeSelectedStyle.lineWidth,
- stroke: editorStyle.edgeSelectedStyle.stroke
- },
- hover: {
- stroke: editorStyle.edgeActivedStyle.stroke
- }
- }
- },
- setState(name, value, item) {
- const group = item.getContainer()
- const path = group.getChildByIndex(0)
- if (name === 'selected') {
- if (value) {
- path.attr('lineWidth', this.options.stateStyles.selected.lineWidth)
- path.attr('stroke', this.options.stateStyles.selected.stroke)
- // path.attr('stroke', this.options.style.stroke)
- } else {
- path.attr('lineWidth', this.options.style.lineWidth)
- path.attr('stroke', this.options.style.stroke)
- }
- } else if (name === 'hover') {
- if (value) { path.attr('stroke', this.options.stateStyles.hover.stroke) } else { path.attr('stroke', this.options.style.stroke) }
- }
- },
- drawShape(cfg, group) {
- this.group = group
- const shapeStyle = this.getShapeStyle(cfg)
- const shape = group.addShape('path', {
- className: 'edge-shape',
- attrs: shapeStyle
- })
- return shape
- },
- drawLabel(cfg, group) {
- const labelCfg = cfg.labelCfg || {}
- const labelStyle = this.getLabelStyle(cfg, labelCfg, group)
- const label = group.addShape('text', {
- attrs: labelStyle
- })
- const labelBBox = label.getBBox()
- group.addShape('rect', {
- className: 'edge-labelRect',
- attrs: {
- x: labelBBox.x - editorStyle.edgeLabelRectPadding / 2,
- y: labelBBox.y - editorStyle.edgeLabelRectPadding / 2,
- width: labelBBox.width + editorStyle.edgeLabelRectPadding,
- height: labelBBox.height + editorStyle.edgeLabelRectPadding,
- fill: '#fff',
- stroke: '#fff'
- }
- })
- group.toBack()
- label.toFront()
- return label
- },
- afterUpdate(cfg, item) {
- const label = item.getContainer().findByClassName('edge-label')
- const labelRect = item.getContainer().findByClassName('edge-labelRect')
- if (label) {
- const labelBBox = label.getBBox()
- labelRect.attr({
- x: labelBBox.x - editorStyle.edgeLabelRectPadding / 2,
- y: labelBBox.y - editorStyle.edgeLabelRectPadding / 2,
- width: labelBBox.width + editorStyle.edgeLabelRectPadding,
- height: labelBBox.height + editorStyle.edgeLabelRectPadding
- })
- }
- },
- getShapeStyle(cfg) {
- cfg = this.getPathPoints(cfg)
- const startPoint = cfg.startPoint
- const endPoint = cfg.endPoint
- const controlPoints = this.getControlPoints(cfg)
- let points = [startPoint]
- if (controlPoints) {
- points = points.concat(controlPoints)
- }
- points.push(endPoint)
- const path = this.getPath(points)
- let style = this.options.style
- if (cfg.reverse) { style = { ...style, lineDash: [1, 3] } } else { style = { ...style, lineDash: null } }
- return {
- path,
- ...style,
- endArrow: {
- path: 'M 0,0 L -10,-4 S -8 0,-10 4 Z'
- }
- }
- },
- getPath(points) {
- const path = []
- for (let i = 0; i < points.length; i++) {
- const point = points[i]
- if (i === 0) {
- path.push(['M', point.x, point.y])
- } else if (i === points.length - 1) {
- path.push(['L', point.x, point.y])
- } else {
- const prevPoint = points[i - 1]
- const nextPoint = points[i + 1]
- let cornerLen = 5
- if (Math.abs(point.y - prevPoint.y) > cornerLen || Math.abs(point.x - prevPoint.x) > cornerLen) {
- if (prevPoint.x === point.x) {
- path.push(['L', point.x, point.y > prevPoint.y ? point.y - cornerLen : point.y + cornerLen])
- } else if (prevPoint.y === point.y) {
- path.push(['L', point.x > prevPoint.x ? point.x - cornerLen : point.x + cornerLen, point.y])
- }
- }
- const yLen = Math.abs(point.y - nextPoint.y)
- const xLen = Math.abs(point.x - nextPoint.x)
- if (yLen > 0 && yLen < cornerLen) {
- cornerLen = yLen
- } else if (xLen > 0 && xLen < cornerLen) {
- cornerLen = xLen
- }
- if (prevPoint.x !== nextPoint.x && nextPoint.x === point.x) {
- path.push(['Q', point.x, point.y, point.x, point.y > nextPoint.y ? point.y - cornerLen : point.y + cornerLen])
- } else if (prevPoint.y !== nextPoint.y && nextPoint.y === point.y) {
- path.push(['Q', point.x, point.y, point.x > nextPoint.x ? point.x - cornerLen : point.x + cornerLen, point.y])
- }
- }
- }
- return path
- },
- getControlPoints(cfg) {
- if (!cfg.sourceNode) {
- return cfg.controlPoints
- }
- return this.polylineFinding(cfg.sourceNode, cfg.targetNode, cfg.startPoint, cfg.endPoint, 15)
- },
- getExpandedBBox(bbox, offset) {
- return bbox.width === 0 && bbox.height === 0 ? bbox : {
- 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
- }
- },
- getExpandedPort(bbox, point) {
- return Math.abs(point.x - bbox.centerX) / bbox.width > Math.abs(point.y - bbox.centerY) / bbox.height
- ? { x: point.x > bbox.centerX ? bbox.maxX : bbox.minX, y: point.y }
- : { x: point.x, y: point.y > bbox.centerY ? bbox.maxY : bbox.minY }
- },
- combineBBoxes(sBBox, tBBox) {
- const minX = Math.min(sBBox.minX, tBBox.minX); const minY = Math.min(sBBox.minY, tBBox.minY)
- const maxX = Math.max(sBBox.maxX, tBBox.maxX); const maxY = Math.max(sBBox.maxY, tBBox.maxY)
- return {
- centerX: (minX + maxX) / 2,
- centerY: (minY + maxY) / 2,
- minX: minX,
- minY: minY,
- maxX: maxX,
- maxY: maxY,
- height: maxY - minY,
- width: maxX - minX
- }
- },
- getBBoxFromVertexes(sPoint, tPoint) {
- const minX = Math.min(sPoint.x, tPoint.x); const maxX = Math.max(sPoint.x, tPoint.x)
- const minY = Math.min(sPoint.y, tPoint.y); const maxY = Math.max(sPoint.y, tPoint.y)
- return {
- centerX: (minX + maxX) / 2,
- centerY: (minY + maxY) / 2,
- maxX: maxX,
- maxY: maxY,
- minX: minX,
- minY: minY,
- height: maxY - minY,
- width: maxX - minX
- }
- },
- vertexOfBBox(bbox) {
- return [{ x: bbox.minX, y: bbox.minY }, { x: bbox.maxX, y: bbox.minY }, { x: bbox.maxX, y: bbox.maxY }, { x: bbox.minX, y: bbox.maxY }]
- },
- crossPointsByLineAndBBox(bbox, centerPoint) {
- let crossPoints = []
- if (!(centerPoint.x < bbox.minX || centerPoint.x > bbox.maxX)) { crossPoints = crossPoints.concat([{ x: centerPoint.x, y: bbox.minY }, { x: centerPoint.x, y: bbox.maxY }]) }
- if (!(centerPoint.y < bbox.minY || centerPoint.y > bbox.maxY)) { crossPoints = crossPoints.concat([{ x: bbox.minX, y: centerPoint.y }, { x: bbox.maxX, y: centerPoint.y }]) }
- return crossPoints
- },
- getConnectablePoints(sBBox, tBBox, sPoint, tPoint) {
- const lineBBox = this.getBBoxFromVertexes(sPoint, tPoint)
- const outerBBox = this.combineBBoxes(sBBox, tBBox)
- const sLineBBox = this.combineBBoxes(sBBox, lineBBox)
- const tLineBBox = this.combineBBoxes(tBBox, lineBBox)
- let points = []
- points = points.concat(this.vertexOfBBox(sLineBBox), this.vertexOfBBox(tLineBBox), this.vertexOfBBox(outerBBox))
- const centerPoint = { x: outerBBox.centerX, y: outerBBox.centerY };
- [outerBBox, sLineBBox, tLineBBox, lineBBox].forEach(bbox => {
- points = points.concat(this.crossPointsByLineAndBBox(bbox, centerPoint))
- })
- points.push({ x: sPoint.x, y: tPoint.y })
- points.push({ x: tPoint.x, y: sPoint.y })
- return points
- },
- filterConnectablePoints(points, bbox) {
- return points.filter(point => point.x <= bbox.minX || point.x >= bbox.maxX || point.y <= bbox.minY || point.y >= bbox.maxY)
- },
- AStar(points, sPoint, tPoint, sBBox, tBBox) {
- const openList = [sPoint]
- const closeList = []
- points = uniqBy(this.fillId(points), 'id')
- points.push(tPoint)
- let endPoint
- while (openList.length > 0) {
- let minCostPoint
- openList.forEach((p, i) => {
- if (!p.parent) { p.f = 0 }
- if (!minCostPoint) { minCostPoint = p }
- if (p.f < minCostPoint.f) { minCostPoint = p }
- })
- if (minCostPoint.x === tPoint.x && minCostPoint.y === tPoint.y) {
- endPoint = minCostPoint
- break
- }
- openList.splice(openList.findIndex(o => o.x === minCostPoint.x && o.y === minCostPoint.y), 1)
- closeList.push(minCostPoint)
- const neighbor = points.filter(p => (p.x === minCostPoint.x || p.y === minCostPoint.y) &&
- !(p.x === minCostPoint.x && p.y === minCostPoint.y) &&
- !this.crossBBox([sBBox, tBBox], minCostPoint, p))
- neighbor.forEach(p => {
- const inOpen = openList.find(o => o.x === p.x && o.y === p.y)
- const currentG = this.getCost(p, minCostPoint)
- // eslint-disable-next-line no-empty
- if (closeList.find(o => o.x === p.x && o.y === p.y)) {
- } else if (inOpen) {
- if (p.g > currentG) {
- p.parent = minCostPoint
- p.g = currentG
- p.f = p.g + p.h
- }
- } else {
- p.parent = minCostPoint
- p.g = currentG
- let h = this.getCost(p, tPoint)
- if (this.crossBBox([tBBox], p, tPoint)) {
- h += (tBBox.width / 2 + tBBox.height / 2) // 如果穿过bbox则增加该点的预估代价为bbox周长的一半
- }
- p.h = h
- p.f = p.g + p.h
- openList.push(p)
- }
- })
- }
- if (endPoint) {
- const result = []
- result.push({ x: endPoint.x, y: endPoint.y })
- while (endPoint.parent) {
- endPoint = endPoint.parent
- result.push({ x: endPoint.x, y: endPoint.y })
- }
- return result.reverse()
- }
- return []
- },
- crossBBox(bboxes, p1, p2) {
- for (let i = 0; i < bboxes.length; i++) {
- const bbox = bboxes[i]
- if (p1.x === p2.x && bbox.minX < p1.x && bbox.maxX > p1.x) {
- if ((p1.y < bbox.maxY && p2.y >= bbox.maxY) || (p2.y < bbox.maxY && p1.y >= bbox.maxY)) { return true }
- } else if (p1.y === p2.y && bbox.minY < p1.y && bbox.maxY > p1.y) {
- if ((p1.x < bbox.maxX && p2.x >= bbox.maxX) || (p2.x < bbox.maxX && p1.x >= bbox.maxX)) { return true }
- }
- }
- return false
- },
- getCost(p1, p2) {
- return Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)
- },
- getPointBBox(t) {
- return { centerX: t.x, centerY: t.y, minX: t.x, minY: t.y, maxX: t.x, maxY: t.y, height: 0, width: 0 }
- },
- fillId(points) {
- points.forEach(p => {
- p.id = p.x + '-' + p.y
- })
- return points
- },
- polylineFinding(sNode, tNode, sPort, tPort, offset) {
- const sourceBBox = sNode && sNode.getBBox() ? sNode.getBBox() : this.getPointBBox(sPort)
- const targetBBox = tNode && tNode.getBBox() ? tNode.getBBox() : this.getPointBBox(tPort)
- const sBBox = this.getExpandedBBox(sourceBBox, offset)
- const tBBox = this.getExpandedBBox(targetBBox, offset)
- const sPoint = this.getExpandedPort(sBBox, sPort)
- const tPoint = this.getExpandedPort(tBBox, tPort)
- let points = this.getConnectablePoints(sBBox, tBBox, sPoint, tPoint)
- points = this.filterConnectablePoints(points, sBBox)
- points = this.filterConnectablePoints(points, tBBox)
- const polylinePoints = this.AStar(points, sPoint, tPoint, sBBox, tBBox)
- return polylinePoints
- }
- }, 'polyline')
- }
|