/*
 * CohenSutherland.java
 * --------------------
 * (c) 2007 by Intevation GmbH
 *
 * @author Sascha L. Teichmann (teichmann@intevation.de)
 * @author Ludwig Reiter       (ludwig@intevation.de)
 *
 * This program is free software under the LGPL (>=v2.1)
 * Read the file LICENSE.txt coming with the sources for details.
 */
package com.knecon.fforesight.service.layoutparser.processor.utils;

import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

/**
 * Implements the well known Cohen Sutherland line
 * clipping algorithm (line against clip rectangle).
 */
@SuppressWarnings("all")
public final class CohenSutherlandClipping {

    private static final int INSIDE = 0;
    private static final int LEFT = 1;
    private static final int RIGHT = 2;
    private static final int BOTTOM = 4;
    private static final int TOP = 8;
    private double xMin;
    private double yMin;
    private double xMax;
    private double yMax;


    /**
     * Creates a Cohen Sutherland clipper with clip rect (0, 0, 0, 0).
     */
    public CohenSutherlandClipping() {

    }


    /**
     * Creates a Cohen Sutherland clipper with the given clip rectangle.
     *
     * @param clip the clip rectangle to use
     */
    public CohenSutherlandClipping(Rectangle2D clip) {

        setClip(clip);
    }


    /**
     * Sets the clip rectangle.
     *
     * @param clip the clip rectangle
     */
    public void setClip(Rectangle2D clip) {

        xMin = clip.getX();
        xMax = xMin + clip.getWidth();
        yMin = clip.getY();
        yMax = yMin + clip.getHeight();
    }


    private final int regionCode(double x, double y) {

        int code = x < xMin ? LEFT : x > xMax ? RIGHT : INSIDE;
        if (y < yMin) {
            code |= BOTTOM;
        } else if (y > yMax) {
            code |= TOP;
        }
        return code;
    }


    /**
     * Clips a given line against the clip rectangle.
     * The modification (if needed) is done in place.
     *
     * @param line the line to clip
     * @return true if line is clipped, false if line is
     * totally outside the clip rect.
     */
    public boolean clip(Line2D.Float line) {

        double p1x = line.getX1();
        double p1y = line.getY1();
        double p2x = line.getX2();
        double p2y = line.getY2();

        double qx = 0d;
        double qy = 0d;

        boolean vertical = p1x == p2x;

        double slope = vertical ? 0d : (p2y - p1y) / (p2x - p1x);

        int c1 = regionCode(p1x, p1y);
        int c2 = regionCode(p2x, p2y);

        while (c1 != INSIDE || c2 != INSIDE) {

            if ((c1 & c2) != INSIDE) {
                return false;
            }

            int c = c1 == INSIDE ? c2 : c1;

            if ((c & LEFT) != INSIDE) {
                qx = xMin;
                qy = (DoubleComparisons.feq(qx, p1x) ? 0 : qx - p1x) * slope + p1y;
            } else if ((c & RIGHT) != INSIDE) {
                qx = xMax;
                qy = (DoubleComparisons.feq(qx, p1x) ? 0 : qx - p1x) * slope + p1y;
            } else if ((c & BOTTOM) != INSIDE) {
                qy = yMin;
                qx = vertical ? p1x : (DoubleComparisons.feq(qy, p1y) ? 0 : qy - p1y) / slope + p1x;
            } else if ((c & TOP) != INSIDE) {
                qy = yMax;
                qx = vertical ? p1x : (DoubleComparisons.feq(qy, p1y) ? 0 : qy - p1y) / slope + p1x;
            }

            if (c == c1) {
                p1x = qx;
                p1y = qy;
                c1 = regionCode(p1x, p1y);
            } else {
                p2x = qx;
                p2y = qy;
                c2 = regionCode(p2x, p2y);
            }
        }
        line.setLine(p1x, p1y, p2x, p2y);
        return true;
    }

}
// end of file
