AbstractLayout.java
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.nad.layout;
import com.powsybl.nad.model.*;
import org.jgrapht.alg.util.Pair;
import java.util.*;
import java.util.stream.Stream;
/**
*
* @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
*/
public abstract class AbstractLayout implements Layout {
private Map<String, Point> initialNodePositions = Collections.emptyMap();
private Set<String> nodesWithFixedPosition = Collections.emptySet();
private final Map<String, TextPosition> textNodesWithFixedPosition = new HashMap<>();
@Override
public void run(Graph graph, LayoutParameters layoutParameters) {
Objects.requireNonNull(graph);
Objects.requireNonNull(layoutParameters);
nodesLayout(graph, layoutParameters);
busNodesLayout(graph);
edgesLayout(graph, layoutParameters);
computeSize(graph);
}
@Override
public Map<String, Point> getInitialNodePositions() {
return initialNodePositions;
}
@Override
public void setInitialNodePositions(Map<String, Point> initialNodePositions) {
Objects.requireNonNull(initialNodePositions);
this.initialNodePositions = initialNodePositions;
}
@Override
public void setNodesWithFixedPosition(Set<String> nodesWithFixedPosition) {
this.nodesWithFixedPosition = nodesWithFixedPosition;
}
@Override
public Set<String> getNodesWithFixedPosition() {
return nodesWithFixedPosition;
}
@Override
public void setTextNodeFixedPosition(String voltageLevelId, Point topLeft, Point edgeConnection) {
Objects.requireNonNull(voltageLevelId);
Objects.requireNonNull(topLeft);
Objects.requireNonNull(edgeConnection);
textNodesWithFixedPosition.put(voltageLevelId, new TextPosition(topLeft, edgeConnection));
}
public void setFixedNodePositions(Map<String, Point> fixedNodePositions) {
setInitialNodePositions(fixedNodePositions);
setNodesWithFixedPosition(fixedNodePositions.keySet());
}
protected abstract void nodesLayout(Graph graph, LayoutParameters layoutParameters);
protected void busNodesLayout(Graph graph) {
Comparator<BusNode> c = Comparator.comparing(bn -> graph.getBusEdges(bn).size());
graph.getVoltageLevelNodesStream().forEach(n -> {
// We store the original position of each bus, to build the correct CSS class later
List<BusNode> nodes = n.getBusNodes();
for (int i = 0; i < nodes.size(); i++) {
nodes.get(i).setBusIndex(i);
}
// We sort the buses to draw them later from less connections (center) to more connections (outer annulus)
n.sortBusNodes(c);
List<BusNode> sortedNodes = n.getBusNodes();
for (int i = 0; i < sortedNodes.size(); i++) {
BusNode busNode = sortedNodes.get(i);
busNode.setRingIndex(i);
busNode.setNbNeighbouringBusNodes(sortedNodes.size() - 1);
busNode.setPosition(n.getPosition());
}
});
}
protected void fixedTextNodeLayout(Pair<VoltageLevelNode, TextNode> nodes, LayoutParameters layoutParameters) {
TextPosition fixedTextPosition = textNodesWithFixedPosition.get(nodes.getFirst().getEquipmentId());
Point textShift = fixedTextPosition != null ? fixedTextPosition.topLeftPosition() : layoutParameters.getTextNodeFixedShift();
Point textPosition = nodes.getFirst().getPosition().shift(textShift.getX(), textShift.getY());
Point connectionShift = fixedTextPosition != null ? fixedTextPosition.edgeConnection() :
new Point(layoutParameters.getTextNodeFixedShift().getX(), layoutParameters.getTextNodeFixedShift().getY() + layoutParameters.getTextNodeEdgeConnectionYShift());
Point edgeConnection = nodes.getFirst().getPosition().shift(connectionShift.getX(), connectionShift.getY());
nodes.getSecond().setPosition(textPosition);
nodes.getSecond().setEdgeConnection(edgeConnection);
}
protected void edgesLayout(Graph graph, LayoutParameters layoutParameters) {
Objects.requireNonNull(graph);
Objects.requireNonNull(layoutParameters);
graph.getBranchEdgeStream().forEach(edge -> {
setEdgeVisibility(graph.getNode1(edge), edge, BranchEdge.Side.ONE);
setEdgeVisibility(graph.getNode2(edge), edge, BranchEdge.Side.TWO);
});
}
private void setEdgeVisibility(Node node, BranchEdge branchEdge, BranchEdge.Side side) {
if (node instanceof VoltageLevelNode && !((VoltageLevelNode) node).isVisible()) {
branchEdge.setVisible(side, false);
}
}
private void computeSize(Graph graph) {
double[] dims = new double[] {Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE};
Stream.concat(graph.getTextNodesStream(), graph.getNodesStream()).forEach(node -> {
dims[0] = Math.min(dims[0], node.getX());
dims[1] = Math.max(dims[1], node.getX());
dims[2] = Math.min(dims[2], node.getY());
dims[3] = Math.max(dims[3], node.getY());
});
graph.setDimensions(dims[0], dims[1], dims[2], dims[3]);
}
}