001package ball.util;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: Coordinate.java 5285 2020-02-05 04:23:21Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/util/Coordinate.java $
007 * %%
008 * Copyright (C) 2008 - 2020 Allen D. Ball
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * ##########################################################################
022 */
023import java.awt.Point;
024import java.awt.geom.Point2D;
025import java.beans.ConstructorProperties;
026import java.io.Serializable;
027import java.util.Arrays;
028import java.util.Comparator;
029import java.util.Objects;
030import java.util.SortedSet;
031import java.util.TreeSet;
032
033/**
034 * X-Y coordinate representation.
035 *
036 * {@bean.info}
037 *
038 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
039 * @version $Revision: 5285 $
040 */
041public class Coordinate implements Comparable<Coordinate>, Serializable {
042    private static final long serialVersionUID = -4428900365914120909L;
043
044    private static final Comparator<? super Coordinate> COMPARATOR =
045        Comparator
046        .comparingInt(Coordinate::getY)
047        .thenComparingInt(Coordinate::getX);
048
049    /** @serial */ private final int y;
050    /** @serial */ private final int x;
051
052    /**
053     * @param   y               The Y-coordinate.
054     * @param   x               The X-coordinate.
055     */
056    @ConstructorProperties({ "y", "x" })
057    public Coordinate(Number y, Number x) { this(y.intValue(), x.intValue()); }
058
059    private Coordinate(int y, int x) {
060        this.y = y;
061        this.x = x;
062    }
063
064    public int getY() { return y; }
065
066    public int getX() { return x; }
067
068    /**
069     * Method to get the {@link Coordinate} relative to {@link.this}
070     * {@link Coordinate}.
071     *
072     * @param   dy              The change in Y-coordinate.
073     * @param   dx              The change in X-coordinate.
074     *
075     * @return  The relative {@link Coordinate}.
076     */
077    public Coordinate translate(Number dy, Number dx) {
078        return new Coordinate(getY() + dy.intValue(), getX() + dx.intValue());
079    }
080
081    /**
082     * Method to getthe {@link Coordinate} relative to {@link.this}
083     * {@link Coordinate}.
084     *
085     * @param   ds              The {@link Coordinate} describing the change.
086     *
087     * @return  The relative {@link Coordinate}.
088     */
089    public Coordinate translate(Coordinate ds) {
090        return translate(ds.getY(), ds.getX());
091    }
092
093    /**
094     * Method to determine if {@link.this} {@link Coordinate} is within the
095     * area described by the argument {@link Coordinate}s.
096     *
097     * @param   min             The minimum {@link Coordinate}.
098     * @param   max             The maximum {@link Coordinate}.
099     *
100     * @return  {@code true} if within the area; {@code false} otherwise.
101     */
102    public boolean within(Coordinate min, Coordinate max) {
103        return ((min.getY() <= getY() && getY() < max.getY())
104                && (min.getX() <= getX() && getX() < max.getX()));
105    }
106
107    /**
108     * Method to translate this {@link Coordinate} to a {@link Point2D}.
109     *
110     * @return  The {@link Point}.
111     */
112    public Point2D asPoint() { return new Point(getX(), getY()); }
113
114    @Override
115    public int compareTo(Coordinate that) {
116        return Objects.compare(this, that, COMPARATOR);
117    }
118
119    @Override
120    public boolean equals(Object object) {
121        return ((object instanceof Coordinate)
122                    ? (this.compareTo((Coordinate) object) == 0)
123                    : super.equals(object));
124    }
125
126    @Override
127    public int hashCode() { return Arrays.asList(y, x).hashCode(); }
128
129    @Override
130    public String toString() { return Arrays.asList(y, x).toString(); }
131
132    /**
133     * Static method to return a {@link SortedSet} of {@link Coordinate}s
134     * specified by the parameters.
135     *
136     * @param   min             {@code [y0, x0]}
137     * @param   max             {@code [yN, xN]}
138     *
139     * @return  The {@link SortedSet} of {@link Coordinate}s.
140     */
141    public static SortedSet<Coordinate> range(Coordinate min, Coordinate max) {
142        return range(Math.min(min.getY(), max.getY()),
143                     Math.min(min.getX(), max.getX()),
144                     Math.max(min.getY(), max.getY()),
145                     Math.max(min.getX(), max.getX()));
146    }
147
148    /**
149     * Static method to return a {@link SortedSet} of {@link Coordinate}s
150     * specified by the parameters.
151     *
152     * @param   y0              {@code MIN(y)}
153     * @param   x0              {@code MIN(x)}
154     * @param   yN              {@code MAX(y) + 1}
155     * @param   xN              {@code MAX(x) + 1}
156     *
157     * @return  The {@link SortedSet} of {@link Coordinate}s.
158     */
159    public static SortedSet<Coordinate> range(int y0, int x0, int yN, int xN) {
160        TreeSet<Coordinate> set = new TreeSet<>();
161
162        for (int y = y0; y < yN; y += 1) {
163            for (int x = x0; x < xN; x += 1) {
164                set.add(new Coordinate(y, x));
165            }
166        }
167
168        return set;
169    }
170}