Просмотр исходного кода

documentation + rework 2 (removed edge subdivision algorithm)

icestormikk 1 месяц назад
Родитель
Сommit
0e7e4caec6

+ 9 - 21
src/v1/graph/layering/Layer.ts

@@ -1,39 +1,27 @@
 import Node from "../node/Node.js";
 
 export default class Layer<TNode extends Node> {
-	public readonly index: number;
 	private readonly _nodes: TNode[];
 
-	public constructor(index: number) {
-		this.index = index;
+	public constructor() {
 		this._nodes = [];
 	}
 
-	public addNodes(...nodes: TNode[]) : this {
-		this._nodes.push(...nodes);
-		return this;
-	}
-
-	public removeNodes(...nodes: TNode[]) : this {
-		for(const node of nodes) {
-			const index = this._nodes.findIndex((n) => n.getId() === node.getId());
-			if(index !== -1)
-				this._nodes.splice(index, 1);
-		}
-
-		return this;
-	}
-
-	public get nodes(): TNode[] {
+	public getNodes(): TNode[] {
 		return [...this._nodes];
 	}
 
-	public set nodes(nodes: TNode[]) {
+	public setNodes(nodes: TNode[]) : this {
 		this._nodes.length = 0;
 		this._nodes.push(...nodes);
+		return this;
+	}
+
+	public addNode(node: TNode) : this {
+		return this._nodes.push(node), this;
 	}
 
-	public get width(): number {
+	public getWidth(): number {
 		return this._nodes.length;
 	}
 }

+ 37 - 66
src/v1/graph/layering/Layering.ts

@@ -1,87 +1,58 @@
-import LayeringError from "../../errors/graph/layering/LayeringError.js";
 import Edge from "../edge/Edge.js";
 import Node from "../node/Node.js";
+import LayeringError from "../../errors/graph/layering/LayeringError.js";
 import Layer from "./Layer.js";
 
 export default class Layering<TNode extends Node, TEdge extends Edge<TNode>> {
-	private readonly _layers: Map<number, Layer<TNode>>;
+	/**
+	 * Слои с вершинами
+	 * @private
+	 */
+	private readonly _layers: Layer<TNode>[];
 
 	public constructor() {
-		this._layers = new Map();
-	}
-
-	public assign(node: TNode, layerIndex: number) : this {
-		if(!this._layers.has(layerIndex))
-			this._layers.set(layerIndex, new Layer(layerIndex));
-
-		this._layers.get(layerIndex)!.addNodes(node);
-
-		return this;
-	}
-
-	public getLayers(): Layer<TNode>[] {
-		return Array.from(this._layers.values()).sort((a, b) => a.index - b.index);
-	}
-
-	public getLayerOf(node: TNode) : number | null {
-		for(const [layerIndex, layer] of this._layers)
-			if(layer.nodes.some((n) => n.getId() === node.getId()))
-				return layerIndex;
-
-		return null;
+		this._layers = [];
 	}
 
-	public setLayerOf(node: TNode, newLayerIndex: number) : this {
-		const oldIndex = this.getLayerOf(node);
-		if(oldIndex !== null) {
-			const oldLayer = this._layers.get(oldIndex);
-			oldLayer?.removeNodes(node);
-		}
+	/**
+	 * Добавление вершины в слой.
+	 * Если слой, в который добавляется вершина, не существует, то он создаётся
+	 * @param layerIndex Номер слоя, начиная с 0, в который добавляется веришна
+	 * @param node Вершина, которую необходимо добавить
+	 */
+	public addToLayer(layerIndex: number, node: TNode) : this {
+		if(layerIndex < 0)
+			throw new LayeringError("Layer index can not be negative");
 
-		if(!this._layers.has(newLayerIndex))
-			this._layers.set(newLayerIndex, new Layer(newLayerIndex));
-		this._layers.get(newLayerIndex)!.addNodes(node);
+		const layer = this._layers[layerIndex];
 
-		return this;
-	}
-
-	public getPositionOf(node: TNode) : number | null {
-		const layerIndex = this.getLayerOf(node);
-		if(layerIndex === null)
-			return null;
-
-		const layer = this._layers.get(layerIndex);
 		if(!layer)
-			return null;
-
-		const position = layer.nodes.indexOf(node);
-		return position !== -1 ? position : null;
-	}
+			this._layers[layerIndex] = new Layer<TNode>().addNode(node);
+		else
+			layer.addNode(node);
 
-	public isEdgeTight(edge: TEdge) : boolean {
-		const fromLayerIndex = this.getLayerOf(edge.getFrom()), toLayerIndex = this.getLayerOf(edge.getTo());
-
-		if(fromLayerIndex == null || toLayerIndex == null)
-			throw new LayeringError(`Node from edge ${edge.getId()} is not assigned to any layer`);
-
-		return Math.abs(fromLayerIndex - toLayerIndex) === 1;
+		return this;
 	}
 
+	/**
+	 * Вычисление длины ребра - разница между номера слоёв, в которых находятся вершины ребра
+	 * @param edge Ребро, для которого необходимо вычислить длину
+	 */
 	public getEdgeSpan(edge: TEdge) : number {
-		const fromLayer = this.getLayerOf(edge.getFrom());
-		const toLayer = this.getLayerOf(edge.getTo());
+		const from = edge.getFrom(), to = edge.getTo();
 
-		if(fromLayer === null || toLayer === null)
-			throw new LayeringError(`Node from edge ${edge.getId()} is not assigned to any layer`);
-
-		return Math.abs(fromLayer - toLayer);
-	}
+		const fromIndex = this.getNodeLayerIndex(from);
+		const toIndex = this.getNodeLayerIndex(to);
 
-	public get height() : number {
-		return this._layers.size;
+		return Math.abs(fromIndex - toIndex);
 	}
 
-	public get width() : number {
-		return Math.max(...Array.from(this._layers.values()).map((layer) => layer.width));
+	/**
+	 * Возвращает индекс слоя, в котором находится вершина.
+	 * @param node Вершина, для которой необходимо найти слой.
+	 * @returns Индекс слоя или `-1`, если вершина отсутствует во всех слоях.
+	 */
+	public getNodeLayerIndex(node: TNode) : number {
+		return this._layers.findIndex((layer) => layer.getNodes().includes(node));
 	}
-}
+}

+ 76 - 76
src/v1/optimizer/steps/CoordinateAssignmentStep.ts

@@ -26,82 +26,82 @@ export default class CoordinateAssignmentStep extends AlgorithmStep<SiguiyamaCon
 		if(!feedbackSet)
 			throw new Error("Feedback set was not found!");
 
-		const grid = this.buildGrid(layering);
-		context.grid = grid;
-
-		this.assignCoordinatesFromGrid(graph, grid);
+		// const grid = this.buildGrid(layering);
+		// context.grid = grid;
+		//
+		// this.assignCoordinatesFromGrid(graph, grid);
 	}
 
-	private buildGrid(layering: Layering<Node, Edge<Node>>): Grid<Node> {
-		const grid = new Grid<Node>();
-		const layers = layering.getLayers();
-
-		for(let col = layers.length - 1; col >= 0; col--) {
-			const layer = layers[col]!;
-
-			for(let row = 0; row < layer.nodes.length; row++)
-				grid.set(row, col, layer.nodes[row]!);
-		}
-
-		return grid;
-	}
-
-	private assignCoordinatesFromGrid(graph: Graph<Node, Edge<Node>>, grid: Grid<Node>): void {
-		const PADDING = 60;
-
-		const colWidths = this.computeColWidths(grid);
-		const rowHeights = this.computeRowHeights(grid);
-
-		const colX = this.computeOffsets(colWidths, this._nodeGap, PADDING);
-		const rowY = this.computeOffsets(rowHeights, this._layerGap, PADDING);
-
-		for(let col = 0; col < grid.getCols(); col++) {
-			for(let row = 0; row < grid.getRows(); row++) {
-				const node = grid.get(row, col);
-				if(!node)
-					continue;
-
-				node.setX(colX[col]! + (colWidths[col]! - node.getWidth()) / 2);
-				node.setY(rowY[row]! + (rowHeights[row]! - node.getHeight()) / 2);
-			}
-		}
-	}
-
-	private computeColWidths(grid: Grid<Node>): number[] {
-		const widths: number[] = new Array(grid.getCols()).fill(0);
-
-		for(let col = 0; col < grid.getCols(); col++)
-			for(let row = 0; row < grid.getRows(); row++) {
-				const node = grid.get(row, col);
-				if(!node)
-					continue;
-
-				widths[col] = Math.max(widths[col]!, node.getWidth());
-			}
-
-		return widths;
-	}
-
-	private computeRowHeights(grid: Grid<Node>): number[] {
-		const heights: number[] = new Array(grid.getRows()).fill(0);
-
-		for(let row = 0; row < grid.getRows(); row++)
-			for(let col = 0; col < grid.getCols(); col++) {
-				const node = grid.get(row, col);
-				if(!node) continue;
-
-				heights[row] = Math.max(heights[row]!, node.getHeight());
-			}
-
-		return heights;
-	}
-
-	private computeOffsets(sizes: number[], gap: number, padding: number): number[] {
-		const offsets: number[] = [padding];
-
-		for(let i = 1; i < sizes.length; i++)
-			offsets.push(offsets[i - 1]! + sizes[i - 1]! + gap);
-
-		return offsets;
-	}
+	// private buildGrid(layering: Layering<Node, Edge<Node>>): Grid<Node> {
+	// 	const grid = new Grid<Node>();
+	// 	const layers = layering.getLayers();
+	//
+	// 	for(let col = layers.length - 1; col >= 0; col--) {
+	// 		const layer = layers[col]!;
+	//
+	// 		for(let row = 0; row < layer.nodes.length; row++)
+	// 			grid.set(row, col, layer.nodes[row]!);
+	// 	}
+	//
+	// 	return grid;
+	// }
+	//
+	// private assignCoordinatesFromGrid(graph: Graph<Node, Edge<Node>>, grid: Grid<Node>): void {
+	// 	const PADDING = 60;
+	//
+	// 	const colWidths = this.computeColWidths(grid);
+	// 	const rowHeights = this.computeRowHeights(grid);
+	//
+	// 	const colX = this.computeOffsets(colWidths, this._nodeGap, PADDING);
+	// 	const rowY = this.computeOffsets(rowHeights, this._layerGap, PADDING);
+	//
+	// 	for(let col = 0; col < grid.getCols(); col++) {
+	// 		for(let row = 0; row < grid.getRows(); row++) {
+	// 			const node = grid.get(row, col);
+	// 			if(!node)
+	// 				continue;
+	//
+	// 			node.setX(colX[col]! + (colWidths[col]! - node.getWidth()) / 2);
+	// 			node.setY(rowY[row]! + (rowHeights[row]! - node.getHeight()) / 2);
+	// 		}
+	// 	}
+	// }
+	//
+	// private computeColWidths(grid: Grid<Node>): number[] {
+	// 	const widths: number[] = new Array(grid.getCols()).fill(0);
+	//
+	// 	for(let col = 0; col < grid.getCols(); col++)
+	// 		for(let row = 0; row < grid.getRows(); row++) {
+	// 			const node = grid.get(row, col);
+	// 			if(!node)
+	// 				continue;
+	//
+	// 			widths[col] = Math.max(widths[col]!, node.getWidth());
+	// 		}
+	//
+	// 	return widths;
+	// }
+	//
+	// private computeRowHeights(grid: Grid<Node>): number[] {
+	// 	const heights: number[] = new Array(grid.getRows()).fill(0);
+	//
+	// 	for(let row = 0; row < grid.getRows(); row++)
+	// 		for(let col = 0; col < grid.getCols(); col++) {
+	// 			const node = grid.get(row, col);
+	// 			if(!node) continue;
+	//
+	// 			heights[row] = Math.max(heights[row]!, node.getHeight());
+	// 		}
+	//
+	// 	return heights;
+	// }
+	//
+	// private computeOffsets(sizes: number[], gap: number, padding: number): number[] {
+	// 	const offsets: number[] = [padding];
+	//
+	// 	for(let i = 1; i < sizes.length; i++)
+	// 		offsets.push(offsets[i - 1]! + sizes[i - 1]! + gap);
+	//
+	// 	return offsets;
+	// }
 }

+ 8 - 96
src/v1/optimizer/steps/LayerAssignmentStep.ts

@@ -1,11 +1,10 @@
 import LayerAssignmentStepError from "../../errors/optimizer/LayerAssignmentStepError.js";
-import DummyNode from "../../graph/node/DummyNode.js";
 import Edge from "../../graph/edge/Edge.js";
 import Graph from "../../graph/Graph.js";
 import Layering from "../../graph/layering/Layering.js";
 import Node from "../../graph/node/Node.js";
 import AlgorithmStep from "../AlgorithmStep.js";
-import {EdgeSubdivision, FeedbackSet, SiguiyamaContext} from "../siguiyama/SiguiyamaContext.js";
+import {SiguiyamaContext} from "../siguiyama/SiguiyamaContext.js";
 
 /**
  * Шаг алгоритма Sugiyama для присвоения слоёв вершинам графа.
@@ -25,7 +24,7 @@ export default class LayerAssignmentStep extends AlgorithmStep<SiguiyamaContext>
 	 * @throws {LayerAssignmentStepError} Если граф не найден, feedback set не определён или граф ацикличен.
 	 */
 	public run(context: SiguiyamaContext): void {
-		const { graph, feedbackSet, edgeSubdivisions } = context;
+		const {graph, feedbackSet, edgeSubdivisions} = context;
 
 		if(!graph)
 			throw new LayerAssignmentStepError("Graph was not found!");
@@ -37,26 +36,22 @@ export default class LayerAssignmentStep extends AlgorithmStep<SiguiyamaContext>
 		if(!graph.isAcyclic())
 			throw new LayerAssignmentStepError("Graph is acyclic, can not assign layers to an acyclic graph!");
 
-		const layering = this.longestPathAlgorithm(graph);
-
-		this.subdivideLongEdges(graph, layering, feedbackSet, context.edgeSubdivisions!);
-
-		context.layering = layering;
+		context.layering = this.longestPathAlgorithm(graph);
 	}
 
 	/**
-	 * Реализует алгоритм longest path layering для присвоения слоёв вершинам DAG.
+	 * Реализация алгоритма longest path layering для присвоения слоёв вершинам DAG.
 	 * Каждая вершина получает слой, равный длине самого длинного пути от неё.
 	 * @param dag Направленный ациклический граф.
 	 * @returns Объект Layering с присвоенными слоями.
 	 * @private
 	 */
-	private longestPathAlgorithm(dag: Graph<Node, Edge<Node>>) : NonNullable<SiguiyamaContext["layering"]> {
+	private longestPathAlgorithm(dag: Graph<Node, Edge<Node>>): NonNullable<SiguiyamaContext["layering"]> {
 		const layering = new Layering<Node, Edge<Node>>();
 		const cache = new Map<string, number>();
 
 		const getLongestPath = (node: Node): number => {
-			if(cache.has(node.getId())) 
+			if(cache.has(node.getId()))
 				return cache.get(node.getId())!;
 
 			const successors = dag.getNodeOutputs(node);
@@ -73,92 +68,9 @@ export default class LayerAssignmentStep extends AlgorithmStep<SiguiyamaContext>
 
 		for(const node of dag.getNodes()) {
 			const layer = getLongestPath(node);
-			layering.assign(node, layer);
+			layering.addToLayer(layer, node);
 		}
 
 		return layering;
 	}
-
-	/**
-	 * Разбивает длинные рёбра (пересекающие более одного слоя) на сегменты с dummy-вершинами.
-	 * @param graph Граф, в котором разбиваются рёбра.
-	 * @param layering Текущая структура слоёв.
-	 * @param feedbackSet Набор рёбер обратной связи.
-	 * @param edgeSubdivisions Массив для хранения информации о разбиениях рёбер.
-	 * @private
-	 */
-	private subdivideLongEdges(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, feedbackSet: FeedbackSet, edgeSubdivisions: NonNullable<SiguiyamaContext["edgeSubdivisions"]>) : void {
-		const edges = graph.getEdges();
-
-		for(const edge of edges) {
-			const edgeSpan = layering.getEdgeSpan(edge);
-
-			if(edgeSpan <= 1)
-				continue;
-
-			this.subdivideLongEdge(graph, layering, edge, feedbackSet, edgeSpan, edgeSubdivisions);
-		}
-	}
-
-	/**
-     * Разбивает одно длинное ребро на сегменты с dummy-вершинами.
-     * @param graph Граф, в котором разбивается ребро.
-     * @param layering Текущая структура слоёв.
-     * @param edge Ребро для разбиения.
-     * @param feedbackSet Набор рёбер обратной связи.
-     * @param span Число слоёв, которые пересекает ребро.
-     * @param subdivisions Массив для хранения информации о разбиениях рёбер.
-     * @private
-     */
-	private subdivideLongEdge(
-		graph: Graph<Node, Edge<Node>>,
-		layering: Layering<Node, Edge<Node>>,
-		edge: Edge<Node>,
-		feedbackSet: FeedbackSet,
-		span: number,
-		subdivisions: Map<string, EdgeSubdivision<Node>>
-	): void {
-		const edgeId = edge.getId();
-		const edgeFrom = edge.getFrom();
-		const edgeTo = edge.getTo();
-
-		const edgeReversedIndex = feedbackSet.findIndex((e) => e.getId() === edgeId);
-		const isReversed = edgeReversedIndex !== -1;
-
-		graph.removeEdge(edgeId);
-
-		const fromLayer = layering.getLayerOf(edgeFrom)!;
-		const toLayer = layering.getLayerOf(edgeTo)!;
-		const direction = Math.sign(toLayer - fromLayer);
-		let previousNode = edgeFrom;
-
-		const segments: Edge<Node>[] = [];
-		const dummies: DummyNode[] = [];
-
-		for(let i = 1; i < span; i++) {
-			const dummy = new DummyNode(0, 0, edgeId, `dummy-${edgeId}-${i}`);
-
-			graph.addNode(dummy);
-			layering.assign(dummy, fromLayer + direction * i);
-			dummies.push(dummy);
-
-			const segment = new Edge(previousNode, dummy, [], `${edgeId}_segment_${i}`);
-			graph.addEdge(segment);
-			segments.push(segment);
-
-			if(isReversed)
-				feedbackSet.push(segment);
-
-			previousNode = dummy;
-		}
-
-		const lastSegment = new Edge(previousNode, edgeTo, [], `${edgeId}_segment_${span}`);
-		graph.addEdge(lastSegment);
-		segments.push(lastSegment);
-
-		if(isReversed)
-			feedbackSet.push(lastSegment);
-
-		subdivisions.set(edgeId, { originalEdge: edge, segments, dummies });
-	}
-}
+}

+ 88 - 88
src/v1/optimizer/steps/NodeOrderingStep.ts

@@ -24,94 +24,94 @@ export default class NodeOrderingStep extends AlgorithmStep<SiguiyamaContext> {
 		if(!layering)
 			throw new NodeOrderingStepError("Layering of graph was not found!");
 
-		this.barycenter(graph, layering);
+		// this.barycenter(graph, layering);
 	}
 
-	private barycenter(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : void {
-		let crossingsCount = Infinity;
-
-		for(let i = 0; i < this._iterationsCount; i++) {
-			this.sweepDown(graph, layering);
-			this.sweepUp(graph, layering);
-
-			const currentCrossingsCount = this.countTotalCrossings(graph, layering);
-
-			if(currentCrossingsCount > crossingsCount)
-				break;
-
-			crossingsCount = currentCrossingsCount;
-		}
-	}
-
-	private sweepDown(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : void {
-		const layers = layering.getLayers();
-
-		for(let i = 0; i < layers.length; i++) {
-			const currLayer = layers[i]!;
-			currLayer.nodes = this.applyBarycenter(graph, layering, currLayer.nodes, "down");
-		}
-	}
-
-	private sweepUp(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : void {
-		const layers = layering.getLayers();
-
-		for(let i = layers.length - 1; i > 0; i--) {
-			const currLayer = layers[i]!;
-			currLayer.nodes = this.applyBarycenter(graph, layering, currLayer.nodes, "up");
-		}
-	}
-
-	private applyBarycenter(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, nodeIds: Node[], direction: "up" | "down") : Node[] {
-		const barycenterValues = nodeIds.map((node) => {
-			const neighbors = direction === "down" ? graph.getNodeInputs(node) : graph.getNodeOutputs(node);
-			if(neighbors.length === 0) {
-				const currentPosition = layering.getPositionOf(node);
-				return { node, value: currentPosition ?? Infinity };
-			}
-
-			const posistionSum = neighbors.reduce((sum, neighbor) => {
-				const position = layering.getPositionOf(neighbor);
-				return sum + (position ?? 0);
-			}, 0);
-
-			return { node, value: posistionSum / neighbors.length };
-		});
-
-		return barycenterValues.sort((a, b) => a.value - b.value).map((entry) => entry.node);
-	}
-
-	private countTotalCrossings(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : number {
-		const layers = layering.getLayers();
-
-		let totalCrossings = 0;
-
-		for(let i = 0; i < layers.length - 1; i++)
-			totalCrossings += this.countCrossingsBetweenLayers(graph, layering, i, i + 1);
-
-		return totalCrossings;
-	}
-
-	private countCrossingsBetweenLayers(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, upperLayerIndex: number, lowerLayerIndex: number) : number {
-		const edges = graph.getEdges().filter((edge) => {
-			const fromLayer = layering.getLayerOf(edge.getFrom());
-			const toLayer = layering.getLayerOf(edge.getTo());
-			return fromLayer === upperLayerIndex && toLayer === lowerLayerIndex;
-		});
-		
-		let crossings = 0;
-
-		for(let i = 0; i < edges.length; i++) {
-			for(let j = i + 1; j < edges.length; j++) {
-				const posU = layering.getPositionOf(edges[i]!.getFrom()) ?? 0;
-				const posV = layering.getPositionOf(edges[i]!.getTo()) ?? 0;
-				const posS = layering.getPositionOf(edges[j]!.getFrom()) ?? 0;
-				const posT = layering.getPositionOf(edges[j]!.getTo()) ?? 0;
-
-				if((posU < posS && posV > posT) || (posU > posS && posV < posT))
-					crossings++;
-			}
-		}
-
-		return crossings;
-	}
+	// private barycenter(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : void {
+	// 	let crossingsCount = Infinity;
+	//
+	// 	for(let i = 0; i < this._iterationsCount; i++) {
+	// 		this.sweepDown(graph, layering);
+	// 		this.sweepUp(graph, layering);
+	//
+	// 		const currentCrossingsCount = this.countTotalCrossings(graph, layering);
+	//
+	// 		if(currentCrossingsCount > crossingsCount)
+	// 			break;
+	//
+	// 		crossingsCount = currentCrossingsCount;
+	// 	}
+	// }
+	//
+	// private sweepDown(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : void {
+	// 	const layers = layering.getLayers();
+	//
+	// 	for(let i = 0; i < layers.length; i++) {
+	// 		const currLayer = layers[i]!;
+	// 		currLayer.nodes = this.applyBarycenter(graph, layering, currLayer.nodes, "down");
+	// 	}
+	// }
+	//
+	// private sweepUp(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : void {
+	// 	const layers = layering.getLayers();
+	//
+	// 	for(let i = layers.length - 1; i > 0; i--) {
+	// 		const currLayer = layers[i]!;
+	// 		currLayer.nodes = this.applyBarycenter(graph, layering, currLayer.nodes, "up");
+	// 	}
+	// }
+	//
+	// private applyBarycenter(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, nodeIds: Node[], direction: "up" | "down") : Node[] {
+	// 	const barycenterValues = nodeIds.map((node) => {
+	// 		const neighbors = direction === "down" ? graph.getNodeInputs(node) : graph.getNodeOutputs(node);
+	// 		if(neighbors.length === 0) {
+	// 			const currentPosition = layering.getPositionOf(node);
+	// 			return { node, value: currentPosition ?? Infinity };
+	// 		}
+	//
+	// 		const posistionSum = neighbors.reduce((sum, neighbor) => {
+	// 			const position = layering.getPositionOf(neighbor);
+	// 			return sum + (position ?? 0);
+	// 		}, 0);
+	//
+	// 		return { node, value: posistionSum / neighbors.length };
+	// 	});
+	//
+	// 	return barycenterValues.sort((a, b) => a.value - b.value).map((entry) => entry.node);
+	// }
+	//
+	// private countTotalCrossings(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>) : number {
+	// 	const layers = layering.getLayers();
+	//
+	// 	let totalCrossings = 0;
+	//
+	// 	for(let i = 0; i < layers.length - 1; i++)
+	// 		totalCrossings += this.countCrossingsBetweenLayers(graph, layering, i, i + 1);
+	//
+	// 	return totalCrossings;
+	// }
+	//
+	// private countCrossingsBetweenLayers(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, upperLayerIndex: number, lowerLayerIndex: number) : number {
+	// 	const edges = graph.getEdges().filter((edge) => {
+	// 		const fromLayer = layering.getLayerOf(edge.getFrom());
+	// 		const toLayer = layering.getLayerOf(edge.getTo());
+	// 		return fromLayer === upperLayerIndex && toLayer === lowerLayerIndex;
+	// 	});
+	//
+	// 	let crossings = 0;
+	//
+	// 	for(let i = 0; i < edges.length; i++) {
+	// 		for(let j = i + 1; j < edges.length; j++) {
+	// 			const posU = layering.getPositionOf(edges[i]!.getFrom()) ?? 0;
+	// 			const posV = layering.getPositionOf(edges[i]!.getTo()) ?? 0;
+	// 			const posS = layering.getPositionOf(edges[j]!.getFrom()) ?? 0;
+	// 			const posT = layering.getPositionOf(edges[j]!.getTo()) ?? 0;
+	//
+	// 			if((posU < posS && posV > posT) || (posU > posS && posV < posT))
+	// 				crossings++;
+	// 		}
+	// 	}
+	//
+	// 	return crossings;
+	// }
 }