CalculateCellHeightBlockVisitor.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 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 {@literal <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.getNodeStream().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(LegParallelBlock 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 <T extends Block> void calculateSubHeight(ComposedBlock<T> block, DoubleBinaryOperator merge) {
        blockHeight = 0.;
        for (T 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);
        }
    }
}