Layout.java

/**
 * Copyright (c) 2025, 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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.diagram.util.layout;

import com.powsybl.diagram.util.layout.geometry.LayoutContext;
import com.powsybl.diagram.util.layout.algorithms.LayoutAlgorithm;
import com.powsybl.diagram.util.layout.algorithms.BasicForceLayoutAlgorithm;
import com.powsybl.diagram.util.layout.algorithms.parameters.BasicForceLayoutParameters;
import com.powsybl.diagram.util.layout.setup.Setup;
import com.powsybl.diagram.util.layout.setup.SquareRandomBarycenterSetup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Writer;
import java.nio.file.Path;
import java.util.Objects;
import java.util.function.Function;

/**
 * Main entrypoint for the layout module, create a layout, use {@link #run(LayoutContext)} to start the calculations.
 * To transform the output of this to a SVG, use either {@link LayoutContext#toSVG(Function, Writer)} or {@link LayoutContext#toSVG(Function, Path)} to get the default svg implementation<br>
 * Note that you do not need to create a new <code>Layout</code> to run on a different <code>layoutContext</code>, you can just re-use the same layout object and start with run again
 * @author Nathan Dissoubray {@literal <nathan.dissoubray at rte-france.com>}
 */
public class Layout<V, E> {
    private final Setup<V, E> setup;
    private final LayoutAlgorithm<V, E> layoutAlgorithm;

    private static final Logger LOGGER = LoggerFactory.getLogger(Layout.class);

    /**
     * Create a new Layout instance with the provided setup and algorithm
     * @param setup the setup to be used
     * @param layoutAlgorithm the algorithm to place the points once the setup has been applied
     */
    public Layout(Setup<V, E> setup, LayoutAlgorithm<V, E> layoutAlgorithm) {
        this.setup = Objects.requireNonNull(setup);
        this.layoutAlgorithm = Objects.requireNonNull(layoutAlgorithm);
    }

    /**
     * Get the default Basic algorithm, with {@link SquareRandomBarycenterSetup} and {@link BasicForceLayoutAlgorithm}
     * @return a ready to run basic algorithm, with default parameters
     */
    public static <V, E> Layout<V, E> createBasicForceLayout() {
        return new Layout<>(
                new SquareRandomBarycenterSetup<>(),
                new BasicForceLayoutAlgorithm<>(
                        new BasicForceLayoutParameters.Builder().build()
                )
        );
    }

    /**
     * Run the setup and the algorithm of layout on the provided layoutContext
     * @param layoutContext the context of the layout, containing the graph and the position of the points
     */
    public void run(LayoutContext<V, E> layoutContext) {
        Objects.requireNonNull(layoutContext);
        long start = System.nanoTime();
        setup.run(layoutContext);
        long setupEnd = System.nanoTime();
        LOGGER.info("Setup took {} s", (setupEnd - start) / 1e9);
        layoutAlgorithm.run(layoutContext);
        LOGGER.info("Layout calculations took {} s", (System.nanoTime() - setupEnd) / 1e9);
    }
}