feat: add performance diagnosis flowchart demo (#6289)

* feat(polyline): ensure correct orthogonal routing with ports

* chore: rename decision tree to fund flow

* feat: add performance diagnosis demo

* refactor: expose hover-activate getActiveIds function

* feat: add flowchart demo

* fix: fix ci issues

* refactor: performance diagnosis demo
This commit is contained in:
Yuxin 2024-09-06 17:41:31 +08:00 committed by GitHub
parent 19858be16b
commit e3a32ffa05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 932 additions and 148 deletions

View File

@ -30,24 +30,24 @@
]
},
"devDependencies": {
"@antv/g-canvas": "^2.0.10",
"@antv/g-plugin-rough-canvas-renderer": "^2.0.12",
"@antv/g-canvas": "^2.0.12",
"@antv/g-plugin-rough-canvas-renderer": "^2.0.13",
"@babel/core": "^7.25.2",
"@babel/plugin-transform-typescript": "^7.25.2",
"@changesets/cli": "^2.27.7",
"@changesets/cli": "^2.27.8",
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@playwright/test": "^1.46.0",
"@playwright/test": "^1.47.0",
"@rollup/plugin-commonjs": "^25.0.8",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"@swc/core": "^1.7.6",
"@swc/core": "^1.7.23",
"@swc/jest": "^0.2.36",
"@types/d3-hierarchy": "^3.1.7",
"@types/jest": "^29.5.12",
"@types/jsdom": "^21.1.7",
"@types/node": "^20.14.14",
"@types/node": "^20.16.5",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"chalk": "^4.1.2",
@ -55,28 +55,28 @@
"eslint": "^8.57.0",
"eslint-plugin-jsdoc": "^46.10.1",
"husky": "^8.0.3",
"iperf": "^0.1.0-beta.14",
"iperf": "0.1.0-beta.14",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jsdom": "^23.2.0",
"lil-gui": "^0.19.2",
"limit-size": "^0.1.4",
"lint-staged": "^15.2.8",
"lint-staged": "^15.2.10",
"npm-run-all": "^4.1.5",
"open": "^10.1.0",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^3.2.4",
"prettier-plugin-packagejson": "^2.5.1",
"prettier-plugin-packagejson": "^2.5.2",
"rimraf": "^5.0.10",
"rollup": "^4.20.0",
"rollup": "^4.21.2",
"rollup-plugin-polyfill-node": "^0.13.0",
"rollup-plugin-visualizer": "^5.12.0",
"stats.js": "^0.17.0",
"ts-node": "^10.9.2",
"tslib": "^2.6.3",
"tslib": "^2.7.0",
"turbo": "^1.13.4",
"typescript": "^5.5.4",
"vite": "^5.3.5",
"vite": "^5.4.3",
"xml-formatter": "^3.6.3"
},
"pnpm": {

View File

@ -35,7 +35,7 @@
"@antv/g6": "^5.0.0"
},
"devDependencies": {
"@antv/g": "^6.0.5",
"@antv/g": "^6.0.13",
"@antv/g-canvas": "^2.0.4",
"@antv/g-svg": "^2.0.5",
"@commitlint/config-conventional": "^19.2.2",
@ -71,7 +71,7 @@
"xmlserializer": "^0.6.1"
},
"peerDependencies": {
"@antv/g": "^6.0.5",
"@antv/g": "^6.0.13",
"@antv/g-canvas": "^2.0.4"
}
}

View File

@ -34,20 +34,20 @@
},
"dependencies": {
"@antv/g-device-api": "^1.6.12",
"@antv/g-plugin-3d": "^2.0.12",
"@antv/g-plugin-device-renderer": "^2.0.12",
"@antv/g-plugin-dragndrop": "^2.0.8",
"@antv/g-webgl": "^2.0.13",
"@antv/layout": "^1.2.14-beta.6",
"@antv/util": "^3.3.7"
"@antv/g-plugin-3d": "^2.0.14",
"@antv/g-plugin-device-renderer": "^2.0.14",
"@antv/g-plugin-dragndrop": "^2.0.9",
"@antv/g-webgl": "^2.0.16",
"@antv/layout": "1.2.14-beta.6",
"@antv/util": "^3.3.8"
},
"devDependencies": {
"@antv/g": "^6.0.12",
"@antv/g-canvas": "^2.0.10",
"@antv/g": "^6.0.13",
"@antv/g-canvas": "^2.0.12",
"@antv/g6": "workspace:*"
},
"peerDependencies": {
"@antv/g": "^6.0.5",
"@antv/g": "^6.0.13",
"@antv/g-canvas": "^2.0.4",
"@antv/g6": "^5.0.20"
},

View File

@ -1,4 +1,5 @@
export * from './euro-cup';
export * from './g-node';
export * from './graph';
export * from './performance-diagnosis';
export * from './react-node';

View File

@ -0,0 +1,155 @@
import { BugOutlined } from '@ant-design/icons';
import type { EdgeData, Element, GraphData, GraphOptions, IPointerEvent, NodeData } from '@antv/g6';
import { ExtensionCategory, HoverActivate, idOf, register } from '@antv/g6';
import { Flex, Typography } from 'antd';
import { CSSProperties, useEffect, useState } from 'react';
import { Graph } from '../../src/graph';
const { Text } = Typography;
const ACTIVE_COLOR = '#f6c523';
const COLOR_MAP: Record<string, string> = {
'pre-inspection': '#3fc1c9',
problem: '#8983f3',
inspection: '#f48db4',
solution: '#ffaa64',
};
class HoverElement extends HoverActivate {
protected getActiveIds(event: IPointerEvent<Element>) {
const { model, graph } = this.context;
const { targetType, target } = event;
const targetId = target.id;
const ids = [targetId];
if (targetType === 'edge') {
const edge = model.getEdgeDatum(targetId);
ids.push(edge.source, edge.target);
} else if (targetType === 'node') {
ids.push(...model.getRelatedEdgesData(targetId).map(idOf));
}
graph.frontElement(ids);
return ids;
}
}
register(ExtensionCategory.BEHAVIOR, 'hover-element', HoverElement);
const Node = ({ data }: { data: NodeData }) => {
const { text, type } = data.data as { text: string; type: string };
const isHovered = data.states?.includes('active');
const isSelected = data.states?.includes('selected');
const color = isHovered ? ACTIVE_COLOR : COLOR_MAP[type];
const containerStyle: CSSProperties = {
width: '100%',
height: '100%',
background: color,
border: `3px solid ${color}`,
borderRadius: 16,
cursor: 'pointer',
};
if (isSelected) {
Object.assign(containerStyle, { border: `3px solid #000` });
}
return (
<Flex style={containerStyle} align="center" justify="center">
<Flex vertical style={{ padding: '8px 16px', textAlign: 'center' }} align="center" justify="center">
{type === 'problem' && <BugOutlined style={{ color: '#fff', fontSize: 24, marginBottom: 8 }} />}
<Text style={{ color: '#fff', fontWeight: 600, fontSize: 16 }}>{text}</Text>
</Flex>
</Flex>
);
};
export const PerformanceDiagnosis = () => {
const [data, setData] = useState<GraphData>();
useEffect(() => {
fetch('https://assets.antv.antgroup.com/g6/performance-diagnosis.json')
.then((res) => res.json())
.then(setData);
}, []);
const options: GraphOptions = {
data,
animation: false,
width: 800,
height: 600,
autoFit: 'view',
node: {
type: 'react',
style: (d: NodeData) => {
const style: NodeData['style'] = {
component: <Node data={d} />,
ports: [{ placement: 'top' }, { placement: 'bottom' }],
};
const size = {
'pre-inspection': [240, 80],
problem: [200, 80],
inspection: [330, 100],
solution: [240, 60],
}[d.data!.type as string] || [200, 80];
Object.assign(style, {
size,
dx: -size[0] / 2,
dy: -size[1] / 2,
});
return style;
},
state: {
active: {
halo: false,
},
selected: {
halo: false,
},
},
},
edge: {
type: 'polyline',
style: {
lineWidth: 3,
radius: 20,
stroke: '#8b9baf',
endArrow: true,
labelText: (d: EdgeData) => d.data!.text as string,
labelFill: '#8b9baf',
labelFontWeight: 600,
labelBackground: true,
labelBackgroundFill: '#f8f8f8',
labelBackgroundOpacity: 1,
labelBackgroundLineWidth: 3,
labelBackgroundStroke: '#8b9baf',
labelPadding: [1, 10],
labelBackgroundRadius: 4,
router: {
type: 'orth',
},
},
state: {
active: {
stroke: ACTIVE_COLOR,
labelBackgroundStroke: ACTIVE_COLOR,
halo: false,
},
},
},
layout: {
type: 'antv-dagre',
nodeSize: [200, 40],
nodesep: 70,
ranksep: 5,
},
behaviors: ['zoom-canvas', 'drag-canvas', 'hover-element', 'click-select'],
};
return <Graph options={options} />;
};

View File

@ -74,6 +74,7 @@ export const ReactNodeDemo = () => {
style: { x: 350, y: 50 },
},
],
edges: [{ source: 'local-server-1', target: 'remote-server-1' }],
},
node: {
type: 'react',

View File

@ -35,19 +35,20 @@
"type-check": "tsc --noEmit -p tsconfig.test.json"
},
"dependencies": {
"@antv/g": "^6.0.12",
"@antv/react-g": "^2.0.12"
"@antv/g": "^6.0.13",
"@antv/g-svg": "^2.0.11",
"@antv/react-g": "^2.0.14"
},
"devDependencies": {
"@ant-design/icons": "^5.4.0",
"@antv/g6": "workspace:*",
"@types/react": "^18.3.3",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"antd": "^5.20.0",
"antd": "^5.20.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.0",
"styled-components": "^6.1.12"
"react-router-dom": "^6.26.1",
"styled-components": "^6.1.13"
},
"peerDependencies": {
"@antv/g6": "^5.0.20",

View File

@ -14,7 +14,7 @@ import {
treeToGraphData,
} from '@antv/g6';
export const caseDecisionTree: TestCase = async (context) => {
export const caseFundFlow: TestCase = async (context) => {
const COLORS: Record<string, string> = {
B: '#1783FF',
R: '#F46649',

View File

@ -0,0 +1,67 @@
import { Graph } from '@antv/g6';
export const elementEdgePolylineOrth: TestCase = async (context) => {
const graph = new Graph({
...context,
data: {
nodes: [
{ id: '0' },
{ id: '1' },
{ id: '2' },
{ id: '3' },
{ id: '4' },
{ id: '5' },
{ id: '6' },
{ id: '7' },
{ id: '8' },
{ id: '9' },
],
edges: [
{ source: '0', target: '1' },
{ source: '0', target: '2' },
{ source: '1', target: '4' },
{ source: '0', target: '3' },
{ source: '3', target: '4' },
{ source: '4', target: '5' },
{ source: '4', target: '6' },
{ source: '5', target: '7' },
{ source: '5', target: '8' },
{ source: '8', target: '9' },
{ source: '2', target: '9' },
{ source: '3', target: '9' },
],
},
layout: {
type: 'antv-dagre',
nodesep: 50,
ranksep: 20,
},
autoFit: 'view',
node: {
type: 'rect',
style: {
size: [60, 30],
radius: 8,
ports: [{ placement: 'top' }, { placement: 'bottom' }],
},
},
edge: {
type: 'polyline',
style: {
endArrow: true,
endArrowSize: 8,
lineWidth: 2,
radius: 10,
router: {
type: 'orth',
},
},
},
animation: false,
behaviors: ['drag-canvas', 'zoom-canvas'],
});
await graph.render();
return graph;
};

View File

@ -21,7 +21,7 @@ export { behaviorScrollCanvas } from './behavior-scroll-canvas';
export { behaviorZoomCanvas } from './behavior-zoom-canvas';
export { bugTooltipResize } from './bug-tooltip-resize';
export { canvasCursor } from './canvas-cursor';
export { caseDecisionTree } from './case-decision-tree';
export { caseFundFlow } from './case-fund-flow';
export { caseIndentedTree } from './case-indented-tree';
export { caseMindmap } from './case-mindmap';
export { caseOrgChart } from './case-org-chart';
@ -44,6 +44,7 @@ export { elementEdgeLoopPolyline } from './element-edge-loop-polyline';
export { elementEdgePolyline } from './element-edge-polyline';
export { elementEdgePolylineAnimation } from './element-edge-polyline-animation';
export { elementEdgePolylineAstar } from './element-edge-polyline-astar';
export { elementEdgePolylineOrth } from './element-edge-polyline-orth';
export { elementEdgePort } from './element-edge-port';
export { elementEdgeQuadratic } from './element-edge-quadratic';
export { elementEdgeSize } from './element-edge-size';

View File

@ -0,0 +1,179 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" style="background: transparent; position: absolute; outline: none;" color-interpolation-filters="sRGB" tabindex="1">
<defs/>
<g transform="matrix(0.869565,0,0,0.869565,-10.869556,-56.521729)">
<g fill="none">
<g fill="none" class="elements">
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 520,405 L 510,405 Q 500 405,500 395 L 500,395 L 500,252.5 Q 500 242.5,490 242.5 L 490,242.5 L 480,242.5" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,485,242.500000)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 520,405 L 510,405 Q 500 405,500 415 L 500,415 L 500,615 Q 500 625,490 625 L 490,625 L 450,625 L 400,625 L 350,625 L 300,625 L 250,625 L 200,625 L 180,625" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,185,625)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 420,242.5 L 410,242.5 Q 400 242.5,400 252.5 L 400,252.5 L 400,280 Q 400 290,390 290 L 390,290 L 380,290" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,385,290)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 520,405 L 510,405 Q 500 405,500 415 L 500,415 L 500,490 Q 500 500,490 500 L 490,500 L 480,500" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,485,500)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 420,500 L 410,500 Q 400 500,400 490 L 400,490 L 400,442.5 L 400,300 Q 400 290,390 290 L 390,290 L 380,290" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,385,290)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 320,290 L 310,290 Q 300 290,300 280 L 300,280 L 300,195 Q 300 185,290 185 L 290,185 L 280,185" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,285,185)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 320,290 L 310,290 Q 300 290,300 300 L 300,300 L 300,385 Q 300 395,290 395 L 290,395 L 280,395" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,285,395)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 220,185 L 210,185 Q 200 185,200 195 L 200,195 L 200,280 Q 200 290,190 290 L 190,290 L 180,290" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,185,290)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 220,185 L 210,185 Q 200 185,200 175 L 200,175 L 200,90 Q 200 80,190 80 L 190,80 L 180,80" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,185,80)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 120,80 L 110,80 Q 100 80,100 90 L 100,90 L 100,500 Q 100 510,90 510 L 90,510 L 80,510" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,85,510)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 120,625 L 110,625 Q 100 625,100 615 L 100,615 L 100,520 Q 100 510,90 510 L 90,510 L 80,510" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,85,510)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 420,500 L 405,500 Q 400 500,400 505 L 400,505 L 400,505 Q 400 510,395 510 L 395,510 L 350,510 L 300,510 L 250,510 L 200,510 L 150,510 L 100,510 L 80,510" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(1,0,0,1,85,510)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" x="550" y="405" transform="matrix(1,0,0,1,550,405)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="450" y="242.5" transform="matrix(1,0,0,1,450,242.500000)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="150" y="625" transform="matrix(1,0,0,1,150,625)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="450" y="500" transform="matrix(1,0,0,1,450,500)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="350" y="290" transform="matrix(1,0,0,1,350,290)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="250" y="185" transform="matrix(1,0,0,1,250,185)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="250" y="395" transform="matrix(1,0,0,1,250,395)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="150" y="290" transform="matrix(1,0,0,1,150,290)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="150" y="80" transform="matrix(1,0,0,1,150,80)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="50" y="510" transform="matrix(1,0,0,1,50,510)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,179 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" style="background: transparent; position: absolute; outline: none;" color-interpolation-filters="sRGB" tabindex="1">
<defs/>
<g transform="matrix(0.826446,0,0,0.826446,-41.322296,2.066129)">
<g fill="none">
<g fill="none" class="elements">
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 405,65 L 405,90 Q 405 100,395 100 L 395,100 L 252.5,100 Q 242.5 100,242.5 110 L 242.5,110 L 242.5,130" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,242.500000,130)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 405,65 L 405,90 Q 405 100,415 100 L 415,100 L 615,100 Q 625 100,625 110 L 625,110 L 625,150 L 625,200 L 625,250 L 625,300 L 625,350 L 625,400 L 625,430" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,625,430)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 242.5,165 L 242.5,190 Q 242.5 200,252.5 200 L 252.5,200 L 280,200 Q 290 200,290 210 L 290,210 L 290,230" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,290,230)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 405,65 L 405,90 Q 405 100,415 100 L 415,100 L 490,100 Q 500 100,500 110 L 500,110 L 500,130" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,500,130)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 500,165 L 500,190 Q 500 200,490 200 L 490,200 L 442.5,200 L 300,200 Q 290 200,290 210 L 290,210 L 290,230" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,290,230)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 290,265 L 290,290 Q 290 300,280 300 L 280,300 L 195,300 Q 185 300,185 310 L 185,310 L 185,330" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,185,330)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 290,265 L 290,290 Q 290 300,300 300 L 300,300 L 385,300 Q 395 300,395 310 L 395,310 L 395,330" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,395,330)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 185,365 L 185,390 Q 185 400,195 400 L 195,400 L 280,400 Q 290 400,290 410 L 290,410 L 290,430" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,290,430)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 185,365 L 185,390 Q 185 400,175 400 L 175,400 L 90,400 Q 80 400,80 410 L 80,410 L 80,430" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,80,430)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 80,465 L 80,490 Q 80 500,90 500 L 90,500 L 500,500 Q 510 500,510 510 L 510,510 L 510,530" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,510,530)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 625,465 L 625,490 Q 625 500,615 500 L 615,500 L 520,500 Q 510 500,510 510 L 510,510 L 510,530" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,510,530)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" marker-start="false" marker-end="true">
<g fill="none" marker-start="false" marker-end="true" stroke="transparent" stroke-width="4"/>
<g>
<path fill="none" d="M 500,165 L 500,195 Q 500 200,505 200 L 505,200 L 505,200 Q 510 200,510 205 L 510,205 L 510,250 L 510,300 L 510,350 L 510,400 L 510,450 L 510,500 L 510,530" class="key" stroke-width="2" stroke="rgba(153,173,209,1)"/>
<path fill="none" d="M 0,-15 L 0,-20.005000000000003 Q 0 -25.01,-5.005000000000001 -25.01 L -5.005000000000001,-25.01 L -35.004999999999995,-25.01 Q -40.01 -25.01,-40.01 -20.005000000000003 L -40.01,-20.005000000000003 L -40.01,-20.005000000000003 Q -40.01 -15,-35.004999999999995 -15 L -35.004999999999995,-15 L -5,-15" class="key" stroke-width="4" stroke="transparent"/>
<g transform="matrix(0,-1,1,0,510,530)">
<path fill="rgba(153,173,209,1)" d="M -4,0 L 4,-4 L 2.4000000000000004,0 L 4,4 Z" stroke="rgba(153,173,209,1)" width="8" height="8" stroke-dasharray="0,0" stroke-linejoin="round" stroke-width="1"/>
</g>
</g>
</g>
<g fill="none" x="405" y="50" transform="matrix(1,0,0,1,405,50)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="242.5" y="150" transform="matrix(1,0,0,1,242.500000,150)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="625" y="450" transform="matrix(1,0,0,1,625,450)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="500" y="150" transform="matrix(1,0,0,1,500,150)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="290" y="250" transform="matrix(1,0,0,1,290,250)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="185" y="350" transform="matrix(1,0,0,1,185,350)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="395" y="350" transform="matrix(1,0,0,1,395,350)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="290" y="450" transform="matrix(1,0,0,1,290,450)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="80" y="450" transform="matrix(1,0,0,1,80,450)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
<g fill="none" x="510" y="550" transform="matrix(1,0,0,1,510,550)">
<g>
<path fill="rgba(23,131,255,1)" d="M -22,-15 l 44,0 a 8,8,0,0,1,8,8 l 0,14 a 8,8,0,0,1,-8,8 l -44,0 a 8,8,0,0,1,-8,-8 l 0,-14 a 8,8,0,0,1,8,-8 z" class="key" stroke-width="0" stroke="rgba(0,0,0,1)" width="60" height="30" x="-30" y="-15"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,23 @@
import { elementEdgePolylineOrth } from '@@/demos';
import { createDemoGraph } from '@@/utils';
describe('element edge polyline orth', () => {
it('render', async () => {
const graph = await createDemoGraph(elementEdgePolylineOrth);
await expect(graph).toMatchSnapshot(__filename);
graph.setNode({
type: 'rect',
style: {
size: [60, 30],
radius: 8,
ports: [{ placement: 'left' }, { placement: 'right' }],
},
});
graph.setLayout((prev) => ({ ...prev, rankdir: 'RL' }));
graph.render();
await expect(graph).toMatchSnapshot(__filename, 'dagre-RL');
graph.destroy();
});
});

View File

@ -5,8 +5,8 @@ import {
getCombinedBBox,
getExpandedBBox,
getIncircleRadius,
getNearestPointToPoint,
getNearestSideToPoint,
getNearestBoundaryPoint,
getNearestBoundarySide,
getNodeBBox,
getPointBBox,
getTriangleCenter,
@ -80,20 +80,20 @@ describe('bbox', () => {
expect(isPointOnBBoxBoundary([0, 2, 0], bbox, true)).toEqual(true);
});
it('getNearestSideToPoint', () => {
expect(getNearestSideToPoint(bbox, [0.2, 0.5, 0])).toBe('left');
expect(getNearestSideToPoint(bbox, [0.5, 0.2, 0])).toBe('top');
expect(getNearestSideToPoint(bbox, [0.8, 0.5, 0])).toBe('right');
expect(getNearestSideToPoint(bbox, [0.5, 0.8, 0])).toBe('bottom');
it('getNearestBoundarySide', () => {
expect(getNearestBoundarySide([0.2, 0.5, 0], bbox)).toBe('left');
expect(getNearestBoundarySide([0.5, 0.2, 0], bbox)).toBe('top');
expect(getNearestBoundarySide([0.8, 0.5, 0], bbox)).toBe('right');
expect(getNearestBoundarySide([0.5, 0.8, 0], bbox)).toBe('bottom');
});
it('getNearestPointToPoint', () => {
expect(getNearestPointToPoint(bbox, [0.2, 0.5, 0])).toEqual([0, 0.5, 0]);
expect(getNearestPointToPoint(bbox, [0.5, 0.2, 0])).toEqual([0.5, 0, 0]);
expect(getNearestPointToPoint(bbox, [0.8, 0.5, 0])).toEqual([1, 0.5, 0]);
expect(getNearestPointToPoint(bbox, [0.5, 0.8, 0])).toEqual([0.5, 1, 0]);
expect(getNearestPointToPoint(bbox, [1.8, 0.5, 0])).toEqual([1, 0.5, 0]);
expect(getNearestPointToPoint(bbox, [0.5, 1.8, 0])).toEqual([0.5, 1, 0]);
it('getNearestBoundaryPoint', () => {
expect(getNearestBoundaryPoint([0.2, 0.5, 0], bbox)).toEqual([0, 0.5, 0]);
expect(getNearestBoundaryPoint([0.5, 0.2, 0], bbox)).toEqual([0.5, 0, 0]);
expect(getNearestBoundaryPoint([0.8, 0.5, 0], bbox)).toEqual([1, 0.5, 0]);
expect(getNearestBoundaryPoint([0.5, 0.8, 0], bbox)).toEqual([0.5, 1, 0]);
expect(getNearestBoundaryPoint([1.8, 0.5, 0], bbox)).toEqual([1, 0.5, 0]);
expect(getNearestBoundaryPoint([0.5, 1.8, 0], bbox)).toEqual([0.5, 1, 0]);
});
it('getTriangleCenter', () => {

View File

@ -45,52 +45,12 @@ describe('router', () => {
]);
expect(orth([100, 100], [150, 150], sourceNode2, targetNode2, [], { padding: 10 })).toEqual([[100, 150]]);
const sourceNode3 = new Rect({ style: { size: 5, x: 5, y: 0 } });
const targetNode3 = new Rect({ style: { size: 5, x: 20, y: 25 } });
expect(orth([10, 5], [25, 30], sourceNode3, targetNode3, [], { padding: 0 })).toEqual([[10, 30]]);
expect(orth([10, 5], [25, 30], sourceNode3, targetNode3, [[20, 20]], { padding: 0 })).toEqual([
[10, 20],
const sourceNode3 = new Rect({ style: { size: 10, x: 5, y: 0 } });
const targetNode3 = new Rect({ style: { size: 10, x: 20, y: 25 } });
expect(orth([5, 5], [20, 20], sourceNode3, targetNode3, [], { padding: 0 })).toEqual([
[5, 5],
[5, 20],
[20, 20],
[25, 20],
]);
expect(
orth(
[10, 5],
[25, 30],
sourceNode3,
targetNode3,
[
[20, 20],
[22, 22],
],
{ padding: 10 },
),
).toEqual([
[10, 20],
[20, 20],
[22, 20],
[22, 22],
[22, 37.51],
[25, 37.51],
]);
expect(
orth(
[10, 5],
[25, 30],
sourceNode3,
targetNode3,
[
[20, 20],
[20, 22],
],
{ padding: 10 },
),
).toEqual([
[10, 20],
[20, 20],
[20, 22],
[20, 37.51],
[25, 37.51],
]);
});
@ -119,18 +79,16 @@ describe('router', () => {
it('nodeToPoint', () => {
const sourceBBox = new AABB();
sourceBBox.setMinMax([0, 0, 0], [10, 10, 0]);
const sourcePoint: Point = [10, 5];
const targetPoint: Point = [20, 20];
expect(nodeToPoint(sourcePoint, targetPoint, sourceBBox).points).toEqual([[10, 20]]);
expect(nodeToPoint(sourcePoint, targetPoint, sourceBBox).direction).toEqual('E');
expect(nodeToPoint([5, 5], [20, 20], sourceBBox).points).toEqual([[5, 20]]);
expect(nodeToPoint([5, 5], [20, 20], sourceBBox).direction).toEqual('E');
expect(nodeToPoint([10, 5], [20, 20], sourceBBox).points).toEqual([[20, 5]]);
expect(nodeToPoint([10, 5], [20, 20], sourceBBox).direction).toEqual('S');
});
it('pointToNode', () => {
const sourcePoint: Point = [10, 5];
const targetPoint: Point = [20, 25];
const targetBBox = new AABB();
targetBBox.setMinMax([10, 10, 0], [25, 25, 0]);
expect(pointToNode(sourcePoint, targetPoint, targetBBox, 'N').points).toEqual([[20, 5]]);
expect(pointToNode([10, 5], [20, 25], targetBBox, 'N').points).toEqual([[20, 5]]);
});
it('nodeToNode', () => {

View File

@ -57,21 +57,21 @@
"version": "node ./scripts/version.mjs"
},
"dependencies": {
"@antv/component": "^2.0.3",
"@antv/component": "^2.0.4",
"@antv/event-emitter": "^0.1.3",
"@antv/g": "^6.0.12",
"@antv/g-canvas": "^2.0.10",
"@antv/g-plugin-dragndrop": "^2.0.8",
"@antv/g": "^6.0.13",
"@antv/g-canvas": "^2.0.12",
"@antv/g-plugin-dragndrop": "^2.0.9",
"@antv/graphlib": "^2.0.3",
"@antv/hierarchy": "^0.6.13",
"@antv/layout": "^1.2.14-beta.6",
"@antv/util": "^3.3.7",
"@antv/layout": "1.2.14-beta.6",
"@antv/util": "^3.3.8",
"bubblesets-js": "^2.3.3",
"hull.js": "^1.0.6"
},
"devDependencies": {
"@antv/g-svg": "^2.0.10",
"@antv/g-webgl": "^2.0.13",
"@antv/g-svg": "^2.0.11",
"@antv/g-webgl": "^2.0.16",
"@antv/layout-gpu": "^1.1.6",
"@antv/layout-wasm": "^1.4.1",
"@types/hull.js": "^1.0.4",

View File

@ -38,7 +38,7 @@ export interface HoverActivateOptions extends BaseBehaviorOptions {
* - `1` means the current node and its directly adjacent nodes and edges are activated, etc
* @defaultValue 0
*/
degree?: number;
degree?: number | ((event: IPointerEvent) => number);
/**
* <zh/>
* - `'both'`:
@ -133,16 +133,29 @@ export class HoverActivate extends BaseBehavior<HoverActivateOptions> {
else onHoverEnd?.(event);
};
protected getActiveIds(event: IPointerEvent<Element>) {
const { graph } = this.context;
const { degree, direction } = this.options;
const { targetType, target } = event;
return degree
? getElementNthDegreeIds(
graph,
targetType as ElementType,
target.id,
typeof degree === 'function' ? degree(event) : degree,
direction,
)
: [target.id];
}
private updateElementsState = (event: IPointerEvent<Element>, add: boolean) => {
if (!this.options.state && !this.options.inactiveState) return;
const { graph } = this.context;
const { state, degree, direction, animation, inactiveState } = this.options;
const { targetType, target } = event;
const { state, animation, inactiveState } = this.options;
const activeIds = degree
? getElementNthDegreeIds(graph, targetType as ElementType, target.id, degree, direction)
: [target.id];
const activeIds = this.getActiveIds(event);
const states: Record<ID, State[]> = {};
@ -162,8 +175,11 @@ export class HoverActivate extends BaseBehavior<HoverActivateOptions> {
const states: Record<ID, State[]> = {};
ids.forEach((id) => {
const currentState = graph.getElementState(id);
const updatedState = add ? [...currentState, state] : currentState.filter((s) => s !== state);
states[id] = updatedState;
if (add) {
states[id] = currentState.includes(state) ? currentState : [...currentState, state];
} else {
states[id] = currentState.filter((s) => s !== state);
}
});
return states;
};

View File

@ -69,6 +69,7 @@ export class Polyline extends BaseEdge {
if (router.type === 'shortest-path') {
const nodes = this.context.element!.getNodes();
controlPoints = aStarSearch(sourceNode, targetNode, nodes, router);
if (!controlPoints.length) {
controlPoints = orth(sourcePoint, targetPoint, sourceNode, targetNode, attributes.controlPoints, {
padding: router.offset,
@ -91,6 +92,7 @@ export class Polyline extends BaseEdge {
protected getKeyPath(attributes: ParsedPolylineStyleProps): PathArray {
const points = this.getPoints(attributes);
return getPolylinePath(points, attributes.radius);
}

View File

@ -150,6 +150,19 @@ export function isPointOutsideBBox(point: Point, bbox: AABB) {
return !isPointInBBox(point, bbox);
}
/**
* <zh/>
*
* <en/> When the point is at the center of the bounding box
* @param point - <zh/> | <en/> Point
* @param bbox - <zh/> | <en/> Bounding box
* @returns <zh/> true false | <en/> Returns true if the point is at the center of the bounding box, false otherwise
*/
export function isPointBBoxCenter(point: Point, bbox: AABB) {
const { center } = bbox;
return point[0] === center[0] && point[1] === center[1];
}
/**
* <zh/> `p`
*
@ -158,7 +171,7 @@ export function isPointOutsideBBox(point: Point, bbox: AABB) {
* @param p - <zh/> | <en/> Point
* @returns <zh/> `p` | <en/> The side nearest to the point `p`
*/
export function getNearestSideToPoint(bbox: AABB, p: Point): 'left' | 'right' | 'top' | 'bottom' {
export function getNearestBoundarySide(p: Point, bbox: AABB): 'left' | 'right' | 'top' | 'bottom' {
const [x, y] = p;
const [minX, minY] = bbox.min;
const [maxX, maxY] = bbox.max;
@ -171,17 +184,17 @@ export function getNearestSideToPoint(bbox: AABB, p: Point): 'left' | 'right' |
}
/**
* <zh/> `p`
* <zh/> `p`
*
* <en/> Get a point on the boundary nearest to the point `p`
* @param bbox - <zh/> | <en/> Bounding box
* @param p - <zh/> | <en/> Point
* @returns <zh/> `p` | <en/> The point nearest to the point `p`
*/
export function getNearestPointToPoint(bbox: AABB, p: Point): Point {
export function getNearestBoundaryPoint(p: Point, bbox: AABB): Point {
const ref = clone(p);
if (isPointInBBox(p, bbox)) {
const side = getNearestSideToPoint(bbox, p);
const side = getNearestBoundarySide(p, bbox);
switch (side) {
case 'left':
ref[0] = bbox.min[0];

View File

@ -15,7 +15,7 @@ import type {
Size,
Vector2,
} from '../types';
import { getBBoxHeight, getBBoxSize, getBBoxWidth, getNearestSideToPoint, getNodeBBox } from './bbox';
import { getBBoxHeight, getBBoxSize, getBBoxWidth, getNearestBoundarySide, getNodeBBox } from './bbox';
import { isCollapsed } from './collapsibility';
import { getAllPorts, getNodeConnectionPoint, getPortConnectionPoint, getPortPosition } from './element';
import { idOf } from './id';
@ -481,7 +481,7 @@ export function getPolylineLoopControlPoints(node: Node, sourcePoint: Point, tar
// 1. 起点和终点相同 | The start and end points are the same
if (isEqual(sourcePoint, targetPoint)) {
const side = getNearestSideToPoint(bbox, sourcePoint);
const side = getNearestBoundarySide(sourcePoint, bbox);
switch (side) {
case 'left':
controlPoints.push([sourcePoint[0] - dist, sourcePoint[1]]);
@ -505,8 +505,8 @@ export function getPolylineLoopControlPoints(node: Node, sourcePoint: Point, tar
break;
}
} else {
const sourceSide = getNearestSideToPoint(bbox, sourcePoint);
const targetSide = getNearestSideToPoint(bbox, targetPoint);
const sourceSide = getNearestBoundarySide(sourcePoint, bbox);
const targetSide = getNearestBoundarySide(targetPoint, bbox);
// 2. 起点与终点同边 | The start and end points are on the same side
if (sourceSide === targetSide) {
const side = sourceSide;

View File

@ -1,4 +1,5 @@
import type { AABB } from '@antv/g';
import { isEqual } from '@antv/util';
import type { Point, PointObject } from '../types';
import { getBBoxHeight, getBBoxWidth } from './bbox';
import type { LineSegment } from './line';
@ -77,6 +78,7 @@ export function round(point: Point, digits = 0): Point {
* @returns <zh/> | <en/> the moved point
*/
export function moveTo(p: Point, ref: Point, distance: number, reverse = false): Point {
if (isEqual(p, ref)) return p;
const direction = reverse ? subtract(p, ref) : subtract(ref, p);
const normalizedDirection = normalize(direction);
const moveVector: Point = [normalizedDirection[0] * distance, normalizedDirection[1] * distance];

View File

@ -5,14 +5,16 @@ import {
getBBoxHeight,
getBBoxWidth,
getCombinedBBox,
getNearestPointToPoint,
getNearestBoundaryPoint,
getNearestBoundarySide,
getNodeBBox,
isPointBBoxCenter,
isPointInBBox,
isPointOnBBoxBoundary,
isPointOutsideBBox,
} from '../bbox';
import { isOrthogonal, moveTo, round } from '../point';
import { angle, distance, subtract, toVector3 } from '../vector';
import { angle, distance, subtract, toVector2, toVector3 } from '../vector';
export type Direction = 'N' | 'S' | 'W' | 'E' | null;
@ -69,6 +71,12 @@ export function orth(
// source -> target
if (sourceBBox.intersects(targetBBox)) {
route = insideNode(from, to, sourceBBox, targetBBox);
} else if (!isPointBBoxCenter(from, sourceBBox) && !isPointBBoxCenter(to, targetBBox)) {
const fromWithPadding = getNearestBoundaryPoint(from, sourceBBox);
const toWithPadding = getNearestBoundaryPoint(to, targetBBox);
route = pointToPoint(fromWithPadding, toWithPadding, getDirection(fromWithPadding, toWithPadding));
route.points.unshift(fromWithPadding);
route.points.push(toWithPadding);
} else if (!isOrth) {
route = nodeToNode(from, to, sourceBBox, targetBBox);
}
@ -104,7 +112,7 @@ export function orth(
if (toIdx < len - 1) result.push(to);
}
return result;
return result.map(toVector2);
}
/**
@ -189,9 +197,17 @@ export function pointToPoint(from: Point, to: Point, direction: Direction): Rout
* @returns <zh/> | <en/> orthogonal route
*/
export function nodeToPoint(from: Point, to: Point, fromBBox: AABB): Route {
const p = freeJoin(from, to, fromBBox);
if (isPointBBoxCenter(from, fromBBox)) {
const p = freeJoin(from, to, fromBBox);
return { points: [p], direction: getDirection(p, to) };
return { points: [p], direction: getDirection(p, to) };
} else {
const fromWithPadding = getNearestBoundaryPoint(from, fromBBox);
const isHorizontal = ['left', 'right'].includes(getNearestBoundarySide(from, fromBBox));
const p: Point = isHorizontal ? [to[0], fromWithPadding[1]] : [fromWithPadding[0], to[1]];
return { points: [p], direction: getDirection(p, to) };
}
}
/**
@ -205,9 +221,10 @@ export function nodeToPoint(from: Point, to: Point, fromBBox: AABB): Route {
* @returns <zh/> | <en/> orthogonal route
*/
export function pointToNode(from: Point, to: Point, toBBox: AABB, direction: Direction): Route {
const toWithPadding = isPointBBoxCenter(to, toBBox) ? to : getNearestBoundaryPoint(to, toBBox);
const points: Point[] = [
[to[0], from[1]],
[from[0], to[1]],
[toWithPadding[0], from[1]],
[from[0], toWithPadding[1]],
];
const freePoints = points.filter((p) => isPointOutsideBBox(p, toBBox) && !isPointOnBBoxBoundary(p, toBBox, true));
@ -265,6 +282,7 @@ export function nodeToNode(from: Point, to: Point, fromBBox: AABB, toBBox: AABB)
route.direction = endRoute.direction;
}
}
return route;
}
@ -292,10 +310,10 @@ export function insideNode(from: Point, to: Point, fromBBox: AABB, toBBox: AABB,
start[0] + halfPerimeter * Math.cos(radians[direction]),
start[1] + halfPerimeter * Math.sin(radians[direction]),
];
// `getNearestPointToPoint` returns a point on the boundary, so we need to move it a bit to ensure it's outside the element and then get the correct `p2` via `freeJoin`.
p1 = moveTo(getNearestPointToPoint(boundary, ref), ref, DEFAULT_OFFSET);
// `getNearestBoundaryPoint` returns a point on the boundary, so we need to move it a bit to ensure it's outside the element and then get the correct `p2` via `freeJoin`.
p1 = moveTo(getNearestBoundaryPoint(ref, boundary), ref, DEFAULT_OFFSET);
} else {
p1 = moveTo(getNearestPointToPoint(boundary, start), start, -DEFAULT_OFFSET);
p1 = moveTo(getNearestBoundaryPoint(start, boundary), start, -DEFAULT_OFFSET);
}
let p2 = freeJoin(p1, end, boundary);
@ -305,7 +323,7 @@ export function insideNode(from: Point, to: Point, fromBBox: AABB, toBBox: AABB,
if (isEqual(round(p1), round(p2))) {
const rad = angle(subtract(p1, start), [1, 0, 0]) + Math.PI / 2;
p2 = [end[0] + halfPerimeter * Math.cos(rad), end[1] + halfPerimeter * Math.sin(rad), 0];
p2 = round(moveTo(getNearestPointToPoint(boundary, p2), end, -DEFAULT_OFFSET), 2);
p2 = round(moveTo(getNearestBoundaryPoint(p2, boundary), end, -DEFAULT_OFFSET), 2);
const p3 = freeJoin(p1, p2, boundary);
points = [p1, p3, p2];
}

View File

@ -53,10 +53,10 @@
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*uKYKSYJ6iMYAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "decision-tree.js",
"filename": "fund-flow.js",
"title": {
"zh": "决策树",
"en": "Decision Tree"
"zh": "资金流向图",
"en": "Fund Flow"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ImBoQIveCtYAAAAAAAAAAAAADmJ7AQ/original"
},
@ -68,6 +68,14 @@
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*wgoUR6dnVcsAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "performance-diagnosis-flowchart.js",
"title": {
"zh": "系统性能诊断流程图",
"en": "System Performance Diagnosis Flowchart"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*gS6BQbU7a0UAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "sub-graph.js",
"title": {

View File

@ -0,0 +1,160 @@
import { BugOutlined } from '@ant-design/icons';
import { ExtensionCategory, Graph, HoverActivate, idOf, register } from '@antv/g6';
import { ReactNode } from '@antv/g6-extension-react';
import { Flex, Typography } from 'antd';
import { useEffect, useRef } from 'react';
import { createRoot } from 'react-dom/client';
const { Text } = Typography;
const ACTIVE_COLOR = '#f6c523';
const COLOR_MAP = {
'pre-inspection': '#3fc1c9',
problem: '#8983f3',
inspection: '#f48db4',
solution: '#ffaa64',
};
class HoverElement extends HoverActivate {
getActiveIds(event) {
const { model, graph } = this.context;
const { targetType, target } = event;
const targetId = target.id;
const ids = [targetId];
if (targetType === 'edge') {
const edge = model.getEdgeDatum(targetId);
ids.push(edge.source, edge.target);
} else if (targetType === 'node') {
ids.push(...model.getRelatedEdgesData(targetId).map(idOf));
}
graph.frontElement(ids);
return ids;
}
}
register(ExtensionCategory.NODE, 'react', ReactNode);
register(ExtensionCategory.BEHAVIOR, 'hover-element', HoverElement);
const Node = ({ data }) => {
const { text, type } = data.data;
const isHovered = data.states?.includes('active');
const isSelected = data.states?.includes('selected');
const color = isHovered ? ACTIVE_COLOR : COLOR_MAP[type];
const containerStyle = {
width: '100%',
height: '100%',
background: color,
border: `3px solid ${color}`,
borderRadius: 16,
cursor: 'pointer',
};
if (isSelected) {
Object.assign(containerStyle, { border: `3px solid #000` });
}
return (
<Flex style={containerStyle} align="center" justify="center">
<Flex vertical style={{ padding: '8px 16px', textAlign: 'center' }} align="center" justify="center">
{type === 'problem' && <BugOutlined style={{ color: '#fff', fontSize: 24, marginBottom: 8 }} />}
<Text style={{ color: '#fff', fontWeight: 600, fontSize: 16 }}>{text}</Text>
</Flex>
</Flex>
);
};
export const PerformanceDiagnosisFlowchart = () => {
const containerRef = useRef();
useEffect(() => {
fetch('https://assets.antv.antgroup.com/g6/performance-diagnosis.json')
.then((res) => res.json())
.then((data) => {
const graph = new Graph({
container: containerRef.current,
data,
animation: false,
autoFit: 'view',
node: {
type: 'react',
style: (d) => {
const style = {
component: <Node data={d} />,
ports: [{ placement: 'top' }, { placement: 'bottom' }],
};
const size = {
'pre-inspection': [240, 80],
problem: [200, 80],
inspection: [280, 100],
solution: [240, 60],
}[d.data.type] || [200, 80];
Object.assign(style, {
size,
dx: -size[0] / 2,
dy: -size[1] / 2,
});
return style;
},
state: {
active: {
halo: false,
},
selected: {
halo: false,
},
},
},
edge: {
type: 'polyline',
style: {
lineWidth: 3,
radius: 20,
stroke: '#8b9baf',
endArrow: true,
labelText: (d) => d.data.text,
labelFill: '#8b9baf',
labelFontWeight: 600,
labelBackground: true,
labelBackgroundFill: '#f8f8f8',
labelBackgroundOpacity: 1,
labelBackgroundLineWidth: 3,
labelBackgroundStroke: '#8b9baf',
labelPadding: [1, 10],
labelBackgroundRadius: 4,
router: {
type: 'orth',
},
},
state: {
active: {
stroke: ACTIVE_COLOR,
labelBackgroundStroke: ACTIVE_COLOR,
halo: false,
},
},
},
layout: {
type: 'antv-dagre',
nodeSize: [200, 40],
nodesep: 50,
ranksep: 5,
},
behaviors: ['zoom-canvas', 'drag-canvas', 'hover-element', 'click-select'],
});
graph.render();
});
}, []);
return <div style={{ width: '100%', height: '100%' }} ref={containerRef}></div>;
};
const root = createRoot(document.getElementById('container'));
root.render(<PerformanceDiagnosisFlowchart />);

View File

@ -41,36 +41,36 @@
"@ant-design/icons": "^5.4.0",
"@antv/algorithm": "^0.1.26",
"@antv/dumi-theme-antv": "^0.5.2",
"@antv/g": "^6.0.12",
"@antv/g-svg": "^2.0.10",
"@antv/g-webgl": "^2.0.13",
"@antv/g2": "^5.2.2",
"@antv/g": "^6.0.13",
"@antv/g-svg": "^2.0.11",
"@antv/g-webgl": "^2.0.16",
"@antv/g2": "^5.2.5",
"@antv/g6": "workspace:*",
"@antv/g6-extension-3d": "workspace:*",
"@antv/g6-extension-react": "workspace:*",
"@antv/layout": "^1.2.14-beta.5",
"@antv/layout": "1.2.14-beta.6",
"@antv/layout-gpu": "^1.1.6",
"@antv/layout-wasm": "^1.4.1",
"@antv/util": "^3.3.7",
"antd": "^5.20.0",
"dumi": "^2.4.7",
"@antv/util": "^3.3.8",
"antd": "^5.20.5",
"dumi": "^2.4.9",
"insert-css": "^2.0.0",
"lodash": "^4.17.21",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"stats.js": "^0.17.0",
"styled-components": "^6.1.12"
"styled-components": "^6.1.13"
},
"devDependencies": {
"@manypkg/get-packages": "^2.2.2",
"@microsoft/api-documenter": "^7.25.10",
"@microsoft/api-extractor": "^7.47.5",
"@microsoft/api-extractor-model": "^7.29.4",
"@microsoft/api-documenter": "^7.25.12",
"@microsoft/api-extractor": "^7.47.7",
"@microsoft/api-extractor-model": "^7.29.6",
"@microsoft/tsdoc": "^0.15.0",
"@rushstack/node-core-library": "^4.3.0",
"@types/fs-extra": "^11.0.4",
"@types/lodash": "^4.17.7",
"@types/react": "^18.3.3",
"@types/react": "^18.3.5",
"@types/resolve": "^1.20.6",
"fs-extra": "^11.2.0",
"gh-pages": "^6.1.1",