-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Decoupling cells and layout, blocks and layout (#380)
* Decoupling by implementing visitors in layout for calculating coordinates and cell height Signed-off-by: BenoitJeanson <benoit.jeanson@rte-france.com>
- Loading branch information
1 parent
7407993
commit 8e6d244
Showing
30 changed files
with
520 additions
and
386 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
...ne-diagram-core/src/main/java/com/powsybl/sld/layout/CalculateCellHeightBlockVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
149 changes: 149 additions & 0 deletions
149
...le-line-diagram-core/src/main/java/com/powsybl/sld/layout/CalculateCoordBlockVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
} | ||
} |
120 changes: 120 additions & 0 deletions
120
single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CalculateCoordCellVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.