| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| import * as curveTool from 'zrender/src/core/curve'; | |
| import * as vec2 from 'zrender/src/core/vector'; | |
| import {getSymbolSize} from './graphHelper'; | |
| import Graph from '../../data/Graph'; | |
| const v1: number[] = []; | |
| const v2: number[] = []; | |
| const v3: number[] = []; | |
| const quadraticAt = curveTool.quadraticAt; | |
| const v2DistSquare = vec2.distSquare; | |
| const mathAbs = Math.abs; | |
| function intersectCurveCircle( | |
| curvePoints: number[][], | |
| center: number[], | |
| radius: number | |
| ) { | |
| const p0 = curvePoints[0]; | |
| const p1 = curvePoints[1]; | |
| const p2 = curvePoints[2]; | |
| let d = Infinity; | |
| let t; | |
| const radiusSquare = radius * radius; | |
| let interval = 0.1; | |
| for (let _t = 0.1; _t <= 0.9; _t += 0.1) { | |
| v1[0] = quadraticAt(p0[0], p1[0], p2[0], _t); | |
| v1[1] = quadraticAt(p0[1], p1[1], p2[1], _t); | |
| const diff = mathAbs(v2DistSquare(v1, center) - radiusSquare); | |
| if (diff < d) { | |
| d = diff; | |
| t = _t; | |
| } | |
| } | |
| // Assume the segment is monotoneļ¼Find root through Bisection method | |
| // At most 32 iteration | |
| for (let i = 0; i < 32; i++) { | |
| // let prev = t - interval; | |
| const next = t + interval; | |
| // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev); | |
| // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev); | |
| v2[0] = quadraticAt(p0[0], p1[0], p2[0], t); | |
| v2[1] = quadraticAt(p0[1], p1[1], p2[1], t); | |
| v3[0] = quadraticAt(p0[0], p1[0], p2[0], next); | |
| v3[1] = quadraticAt(p0[1], p1[1], p2[1], next); | |
| const diff = v2DistSquare(v2, center) - radiusSquare; | |
| if (mathAbs(diff) < 1e-2) { | |
| break; | |
| } | |
| // let prevDiff = v2DistSquare(v1, center) - radiusSquare; | |
| const nextDiff = v2DistSquare(v3, center) - radiusSquare; | |
| interval /= 2; | |
| if (diff < 0) { | |
| if (nextDiff >= 0) { | |
| t = t + interval; | |
| } | |
| else { | |
| t = t - interval; | |
| } | |
| } | |
| else { | |
| if (nextDiff >= 0) { | |
| t = t - interval; | |
| } | |
| else { | |
| t = t + interval; | |
| } | |
| } | |
| } | |
| return t; | |
| } | |
| // Adjust edge to avoid | |
| export default function adjustEdge(graph: Graph, scale: number) { | |
| const tmp0: number[] = []; | |
| const quadraticSubdivide = curveTool.quadraticSubdivide; | |
| const pts: number[][] = [[], [], []]; | |
| const pts2: number[][] = [[], []]; | |
| const v: number[] = []; | |
| scale /= 2; | |
| graph.eachEdge(function (edge, idx) { | |
| const linePoints = edge.getLayout(); | |
| const fromSymbol = edge.getVisual('fromSymbol'); | |
| const toSymbol = edge.getVisual('toSymbol'); | |
| if (!linePoints.__original) { | |
| linePoints.__original = [ | |
| vec2.clone(linePoints[0]), | |
| vec2.clone(linePoints[1]) | |
| ]; | |
| if (linePoints[2]) { | |
| linePoints.__original.push(vec2.clone(linePoints[2])); | |
| } | |
| } | |
| const originalPoints = linePoints.__original; | |
| // Quadratic curve | |
| if (linePoints[2] != null) { | |
| vec2.copy(pts[0], originalPoints[0]); | |
| vec2.copy(pts[1], originalPoints[2]); | |
| vec2.copy(pts[2], originalPoints[1]); | |
| if (fromSymbol && fromSymbol !== 'none') { | |
| const symbolSize = getSymbolSize(edge.node1); | |
| const t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale); | |
| // Subdivide and get the second | |
| quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0); | |
| pts[0][0] = tmp0[3]; | |
| pts[1][0] = tmp0[4]; | |
| quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0); | |
| pts[0][1] = tmp0[3]; | |
| pts[1][1] = tmp0[4]; | |
| } | |
| if (toSymbol && toSymbol !== 'none') { | |
| const symbolSize = getSymbolSize(edge.node2); | |
| const t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale); | |
| // Subdivide and get the first | |
| quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0); | |
| pts[1][0] = tmp0[1]; | |
| pts[2][0] = tmp0[2]; | |
| quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0); | |
| pts[1][1] = tmp0[1]; | |
| pts[2][1] = tmp0[2]; | |
| } | |
| // Copy back to layout | |
| vec2.copy(linePoints[0], pts[0]); | |
| vec2.copy(linePoints[1], pts[2]); | |
| vec2.copy(linePoints[2], pts[1]); | |
| } | |
| // Line | |
| else { | |
| vec2.copy(pts2[0], originalPoints[0]); | |
| vec2.copy(pts2[1], originalPoints[1]); | |
| vec2.sub(v, pts2[1], pts2[0]); | |
| vec2.normalize(v, v); | |
| if (fromSymbol && fromSymbol !== 'none') { | |
| const symbolSize = getSymbolSize(edge.node1); | |
| vec2.scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale); | |
| } | |
| if (toSymbol && toSymbol !== 'none') { | |
| const symbolSize = getSymbolSize(edge.node2); | |
| vec2.scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale); | |
| } | |
| vec2.copy(linePoints[0], pts2[0]); | |
| vec2.copy(linePoints[1], pts2[1]); | |
| } | |
| }); | |
| } |