package de.renew.gui;

import java.awt.Dimension;
import java.awt.Rectangle;


/**
 * Layouter that uses the {@link GraphLayout} class with simulated annealing.
 *
 * @author Michael Simon
 */
public class AnnealingGraphLayout {
    public final double springLength = 1.0;
    public final double springStrength = 0.05;
    public final double repulsionDistance = 400;
    public final double repulsionStrength = 2.0;
    public final double torqueStrength = 0.25;
    public final double friction = 0.25;
    public final int layoutOffset = 65536;
    public final int steps = 2048;

    public GraphLayout annealingLayout(LayoutableDrawing drawing) {
        GraphLayout layout = new GraphLayout();
        drawing.fillInGraph(layout);

        randomInit(layout, drawing.defaultSize());
        anneal(layout);
        {
            // Move far to a negative position so the figures are top left
            // of everything else in the display box.
            int overOffset = 2 * layoutOffset;
            layout.moveBy(-overOffset, -overOffset);

            // Move to the top left of the drawing
            Rectangle box = drawing.displayBox();
            layout.moveBy(-box.x, -box.y);
        }

        // Force update
        drawing.figureRequestUpdate(null);

        return layout;
    }

    /**
     * Add some offset so the layout algorithm is not hindered.
     * It gets removed by {@link #moveToTopLeft} later.
     */
    private void randomInit(GraphLayout layout, Dimension windowSize) {
        layout.randomInit(layoutOffset, layoutOffset, windowSize.width, windowSize.height);
    }

    private void anneal(GraphLayout layout) {
        layout.lengthFactor = springLength;
        layout.repulsionLimit = repulsionDistance;
        layout.torqueStrength = torqueStrength;

        for (int i = 0; i < steps; i++) {
            double progress = ((double) i) / steps;
            double annealingFactor = 1 + (1 - progress) * 10;

            layout.springStrength = annealingFactor * springStrength;
            layout.repulsionStrength = annealingFactor * repulsionStrength;
            layout.frictionFactor = 1 - progress * friction;
            layout.relax();
        }
    }
}