Код: Выделить всё
// Simple adj list for my graph
const adjList = {
"A": ["B", "C"],
"B": ["D"],
"C": [],
"D": []
};
// Fixed positions.
// This will be from an external source which CANNOT be changed.
// I can't figure out to pass this to dagre's layout engine.
const fixedPositions = {
A: { x: 300, y: 50 },
B: { x: 150, y: 150 },
C: { x: 450, y: 150 },
D: { x: 100, y: 250 }
};
const g = new dagre.graphlib.Graph();
g.setGraph({});
g.setDefaultEdgeLabel(function() { return {}; });
// Adidng nodes and edges to graph
Object.keys(adjList).forEach(node => {
g.setNode(node, { label: node, width: 50, height: 30 });
});
Object.entries(adjList).forEach(([node, edges]) => {
edges.forEach(target => {
g.setEdge(node, target);
});
});
// Run the layout algorithm (this part computes the layout considering everything and can't be forced to work with my fixed positions)
dagre.layout(g);
// Create the SVG
const svg = d3.select("#graph")
.append("svg")
.attr("width", "100%")
.attr("height", "100%");
const inner = svg.append("g");
// zoom behavior
const zoom = d3.zoom().on("zoom", function(event) {
inner.attr("transform", event.transform);
});
svg.call(zoom);
// Create nodes
const nodes = inner.selectAll(".node")
.data(g.nodes())
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(v) {
const node = g.node(v);
return `translate(${node.x}, ${node.y})`;
});
// draw rectangles for nodes
nodes.append("rect")
.attr("width", function(v) { return g.node(v).width; })
.attr("height", function(v) { return g.node(v).height; })
.attr("x", function(v) { return -g.node(v).width / 2; })
.attr("y", function(v) { return -g.node(v).height / 2; });
// print labels for nodes
nodes.append("text")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.text(function(v) { return v; });
// Create edges (this will use dagre's layout below)
const line = d3.line()
.x(d => d.x)
.y(d => d.y)
.curve(d3.curveBasis);
const edges = inner.selectAll(".edge")
.data(g.edges())
.enter()
.append("path")
.attr("class", "edge")
.attr("d", function(e) {
const edge = g.edge(e);
return line(edge.points);
});
const graphBounds = inner.node().getBBox();
const width = svg.node().clientWidth;
const height = svg.node().clientHeight;
const scale = Math.min(width / graphBounds.width, height / graphBounds.height) * 0.9;
const translate = [
(width - graphBounds.width * scale) / 2 - graphBounds.x * scale,
(height - graphBounds.height * scale) / 2 - graphBounds.y * scale
];
svg.call(zoom.transform, d3.zoomIdentity
.translate(translate[0], translate[1])
.scale(scale));< /code>
#graph {
width: 100%;
height: 600px;
background-color: #f9f9f9;
}
.node rect {
stroke: #333;
fill: #fff;
stroke-width: 1px;
}
.node text {
font: 12px sans-serif;
}
.edge {
stroke: #333;
stroke-width: 1px;
fill: none;
}< /code>
Simple Graph
Моя единственная проблема с этим заключается в том, что, похоже, нет способа передать мои фиксированные разложения , чтобы заставить их использовать их для размещения узлов, а затем сделать макет только для регенсов. const fixedPositions = {
A: { x: 300, y: 50 },
B: { x: 150, y: 150 },
C: { x: 450, y: 150 },
D: { x: 100, y: 250 }
};
// cannot do
// dagre.layout(g, fixedPositions);
< /code>
Есть ли способ достичь этого либо с Dagre, либо с какой -либо другой библиотекой? Этот вопрос задает об этом в теоретической перспективе, но я спрашиваю в более практичном смысле с использованием доступных библиотек с открытым исходным кодом или иным образом. Например, это может быть результатом, если я вручную нарисую ребра прямой линии для другого (не единственного в моем коде).>
Подробнее здесь: https://stackoverflow.com/questions/796 ... s-given-fi