draw line

This commit is contained in:
Fine 2022-08-17 16:33:12 +08:00
parent 7dfc87324d
commit 57e701ee4e
10 changed files with 233 additions and 56 deletions

View File

@ -70,7 +70,7 @@ export const networkProfilingStore = defineStore({
},
setTopology(data: { nodes: ProcessNode[]; calls: Call[] }) {
const obj = {} as any;
const calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
let calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
if (!obj[next.id]) {
obj[next.id] = true;
next.value = next.value || 1;
@ -87,7 +87,16 @@ export const networkProfilingStore = defineStore({
}
return prev;
}, []);
calls = calls.map((d: any) => {
d.sourceId = d.source;
d.targetId = d.target;
d.source = d.sourceObj;
d.target = d.targetObj;
delete d.sourceObj;
delete d.targetObj;
return d;
});
console.log(calls);
this.calls = calls;
this.nodes = data.nodes;
},

View File

@ -0,0 +1,93 @@
/**
* 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.
*/
export const linkElement = (graph: any) => {
const linkEnter = graph
.append("path")
.attr("class", "topo-call")
.attr("marker-end", "url(#arrow)")
.attr("stroke", "#afc4dd")
.attr("d", (d: any) => {
const controlPos = computeControlPoint(
[d.source.x, d.source.y - 5],
[d.target.x, d.target.y - 5],
1
);
return (
"M" +
d.source.x +
" " +
(d.source.y - 5) +
" " +
"Q" +
controlPos[0] +
" " +
controlPos[1] +
" " +
d.target.x +
" " +
(d.target.y - 5)
);
});
return linkEnter;
};
export const anchorElement = (graph: any, funcs: any, tip: any) => {
const linkEnter = graph
.append("circle")
.attr("class", "topo-line-anchor")
.attr("r", 5)
.attr("fill", "#217EF25f")
.on("mouseover", function (event: unknown, d: unknown) {
tip.html(funcs.tipHtml).show(d, this);
})
.on("mouseout", function () {
tip.hide(this);
})
.on("click", (event: unknown, d: unknown) => {
funcs.handleLinkClick(event, d);
});
return linkEnter;
};
export const arrowMarker = (graph: any) => {
const defs = graph.append("defs");
const arrow = defs
.append("marker")
.attr("id", "arrow")
.attr("class", "topo-line-arrow")
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", "6")
.attr("markerHeight", "6")
.attr("viewBox", "0 0 12 12")
.attr("refX", "10")
.attr("refY", "6")
.attr("orient", "auto");
const arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2";
arrow.append("path").attr("d", arrowPath).attr("fill", "#afc4dd");
return arrow;
};
function computeControlPoint(ps: number[], pe: number[], arc = 0.5) {
const deltaX = pe[0] - ps[0];
const deltaY = pe[1] - ps[1];
const theta = Math.atan(deltaY / deltaX);
const len = (Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2) * arc;
const newTheta = theta - Math.PI / 2;
return [
(ps[0] + pe[0]) / 2 + len * Math.cos(newTheta),
(ps[1] + pe[1]) / 2 + len * Math.sin(newTheta),
];
}

View File

@ -37,16 +37,16 @@ export default (d3: any, graph: any, funcs: any, tip: any) => {
.append("image")
.attr("width", 35)
.attr("height", 35)
.attr("x", (d: any) => d.x)
.attr("y", (d: any) => d.y)
.attr("x", (d: any) => d.x - 15)
.attr("y", (d: any) => d.y - 15)
.attr("style", "cursor: move;")
.attr("xlink:href", icons.CUBE);
nodeEnter
.append("text")
.attr("fill", "#000")
.attr("text-anchor", "middle")
.attr("x", (d: any) => d.x + 15)
.attr("y", (d: any) => d.y + 42)
.attr("x", (d: any) => d.x + 5)
.attr("y", (d: any) => d.y + 28)
.text((d: { name: string }) =>
d.name.length > 10 ? `${d.name.substring(0, 10)}...` : d.name
);

View File

@ -13,16 +13,16 @@ 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. -->
<template>
<div ref="chart" class="micro-topo-chart"></div>
<div ref="chart" class="process-topo"></div>
<div
class="switch-icon ml-5"
class="switch-icon-edit ml-5"
title="Settings"
@click="setConfig"
v-if="dashboardStore.editMode"
>
<Icon size="middle" iconName="settings" />
</div>
<div class="setting" v-if="showSettings && dashboardStore.editMode">
<div class="process-setting" v-if="showSettings && dashboardStore.editMode">
<Settings @update="updateSettings" @updateNodes="freshNodes" />
</div>
</template>
@ -37,12 +37,8 @@ import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import d3tip from "d3-tip";
import {
linkElement,
anchorElement,
arrowMarker,
} from "../../components/D3Graph/linkElement";
import nodeElement from "../../components/D3Graph/nodeProcess";
import { linkElement, anchorElement, arrowMarker } from "./Graph/linkProcess";
import nodeElement from "./Graph/nodeProcess";
import { Call } from "@/types/topology";
// import zoom from "../../components/D3Graph/zoom";
import { ProcessNode } from "@/types/ebpf";
@ -109,7 +105,7 @@ function drawGraph() {
.attr("transform", `translate(300, 300)`);
graph.value.call(tip.value);
node.value = graph.value.append("g").selectAll(".topo-node");
link.value = graph.value.append("g").selectAll(".topo-line");
link.value = graph.value.append("g").selectAll(".topo-call");
anchor.value = graph.value.append("g").selectAll(".topo-line-anchor");
arrow.value = graph.value.append("g").selectAll(".topo-line-arrow");
// svg.value.call(zoom(d3, graph.value));
@ -157,7 +153,7 @@ function update() {
if (!node.value || !link.value) {
return;
}
const obj: any = (chart.value && chart.value.getBoundingClientRect()) || {};
// const obj: any = (chart.value && chart.value.getBoundingClientRect()) || {};
const p = {
hexagonParam: [27, 0.04, 5, 0.04, 0],
count: 1,
@ -180,7 +176,7 @@ function update() {
const centers = hexGrid(p.count, 100, origin); // cube centers
const cubeCenters = [];
for (let i = 0; i < centers.length; i++) {
// const polygon = createPolygon(90, 6, 0);
// const polygon = createPolygon(100, 6, 0);
// const vertices: any = []; // a hexagon vertices
// for (let v = 0; v < polygon.length; v++) {
// vertices.push([
@ -196,9 +192,27 @@ function update() {
// .attr("stroke", "#ccc")
// .attr("stroke-width", 1)
// .style("fill", "none");
const c = hexGrid(p.count, 25, centers[i]);
const c = hexGrid(p.count, 30, centers[i]);
cubeCenters.push(...c);
}
// for (let i = 0; i < cubeCenters.length; i++) {
// const polygon = createPolygon(30, 6, 0);
// const vertices: any = []; // a hexagon vertices
// for (let v = 0; v < polygon.length; v++) {
// vertices.push([
// cubeCenters[i][0] + polygon[v][0],
// cubeCenters[i][1] + polygon[v][1],
// ]);
// }
// const linePath = d3.line();
// linePath.curve(d3.curveLinearClosed);
// graph.value
// .append("path")
// .attr("d", linePath(vertices))
// .attr("stroke", "#ccc")
// .attr("stroke-width", 1)
// .style("fill", "none");
// }
shuffleArray(cubeCenters);
const pos = hexGrid(p.count, 30, [p.radius * 2 + 20]); // cube centers
const nodeArr = networkProfilingStore.nodes.filter(
@ -211,7 +225,6 @@ function update() {
nodeArr[v].y = y;
}
node.value = node.value.data(nodeArr, (d: ProcessNode) => d.id);
node.value.exit().remove();
node.value = nodeElement(
d3,
@ -223,6 +236,50 @@ function update() {
},
tip.value
).merge(node.value);
// line element
const obj = {} as any;
const calls = networkProfilingStore.calls.reduce((prev: any[], next: any) => {
if (
!(
obj[next.sourceId + next.targetId] && obj[next.targetId + next.sourceId]
)
) {
obj[next.sourceId + next.targetId] = true;
obj[next.targetId + next.sourceId] = true;
prev.push(next);
}
return prev;
}, []);
link.value = link.value.data(calls, (d: Call) => d.id);
link.value.exit().remove();
link.value = linkElement(link.value.enter()).merge(link.value);
// anchorElement
// anchor.value = anchor.value.data(
// networkProfilingStore.calls,
// (d: Call) => d.id
// );
// anchor.value.exit().remove();
// anchor.value = anchorElement(
// anchor.value.enter(),
// {
// handleLinkClick: handleLinkClick,
// tipHtml: (data: Call) => {
// const html = `<div><span class="grey">${t(
// "detectPoint"
// )}:</span>${data.detectPoints.join(" | ")}</div>`;
// return html;
// },
// },
// tip.value
// ).merge(anchor.value);
// arrow marker
arrow.value = arrow.value.data(
networkProfilingStore.calls,
(d: Call) => d.id
);
arrow.value.exit().remove();
arrow.value = arrowMarker(arrow.value.enter()).merge(arrow.value);
}
function shuffleArray(array: number[][]) {
@ -261,27 +318,6 @@ function handleLinkClick(event: any, d: Call) {
window.open(routeUrl.href, "_blank");
}
function ticked() {
link.value.attr(
"d",
(d: Call | any) =>
`M${d.source.x} ${d.source.y} Q ${(d.source.x + d.target.x) / 2} ${
(d.target.y + d.source.y) / 2 - d.loopFactor * 90
} ${d.target.x} ${d.target.y}`
);
anchor.value.attr(
"transform",
(d: Call | any) =>
`translate(${(d.source.x + d.target.x) / 2}, ${
(d.target.y + d.source.y) / 2 - d.loopFactor * 45
})`
);
node.value.attr(
"transform",
(d: Node | any) => `translate(${d.x - 22},${d.y - 22})`
);
}
function updateSettings(config: any) {
config.value = config;
}
@ -324,8 +360,8 @@ watch(
}
);
</script>
<style lang="scss" scoped>
.micro-topo-chart {
<style lang="scss">
.process-topo {
width: calc(100% - 10px);
margin: 0 5px 5px 0;
height: 100%;
@ -340,7 +376,7 @@ watch(
cursor: move;
}
.switch-icon {
.switch-icon-edit {
cursor: pointer;
transition: all 0.5ms linear;
background-color: #252a2f99;
@ -353,7 +389,7 @@ watch(
right: 10px;
}
.setting {
.process-setting {
position: absolute;
top: 65px;
right: 10px;
@ -366,4 +402,22 @@ watch(
color: #ccc;
transition: all 0.5ms linear;
}
.topo-call {
stroke-linecap: round;
stroke-width: 2px;
stroke-dasharray: 13 7;
fill: none;
animation: topo-dash 0.5s linear infinite;
}
@keyframes topo-dash {
from {
stroke-dashoffset: 20;
}
to {
stroke-dashoffset: 0;
}
}
</style>

View File

@ -94,17 +94,10 @@ import {
import { useI18n } from "vue-i18n";
import * as d3 from "d3";
import d3tip from "d3-tip";
import zoom from "../../components/D3Graph/zoom";
import {
simulationInit,
simulationSkip,
} from "../../components/D3Graph/simulation";
import nodeElement from "../../components/D3Graph/nodeElement";
import {
linkElement,
anchorElement,
arrowMarker,
} from "../../components/D3Graph/linkElement";
import zoom from "./utils/zoom";
import { simulationInit, simulationSkip } from "./utils/simulation";
import nodeElement from "./utils/nodeElement";
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
import { Node, Call } from "@/types/topology";
import { useSelectorStore } from "@/store/modules/selectors";
import { useTopologyStore } from "@/store/modules/topology";

View File

@ -0,0 +1,28 @@
/**
* 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.
*/
export default (d3: any, graph: any) =>
d3
.zoom()
.scaleExtent([0.3, 10])
.on("zoom", (d: any) => {
graph
.attr("transform", d3.zoomTransform(graph.node()))
.attr(
`translate(${d.transform.x},${d.transform.y})scale(${d.transform.k})`
);
});