
com.threerings.geom.GeomUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nenya Show documentation
Show all versions of nenya Show documentation
Facilities for making networked multiplayer games.
The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.geom;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import static com.threerings.geom.Log.log;
/**
* General geometry utilities.
*/
public class GeomUtil
{
/**
* Computes and returns the dot product of the two vectors.
*
* @param v1s the starting point of the first vector.
* @param v1e the ending point of the first vector.
* @param v2s the starting point of the second vector.
* @param v2e the ending point of the second vector.
*/
public static int dot (Point v1s, Point v1e, Point v2s, Point v2e)
{
return ((v1e.x - v1s.x) * (v2e.x - v2s.x) + (v1e.y - v1s.y) * (v2e.y - v2s.y));
}
/**
* Computes and returns the dot product of the two vectors. See {@link
* #dot(Point,Point,Point,Point)} for an explanation of the arguments
*/
public static int dot (int v1sx, int v1sy, int v1ex, int v1ey,
int v2sx, int v2sy, int v2ex, int v2ey)
{
return ((v1ex - v1sx) * (v2ex - v2sx) + (v1ey - v1sy) * (v2ey - v2sy));
}
/**
* Computes and returns the dot product of the two vectors. The vectors are assumed to start
* with the same coordinate and end with different coordinates.
*
* @param vs the starting point of both vectors.
* @param v1e the ending point of the first vector.
* @param v2e the ending point of the second vector.
*/
public static int dot (Point vs, Point v1e, Point v2e)
{
return ((v1e.x - vs.x) * (v2e.x - vs.x) + (v1e.y - vs.y) * (v2e.y - vs.y));
}
/**
* Computes and returns the dot product of the two vectors. See {@link
* #dot(Point,Point,Point)} for an explanation of the arguments
*/
public static int dot (int vsx, int vsy, int v1ex, int v1ey, int v2ex, int v2ey)
{
return ((v1ex - vsx) * (v2ex - vsx) + (v1ey - vsy) * (v2ey - vsy));
}
/**
* Computes the point nearest to the specified point p3
on the line defined by the
* two points p1
and p2
. The computed point is stored into
* n
. Note: p1
and p2
must not be coincident.
*
* @param p1 one point on the line.
* @param p2 another point on the line (not equal to p1
).
* @param p3 the point to which we wish to be most near.
* @param n the point on the line defined by p1
and p2
that is
* nearest to p
.
*
* @return the point object supplied via n
.
*/
public static Point nearestToLine (Point p1, Point p2, Point p3, Point n)
{
// see http://astronomy.swin.edu.au/~pbourke/geometry/pointline/ for a (not very good)
// explanation of the math
int Ax = p2.x - p1.x, Ay = p2.y - p1.y;
float u = (p3.x - p1.x) * Ax + (p3.y - p1.y) * Ay;
u /= (Ax * Ax + Ay * Ay);
n.x = p1.x + Math.round(Ax * u);
n.y = p1.y + Math.round(Ay * u);
return n;
}
/**
* Calculate the intersection of two lines. Either line may be considered as a line segment,
* and the intersecting point is only considered valid if it lies upon the segment. Note that
* Point extends Point2D.
*
* @param p1 and p2 the coordinates of the first line.
* @param seg1 if the first line should be considered a segment.
* @param p3 and p4 the coordinates of the second line.
* @param seg2 if the second line should be considered a segment.
* @param result the point that will be filled in with the intersecting point.
*
* @return true if result was filled in, or false if the lines are parallel or the point of
* intersection lies outside of a segment.
*/
public static boolean lineIntersection (Point2D p1, Point2D p2, boolean seg1,
Point2D p3, Point2D p4, boolean seg2, Point2D result)
{
// see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
double y43 = p4.getY() - p3.getY();
double x21 = p2.getX() - p1.getX();
double x43 = p4.getX() - p3.getX();
double y21 = p2.getY() - p1.getY();
double denom = y43 * x21 - x43 * y21;
if (denom == 0) {
return false;
}
double y13 = p1.getY() - p3.getY();
double x13 = p1.getX() - p3.getX();
double ua = (x43 * y13 - y43 * x13) / denom;
if (seg1 && ((ua < 0) || (ua > 1))) {
return false;
}
if (seg2) {
double ub = (x21 * y13 - y21 * x13) / denom;
if ((ub < 0) || (ub > 1)) {
return false;
}
}
double x = p1.getX() + ua * x21;
double y = p1.getY() + ua * y21;
result.setLocation(x, y);
return true;
}
/**
* Returns less than zero if p2
is on the left hand side of the line created by
* p1
and theta
and greater than zero if it is on the right hand
* side. In theory, it will return zero if the point is on the line, but due to rounding errors
* it almost always decides that it's not exactly on the line.
*
* @param p1 the point on the line whose side we're checking.
* @param theta the (logical) angle defining the line.
* @param p2 the point that lies on one side or the other of the line.
*/
public static int whichSide (Point p1, double theta, Point p2)
{
// obtain the point defining the right hand normal (N)
theta += Math.PI/2;
int x = p1.x + (int)Math.round(1000*Math.cos(theta)),
y = p1.y + (int)Math.round(1000*Math.sin(theta));
// now dot the vector from p1->p2 with the vector from p1->N, if it's positive, we're on
// the right hand side, if it's negative we're on the left hand side and if it's zero,
// we're on the line
return dot(p1.x, p1.y, p2.x, p2.y, x, y);
}
/**
* Shifts the position of the tainer
rectangle to ensure that it contains the
* tained
rectangle. The tainer
rectangle must be larger than or
* equal to the size of the tained
rectangle.
*/
public static void shiftToContain (Rectangle tainer, Rectangle tained)
{
if (tained.x < tainer.x) {
tainer.x = tained.x;
}
if (tained.y < tainer.y) {
tainer.y = tained.y;
}
if (tained.x + tained.width > tainer.x + tainer.width) {
tainer.x = tained.x - (tainer.width - tained.width);
}
if (tained.y + tained.height > tainer.y + tainer.height) {
tainer.y = tained.y - (tainer.height - tained.height);
}
}
/**
* Adds the target rectangle to the bounds of the source rectangle. If the source rectangle is
* null, a new rectangle is created that is the size of the target rectangle.
*
* @return the source rectangle.
*/
public static Rectangle grow (Rectangle source, Rectangle target)
{
if (target == null) {
log.warning("Can't grow with null rectangle [src=" + source + ", tgt=" + target + "].",
new Exception());
} else if (source == null) {
source = new Rectangle(target);
} else {
source.add(target);
}
return source;
}
/**
* Returns the rectangle containing the specified tile in the supplied larger rectangle. Tiles
* go from left to right, top to bottom.
*/
public static Rectangle getTile (
int width, int height, int tileWidth, int tileHeight, int tileIndex)
{
Rectangle bounds = new Rectangle();
getTile(width, height, tileWidth, tileHeight, tileIndex, bounds);
return bounds;
}
/**
* Fills in the bounds of the specified tile in the supplied larger rectangle. Tiles go from
* left to right, top to bottom.
*/
public static void getTile (int width, int height, int tileWidth, int tileHeight, int tileIndex,
Rectangle bounds)
{
// figure out from whence to crop the tile
int tilesPerRow = width / tileWidth;
// if we got a bogus region, return bogus tile bounds
if (tilesPerRow == 0) {
bounds.setBounds(0, 0, width, height);
} else {
int row = tileIndex / tilesPerRow;
int col = tileIndex % tilesPerRow;
// crop the tile-sized image chunk from the full image
bounds.setBounds(tileWidth*col, tileHeight*row, tileWidth, tileHeight);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy