Java libGDX - merge polygons - java

I'm working at a underwater game, where there are some ruins, made out of blocks.
Currently, I am checking for collision with the submarine's polygon and each block of the ruin, with a function that returns the vertices of a rectangle that I made.
public static float[] rectangleToVertices(float x, float y, float width,
float height) {
float[] result = new float[8];
result[0] = x;
result[1] = y;
result[2] = x + width;
result[3] = y;
result[4] = x + width;
result[5] = y + height;
result[6] = x;
result[7] = y + height;
return result;
}
I don't think that is very efficient, some of the ruins got over 10 blocks, and I don't want to check for 10 times a collision of a single object.
Is there a way to merge more polygons into one?
This picture can explain better:
There red area is the polygon.

If I understand your question, you are removing shared edges.
The simplest solution would be to start with one block, adding its edges to a HashSet (say S1). Then, while iterating over the list of blocks, check if any of those other blocks shares any edge from S1. If so, add all edges of that block to S1. For the edge(s) which already existed in S1, add them to another HashSet (say S2) to keep track of such edges. In the end, compute S1-S2, which will be the set of edges that you want. Use those edges to reconstruct your final polygon.
As an aside, you might want to take a look at The Skyline Problem‍​​.

Related

Rotate discrete Points in Java without conflicts

I am generating a List of points that have only integer components using GenerateSolidThetaZero function. My goal is to rotate these discrete points using an angle theta in radians and the new point after the rotation should still have integer components. The problem is I do not want any two points mapping to the same value. I want the same number of unique points before and after the rotation. I used the round function to remedy this problem a bit but I will still get some non-unique mappings. Basically I just want to find a way to rotate these points and preserve as much of the structure as possible(losing the least amount of points as possible). I am willing to use any library. Any help or guidance would be great.
Note: In my code the radius is 2 and 13 points are generated. After the rotation of Pi/6 I end up losing 4 points due to those points mapping to the same value another point already mapped to.
public class pointcheck{
// this HashSet will be used to check if a point is already in the rotated list
public static HashSet<Point> pointSet = new HashSet<Point>();
public static void main(String args[]) {
//generates sort of circular solid with param being the radius
ArrayList<Point> solid_pointList = GenerateSolidThetaZero(2);
//used to store original point as first part of pair and rotated point as second part of pair
ArrayList<Pair> point_pair = new ArrayList<Pair>();
//goes through all points in Solid_pointList adds each point to Point List with its corresponding rotated angle
for(Point t : solid_pointList){
point_pair.add(new Pair(t,rotation_about_origin(t,Math.PI / 6)));
}
for(Pair t : point_pair){
System.out.println(t.getFirst() + " " + t.getSecond());
}
System.out.println(pointSet.size());
}
//takes the point we want to rotate and then the angle to rotate it by
public static Point rotation_about_origin(Point P, double theta){
Point new_P = null;
double old_X = P.x;
double old_Y = P.y;
double cos_theta = Math.cos(theta);
double sin_theta = Math.sin(theta);
double new_X = old_X * cos_theta - old_Y * sin_theta;
double new_Y = old_X * sin_theta + old_Y * cos_theta;
new_P = new Point((int)Math.round(new_X),(int)Math.round(new_Y));
//if new_p is already in rotated solid
if(pointSet.contains(new_P))
System.out.println("Conflict " + P + " " + new_P);
else
//add new_P to pointSet so we know a point already rotated to that spot
pointSet.add(new_P);
return new_P;
}
private static ArrayList<Point> GenerateSolidThetaZero(int r){
int rsq = r * r;
ArrayList<Point> solidList=new ArrayList<Point>();
for (int x=-r;x<=r;x++)
for (int y=-r;y<=r;y++)
if (x*x + y*y <= rsq)
solidList.add(new Point(x,y));
return solidList;
}
public static class Pair<F,S>{
private F first; //first member of pair
private S second; //second member of pair
public Pair(F first, S second) {
this.first = first;
this.second = second;
}
public void setFirst(F first) {
this.first = first;
}
public void setSecond(S second) {
this.second = second;
}
public F getFirst() {
return first;
}
public S getSecond() {
return second;
}
}
}//end of pointcheck class
How would I be able to rotate the points using angles that aren't using integer multiples of 90? Where should I translate a point after rotation if the mapping is already taken?
The rotated disk will cover the exact same pixels as the original one. Therefore, you actually want to solve an assignment problem from original pixels to rotated pixels.
The cost for assigning an original pixel (ox, oy) to a corresponding pixel (cx, cy) can be expressed with a potential. For example, the distance:
E_o,c = length(R(ox, oy, theta) - (cx, cy))
, where R is the rotation operator. Alternatively, you could also try other norms, e.g. the quadratic distance.
Then, the problem is finding the correspondences that minimize the overall energy:
min_C Sum_{o \in O} E_o,c
An algorithm that solves this exactly is the Hungarian Algorithm. However, it is quite expensive if you have a large number of pixels.
Instead, here is an idea of an approximation:
In the target pixels, instead of having only the color, also store the rotated position. Then, rotate the original pixels sequentially as you did before. Round the rotated position and check if the according pixel is still occupied.
If not, store the rotated (unrounded) position along with the new color.
If it is occupied, check if the energy would decrease if you swapped the correspondences. If it does, swap them, which leaves you with the former pixel. In any case, you have an unmapped original pixel. Store this pixel in a list.
After this procedure, you have a partial correspondence map and a list of unmapped pixels. Pick any of the unmapped pixels. Analyze the target pixel's neighbors. There will probably always be an unoccupied pixel (although I have no proof for that). If so, choose this one. If not, check all neighboring pixels for the best energy decrease and swap. Continue until the list is empty.
The approximation algorithm is just an idea and I have no proof that it will actually work. But it sounds as if it is worth a try. And it will definitely be faster than the Hungarian algorithm. Though, this approximation will only work with Lp-norms having p>=1 for the potential definition.

Java equal too, but with a buffer

I have made a simple game and I have a simple way to detect when I have collected a coin but it is very hard to match its position exactly.
public class Token {
private String name;
int x;
int y;
private BufferedImage image;
public Token (String nameIn, int xIn, int yIn, BufferedImage imageIn)
{
name = nameIn;
x = xIn;
y = yIn;
image = imageIn;
}
public boolean collected(Hero frylark) {
if (frylark.getX() == x && frylark.getY() == y) {
return true;
}
else {
return false;
}
}
}
Is there any way i can have a buffer of say 10 pixels instead of
matching the position of the coin exactly.
A distance between two points in a two-dimensional field is the sum of the squares of the differences between their corresponding coordinates:
public boolean collected(Hero frylark) {
return Math.sqrt(Math.pow(frylark.getX() - x , 2) +
Math.pow(frylark.getY() - y , 2)
) <= 10.0;
}
Based on Mureinik's answer, you can do this faster by not use Math.pow nor Math.sqrt.
double dx = frylark.getX() - x;
double dy = frylark.getY() - y;
return dx*dx + dy*dy <= 10.0*10.0;
I have made a simple game and I have a simple way to detect when I have collected a coin but it is very hard to match its position exactly.
I will propose a slightly different approach for you. If you attempt to detect collision by using only the x and y coordinates, it is very hard to detect collision since you need both pixels to hit at the same spot.
This problem arises especially when you try to check collision for images of different sizes:
Exmaple:
With your current implementation, in order for the Game Character to hit the coin, the red pixel (top left hand corner) has to collide, and you end up needed to add a buffer for images of different sizes to check for collision.
I will advise returning a bounding box for each object and check weather their bounding boxes intersects:
public boolean collected(Hero h){
Rectangle heroBox = new Rectangle (h.getX(), h.getY(), h.getWidth(), h.getHeight());
Rectangle coinBox = new Rectangle (x, y, width, height);
return(coinBox.intersects(heroBox));
}
You will need the width and height (which is usually the width and height of your images) of your objects for creating the bounding box.
Advantage:
You no longer have to check the size of each image and set the buffer for them individually.
Is there any way i can have a buffer of say 10 pixels instead of
matching the position of the coin exactly.
Adding a buffer:
If you still want a buffer, say 10 pixel. We can still apply it in this implementation:
public boolean collected(Hero h, int buffer){
Rectangle heroBox = new Rectangle (h.getX(), h.getY(), h.getWidth() + buffer, h.getHeight() + buffer);
Rectangle coinBox = new Rectangle (x, y, width + buffer , height + buffer);
return(coinBox.intersects(heroBox));
}
By adding the given buffer, we enlarge the area of the bounding boxes, hence making it more sensitive. You can always tweak from my example to add the buffer on one of the objects, both objects, or on only the width or the height of either objects.

Getting bullet X to Y movement ratio from 2 points

I'm making pretty simple game. You have a sprite onscreen with a gun, and he shoots a bullet in the direction the mouse is pointing. The method I'm using to do this is to find the X to Y ratio based on 2 points (the center of the sprite, and the mouse position). The X to Y ratio is essentially "for every time the X changes by 1, the Y changes by __".
This is my method so far:
public static Vector2f getSimplifiedSlope(Vector2f v1, Vector2f v2) {
float x = v2.x - v1.x;
float y = v2.y - v1.y;
// find the reciprocal of X
float invert = 1.0f / x;
x *= invert;
y *= invert;
return new Vector2f(x, y);
}
This Vector2f is then passed to the bullet, which moves that amount each frame.
But it isn't working. When my mouse is directly above or below the sprite, the bullets move very fast. When the mouse is to the right of the sprite, they move very slow. And if the mouse is on the left side, the bullets shoot out the right side all the same.
When I remove the invert variable from the mix, it seems to work fine. So here are my 2 questions:
Am I way off-track, and there's a simpler, cleaner, more widely used, etc. way to do this?
If I'm on the right track, how do I "normalize" the vector so that it stays the same regardless of how far away the mouse is from the sprite?
Thanks in advance.
Use vectors to your advantage. I don't know if Java's Vector2f class has this method, but here's how I'd do it:
return (v2 - v1).normalize(); // `v2` is obj pos and `v1` is the mouse pos
To normalize a vector, just divide it (i.e. each component) by the magnitude of the entire vector:
Vector2f result = new Vector2f(v2.x - v1.x, v2.y - v1.y);
float length = sqrt(result.x^2 + result.y^2);
return new Vector2f(result.x / length, result.y / length);
The result is unit vector (its magnitude is 1). So to adjust the speed, just scale the vector.
Yes for both questions:
to find what you call ratio you can use the arctan function which will provide the angle of of the vector which goes from first object to second object
to normalize it, since now you are starting from an angle you don't need to do anything: you can directly use polar coordinates
Code is rather simple:
float magnitude = 3.0; // your max speed
float angle = Math.atan2(y,x);
Vector2D vector = new Vector(magnitude*sin(angle), magnitude*cos(angle));

Is a point inside regular hexagon

I'm looking for advice on the best way to proceed. I'm trying to find whether a given point A:(a, b) is inside a regular hexagon, defined with center O:(x, y) and diameter of circumscribing circle.
It seems like overkill to use Ray-casting, or Winding-number to determine this, for such a simple case, and I'm currently looking at the option of finding the angle (from horizontal) of the line OA, and "normalising" (probably not the right word) it into one of the 6 equilateral triangles and seeing if this new point lies within this triangle.
I get the feeling I'm missing something simple, and there's an easy way (or if I'm really lucky, a Java API) to do this simply and efficiently.
Thanks for your help.
Edit: The hexagon is oriented such that one of the sides is flat with the horizontal.
You can use the equations for each of the sides of the hexagon; with them you can find out if a given point is in the same half-plane as the center of the hexagon.
For example, the top-right side has the equation:
-sqrt(3)x - y + sqrt(3)/2 = 0
You plug in this the coordinates of the point and then the coordinates of the center. If the results have the same sign, then the point is in the bottom-left half-plane (so it may be inside the hexagon).
You then repeat by using the equations of the others sides.
Note that this algorithm will work for any convex polygon.
If you reduce the problem down to checking {x = 0, y = 0, d = 1} in a single quadrant, you could make very simple.
public boolean IsInsideHexagon(float x0, float y0, float d, float x, float y) {
float dx = Math.abs(x - x0)/d;
float dy = Math.abs(y - y0)/d;
float a = 0.25 * Math.sqrt(3.0);
return (dy <= a) && (a*dx + 0.25*dy <= 0.5*a);
}
dy <= a checks that the point is below the horizontal edge.
a*dx + 0.25*dy <= 0.5*a checks that the point is to the left of the sloped right edge.
For {x0 = 0, y0 = 0, d = 1}, the corner points would be (±0.25, ±0.43) and (±0.5, 0.0).
This is what I have been using:
public bool InsideHexagon(float x, float y)
{
// Check length (squared) against inner and outer radius
float l2 = x * x + y * y;
if (l2 > 1.0f) return false;
if (l2 < 0.75f) return true; // (sqrt(3)/2)^2 = 3/4
// Check against borders
float px = x * 1.15470053838f; // 2/sqrt(3)
if (px > 1.0f || px < -1.0f) return false;
float py = 0.5f * px + y;
if (py > 1.0f || py < -1.0f) return false;
if (px - py > 1.0f || px - py < -1.0f) return false;
return true;
}
px and py are the coordinates of x and y projected onto a coordinate system where it is much easier to check the boundaries.
Looks like you know general solution: "It seems like overkill to use...". So here is my idea:
Calculate distance from point to center and let's call it l.
Then you can compare it to inradius (r) and circumradius (R). if l < r then point is inside hexagon, if l > R then outside. If r < l < R then you have to check against each side respectively, but since R - r is very small (13% of length of side of hex) so probability that you will have to do complex calculations is tiny.
Formulas can be found here: http://mathworld.wolfram.com/Hexagon.html
I would first check if the point is inside the inscribed circle (you can compute the inscribed circle radius easily) or outside the circumscribed circle (that you already have).
The first means the point is in, the latter means it's out.
Statistically, most of the input points should allow you to decide based on the above simple tests.
For the worst case scenario (point is in between the inscribed and circumscribed circles), I think you can find the two vertices that are closest to the point and then see on which side of the segment V1V2 the point is (inner or outer, as relative to the O center).
Special case: point is equal to one of the vertices => it's in.
If I'll have a more clever idea (or if I'll ever start to really learn trigonometry), I'll edit the answer to let you know :)
Subtract the position of the center of the hexagon from your point P to get a vector V. Then, take the dot product of V with the following vectors, which correspond to the three pairs of opposing hexagon edges:
[0,1] ; the edges that are flat with the horizontal
[cos(30),sin(30)] ; the upper-right and lower-left edges
[cos(-30),sin(-30)] ; the lower-right and upper-left edges
If any of the dot products are greater in magnitude than the distance from the center of the hexagon to one of its edges, then the point is not inside the hexagon.
For reference, the dot product of vectors [a,b] and [c,d] is a*c+b*d.
The angle "30" above is in degrees ;)
What you want is the code to find out whether a point is inside a convex polygon, an hexagon being a particular case of that.
Here's a good answer:
https://stackoverflow.com/a/34689268/516188
I did modify that function for my use, I find my version clearer. It's typescript (you just squint and it's javascript):
function vectorX(v: Vector): number {
return v[1].x - v[0].x;
}
function vectorY(v: Vector): number {
return v[1].y - v[0].y;
}
function crossProduct(v1: Vector, v2: Vector): number {
return vectorX(v1)*vectorY(v2) - vectorY(v1)*vectorX(v2);
}
function isInConvexPolygon(testPoint: Point, polygon: Polygon): boolean {
// https://stackoverflow.com/a/34689268/516188
if (polygon.length < 3) {
throw "Only supporting polygons of length at least 3";
}
// going through all the edges around the polygon. compute the
// vector cross-product http://allenchou.net/2013/07/cross-product-of-2d-vectors/
// to find out for each edge on which side of the edge is the point.
// if the point is on the same side for all the edges, it's inside
let initCrossIsPositive = undefined;
for (var i=0;i<polygon.length;i++) {
if (polygon[i].x === testPoint.x &&
polygon[i].y === testPoint.y) {
// testPoint is an edge of the polygon
return true;
}
const curPointOnEdge = polygon[i];
const nextPointOnEdge = polygon[(i+1)%polygon.length];
const vector1 = <[Point,Point]>[curPointOnEdge, nextPointOnEdge];
const vector2 = <[Point,Point]>[curPointOnEdge, testPoint];
const cross = crossProduct(vector1, vector2);
if (initCrossIsPositive === undefined) {
initCrossIsPositive = cross > 0;
} else {
if (initCrossIsPositive !== (cross > 0)) {
return false;
}
}
}
// all the cross-products have the same sign: we're inside
return true;
}
There's a nice generalization to a hex lattice using homogeneous coordinates, by representing the lattice as the cube-lattice intersected with the plane x+y+z=0, see https://www.redblobgames.com/grids/hexagons/#coordinates

Math help - Can't rotate something (knowing Java would be a plus)

Okay I'm trying to rotate a Java Polygon based on it's original position of angle 0. x, and y end up being converted to an int at the end of me using them, so I could understand not seeing some change, but when the difference in angles is big like 0 to 180 I think I should see something.
I've been at this for a little while and can't think of what it is. Here's the method. (Sorry if it messes up in the code tags, my firefox messes them up.)
public void rotate(double x, double y, obj o1)
{
double dist = Math.sqrt(Math.pow(x - (o1.x + (o1.w/2)), 2) + Math.pow(y - (o1.y + (o1.h/2)),2));
x += (Math.sin(Math.toRadians(o1.a)) * dist);
y -= (Math.cos(Math.toRadians(o1.a)) * dist);
}
The values of x and y that are being manipulated in the rotate method will not be seen in the method that is calling it because Java passes method arguments by value.
Therefore, the x and y values that are being changed in the rotate method is a local copy, so once it goes out of scope (i.e. returning from the rotate method to its calling method), the values of x and y will disappear.
So currently, what is happening is:
x = 10;
y = 10;
o1 = new obj();
o1.a = 100;
rotate(x, y, obj);
System.out.println(x); // Still prints 10
System.out.println(y); // Still prints 10
The only way to get multiple values back from a method in Java is to pass an object, and manipulate the object that is passed in. (Actually, a copy of the reference to the object is passed in when an method call is made.)
For example, redefining rotate to return a Point:
public Point rotate(int x, int y, double angle)
{
// Do rotation.
return new Point(newX, newY);
}
public void callingMethod()
{
int x = 10;
int y = 10;
p = rotate(x, y, 45);
System.out.println(x); // Should print something other than 10.
System.out.println(y); // Should print something other than 10.
}
That said, as Pierre suggests, using the AffineTransform would be much easier in my opinion.
For example, creating a Rectangle object and rotating it using AffineTransform can be performed by the following:
Rectangle rect = new Rectangle(0, 0, 10, 10);
AffineTransform at = new AffineTransform();
at.rotate(Math.toRadians(45));
Shape rotatedRect = at.createTransformedShape(rect);
AffineTransform can be applied to classes which implement the Shape interface. A list of classes implementing Shape can be found in the linked Java API specifications for the Shape interface.
For more information on how to use AffineTransform and Java 2D:
Trail: 2D Graphics
Lesson: Advanced Topics in Java2D
Transforming Shapes, Text, and Images
FYI: Rotating shapes and points has been implemented in java.awt.geom.AffineTransform
You're performing a 2D rotational transformation.
It ought to look something like this:
xnew = xold*cos(t) - yold*sin(t)
ynew = xold*sin(t) + yold*cos(t)
The rotation angle t must be in radians, of course. It's zero at the x-axis, and increased in the anti-clockwise direction.
Both the old and new points need to be expressed relative to the origin you're rotating about.

Categories