find the minimum and maximum distance between 2 rectangles of any dimension - java

I have a Rectangle class that represents a d-dimension rectangle with 2*d numbers for every dimension. For every dimension i have a lower and an upper bound. Dimensionality stores the number of dimensions of the rectangle, and for the lower and upper bounds i use a double array.
I want to create 2 methods that they have as input another rectangle object of the same Dimensionality and return the minimum and maximum distance between the rectangles, im trying to do this using the minimum/maximum distances of each of their projections in every axis. I also have a method that creates the projections.
//returns 2 position array
public double[] project(int x)
{
//x is the selected dimension
double proj[] = new double[2];
proj[0] = this.lb[x];
proj[1] = this.ub[x];
return proj;
}
you can see on the third set of shapes what i want to do more clearly
https://s15.postimg.org/l8aijyl1n/imageedit_2_6689786765.jpg

Find mutual orientation of rectangles (as direction of vector between centers, it is enough to get signs of center.x and center.y differences)
Depending on orientation quadrant, get distances from selected edges of the first rectangle to selected edges of the second. Using signs of these distances, find extremal distances:
For the case at the picture (direction in the 4th quadrant) one have to check distances from right bottom corner of the first rectangle to the left and top edges of the second one, and from left top corner of the second - to the right and bottom edges of the first one.
min_dx = rect2.left - rect1.right
min_dy = rect2.top - rect.bottom
if both values > 0 (++ case)
min_dist = sqrt(min_dx^2 + min_dy^2) //corner-corner case
+- case
min_dist = min_dx
-+ case
min_dist = min_dy
-- case
min_dist = 0
Seems the same approach might be used for higher dimensions. Number of cases becomes too high, so it is worth to select planes for checking with indexes corresponding to center-center direction vector components' signs

Related

Libgdx: How to move objects on X axis without overlapping?

I have a rectangle array holding multiple objects, moving back and forth on X axis.
Iterator<Rectangle> iter = array.iterator();
while ( iter.hasNext() ) {
Rectangle obj = iter.next();
array.get(i).x += speed * Gdx.graphics.getDeltaTime() ;
if (obj.x + obj.width > 800 || obj.x < 0) {
speed = -speed;
}
}
When the speed gets bigger, you'll start noticing the first object in the array overlapping with the other objects and pushing them apart. How to fix that?
Basically each object has
Rectangle obj = new Rectangle();
obj.x = xpos;
obj.y = ypos;
obj.width = width;
obj.height = height;
xpos += width + 4;
And has a texture, image, a sqaure, a rectange, a triangle... And each object is generated at an X position xpos different than the other. All they do is keep moving on the X axis, from x=0 till 800 and back.
What happens is that when the first object gets to 0, it tries to increase its speed again and overlapping with other objects, and then time after time, all objects keep overlapping and get further apart from each other. I want the distance between the objects to stay constant at any speed.
From what you've commented, the questions appears to be "How can I make all these blocks move together, bouncing from one edge to another". The issue being that you're getting bouncing, but they stop acting as a group.
Firstly, if you want to treat them as a group - the simplest way is to consider them as one large bounding box containing lots of smaller (inconsequential) objects. Moving that as a single object from side to side will give you the behaviour you need.
That aside, the direct answer to your question is "you're changing the direction mid-way through iteration". So in any single tick, some objects have moved left and some have moved right - meaning they stop acting as a group.
How you organise it is up to you, but this is the basic idea you need:
// assume "speedForThisFrame" is a float defined outside this function
float speedForNextFrame = speedForThisFrame
// iterate through however you want
Iterator<Rectangle> iter = array.iterator();
while ( iter.hasNext() ) {
Rectangle obj = iter.next();
obj.x += speedForThisFrame * Gdx.graphics.getDeltaTime() ;
// if it's moved out of bounds, we will change direction NEXT fame
if (obj.x + obj.width > 800 || obj.x < 0) {
speedForNextFrame = -speedForThisFrame;
}
}
// now that all movement has finished, we update the speed
speedForThisFrame = speedForNextFrame
The key thing is everything must move by the same amount, in the same direction, every frame. Changing the speed mid-update will cause them to act independently.
Note, you will still have issues when your group is larger than the bounds - or when they go over the bounds in one frame and don't fully get back the next frame. These are separate issues though and can be asked in a separate question.
I think your problem is that, caused by variations in Gdx.graphics.getDeltaTime(), the rectangles exceed your 0/800 borders by different distances.
An example:
First step:
Rect #1 x=790
Rect #2 x=780
Speed=100, DeltaTime=0.11 => DeltaX=11
After this step, Rect#1 would be at 801, Rect#2 at 791, their distance is 10.
Next step:
DeltaTime=0.12 => DeltaX=12
After this step, Rect#1 is at 789, Rect#2 at 803, their distance is 14.
Your rectangles vary their distance because they travel different distances. A possible solution would be to really bounce at the borders. So you should not only invert the speed but also take the distance a rectangle exceeded the border and let it travel this distance in the opposite direction:
So Rect#1 at 790, moving 11 pixels rightwards, should not be at 801 in the end of the step but at 799 (moving 10 pixels to the right and one to the left).

Tile id on grid from x/y position

(this image is not mine, but it helps ilustrate the point)
Give this grid with each tile beign 32x32 pixels, how can i calculate the tile ID where the mouse is?
In this case, mouse is on tile: 40.
Let us say the current mouse position is (x,y) and the length of each small square is l(in the given case 32). Then, the grid x and y value will be given by:
gridX = x/l; //be sure it is integer division and not float
gridY = y/l; //
Then calculate the tile id on the basis of those values:
currentTileId = (boxesEachRow * gridY) + gridX + 1;
where boxesEachRow is the number of boxes along each row(here it is 8). The plus 1 is needed if you are treating the first box's id as 1 and not 0.

What is the source of these pixel gaps in between identical vertices in OpenGL's Ortho? How can I eliminate them?

Despite passing equal (exactly equal) coordinates for 'adjacent' edges, I'm ending up with some strange lines between adjacent elements when scaling my grid of rendered tiles.
My tile grid rendering algorithm accepts scaled tiles, so that I can adjust the grid's visual size to match a chosen window size of the same aspect ratio, among other reasons. It seems to work correctly when scaled to exact integers, and a few non-integer values, but I get some inconsistent results for the others.
Some Screenshots:
The blue lines are the clear color showing through. The chosen texture has no transparent gaps in the tilesheet, as unused tiles are magenta and actual transparency is handled by the alpha layer. The neighboring tiles in the sheet have full opacity. Scaling is achieved by setting the scale to a normalized value obtained through a gamepad trigger between 1f and 2f, so I don't know what actual scale was applied when the shot was taken, with the exception of the max/min.
Attribute updates and entity drawing are synchronized between threads, so none of the values could have been applied mid-draw. This isn't transferred well through screenshots, but the lines don't flicker when the scale is sustained at that point, so it logically shouldn't be an issue with drawing between scale assignment (and thread locks prevent this).
Scaled to 1x:
Scaled to A, 1x < Ax < Bx :
Scaled to B, Ax < Bx < Cx :
Scaled to C, Bx < Cx < 2x :
Scaled to 2x:
Projection setup function
For setting up orthographic projection (changes only on screen size changes):
.......
float nw, nh;
nh = Display.getHeight();
nw = Display.getWidth();
GL11.glOrtho(0, nw, nh, 0, 1, -1);
orthocenter.setX(nw/2); //this is a Vector2, floats for X and Y, direct assignment.
orthocenter.setY(nh/2);
.......
For the purposes of the screenshot, nw is 512, nh is 384 (implicitly casted from int). These never change throughout the example above.
General GL drawing code
After cutting irrelevant attributes that didn't fix the problem when cut:
#Override
public void draw(float xOffset, float yOffset, float width, float height,
int glTex, float texX, float texY, float texWidth, float texHeight) {
GL11.glLoadIdentity();
GL11.glTranslatef(0.375f, 0.375f, 0f); //This is supposed to fix subpixel issues, but makes no difference here
GL11.glTranslatef(xOffset, yOffset, 0f);
if(glTex != lastTexture){
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTex);
lastTexture = glTex;
}
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(texX,texY + texHeight);
GL11.glVertex2f(-height/2, -width/2);
GL11.glTexCoord2f(texX + texWidth,texY + texHeight);
GL11.glVertex2f(-height/2, width/2);
GL11.glTexCoord2f(texX + texWidth,texY);
GL11.glVertex2f(height/2, width/2);
GL11.glTexCoord2f(texX,texY);
GL11.glVertex2f(height/2, -width/2);
GL11.glEnd();
}
Grid drawing code (dropping the same parameters dropped from 'draw'):
//Externally there is tilesize, which contains tile pixel size, in this case 32x32
public void draw(Engine engine, Vector2 offset, Vector2 scale){
int xp, yp; //x and y position of individual tiles
for(int c = 0; c<width; c++){ //c as in column
xp = (int) (c*tilesize.a*scale.getX()); //set distance from chunk x to column x
for(int r = 0; r<height; r++){ //r as in row
if(tiles[r*width+c] <0) continue; //skip empty tiles ('air')
yp = (int) (r*tilesize.b*scale.getY()); //set distance from chunk y to column y
tileset.getFrame(tiles[r*width+c]).draw( //pull 'tile' frame from set, render.
engine, //drawing context
new Vector2(offset.getX() + xp, offset.getY() + yp), //location of tile
scale //scale of tiles
);
}
}
}
Between the tiles and the platform specific code, vectors' components are retrieved and passed along to the general drawing code as pasted earlier.
My analysis
Mathematically, each position is an exact multiple of the scale*tilesize in either the x or y direction, or both, which is then added to the offset of the grid's location. It is then passed as an offset to the drawing code, which translates that offset with glTranslatef, then draws a tile centered at that location through halving the dimensions then drawing each plus-minus pair.
This should mean that when tile 1 is drawn at, say, origin, it has an offset of 0. Opengl then is instructed to draw a quad, with the left edge at -halfwidth, right edge at +halfwidth, top edge at -halfheight, and bottom edge at +halfheight. It then is told to draw the neighbor, tile 2, with an offset of one width, so it translates from 0 to that width, then draws left edge at -halfwidth, which should coordinate-wise be exactly the same as tile1's right edge. By itself, this should work, and it does. When considering a constant scale, it breaks somehow.
When a scale is applied, it is a constant multiple across all width/height values, and mathematically shouldn't make anything change. However, it does make a difference, for what I think could be one of two reasons:
OpenGL is having issues with subpixel filling, ie filling left of a vertex doesn't fill the vertex's containing pixel space, and filling right of that same vertex also doesn't fill the vertex's containing pixel space.
I'm running into float accuracy problems, where somehow X+width/2 does not equal X+width - width/2 where width = tilewidth*scale, tilewidth is an integer, and X is a float.
I'm not really sure about how to tell which one is the problem, or how to remedy it other than to simply avoid non-integer scale values, which I'd like to be able to support. The only clue I think might apply to finding the solution is how the pattern of line gaps isn't really consistant (see how it skips tiles in some cases, only has vertical or horizontal but not both, etc). However, I don't know what this implies.
This looks like it's probably a floating point precision issue. The critical statement in your question is this:
Mathematically, each position is an exact multiple [..]
While that's mathematically true, you're dealing with limited floating point precision. Sequences of operations that should mathematically produce the same result can (and often do) produce slightly different results due to rounding errors during expression evaluation.
Specifically in your case, it looks like you're relying on identities of this form:
i * width + width/2 == (i + 1) * width - width/2
This is mathematically correct, but you can't expect to get exactly the same numbers when evaluating the values with limited floating point precision. Depending on how the small errors end up getting rounded to pixels, it can result in visual artifacts.
The only good way to avoid this is that you actually use the same values for coordinates that must be the same, instead of using calculations that mathematically produce the same results.
In the case of coordinates on a grid, you could calculate the coordinates for each grid line (tile boundary) once, and then use those values for all draw operations. Say if you have n tiles in the x-direction, you calculate all the x-values as:
x[i] = i * width;
and then when drawing tile i, use x[i] and x[i + 1] as the left and right x-coordinates.

Convert 2d game world coordinates to screen position

I have a system that generates chunks of 2d game map tiles. Chunks are 16x16 tiles, tiles are 25x25.
The chunks are given their own coordinates, like 0,0, 0,1, etc. The tiles determine their coordinates in the world based on which chunk they're in. I've verified that the chunks/tiles are all showing the proper x/y coordinates.
My problem is translating those into screen coordinates. In a previous question someone recommended using:
(worldX * tileWidth) % viewport_width
Each tile's x/y are run through this calculation and a screen x/y coordinate is returned.
This works for tiles that fit within the viewport, but it resets the screen x/y position calculation for anything off-screen.
In my map, I load chunks of tiles within a radius around the player so some of the inner tiles will be off-screen (until they move around, tile positions on the screen are moved).
I tried a test with a tile that would be off screen:
Tile's x coord: 41
41 * 25 = 1025
Game window: 1024
1025 % 1024 = 1
This means that tile x (which, if the screen 0,0 is at map 0,0, should be at x:1025, just off the right-hand side of the screen) is actually at x:1, appearing in the top-left.
I can't think of how to properly handle this - it seems to me like I need take the tileX * tileWidth to determine it's "initial screen position" and then somehow use an offset to determine how to make it appear on screen. But what offset?
Update: I already store an x/y offset value when the player moves, so I know how to move the map. I can use these values as the current offset, and if someone saves the game I can simply store those and re-use them. There's no equation necessary, I would just have to store the cumulative offsets.
The modulo (worldX*tileWidth % screenWidth) is what's causing it to reset. Modulo (%) gives you the remainder of an integer division operation; so, if worldX * tileWidth is greater than screenWidth, it will give you the remainder of (worldX * tileWidth) / screenWidth; if worldX * tileWidth is screenWidth+1, remainder is 1: it starts over at the beginning of the row.
If you eliminate the modulo, it will continue to draw tiles past the edge of the screen. If your drawing buffer is the same size as the screen, you'll need to add a check for tiles at the edge of the screen to make sure you only draw the tile portion that will be visible.
If you're trying to keep the player centered on the screen, you need to offset each tile by the player's offset from tile 0,0 in pixels, minus half the screen width:
offsetX = (playerWorldX * tileWidth) - (screenWidth / 2);
screenX = (worldX * tileWidth) - offsetX;
x = ((worldX*tileWidth) > screenWidth) ? worldX*tileWidth : (worldX*tileWidth)%screenWidth;
That should work. Though I recommend implementing something like an interface and letting each tile decide where they want to be rendered. Something like this
interface Renderable {
void Render(Graphics2D g)
..
}
class Tile implements Renderable{
int x,y
//other stuff
Render(Graphics2D g){
if (!inScreen()){
return;
}
//...
//render
}
boolean inScreen(){
//if the map moves with the player you need to define the boundaries of your current screenblock in terms of the global map coordinates
//What you can do is store this globally in a singleton somewhere or pass it to the constructor of each tile.
//currentBlock.x is then player.x - screenWidth/2
//currentBlock.width is then player.x + screenWidth/2;
//similar for y
if(this.x < currentBlock.x || this.x > currentBlock.Width)
return false;
if (this.y < currentBlock.y || this.y > currentBlock.height)
return false;
return true;
//If the map are in blocks (think zelda on snes where you go from one screenblock to another) you still need to define the boundaries
//currentBlock.x = (player.x / screenWidth) (integer division) *screenWidth;
//currentBlock.width = (player.x /screenWidth) (...) * screenWidth + screenWidth;
//same for y
//Then perform above tests
}

Centroid of convex polyhedron

I have a closed convex polyhedron which is defined by an array of convex polygons (faces) which are defined by arrays of vertices in 3D space. I'm trying to find the centroid of the polyhedron, assuming uniform density. At the moment I calculate it with the algorithm in this pseudo-code.
public Vector3 getCentroid() {
Vector3 centroid = (0, 0, 0);
for (face in faces) {
Vector3 point = face.centroid;
point.multiply(face.area());
centroid.add(point);
}
centroid.divide(faces.size());
return centroid;
}
This essentially takes the weighted average of the centroids of the faces. I'm not 100% sure this is correct as I haven't been able to find a correct algorithm online. If someone could either confirm my algorithm or refer me to a correct one I would appreciate it.
Thanks.
[EDIT]
So here is the actual Java code I am using to find the centroid. It breaks the polyhedron into pyramids converging on an arbitrary point inside the polyhedron. The weighted average for the pyramid centroids is based on the following formula.
Call = SUMall pyramids(Cpyramid * volumepyramid) / volumeall
Here is the (heavily commented code):
// Compute the average of the facial centroids.
// This gives an arbitrary point inside the polyhedron.
Vector3 avgPoint = new Vector3(0, 0, 0);
for (int i = 0; i < faces.size(); i++) {
avgPoint.add(faces.get(i).centroid);
}
avgPoint.divide(faces.size());
// Initialise the centroid and the volume.
centroid = new Vector3(0, 0, 0);
volume = 0;
// Loop through each face.
for (int i = 0; i < faces.size(); i++) {
Face face = faces.get(i);
// Find a vector from avgPoint to the centroid of the face.
Vector3 avgToCentroid = face.centroid.clone();
avgToCentroid.sub(avgPoint);
// Gives the unsigned minimum distance between the face and a parallel plane on avgPoint.
float distance = avgToCentroid.scalarProjection(face.getNormal());
// Finds the volume of the pyramid using V = 1/3 * B * h
// where: B = area of the pyramid base.
// h = pyramid height.
float pyramidVolume = face.getArea() * distance / 3;
// Centroid of a pyramid is 1/4 of the height up from the base.
// Using 3/4 here because vector is travelling 'down' the pyramid.
avgToCentroid.multiply(0.75f);
avgToCentroid.add(avgPoint);
// avgToCentroid is now the centroid of the pyramid.
// Weight it by the volume of the pyramid.
avgToCentroid.multiply(pyramidVolume);
volume += pyramidVolume;
}
// Average the weighted sum of pyramid centroids.
centroid.divide(volume);
Please feel free to ask me any questions you may have about it or point out any errors you see.
Generally that depends on the structure of your polyhedron. There are 4 possible cases:
Only vertices have weight, i.e. your polyhedron is the system of points. Then you can just calculate the mean value of the weighted sum of all the points:
r_c = sum(r_i * m_i) / sum(m_i)
Here r_i is the vector representing the i-th vertex, m_i - its mass. Case of equal masses leaves us with the simpler formula:
r_c = sum(r_i) / n
Where n is the number of vertices. Note that both sums are vectorized.
Only edges have weight, and polyhedron is essentially a carcass. This case can be reduced to the previous one by substituting each edge with vertex situated right in the middle of the edge and have the weight of the whole edge.
Only faces have weight. This case can be reduced to the first one as well. Each face is a 2D convex figure, of which the centroid can be found. Substituting each face with its centroid brings this case to the first one.
Solid polyhedron (your case, inferring from the "assuming uniform density"). This problem requires a more complicated approach. First step is to split polyhedron into tetrahedrons. Here is the short description on how to do this. For a tetrahedron centroid is situated in the point where all its medians intersect. (Median of a tetrahedron is the line that connects its vertex and the centroid of the opposite face.) The next step is to substitute each tetrahedron in the partition with its centroid. And the last step is to find the centroid of the resulting set of weighted points, which is exactly the first case.
For the solid case, there's a much simpler method than trying to tetrahedralize the polyhedron (which has pitfalls).
Here's pseudo-ish java-ish code (assuming decent Vector3 implementation):
// running sum for total volume
double vol = 0;
// running sum for centroid
Vector3 centroid = (0, 0, 0);
for each triangle (a,b,c)
{
// Compute area-magnitude normal
Vector3 n = (b-a).cross(c-a);
vol += a.dot(n)/6.;
// Compute contribution to centroid integral for each dimension
for(int d = 0;d<3;d++)
centroid[d] += n[d] * ((a[d]+b[d])^2 + (b[d]+c[d])^2 + (c[d]+a[d])^2);
}
// final scale by inverse volume
centroid *= 1./(24.*2.*vol);
Note, if you have higher degree faces than triangles you can trivially triangulate with a fan and this will still work.
This conveniently works even if the polyhedron is not convex.
I've also posted matlab code

Categories