mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-13 08:17:33 +00:00
127 lines
3.8 KiB
TypeScript
127 lines
3.8 KiB
TypeScript
/**
|
|
* 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 d3 from "d3";
|
|
export function constructTangleLayout(levels: any, options: any = {}) {
|
|
// precompute level depth
|
|
levels.forEach((l: any, i: any) => l.forEach((n: any) => (n.level = i)));
|
|
|
|
const nodes = levels.reduce((a: any, x: any) => a.concat(x), []);
|
|
const nodes_index: any = {};
|
|
nodes.forEach((d: any) => (nodes_index[d.id] = d));
|
|
|
|
// objectification
|
|
nodes.forEach((d: any) => {
|
|
d.parents = (d.parents === undefined ? [] : d.parents).map((p: any) => nodes_index[p]);
|
|
});
|
|
|
|
// precompute bundles
|
|
levels.forEach((l: any, i: any) => {
|
|
const index: any = {};
|
|
l.forEach((n: any) => {
|
|
if (n.parents.length == 0) {
|
|
return;
|
|
}
|
|
|
|
const id = n.parents
|
|
.map((d: any) => d.id)
|
|
.sort()
|
|
.join("-X-");
|
|
if (id in index) {
|
|
index[id].parents = index[id].parents.concat(n.parents);
|
|
} else {
|
|
index[id] = {
|
|
id: id,
|
|
parents: n.parents.slice(),
|
|
level: i,
|
|
span: i - n.parents && d3.min(n.parents, (p: any) => p.level),
|
|
};
|
|
}
|
|
n.bundle = index[id];
|
|
});
|
|
l.bundles = Object.keys(index).map((k) => index[k]);
|
|
l.bundles.forEach((b: any, i: any) => (b.i = i));
|
|
});
|
|
|
|
const links: any = [];
|
|
nodes.forEach((d: any) => {
|
|
d.parents.forEach((p: any) => links.push({ source: d, bundle: d.bundle, target: p }));
|
|
});
|
|
|
|
const bundles = levels.reduce((a: any, x: any) => a.concat(x.bundles), []);
|
|
|
|
// reverse pointer from parent to bundles
|
|
bundles.forEach((b: any) =>
|
|
b.parents.forEach((p: any) => {
|
|
if (p.bundles_index === undefined) {
|
|
p.bundles_index = {};
|
|
}
|
|
if (!(b.id in p.bundles_index)) {
|
|
p.bundles_index[b.id] = [];
|
|
}
|
|
p.bundles_index[b.id].push(b);
|
|
}),
|
|
);
|
|
|
|
nodes.forEach((n: any) => {
|
|
if (n.bundles_index !== undefined) {
|
|
n.bundles = Object.keys(n.bundles_index).map((k) => n.bundles_index[k]);
|
|
} else {
|
|
n.bundles_index = {};
|
|
n.bundles = [];
|
|
}
|
|
n.bundles.sort((a: any, b: any) =>
|
|
d3.descending(
|
|
d3.max(a, (d: any) => d.span),
|
|
d3.max(b, (d: any) => d.span),
|
|
),
|
|
);
|
|
n.bundles.forEach((b: any, i: any) => (b.i = i));
|
|
});
|
|
|
|
// layout
|
|
const padding = 30;
|
|
const node_height = 120;
|
|
const node_width = 100;
|
|
const bundle_width = 14;
|
|
const metro_d = 4;
|
|
|
|
options.c ||= 16;
|
|
const c = options.c;
|
|
options.bigc ||= node_width + c;
|
|
|
|
nodes.forEach((n: any) => (n.height = (Math.max(1, n.bundles.length) - 1) * metro_d));
|
|
|
|
let x_offset = padding;
|
|
let y_offset = 0;
|
|
levels.forEach((l: any) => {
|
|
y_offset = 0;
|
|
x_offset += 5 * bundle_width;
|
|
l.forEach((n: any) => {
|
|
n.x = n.level * node_width + x_offset;
|
|
n.y = node_height + y_offset + n.height / 2;
|
|
y_offset += node_height + n.height;
|
|
});
|
|
});
|
|
|
|
const layout = {
|
|
width: d3.max(nodes, (n: any) => n.x) || 0 + node_width + 2 * padding,
|
|
height: d3.max(nodes, (n: any) => n.y) || 0 + node_height / 2 + 2 * padding,
|
|
};
|
|
|
|
return { nodes, bundles, layout };
|
|
}
|