CalculateCoordBlockVisitor.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.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 java.util.List;

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 {@literal <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) {
        List<Node> blockNodes = block.getNodes();
        if (blockNodes.size() == 1) {
            blockNodes.get(0).setCoordinates(block.getCoord().get(X), block.getCoord().get(Y));
            return;
        }

        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 = sign * block.getCoord().getSpan(Y) / (blockNodes.size() - 1);
            for (int i = 0; i < blockNodes.size(); i++) {
                blockNodes.get(i).setCoordinates(block.getCoord().get(X), y0 - yPxStep * i);
            }
        } 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) / (blockNodes.size() - 1);
            for (int i = 0; i < blockNodes.size(); i++) {
                blockNodes.get(i).setCoordinates(x0 + xPxStep * i, block.getCoord().get(Y));
            }
        }
    }

    @Override
    public void visit(LegPrimaryBlock block) {
        Coord blockCoord = block.getCoord();
        if (block.getPosition().getOrientation().isVertical()) {
            block.getNodeOnBus().setCoordinates(blockCoord.get(X), block.getBusNode().getY());
            block.getLegNode().setCoordinates(blockCoord.get(X), blockCoord.get(Y));
            if (layoutContext.isInternCell() && layoutContext.isUnileg()) {
                block.getLegNode().setY(blockCoord.get(Y)
                        + (block.getOrientation() == UP ? -1 : 1) * layoutParameters.getInternCellHeight());
            }
        } else {
            block.getNodeOnBus().setCoordinates(blockCoord.get(X) + blockCoord.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(LegParallelBlock block) {
        if (block.getPosition().getOrientation().isVertical()) {
            translatePosInCoord(block, Y, X, H, 1);
        } else {
            // case HORIZONTAL cannot happen
        }
    }

    <T extends Block> void translatePosInCoord(ComposedBlock<T> 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));
    }

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

    <T extends Block> void distributeCoordInSubblocs(ComposedBlock<T> 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 -> {
            double value = init + sign * step * (sub.getPosition().get(pDim) + (double) sub.getPosition().getSpan(pDim) / 2);
            double span = sub.getPosition().getSpan(pDim) * step;
            sub.getCoord().set(cDim, value, span);
        });
    }
}