Skip to content
This repository has been archived by the owner on Nov 18, 2022. It is now read-only.

Compute exactly edges #48

Merged
merged 13 commits into from
Jan 25, 2022
19 changes: 13 additions & 6 deletions src/main/java/com/powsybl/nad/build/iidm/NetworkGraphBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private void addGraphNodes(Graph graph) {
VoltageLevelNode vlNode = new VoltageLevelNode(idProvider.createId(vl), vl.getId(), vl.getNameOrId());
TextNode textNode = new TextNode(vlNode.getDiagramId() + "_text", vl.getNameOrId());
vl.getBusView().getBusStream()
.map(bus -> new BusInnerNode(idProvider.createId(bus), bus.getId()))
.map(bus -> new BusNode(idProvider.createId(bus), bus.getId()))
.forEach(vlNode::addBusNode);
graph.addNode(vlNode);
graph.addNode(textNode);
Expand Down Expand Up @@ -132,8 +132,8 @@ private void addEdge(Terminal terminalA, Terminal terminalB, Identifiable<?> ide
.orElseThrow(() -> new PowsyblException("Cannot add edge, corresponding voltage level is unknown: '" + terminalA.getVoltageLevel().getId() + "'"));
VoltageLevelNode vlNodeB = getOrCreateInvisibleVoltageLevelNode(terminalB);

BusInnerNode busNodeA = getBusInnerNode(terminalA, vlNodeA);
BusInnerNode busNodeB = getBusInnerNode(terminalB, vlNodeB);
BusNode busNodeA = getBusNode(terminalA);
BusNode busNodeB = getBusNode(terminalB);

BranchEdge edge = new BranchEdge(idProvider.createId(identifiable), identifiable.getId(), identifiable.getNameOrId(), edgeType);
if (!terminalsInReversedOrder) {
Expand All @@ -148,7 +148,7 @@ private void addThreeWtEdge(ThreeWindingsTransformer twt, ThreeWtNode tn, ThreeW
VoltageLevelNode vlNode = getOrCreateInvisibleVoltageLevelNode(terminal);
ThreeWtEdge edge = new ThreeWtEdge(idProvider.createId(IidmUtils.get3wtLeg(twt, side)),
twt.getId(), twt.getNameOrId(), IidmUtils.getThreeWtEdgeSideFromIidmSide(side), vlNode.isVisible());
graph.addEdge(vlNode, getBusInnerNode(terminal, vlNode), tn, edge);
graph.addEdge(vlNode, getBusNode(terminal), tn, edge);
}

private ThreeWindingsTransformer.Side[] getSidesArray(ThreeWindingsTransformer.Side sideA) {
Expand All @@ -167,9 +167,13 @@ private ThreeWindingsTransformer.Side[] getSidesArray(ThreeWindingsTransformer.S
return new ThreeWindingsTransformer.Side[] {sideA, sideB, sideC};
}

private BusInnerNode getBusInnerNode(Terminal terminal, VoltageLevelNode vlNode) {
private BusNode getBusNode(Terminal terminal) {
Bus connectableBusA = terminal.getBusView().getConnectableBus();
return connectableBusA != null ? vlNode.getBusInnerNode(connectableBusA.getId()) : null;
if (connectableBusA == null) {
graph.getVoltageLevelNode(terminal.getVoltageLevel().getId()).ifPresent(vlNode -> vlNode.setHasUnknownBusNode(true));
return BusNode.UNKNOWN;
}
return graph.getBusNode(connectableBusA.getId());
}

private VoltageLevelNode getOrCreateInvisibleVoltageLevelNode(Terminal terminal) {
Expand All @@ -179,6 +183,9 @@ private VoltageLevelNode getOrCreateInvisibleVoltageLevelNode(Terminal terminal)

private VoltageLevelNode createInvisibleVoltageLevelNode(VoltageLevel vl) {
VoltageLevelNode invisibleVlNode = new VoltageLevelNode(idProvider.createId(vl), vl.getId(), vl.getNameOrId(), false);
vl.getBusView().getBusStream()
.map(bus -> new BusNode(idProvider.createId(bus), bus.getId()))
.forEach(invisibleVlNode::addBusNode);
graph.addNode(invisibleVlNode);
return invisibleVlNode;
}
Expand Down
89 changes: 25 additions & 64 deletions src/main/java/com/powsybl/nad/layout/AbstractLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,33 @@
import com.powsybl.nad.model.*;

import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

public abstract class AbstractLayout implements Layout {

protected void edgeLayout(Graph graph, LayoutParameters layoutParameters) {
@Override
public void run(Graph graph, LayoutParameters layoutParameters) {
Objects.requireNonNull(graph);
Objects.requireNonNull(layoutParameters);
graph.getNonMultiBranchEdgesStream().forEach(edge -> singleBranchEdgeLayout(graph, edge));
graph.getMultiBranchEdgesStream().forEach(edges -> multiBranchEdgesLayout(graph, edges, layoutParameters));
graph.getThreeWtEdgesStream().forEach(edge -> threeWtEdgeLayout(graph.getNode1(edge), graph.getNode2(edge), edge));
graph.getTextEdgesMap().forEach((edge, nodes) -> textEdgeLayout(nodes.getFirst(), nodes.getSecond(), edge));
}

protected void textEdgeLayout(Node node1, Node node2, TextEdge edge) {
Point point1 = new Point(node1.getX(), node1.getY());
Point point2 = new Point(node2.getX(), node2.getY());
edge.setPoints(point1, point2);
}
nodesLayout(graph, layoutParameters);
busNodesLayout(graph, layoutParameters);
edgesLayout(graph, layoutParameters);

private void singleBranchEdgeLayout(Graph graph, BranchEdge edge) {
Node node1 = graph.getNode1(edge);
Node node2 = graph.getNode2(edge);
Point point1 = new Point(node1.getX(), node1.getY());
Point point2 = new Point(node2.getX(), node2.getY());
Point middle = Point.createMiddlePoint(point1, point2);
edge.setPoints1(point1, middle);
edge.setPoints2(point2, middle);
setEdgeVisibility(node1, edge, BranchEdge.Side.ONE);
setEdgeVisibility(node2, edge, BranchEdge.Side.TWO);
computeSize(graph);
}

private void multiBranchEdgesLayout(Graph graph, Set<Edge> edges, LayoutParameters layoutParameters) {
Edge firstEdge = edges.iterator().next();
Node node1 = graph.getNode1(firstEdge);
Node node2 = graph.getNode2(firstEdge);
Point pointA = new Point(node1.getX(), node1.getY());
Point pointB = new Point(node2.getX(), node2.getY());

double dx = pointB.getX() - pointA.getX();
double dy = pointB.getY() - pointA.getY();
double angle = Math.atan2(dy, dx);

int nbForks = edges.size();
double forkAperture = layoutParameters.getEdgesForkAperture();
double forkLength = layoutParameters.getEdgesForkLength();
double angleStep = forkAperture / (nbForks - 1);
protected abstract void nodesLayout(Graph graph, LayoutParameters layoutParameters);

int i = 0;
for (Edge edge : edges) {
if (!(edge instanceof BranchEdge)) {
continue;
}
BranchEdge branchEdge = (BranchEdge) edge;
if (2 * i + 1 == nbForks) { // in the middle, hence alpha = 0
singleBranchEdgeLayout(graph, branchEdge);
} else {
double alpha = -forkAperture / 2 + i * angleStep;
double angleFork1 = angle - alpha;
double angleFork2 = angle + Math.PI + alpha;
Point fork1 = pointA.shift(forkLength * Math.cos(angleFork1), forkLength * Math.sin(angleFork1));
Point fork2 = pointB.shift(forkLength * Math.cos(angleFork2), forkLength * Math.sin(angleFork2));
protected abstract void busNodesLayout(Graph graph, LayoutParameters layoutParameters);

Point middle = Point.createMiddlePoint(fork1, fork2);
BranchEdge.Side side = graph.getNode1(edge) == node1 ? BranchEdge.Side.ONE : BranchEdge.Side.TWO;
branchEdge.setPoints(side, pointA, fork1, middle);
branchEdge.setPoints(side.getOpposite(), pointB, fork2, middle);
setEdgeVisibility(node1, branchEdge, BranchEdge.Side.ONE);
setEdgeVisibility(node2, branchEdge, BranchEdge.Side.TWO);
}
i++;
}
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) {
Expand All @@ -82,9 +38,14 @@ private void setEdgeVisibility(Node node, BranchEdge branchEdge, BranchEdge.Side
}
}

private void threeWtEdgeLayout(Node node1, Node node2, ThreeWtEdge edge) {
Point point1 = new Point(node1.getX(), node1.getY());
Point point2 = new Point(node2.getX(), node2.getY());
edge.setPoints(point1, point2);
private void computeSize(Graph graph) {
double[] dims = new double[4];
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]);
}
}
30 changes: 15 additions & 15 deletions src/main/java/com/powsybl/nad/layout/BasicForceLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@
import com.powsybl.nad.model.*;
import org.jgrapht.alg.util.Pair;

import java.util.Objects;
import java.util.Comparator;
import java.util.List;

/**
* @author Florian Dupuy <florian.dupuy at rte-france.com>
*/
public class BasicForceLayout extends AbstractLayout {

@Override
public void run(Graph graph, LayoutParameters layoutParameters) {
Objects.requireNonNull(graph);
Objects.requireNonNull(layoutParameters);

protected void nodesLayout(Graph graph, LayoutParameters layoutParameters) {
org.jgrapht.Graph<Node, Edge> jgraphtGraph = graph.getJgraphtGraph(layoutParameters.isTextNodesForceLayout());
ForceLayout<Node, Edge> forceLayout = new ForceLayout<>(jgraphtGraph);
forceLayout.execute();
Expand All @@ -35,18 +33,20 @@ public void run(Graph graph, LayoutParameters layoutParameters) {
if (!layoutParameters.isTextNodesForceLayout()) {
graph.getTextEdgesMap().forEach(this::fixedTextNodeLayout);
}
}

edgeLayout(graph, layoutParameters);

double[] dims = new double[4];
jgraphtGraph.vertexSet().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());
protected void busNodesLayout(Graph graph, LayoutParameters layoutParameters) {
Comparator<BusNode> c = Comparator.comparing(bn -> graph.getBusEdges(bn).size());
graph.getVoltageLevelNodesStream().forEach(n -> {
n.sortBusNodes(c);
List<BusNode> sortedNodes = n.getBusNodes();
for (int i = 0; i < sortedNodes.size(); i++) {
BusNode busNode = sortedNodes.get(i);
busNode.setIndex(i);
busNode.setNbNeighbouringBusNodes(sortedNodes.size() - 1);
busNode.setPosition(n.getX(), n.getY());
}
});
graph.setDimensions(dims[0], dims[1], dims[2], dims[3]);

}

private void fixedTextNodeLayout(TextEdge textEdge, Pair<VoltageLevelNode, TextNode> nodes) {
Expand Down
20 changes: 0 additions & 20 deletions src/main/java/com/powsybl/nad/layout/LayoutParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,8 @@
* @author Florian Dupuy <florian.dupuy at rte-france.com>
*/
public class LayoutParameters {
private double edgesForkAperture = Math.toRadians(60);
private double edgesForkLength = 0.8;
private boolean textNodesForceLayout = false;

public double getEdgesForkAperture() {
return edgesForkAperture;
}

public LayoutParameters setEdgesForkAperture(double edgesForkApertureDegrees) {
this.edgesForkAperture = Math.toRadians(edgesForkApertureDegrees);
return this;
}

public double getEdgesForkLength() {
return edgesForkLength;
}

public LayoutParameters setEdgesForkLength(double edgesForkLength) {
this.edgesForkLength = edgesForkLength;
return this;
}

public boolean isTextNodesForceLayout() {
return textNodesForceLayout;
}
Expand Down
18 changes: 0 additions & 18 deletions src/main/java/com/powsybl/nad/model/BusInnerNode.java

This file was deleted.

38 changes: 38 additions & 0 deletions src/main/java/com/powsybl/nad/model/BusNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2021, 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.model;

/**
* @author Florian Dupuy <florian.dupuy at rte-france.com>
*/
public class BusNode extends AbstractNode {

public static final BusNode UNKNOWN = new BusNode("", "");

private int index;
private int nbNeighbouringBusNodes;

public BusNode(String diagramId, String id) {
super(diagramId, id, null);
}

public void setIndex(int index) {
this.index = index;
}

public int getIndex() {
return index;
}

public void setNbNeighbouringBusNodes(int nbNeighbouringBusNodes) {
this.nbNeighbouringBusNodes = nbNeighbouringBusNodes;
}

public int getNbNeighbouringBusNodes() {
return nbNeighbouringBusNodes;
}
}
Loading