/**
 * 
 */

package math.geom2d.grid;

import java.util.ArrayList;
import java.util.Collection;

import math.geom2d.Box2D;
import math.geom2d.Point2D;
import math.geom2d.point.PointArray2D;
import math.geom2d.point.PointSet2D;
import math.geom2d.line.LineSegment2D;
import math.geom2d.line.StraightLine2D;
import math.geom2d.line.LinearShape2D;

/**
 * Defines a triangle grid, with various size and orientation. The grid contains
 * triangle with all edges the same length.
 * 
 * @author dlegland
 */
public class TriangleGrid2D implements Grid2D {

    double x0    = 0;
    double y0    = 0;
    double s     = 1;

    double theta = 0;

    /**
     * Returns TRUE if the number n is even (like 0, 2, 4...).
     * 
     * @param n an integer
     * @return TRUE if n is even.
     */
    private final static boolean isEven(int n) {
        return Math.abs(n*.5-Math.floor(n*.5))<.25;
    }

    public TriangleGrid2D() {
        this(0, 0, 1, 0);
    }

    /**
     * @param s size of the triangle tile
     */
    public TriangleGrid2D(double s) {
        this(0, 0, s, 0);
    }

    /**
     * @param x0 x-coord of grid origin
     * @param y0 y-coord of grid origin
     */
    public TriangleGrid2D(double x0, double y0) {
        this(x0, y0, 1, 0);
    }

    /**
     * @param x0 x-coord of grid origin
     * @param y0 y-coord of grid origin
     * @param s size of the triangle tile
     */
    public TriangleGrid2D(double x0, double y0, double s) {
        this(x0, y0, s, 0);
    }

    /**
     * @param x0 x-coord of grid origin
     * @param y0 y-coord of grid origin
     * @param s size of the triangle tile
     * @param theta orientation of the grid with horizontal
     */
    public TriangleGrid2D(double x0, double y0, double s, double theta) {
        this.x0 = x0;
        this.y0 = y0;
        this.s = s;
        this.theta = theta;
    }

    /**
     * Assumes unit grid.
     * 
     * @param point the grid origin
     */
    public TriangleGrid2D(Point2D point) {
        this(point.x(), point.y(), 1, 0);
    }

    /**
     * @param point the grid origin
     * @param s size of the triangle tile
     */
    public TriangleGrid2D(Point2D point, double s) {
        this(point.x(), point.y(), s, 0);
    }

    /**
     * @param point the grid origin
     * @param s size of the triangle tile
     * @param theta orientation of the grid with horizontal
     */
    public TriangleGrid2D(Point2D point, double s, double theta) {
        this(point.x(), point.y(), s, theta);
    }

    /**
     * @deprecated grids are supposed to be immutable (0.8.0)
     */
    @Deprecated
    public void setOrigin(Point2D point) {
        this.x0 = point.getX();
        this.y0 = point.getY();
    }

    public Point2D getOrigin() {
        return new Point2D(x0, y0);
    }

    public double getSize() {
        return s;
    }

    /**
     * @deprecated grids are supposed to be immutable (0.8.0)
     */
    @Deprecated
    public void setSize(double s) {
        this.s = s;
    }

    /**
     * @deprecated grids are supposed to be immutable (0.8.0)
     */
    @Deprecated
    public void setAngle(double theta) {
        this.theta = theta;
    }

    public double getTheta() {
        return theta;
    }

    /*
     * (non-Javadoc)
     * 
     * @see math.geom2d.grid.Grid2D#getClosestVertex(Point2D)
     */
    public Point2D getClosestVertex(Point2D point) {
        // create the base line
        double cot = Math.cos(theta);
        double sit = Math.sin(theta);
        StraightLine2D baseLine = new StraightLine2D(x0, y0, cot, sit);

        // compute distance to line, and deduces indices of surrounding lines
        double s2 = s*Math.sqrt(3)/2;
        double d = baseLine.signedDistance(point);
        int n1 = (int) Math.floor(d/s2);
        int n2 = (int) Math.ceil(d/s2);

        // compute the two surrounding lines
        StraightLine2D line1 = baseLine.parallel(n1*s2);
        StraightLine2D line2 = baseLine.parallel(n2*s2);

        // projection of point on the surrounding lines
        double t = line1.project(point);

        Point2D p1, p2, p3;
        if (isEven(n1)) {
            p1 = line1.point(Math.floor(t/s)*s);
            p2 = line1.point(Math.ceil(t/s)*s);
            p3 = line2.point((Math.floor(t/s)+.5)*s);
        } else {
            p1 = line1.point((Math.floor(t/s)+.5)*s);
            p2 = line2.point(Math.floor(t/s)*s);
            p3 = line2.point(Math.ceil(t/s)*s);
        }

        Point2D res = p1;
        double minDist = res.distance(point);

        double d2 = p2.distance(point);
        if (d2 getEdges(Box2D box) {

        // init the array of line segments
        ArrayList array = new ArrayList();

        double d = s*Math.sqrt(3)/2;
        double dmin, dmax;

        for (int k = 0; k<3; k++) {
            // consider a line through origin with one of the 2 orientations
            double theta2 = this.theta+Math.PI*(k)/3.0;
            double cot = Math.cos(theta2);
            double sit = Math.sin(theta2);
            StraightLine2D baseLine = new StraightLine2D(x0, y0, cot, sit);

            // get extreme distances of box corners to the base line
            dmin = Double.POSITIVE_INFINITY;
            dmax = Double.NEGATIVE_INFINITY;
            for (Point2D point : box.vertices()) {
                double dist = baseLine.signedDistance(point);
                dmin = Math.min(dmin, dist);
                dmax = Math.max(dmax, dist);
            }

            // compute the number of lines in each direction
            double s2 = s*Math.sqrt(3)/2;
            int i0 = (int) Math.ceil(dmin/s2);
            int i1 = (int) Math.floor(dmax/s2);

            // add each clipped line
            for (int i = i0; i<=i1; i++) {
                StraightLine2D line = baseLine.parallel(d*i);
                for (LinearShape2D arc : line.clip(box)) {
                    if (arc instanceof LineSegment2D)
                        array.add((LineSegment2D) arc);
                }
            }
        }
        return array;
    }

    /*
     * (non-Javadoc)
     * 
     * @see math.geom2d.grid.Grid2D#getVertices(math.geom2d.Box2D)
     */
    public PointSet2D getVertices(Box2D box) {

        // init the array of line segments
        ArrayList array = new ArrayList();

        double d = s*Math.sqrt(3)/2;
        double dmin, dmax;

        // consider a line through origin with one of the 2 orientations
        double cot = Math.cos(theta);
        double sit = Math.sin(theta);
        StraightLine2D baseLine = new StraightLine2D(x0, y0, cot, sit);

        // get extreme distances of box corners to the base line
        dmin = Double.POSITIVE_INFINITY;
        dmax = Double.NEGATIVE_INFINITY;
        for (Point2D point : box.vertices()) {
            double dist = baseLine.signedDistance(point);
            dmin = Math.min(dmin, dist);
            dmax = Math.max(dmax, dist);
        }

        // compute the number of lines in each direction
        int i0 = (int) Math.ceil(dmin/s);
        int i1 = (int) Math.floor(dmax/s);

        // consider only the first line
        for (int i = i0; i<=i1; i++) {
            // compute supporting line, supposing that the norm of the
            // direction vector equals 1 (should be the case)
            StraightLine2D line = baseLine.parallel(d*i);

            // extract the line segment
            LineSegment2D seg = (LineSegment2D) line.clip(box).firstCurve();

            // compute position of extreme points, which is also the geodesic
            // distance
            double t1 = line.position(seg.firstPoint());
            double t2 = line.position(seg.lastPoint());

            // check if point on this line are shifted or not
            double t0 = isEven(i) ? 0 : s*.5;

            // compute the number of points in each side of the origin
            int j0 = (int) Math.ceil((t1-t0)/s);
            int j1 = (int) Math.floor((t2-t0)/s);

            // iterate on points
            if (j1 

Ads help maintain this website.