Skip to content

Commit

Permalink
Decoupling cells and layout, blocks and layout (#380)
Browse files Browse the repository at this point in the history
* Decoupling by implementing visitors in layout for calculating coordinates and cell height

Signed-off-by: BenoitJeanson <benoit.jeanson@rte-france.com>
  • Loading branch information
BenoitJeanson authored Apr 5, 2022
1 parent 7407993 commit 8e6d244
Show file tree
Hide file tree
Showing 30 changed files with 520 additions and 386 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public VoltageLevelGraph getGraph() {
@Override
protected List<Point> calculatePolylineSnakeLine(LayoutParameters layoutParam, Node node1, Node node2, boolean increment) {
double yMin = getGraph().getY();
double yMax = getGraph().getY() + getGraph().getInnerHeight(layoutParam);
double yMax = getGraph().getY() + getGraph().getInnerHeight(layoutParam.getVerticalSpaceBus());
return calculatePolylineSnakeLineForHorizontalLayout(layoutParam, node1, node2, increment, infosNbSnakeLines, yMin, yMax);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* 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.sld.layout;

import java.util.HashSet;
import java.util.Set;
import java.util.function.DoubleBinaryOperator;

import com.powsybl.sld.model.blocks.*;
import com.powsybl.sld.model.nodes.Node;

import static com.powsybl.sld.model.nodes.Node.NodeType.*;

/**
* @author Benoit Jeanson <benoit.jeanson at rte-france.com>
*/

public final class CalculateCellHeightBlockVisitor implements BlockVisitor {
private double blockHeight;
private final Set<Node> encounteredNodes;
private final LayoutParameters layoutParameters;

private CalculateCellHeightBlockVisitor(LayoutParameters layoutParameters, Set<Node> encounteredNodes) {
this.layoutParameters = layoutParameters;
this.encounteredNodes = encounteredNodes;
}

public static CalculateCellHeightBlockVisitor create(LayoutParameters layoutParameters, Set<Node> encounteredNodes) {
return new CalculateCellHeightBlockVisitor(layoutParameters, encounteredNodes);
}

public static CalculateCellHeightBlockVisitor create(LayoutParameters layoutParameters) {
return new CalculateCellHeightBlockVisitor(layoutParameters, new HashSet<>());
}

public double getBlockHeight() {
return blockHeight;
}

@Override
public void visit(LegPrimaryBlock block) {
blockHeight = 0;
}

@Override
public void visit(FeederPrimaryBlock block) {
blockHeight = 0;
}

@Override
public void visit(BodyPrimaryBlock block) {
// we do not consider the exact height of components as the maximum height will
// later be split up equally
// between nodes
double componentHeight = layoutParameters.getMaxComponentHeight()
+ layoutParameters.getMinSpaceBetweenComponents();

// we increment the height only if the node is not a bus node and has not been
// already encountered
long nbNodes = block.getNodes().stream().filter(n -> !encounteredNodes.contains(n) && n.getType() != BUS)
.count();

this.blockHeight = (nbNodes - 1) * componentHeight;
}

@Override
public void visit(SerialBlock block) {
calculateSubHeight(block, Double::sum);
}

@Override
public void visit(LegParralelBlock block) {
calculateSubHeight(block, Math::max);
}

@Override
public void visit(BodyParallelBlock block) {
calculateSubHeight(block, Math::max);
}

@Override
public void visit(UndefinedBlock block) {
calculateSubHeight(block, Math::max);
}

private void calculateSubHeight(ComposedBlock block, DoubleBinaryOperator merge) {
blockHeight = 0.;
for (Block sub : block.getSubBlocks()) {
// Here, when the subBlocks are positioned in parallel we calculate the max
// height of all these subBlocks
// when the subBlocks are serial, we calculate the sum
CalculateCellHeightBlockVisitor cch = create(layoutParameters, encounteredNodes);
sub.accept(cch);
blockHeight = merge.applyAsDouble(blockHeight, cch.blockHeight);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* 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.sld.layout;

import com.powsybl.sld.model.blocks.*;
import com.powsybl.sld.model.coordinate.Coord;
import com.powsybl.sld.model.coordinate.Position;
import com.powsybl.sld.model.nodes.Node;

import static com.powsybl.sld.model.coordinate.Orientation.*;
import static com.powsybl.sld.model.coordinate.Position.Dimension.*;
import static com.powsybl.sld.model.coordinate.Coord.Dimension.*;

/**
* @author Benoit Jeanson <benoit.jeanson at rte-france.com>
*/
public final class CalculateCoordBlockVisitor implements BlockVisitor {
private final LayoutParameters layoutParameters;
private final LayoutContext layoutContext;

private CalculateCoordBlockVisitor(LayoutParameters layoutParameters, LayoutContext layoutContext) {
this.layoutParameters = layoutParameters;
this.layoutContext = layoutContext;
}

public static CalculateCoordBlockVisitor create(LayoutParameters layoutParameters, LayoutContext layoutContext) {
return new CalculateCoordBlockVisitor(layoutParameters, layoutContext);
}

@Override
public void visit(BodyPrimaryBlock block) {
if (block.getPosition().getOrientation().isVertical()) {
int sign = block.getOrientation() == UP ? 1 : -1;
double y0 = block.getCoord().get(Y) + sign * block.getCoord().getSpan(Y) / 2;
double yPxStep = block.getPosition().getSpan(V) == 0 ? 0
: sign * block.getCoord().getSpan(Y) / (block.getNodes().size() - 1);
int v = 0;
for (Node node : block.getNodes()) {
node.setCoordinates(block.getCoord().get(X), y0 - yPxStep * v);
node.setRotationAngle(null);
v++;
}
} else {
double x0 = block.getCoord().get(X) - block.getCoord().getSpan(X) / 2;
if (layoutContext.isInternCell() && !layoutContext.isFlat()) {
x0 += layoutParameters.getCellWidth() / 2;
}
double xPxStep = block.getCoord().getSpan(X) / (block.getNodes().size() - 1);
int h = 0;
for (Node node : block.getNodes()) {
node.setCoordinates(x0 + xPxStep * h, block.getCoord().get(Y));
node.setRotationAngle(90.);
h++;
}
}
}

@Override
public void visit(LegPrimaryBlock block) {
if (block.getPosition().getOrientation().isVertical()) {
block.getNodeOnBus().setCoordinates(block.getCoord().get(X), block.getBusNode().getY());
block.getLegNode().setX(block.getCoord().get(X));
if (layoutContext.isInternCell() && layoutContext.isUnileg()) {
block.getLegNode().setY(block.getCoord().get(Y)
+ (block.getOrientation() == UP ? -1 : 1) * layoutParameters.getInternCellHeight());
}
} else {
block.getNodeOnBus().setCoordinates(block.getCoord().get(X) + block.getCoord().getSpan(X) / 2,
block.getBusNode().getY());
block.getLegNode().setY(block.getBusNode().getY());
}
}

@Override
public void visit(FeederPrimaryBlock block) {
if (block.getPosition().getOrientation().isVertical()) {
double yFeeder = block.getConnectedNode().getY()
+ block.getOrientation().progressionSign() * layoutParameters.getFeederSpan();
block.getFeederNode().setCoordinates(block.getCoord().get(X), yFeeder);
} else {
// Will never happen
}
}

@Override
public void visit(UndefinedBlock block) {
replicateCoordInSubblocks(block, X);
replicateCoordInSubblocks(block, Y);
block.getSubBlocks().forEach(b -> b.accept(this));
}

@Override
public void visit(BodyParallelBlock block) {
if (block.getPosition().getOrientation().isVertical()) {
translatePosInCoord(block, Y, X, H, 1);
} else {
translatePosInCoord(block, X, Y, V, 1);
}
}

@Override
public void visit(SerialBlock block) {
if (block.getPosition().getOrientation().isVertical()) {
translatePosInCoord(block, X, Y, V, block.getOrientation().progressionSign());
block.getChainingNodes().forEach(n -> n.setX(block.getCoord().get(X)));
} else {
translatePosInCoord(block, Y, X, H, block.getOrientation().progressionSign());
block.getChainingNodes().forEach(n -> n.setY(block.getCoord().get(Y)));
}
}

@Override
public void visit(LegParralelBlock block) {
if (block.getPosition().getOrientation().isVertical()) {
translatePosInCoord(block, Y, X, H, 1);
} else {
// case HORIZONTAL cannot happen
}
}

void translatePosInCoord(ComposedBlock block, Coord.Dimension cDimSteady,
Coord.Dimension cDimVariable, Position.Dimension pDim, int sign) {
replicateCoordInSubblocks(block, cDimSteady);
distributeCoordInSubblocs(block, pDim, cDimVariable, sign);
block.getSubBlocks().forEach(sub -> sub.accept(this));
}

void replicateCoordInSubblocks(ComposedBlock block, Coord.Dimension dim) {
block.getCoord().getSegment(dim)
.replicateMe(block.getSubBlocks().stream().map(b -> b.getCoord().getSegment(dim)));
}

void distributeCoordInSubblocs(ComposedBlock block, Position.Dimension pDim, Coord.Dimension cDim, int sign) {
// Computes the step, avoiding the division by 0 for 0-span composed block (e.g.
// LegPrimaryBlock + Feeder)
double init = block.getCoord().get(cDim) - sign * block.getCoord().getSpan(cDim) / 2;
int pSpan = block.getPosition().getSpan(pDim);
double step = pSpan == 0 ? 0 : block.getCoord().getSpan(cDim) / pSpan;
block.getSubBlocks().forEach(sub -> {
sub.getCoord().set(cDim, init
+ sign * step * (sub.getPosition().get(pDim) + (double) sub.getPosition().getSpan(pDim) / 2));
sub.getCoord().setSpan(cDim, sub.getPosition().getSpan(pDim) * step);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* 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.sld.layout;

import com.powsybl.sld.model.blocks.Block;
import com.powsybl.sld.model.blocks.BodyPrimaryBlock;
import com.powsybl.sld.model.cells.*;
import com.powsybl.sld.model.cells.InternCell.Shape;
import com.powsybl.sld.model.coordinate.Coord;
import com.powsybl.sld.model.coordinate.Position;
import com.powsybl.sld.model.coordinate.Side;
import com.powsybl.sld.model.nodes.Node;

import static com.powsybl.sld.model.coordinate.Position.Dimension.*;
import static com.powsybl.sld.model.coordinate.Coord.Dimension.*;
import static com.powsybl.sld.model.blocks.Block.Extremity.*;

/**
* @author Benoit Jeanson <benoit.jeanson at rte-france.com>
*/
public final class CalculateCoordCellVisitor implements CellVisitor {
private final LayoutParameters layoutParameters;
private final LayoutContext layoutContext;

CalculateCoordCellVisitor(LayoutParameters layoutParameters, LayoutContext layoutContext) {
this.layoutParameters = layoutParameters;
this.layoutContext = layoutContext;
}

@Override
public void visit(InternCell cell) {
if (cell.getShape().checkIsNotShape(Shape.UNILEG, Shape.UNDEFINED, Shape.UNHANDLEDPATTERN)) {
calculateRootCoord(cell.getBodyBlock(), layoutContext);
}
cell.getLegs().values().forEach(lb -> calculateRootCoord(lb, layoutContext));
}

@Override
public void visit(ExternCell cell) {
calculateRootCoord(cell.getRootBlock(), layoutContext);
}

@Override
public void visit(ShuntCell cell) {
Position lPos = cell.getSidePosition(Side.LEFT);
coordShuntCase(cell.getRootBlock(), lPos.get(H) + lPos.getSpan(H), cell.getSidePosition(Side.RIGHT).get(H));
}

private void calculateRootCoord(Block block, LayoutContext layoutContext) {
Position position = block.getPosition();
Coord coord = block.getCoord();
double spanX = position.getSpan(H) / 2. * layoutParameters.getCellWidth();
coord.setSpan(X, spanX);
coord.set(X, hToX(layoutParameters, position.get(H)) + spanX / 2);

double spanY = getRootSpanYCoord(position, layoutParameters, layoutContext.getMaxInternCellHeight(), layoutContext.isInternCell());
coord.setSpan(Y, spanY);
coord.set(Y, getRootYCoord(position, layoutParameters, spanY, layoutContext));
CalculateCoordBlockVisitor cc = CalculateCoordBlockVisitor.create(layoutParameters, layoutContext);
block.accept(cc);
}

private double hToX(LayoutParameters layoutParameters, int h) {
return layoutParameters.getCellWidth() * h / 2;
}

private double getRootSpanYCoord(Position position, LayoutParameters layoutParam, double externCellHeight, boolean isInternCell) {
double ySpan;
if (isInternCell) {
ySpan = position.getSpan(V) / 2. * layoutParam.getInternCellHeight();
} else {
// The Y span of root block does not consider the space needed for the FeederPrimaryBlock (feeder span)
// nor the one needed for the LegPrimaryBlock (layoutParam.getStackHeight())
ySpan = externCellHeight - layoutParam.getStackHeight() - layoutParam.getFeederSpan();
}
return ySpan;
}

private double getRootYCoord(Position position, LayoutParameters layoutParam, double spanY, LayoutContext layoutContext) {
double dyToBus = 0;
if (layoutContext.isInternCell() && !layoutContext.isFlat()) {
dyToBus = spanY / 2 + layoutParam.getInternCellHeight() * (1 + position.get(V)) / 2.;
} else {
dyToBus = spanY / 2 + layoutParam.getStackHeight();
}
switch (layoutContext.getDirection()) {
case BOTTOM:
return layoutContext.getLastBusY() + dyToBus;
case TOP:
return layoutContext.getFirstBusY() - dyToBus;
case MIDDLE:
return layoutContext.getFirstBusY() + (position.get(V) - 1) * layoutParam.getVerticalSpaceBus();
default:
return 0;
}
}

public void coordShuntCase(BodyPrimaryBlock block, int hLeft, int hRight) {
double x0 = hToX(layoutParameters, hLeft);
double y0 = block.getExtremityNode(START).getY();
double x1 = hToX(layoutParameters, hRight);
double y1 = block.getExtremityNode(END).getY();
double y = (y0 + y1) / 2;

block.getNodes().get(1).setCoordinates(x0, y);
block.getNodes().get(block.getNodes().size() - 2).setCoordinates(x1, y);

double dx = (x1 - x0) / (block.getNodes().size() - 3);
for (int i = 2; i < block.getNodes().size() - 2; i++) {
Node node = block.getNodes().get(i);
node.setCoordinates(x0 + (i - 1) * dx, y);
node.setRotationAngle(90.);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public HorizontalSubstationLayout(SubstationGraph graph, VoltageLevelLayoutFacto
protected List<Point> calculatePolylineSnakeLine(LayoutParameters layoutParam, Node node1, Node node2,
boolean increment) {
double yMin = getGraph().getVoltageLevels().stream().mapToDouble(VoltageLevelGraph::getY).min().orElse(0.0);
double yMax = getGraph().getVoltageLevels().stream().mapToDouble(g -> g.getY() + g.getInnerHeight(layoutParam)).max().orElse(0.0);
double yMax = getGraph().getVoltageLevels().stream().mapToDouble(g -> g.getY() + g.getInnerHeight(layoutParam.getVerticalSpaceBus())).max().orElse(0.0);
return calculatePolylineSnakeLineForHorizontalLayout(layoutParam, node1, node2, increment, infosNbSnakeLines, yMin, yMax);
}

Expand Down
Loading

0 comments on commit 8e6d244

Please sign in to comment.