Package squidpony.squidmath
Class Coord
java.lang.Object
squidpony.squidmath.Coord
- All Implemented Interfaces:
Serializable
- Direct Known Subclasses:
Coord3D
public class Coord extends Object implements Serializable
A 2D coordinate with (constant) x and y fields. Coord objects are immutable; a single pool of Coord values, with
x and y each ranging from -3 to 255, is shared by all users of Coord. This pool helps reduce pressure on the
garbage collector when many Coord values would have been created for some purpose and quickly discarded; instead
of creating a new Coord with a constructor, you use the static method
The Coord class is a fundamental part of SquidLib; any class that uses positions on a grid makes use of it here. It finds usage naturally in classes throughout
More on the Coord pool used by this class: Coords can't always be retrieved from the pool; Coord.get constructs a new Coord if one of x or y is unusually large (greater than 255) or too negative (below -3). The upper limit of 255 is not a hard rule; you can increase the limit on the pool by calling
get(int, int)
, which retrieves an
already-existing Coord from the pool if possible, and always returns a usable Coord.
The Coord class is a fundamental part of SquidLib; any class that uses positions on a grid makes use of it here. It finds usage naturally in classes throughout
squidpony.squidgrid
, with squidpony.squidgrid.zone
providing an abstraction around groups of Coord and squidpony.squidgrid.iterator
providing various ways to
iterate through the Coords that make up a larger shape. In this package, squidpony.squidmath
, a few classes
should be pointed out. CoordPacker
is a class with all static methods that provides various ways to compress
the memory usage of regions made of many Coord values (and can be constructed in other ways but still provide Coords
later), but since Coords don't use much memory anyway, the real use of the class is for manipulating the shapes and
sizes of the regions those Coords are part of. GreasedRegion
has similar functionality to CoordPacker, but
where CoordPacker is purely static, taking and returning regions as encoded, usually-low-memory-cost arrays of
short
that it considers immutable, a GreasedRegion is a mutable object that allows the same region-altering
techniques to be applied in-place in a way that is relatively (very) low-time-cost. If deciding between the two,
GreasedRegion should usually be preferred, and CoordPacker cannot actually be used when storing regions in larger
than a 256x256 space (usually when the Coord pool has been expanded; see below); GreasedRegion can store potentially
large positions.
More on the Coord pool used by this class: Coords can't always be retrieved from the pool; Coord.get constructs a new Coord if one of x or y is unusually large (greater than 255) or too negative (below -3). The upper limit of 255 is not a hard rule; you can increase the limit on the pool by calling
expandPoolTo(int, int)
or
expandPool(int, int)
, which cause more memory to be spent initially on storing Coords but can save memory
or ease GC pressure over the long term by preventing duplicate Coords from being created many times. The pool can
never shrink because allowing that would cause completely unpredictable results if existing Coords were in use, or
could easily cause crashes on Android after resuming an application that had previously shrunken the pool due to
platform quirks. Long story short, you should only expand the pool size when your game needs a larger set of 2D
points it will commonly use, and in most cases you shouldn't need to change it at all.
Created by Tommy Ettinger on 8/12/2015.- See Also:
- Serialized Form
-
Field Summary
-
Constructor Summary
-
Method Summary
Modifier and Type Method Description Coord
add(double operand)
Separately adds the x and y positions of this Coord to operand, rounding to the nearest int for each of x and y and producing a different Coord as their "sum."Coord
add(int operand)
Separately adds the x and y positions of this Coord to operand, producing a different Coord as their "sum."Coord
add(Coord other)
Separately combines the x and y positions of this Coord and other, producing a different Coord as their "sum."Coord
average(Coord other)
Separately averages the x and y positions of this Coord with other, producing a different Coord as their "midpoint."static int
cantorHashCode(int x, int y)
A static version of an earlierhashCode()
method of this class, taking x and y as parameters instead of requiring a Coord object.static Coord
decode(int code)
This can take an int produced bysomeCoord.encode()
and get the original Coord back out of it.static double
degrees(Coord from, Coord to)
Gets the angle in degrees to go between two Coords.double
distance(double x2, double y2)
double
distance(Coord co)
double
distanceSq(double x2, double y2)
double
distanceSq(Coord co)
Coord
divide(double operand)
Separately divides the x and y positions of this Coord by operand, flooring to a lower int for each of x and y and producing a different Coord as their "quotient." If operand is 0.0, expect strange results (infinity and NaN are both possibilities).Coord
divide(int operand)
Separately divides the x and y positions of this Coord by operand, producing a different Coord as their "quotient." If operand is 0, this will throw an exception, as dividing by 0 is expected to do.Coord
divide(Coord other)
Separately divides the x and y positions of this Coord by other, producing a different Coord as their "quotient." If other has 0 for x or y, this will throw an exception, as dividing by 0 is expected to do.Coord
divideRounding(double operand)
Separately divides the x and y positions of this Coord by operand, rounding to the nearest int for each of x and y and producing a different Coord as their "quotient." If operand is 0.0, expect strange results (infinity and NaN are both possibilities).int
encode()
Something like hashCode(), but reversible withCoord.decode()
.boolean
equals(Object o)
static void
expandPool(int xIncrease, int yIncrease)
Enlarges the pool of cached Coords by the given amount of expansion for x and y.static void
expandPoolTo(int width, int height)
Enlarges the pool of cached Coords to the given width and height, and doesn't change a dimension if it would be reduced in size.static Coord
get(int x, int y)
static int
getCacheHeight()
Gets the height of the pool used as a cache for Coords, not including negative Coords.static int
getCacheWidth()
Gets the width of the pool used as a cache for Coords, not including negative Coords.Coord
getLocation()
Provided for compatibility with earlier code that used the AWT Point API.int
getX()
int
getY()
int
hashCode()
Gets the hash code for this Coord; does not use the standard "auto-complete" style of hash that most IDEs will generate, but instead uses a highly-specific technique based on the Rosenberg-Strong pairing function, a Gray code, and two XLCG steps at the end.Coord
interpolate(Coord end, float amountTraveled)
boolean
isAdjacent(Coord c)
boolean
isWithin(int width, int height)
Returns true if x is between 0 (inclusive) and width (exclusive) and y is between 0 (inclusive) and height (exclusive), false otherwise.boolean
isWithinRectangle(int minX, int minY, int maxX, int maxY)
Returns true if x is between minX (inclusive) and maxX (exclusive) and y is between minY (inclusive) and maxY (exclusive), false otherwise.Coord
makeEven()
Gets a Coord based off this instance but with odd values for x and/or y decreased to the nearest even number.Coord
makeOdd()
Gets a Coord based off this instance but with even values for x and/or y increased to the nearest odd number.Coord
multiply(double operand)
Separately multiplies the x and y positions of this Coord by operand, rounding to the nearest int for each of x and y and producing a different Coord as their "product."Coord
multiply(int operand)
Separately multiplies the x and y positions of this Coord by operand, producing a different Coord as their "product."Coord
multiply(Coord other)
Separately multiplies the x and y positions of other from this Coord, producing a different Coord as their "product."static int
pureEncode(int x, int y)
An alternative to getting a Coord with Coord.get() only to encode() it as the next step.static int
rosenbergStrongHashCode(int x, int y)
A static version of the currenthashCode()
method of this class, taking x and y as parameters instead of requiring a Coord object.Coord
scale(int i)
Coord
scale(int i, int j)
Coord
setX(int x)
Coord
setY(int y)
Coord
subtract(double operand)
Separately subtracts operand from the x and y positions of this Coord, rounding to the nearest int for each of x and y and producing a different Coord as their "difference."Coord
subtract(int operand)
Separately subtracts operand from the x and y positions of this Coord, producing a different Coord as their "difference."Coord
subtract(Coord other)
Separately subtracts the x and y positions of other from this Coord, producing a different Coord as their "difference."Direction
toGoTo(Coord target)
Gets theDirection
needed to get totarget
from this; typically this is more useful when target and this are adjacent (byisAdjacent(Coord)
) since that should make it possible to go to target.String
toString()
Coord
translate(int x, int y)
Takes this Coord, adds x to its x and y to its y, and returns the Coord at that position.Coord
translate(Direction d)
Coord
translateCapped(int x, int y, int width, int height)
Takes this Coord, adds x to its x and y to its y, limiting x from 0 to width and limiting y from 0 to height, and returns the Coord at that position.static int
xoroHashCode(int x, int y)
An earlier hashCode() implementation used by this class, now standalone in case you want to replicate the results of the older code.
-
Field Details
-
Constructor Details
-
Method Details
-
get
-
degrees
Gets the angle in degrees to go between two Coords. When only x is different andto.x
is greater thanfrom.x
, this returns 0. When only y is different andto.y
is greater thanfrom.y
, this returns 90. When only x is different andto.x
is less thanfrom.x
, this returns 180. When only y is different andto.y
is less thanfrom.y
, this returns 270. In cases between these, the angle is between those values; it cannot be 360 but it can be very close. This never returns a negative angle. Keep in mind, "up" depends on how your code orients the y-axis, and SquidLib generally defaults to positive y going toward the bottom of the screen, like how later lines in a paragraph are further down on the page.
As a compatibility note, before SquidLib 3.0.0 stable, this used an odd rotation of the normal degrees where 0 degrees were used whento.y
was greater thanfrom.y
and x was equal. Because that typically runs counter to expectations from actual math, the behavior was changed.- Parameters:
from
- the starting Coord to measure fromto
- the ending Coord to measure to- Returns:
- The degree from
from
toto
; 0 is up
-
getLocation
Provided for compatibility with earlier code that used the AWT Point API.- Returns:
- this Coord, without changes
-
translate
Takes this Coord, adds x to its x and y to its y, and returns the Coord at that position.- Parameters:
x
- the amount of x distance to movey
- the amount of y distance to move- Returns:
- a Coord (usually cached and not a new instance) that has been moved the specified distance
-
translateCapped
Takes this Coord, adds x to its x and y to its y, limiting x from 0 to width and limiting y from 0 to height, and returns the Coord at that position.- Parameters:
x
- the amount of x distance to movey
- the amount of y distance to movewidth
- one higher than the maximum x value this can use; typically the length of an arrayheight
- one higher than the maximum y value this can use; typically the length of an array- Returns:
- a Coord (usually cached and not a new instance) that has been moved the specified distance
-
add
Separately combines the x and y positions of this Coord and other, producing a different Coord as their "sum."- Parameters:
other
- another Coord- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x + other.x; y = this.y + other.y
-
add
Separately adds the x and y positions of this Coord to operand, producing a different Coord as their "sum."- Parameters:
operand
- a value to add each of x and y to- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x + operand; y = this.y + operand
-
add
Separately adds the x and y positions of this Coord to operand, rounding to the nearest int for each of x and y and producing a different Coord as their "sum."- Parameters:
operand
- a value to add each of x and y to- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x + operand; y = this.y + operand
, with both x and y rounded accordingly
-
subtract
Separately subtracts the x and y positions of other from this Coord, producing a different Coord as their "difference."- Parameters:
other
- another Coord- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x - other.x; y = this.y - other.y
-
subtract
Separately subtracts operand from the x and y positions of this Coord, producing a different Coord as their "difference."- Parameters:
operand
- a value to subtract from each of x and y- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x - operand; y = this.y - operand
-
subtract
Separately subtracts operand from the x and y positions of this Coord, rounding to the nearest int for each of x and y and producing a different Coord as their "difference."- Parameters:
operand
- a value to subtract from each of x and y- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x - operand; y = this.y - operand
, with both x and y rounded accordingly
-
multiply
Separately multiplies the x and y positions of other from this Coord, producing a different Coord as their "product."- Parameters:
other
- another Coord- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x * other.x; y = this.y * other.y
-
multiply
Separately multiplies the x and y positions of this Coord by operand, producing a different Coord as their "product."- Parameters:
operand
- a value to multiply each of x and y by- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x * operand; y = this.y * operand
-
multiply
Separately multiplies the x and y positions of this Coord by operand, rounding to the nearest int for each of x and y and producing a different Coord as their "product."- Parameters:
operand
- a value to multiply each of x and y by- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x * operand; y = this.y * operand
, with both x and y rounded accordingly
-
divide
Separately divides the x and y positions of this Coord by other, producing a different Coord as their "quotient." If other has 0 for x or y, this will throw an exception, as dividing by 0 is expected to do.- Parameters:
other
- another Coord- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x / other.x; y = this.y / other.y
-
divide
Separately divides the x and y positions of this Coord by operand, producing a different Coord as their "quotient." If operand is 0, this will throw an exception, as dividing by 0 is expected to do.- Parameters:
operand
- a value to divide each of x and y by- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x / operand; y = this.y / operand
-
divide
Separately divides the x and y positions of this Coord by operand, flooring to a lower int for each of x and y and producing a different Coord as their "quotient." If operand is 0.0, expect strange results (infinity and NaN are both possibilities).- Parameters:
operand
- a value to divide each of x and y by- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x / operand; y = this.y / operand
, with both x and y rounded accordingly
-
divideRounding
Separately divides the x and y positions of this Coord by operand, rounding to the nearest int for each of x and y and producing a different Coord as their "quotient." If operand is 0.0, expect strange results (infinity and NaN are both possibilities).- Parameters:
operand
- a value to divide each of x and y by- Returns:
- a Coord (usually cached and not a new instance) with
x = this.x / operand; y = this.y / operand
, with both x and y rounded accordingly
-
average
Separately averages the x and y positions of this Coord with other, producing a different Coord as their "midpoint."- Parameters:
other
- another Coord- Returns:
- a Coord (usually cached and not a new instance) halfway between this and other, rounded nearest.
-
translate
- Parameters:
d
- A non-null
direction.- Returns:
- The coordinate obtained by applying
d
onthis
.
-
scale
- Parameters:
i
-- Returns:
(x*i,y*i)
.
-
scale
- Parameters:
i
-- Returns:
(x*i,y*j)
.
-
distance
-
distance
-
distanceSq
-
distanceSq
-
makeEven
Gets a Coord based off this instance but with odd values for x and/or y decreased to the nearest even number. May be useful for thin-wall maps as produced byThinDungeonGenerator
and used withAdjacency.ThinWallAdjacency
.- Returns:
- a Coord (probably from the pool) with even x and even y, changing (decrementing) only if they are odd
-
makeOdd
Gets a Coord based off this instance but with even values for x and/or y increased to the nearest odd number. May be useful for thin-wall maps as produced byThinDungeonGenerator
and used withAdjacency.ThinWallAdjacency
.- Returns:
- a Coord (probably from the pool) with odd x and odd y, changing (incrementing) only if they are even
-
isAdjacent
- Parameters:
c
-- Returns:
- Whether
this
is adjacent toc
. Not that a cell is not adjacent to itself with this method.
-
toGoTo
Gets theDirection
needed to get totarget
from this; typically this is more useful when target and this are adjacent (byisAdjacent(Coord)
) since that should make it possible to go to target.
Internally, this delegates toDirection.toGoTo(Coord, Coord)
, and some code may prefer using the method in Direction instead of this one. Earlier versions of this code only worked for adjacent Coords, which seemed like an unnecessary limitation since Direction's version worked for any arguments.- Parameters:
target
- a non-nullCoord
- Returns:
- the direction to go from
this
totarget
-
isWithin
Returns true if x is between 0 (inclusive) and width (exclusive) and y is between 0 (inclusive) and height (exclusive), false otherwise.- Parameters:
width
- the upper limit on x to check, exclusiveheight
- the upper limit on y to check, exclusive- Returns:
- true if this Coord is within the limits of width and height and has non-negative x and y
-
isWithinRectangle
Returns true if x is between minX (inclusive) and maxX (exclusive) and y is between minY (inclusive) and maxY (exclusive), false otherwise.- Parameters:
minX
- the lower limit on x to check, inclusiveminY
- the lower limit on y to check, inclusivemaxX
- the upper limit on x to check, exclusivemaxY
- the upper limit on y to check, exclusive- Returns:
- true if this Coord is within the limits of the given parameters
-
getX
-
setX
-
getY
-
setY
-
toString
-
hashCode
Gets the hash code for this Coord; does not use the standard "auto-complete" style of hash that most IDEs will generate, but instead uses a highly-specific technique based on the Rosenberg-Strong pairing function, a Gray code, and two XLCG steps at the end. It manages to get extremely low collision rates under many circumstances, and very frequently manages to avoid colliding on more than 25% of Coords (making the load factor of most hash-based collections fine at a default of 0.75) while often having 0 collisions with some data sets. It does much better when Coords are in the default pooled range of -3 or greater.
This gets slightly better collision rates than previous versions used by SquidLib, around 4% across a wide variety of rectangular areas (most earlier hashes got about-5%-range collision rates, and usingObjects.hash(Object...)
gives more than a 75% collision rate). The previous version, which is still available ascantorHashCode(int, int)
, has slightly better results when used for seeding procedural generation based on a Coord (a reasonable usage of this method), but both this hash code and the Cantor-based one have excellent randomness in the upper bits of the hash (so if you use a hashCode() result as a whole int, then it should be pretty good as a seed). The method before the Cantor-based one,xoroHashCode(int, int)
was structured a little like xoroshiro (XoRoRNG
uses the 64-bit version of xoroshiro), and while it had pretty low collision rates (low 5% range), its hash codes changed bits in large checkerboard patterns, leaving heavy square-shaped biases in generated results.
This changed at least 7 times in SquidLib's history. In general, you shouldn't rely on hashCodes to stay the same across platforms and versions, whether for the JDK or this library. SquidLib (tries to) never depend on the unpredictable ordering of some hash-based collections like HashSet and HashMap, instead using its ownOrderedSet
andOrderedMap
; if you use the ordered kinds, then the only things that matter about this hash code are that it's fast (it's fast enough), it's cross-platform compatible (this version avoids using long values, which are slow on GWT, and is carefully written to behave the same on GWT as desktop) and that it doesn't collide often (which is now much more accurate than in earlier versions of this method).- Overrides:
hashCode
in classObject
- Returns:
- an int that should, for most different Coord values, be significantly different from the other hash codes
- See Also:
A static method that gets the same result as this method without involving a Coord
-
cantorHashCode
A static version of an earlierhashCode()
method of this class, taking x and y as parameters instead of requiring a Coord object. Like the earlier hashCode() method, this involves the close-to-optimal mathematical Cantor pairing function to distribute x and y without overlap until they get very large. Cantor's pairing function can be written simply as((x + y) * (x + y + 1)) / 2 + y
; it produces sequential results for a sequence of positive points traveling in diagonal stripes away from the origin. The finalization steps this performs improve the randomness of the lower bits, but also worsen collision rates; most cases involving Coords will see lower collision rates fromrosenbergStrongHashCode(int, int)
, but more random results from this method.- Parameters:
x
- the x coordinate of the "imaginary Coord" to hashy
- the y coordinate of the "imaginary Coord" to hash- Returns:
- the equivalent to the hashCode() of an "imaginary Coord"
-
rosenbergStrongHashCode
A static version of the currenthashCode()
method of this class, taking x and y as parameters instead of requiring a Coord object. Like the current hashCode() method, this involves the close-to-optimal mathematical Rosenberg-Strong pairing function to distribute x and y without overlap until they get very large. The Rosenberg-Strong pairing function can be written simply as((x >= y) ? x * (x + 2) - y : y * y + x)
; it produces sequential results for a sequence of positive points traveling in square "shells" away from the origin. the algorithm is discussed more here; the only changes this makes are adding 3 to x and y (to account for the minimum of -3 in most cases for a Coord), and some finalizing steps that help randomize the upper bits of the hash code (the lower bits are quite non-random because they can't permit any gaps while optimizing collision rates).- Parameters:
x
- the x coordinate of the "imaginary Coord" to hashy
- the y coordinate of the "imaginary Coord" to hash- Returns:
- the equivalent to the hashCode() of an "imaginary Coord"
-
xoroHashCode
An earlier hashCode() implementation used by this class, now standalone in case you want to replicate the results of the older code. This uses only bitwise operations, which tend to be fairly fast on all platforms, and when used in a collection it has comparable collision rates to the current hashCode() method (very, very low rates), but if used for procedural generation it's simply terrible, with large blocks of nearby x,y points having identical values for several bits and all changes happening in a repetitive checkerboard pattern. It is structured very similarly toXoRoRNG
andLathe32RNG
in particular, but using only bitwise math.- Parameters:
x
- the x coordinate of the "imaginary Coord" to hashy
- the y coordinate of the "imaginary Coord" to hash- Returns:
- the equivalent to the hashCode() of an "imaginary Coord"
-
encode
Something like hashCode(), but reversible withCoord.decode()
. Works for Coords between roughly -256 and 32000 in each of x and y, but will probably only decode to pooled Coords if x and y are both between -3 and 255 (inclusive for both).- Returns:
- an int as a unique code for this Coord
-
pureEncode
An alternative to getting a Coord with Coord.get() only to encode() it as the next step. This doesn't create a Coord in the middle step. Can be decoded with Coord.decode() to get the (x,y) Coord.- Parameters:
x
- the x position to encodey
- the y position to encode- Returns:
- the coded int that a Coord at (x,y) would produce with encode()
-
decode
This can take an int produced bysomeCoord.encode()
and get the original Coord back out of it. It works for all pooled Coords where the pool hasn't been expanded past about 32,000 in either dimension. It even works for Coords with negative x or y as well, if they are no lower than -256 in either dimension. This will almost certainly fail (producing a gibberish Coord that probably won't be pooled) on hashes produced by any other class, including subclasses of Coord.- Parameters:
code
- an encoded int from a Coord, but not a subclass of Coord- Returns:
- the Coord that gave hash as its hashCode()
-
equals
-
getCacheWidth
Gets the width of the pool used as a cache for Coords, not including negative Coords. Unless expandPool() has been called, this should be 256. Useful for finding the upper (exclusive) bound for x values that can be used efficiently in Coords. Requesting a Coord with a x greater than or equal to this value will result in a new Coord being allocated and not cached, which may cause problems with code that expects the normal reference equality of Coords to be upheld and in extreme cases may require more time garbage collecting than is normally necessary.- Returns:
- the width of the Coord cache, disregarding negative Coords
-
getCacheHeight
Gets the height of the pool used as a cache for Coords, not including negative Coords. Unless expandPool() has been called, this should be 256. Useful for finding the upper (exclusive) bound for y values that can be used efficiently in Coords. Requesting a Coord with a y greater than or equal to this value will result in a new Coord being allocated and not cached, which may cause problems with code that expects the normal reference equality of Coords to be upheld and in extreme cases may require more time garbage collecting than is normally necessary.- Returns:
- the height of the Coord cache, disregarding negative Coords
-
expandPoolTo
Enlarges the pool of cached Coords to the given width and height, and doesn't change a dimension if it would be reduced in size. Cached Coord values will be reused by Coord.get instead of re-allocated each time. The default pool allows Coords with x and y each between -3 and 255, inclusive, to be cached, and is considered to have width and height of 256 to begin with. Giving a width greater than 256 will allow Coords with x greater than 255 to be cached; likewise for height. If width or height is smaller than the current cache width or height, that dimension will not change, but the other still may if it is valid. You cannot shrink the pool size.- Parameters:
width
- the new width for the pool of cached Coords; will be ignored if smaller than the current widthheight
- the new height for the pool of cached Coords; will be ignored if smaller than the current height
-
expandPool
Enlarges the pool of cached Coords by the given amount of expansion for x and y. Cached Coord values will be reused by Coord.get instead of re-allocated each time. The default pool allows Coords with x and y each between -3 and 255, inclusive, to be cached, and this can increase the size in the positive direction. If either xIncrease or yIncrease is negative, this method returns immediately and does nothing else; the same is true of both arguments are zero. You cannot shrink the pool size.- Parameters:
xIncrease
- the amount to increase cache's width byyIncrease
- the amount to increase cache's height by
-
interpolate
-