What are the best algorithms (and explanations) for representing and rotating the pieces of a tetris game? I always find the piece rotation and representation schemes confusing.
Most tetris games seem to use a naive "remake the array of blocks" at each rotation:
http://www.codeplex.com/Project/ProjectDirectory.aspx?ProjectSearchText=tetris
However, some use pre-built encoded numbers and bit shifting to represent each piece:
http://www.codeplex.com/wintris
Is there a method to do this using mathematics (not sure that would work on a cell based board)?
When I was trying to figure out how rotations would work for my tetris game, this was the first question that I found on stack overflow. Even though this question is old, I think my input will help others trying to figure this out algorithmically. First, I disagree that hard coding each piece and rotation will be easier. Gamecat's answer is correct, but I wanted to elaborate on it. Here are the steps I used to solve the rotation problem in Java.
For each shape, determine where its origin will be. I used the points on the diagram from this page to assign my origin points. Keep in mind that, depending on your implementation, you may have to modify the origin every time the piece is moved by the user.
Rotation assumes the origin is located at point (0,0), so you will have to translate each block before it can be rotated. For example, suppose your origin is currently at point (4, 5). This means that before the shape can be rotated, each block must be translated -4 in the x-coordinate and -5 in the y-coordinate to be relative to (0,0).
In Java, a typical coordinate plane starts with point (0,0) in the upper left most corner and then increases to the right and down. To compensate for this in my implementation, I multiplied each point by -1 before rotation.
Here are the formulae I used to figure out the new x and y coordinate after a counter-clockwise rotation. For more information on this, I would check out the Wikipedia page on Rotation Matrix. x' and y' are the new coordinates:
x' = x * cos(PI/2) - y * sin(PI/2) and y' = x * sin(PI/2) + y * cos(PI/2)
.
For the last step, I just went through steps 2 and 3 in reverse order. So I multiplied my results by -1 again and then translated the blocks back to their original coordinates.
Here is the code that worked for me (in Java) to get an idea of how to do it in your language:
public synchronized void rotateLeft(){
Point[] rotatedCoordinates = new Point[MAX_COORDINATES];
for(int i = 0; i < MAX_COORDINATES; i++){
// Translates current coordinate to be relative to (0,0)
Point translationCoordinate = new Point(coordinates[i].x - origin.x, coordinates[i].y - origin.y);
// Java coordinates start at 0 and increase as a point moves down, so
// multiply by -1 to reverse
translationCoordinate.y *= -1;
// Clone coordinates, so I can use translation coordinates
// in upcoming calculation
rotatedCoordinates[i] = (Point)translationCoordinate.clone();
// May need to round results after rotation
rotatedCoordinates[i].x = (int)Math.round(translationCoordinate.x * Math.cos(Math.PI/2) - translationCoordinate.y * Math.sin(Math.PI/2));
rotatedCoordinates[i].y = (int)Math.round(translationCoordinate.x * Math.sin(Math.PI/2) + translationCoordinate.y * Math.cos(Math.PI/2));
// Multiply y-coordinate by -1 again
rotatedCoordinates[i].y *= -1;
// Translate to get new coordinates relative to
// original origin
rotatedCoordinates[i].x += origin.x;
rotatedCoordinates[i].y += origin.y;
// Erase the old coordinates by making them black
matrix.fillCell(coordinates[i].x, coordinates[i].y, Color.black);
}
// Set new coordinates to be drawn on screen
setCoordinates(rotatedCoordinates.clone());
}
This method is all that is needed to rotate your shape to the left, which turns out to be much smaller (depending on your language) than defining each rotation for every shape.
There is a limited amount of shapes, so I would use a fixed table and no calculation. That saves time.
But there are rotation algorithms.
Chose a centerpoint and rotate pi/2.
If a block of a piece starts at (1,2) it moves clockwise to (2,-1) and (-1,-2) and (-1, 2).
Apply this for each block and the piece is rotated.
Each x is the previous y and each y - the previous x. Which gives the following matrix:
[ 0 1 ]
[ -1 0 ]
For counterclockwise rotation, use:
[ 0 -1 ]
[ 1 0 ]
This is how I did it recently in a jQuery/CSS based tetris game.
Work out the centre of the block (to be used as a pivot point), i.e. the centre of the block shape.
Call that (px, py).
Each brick that makes up the block shape will rotate around that point.
For each brick, you can apply the following calculation...
Where each brick's width and height is q, the brick's current location (of the upper left corner) is (x1, y1) and the new brick location is (x2, y2):
x2 = (y1 + px - py)
y2 = (px + py - x1 - q)
To rotate the opposite direction:
x2 = (px + py - y1 - q)
y2 = (x1 + py - px)
This calculation is based on a 2D affine matrix transformation.
If you are interested in how I got to this let me know.
Personally I've always just represented the rotations by hand - with very few shapes, it's easy to code that way. Basically I had (as pseudo-code)
class Shape
{
Color color;
ShapeRotation[] rotations;
}
class ShapeRotation
{
Point[4] points;
}
class Point
{
int x, y;
}
At least conceptually - a multi-dimensional array of points directly in shape would do the trick too :)
You can rotate a matrix only by applying mathematical operations to it. If you have a matrix, say:
Mat A = [1,1,1]
[0,0,1]
[0,0,0]
To rotate it, multiply it by its transpose and then by this matrix ([I]dentity [H]orizontaly [M]irrored):
IHM(A) = [0,0,1]
[0,1,0]
[1,0,0]
Then you'll have:
Mat Rotation = Trn(A)*IHM(A) = [1,0,0]*[0,0,1] = [0,0,1]
[1,0,0] [0,1,0] = [0,0,1]
[1,1,0] [1,0,0] = [0,1,1]
Note: Center of rotation will be the center of the matrix, in this case at (2,2).
Representation
Represent each piece in the minimum matrix where 1's represent spaces occupied by the tetriminoe and 0's represent empty space. Example:
originalMatrix =
[0, 0, 1]
[1, 1, 1]
Rotation Formula
clockwise90DegreesRotatedMatrix = reverseTheOrderOfColumns(Transpose(originalMatrix))
anticlockwise90DegreesRotatedMatrix = reverseTheOrderOfRows(Transpose(originalMatrix))
Illustration
originalMatrix =
x y z
a[0, 0, 1]
b[1, 1, 1]
transposed = transpose(originalMatrix)
a b
x[0, 1]
y[0, 1]
z[1, 1]
counterClockwise90DegreesRotated = reverseTheOrderOfRows(transposed)
a b
z[1, 1]
y[0, 1]
x[0, 1]
clockwise90DegreesRotated = reverseTheOrderOfColumns(transposed)
b a
x[1, 0]
y[1, 0]
z[1, 1]
Since there are only 4 possible orientations for each shape, why not use an array of states for the shape and rotating CW or CCW simply increments or decrements the index of the shape state (with wraparound for the index)? I would think that might be quicker than performing rotation calculations and whatnot.
I derived a rotation algorithm from matrix rotations here. To sum it up: If you have a list of coordinates for all cells that make up the block, e.g. [(0, 1), (1, 1), (2, 1), (3, 1)] or [(1, 0), (0, 1), (1, 1), (2, 1)]:
0123 012
0.... 0.#.
1#### or 1###
2.... 2...
3....
you can calculate the new coordinates using
x_new = y_old
y_new = 1 - (x_old - (me - 2))
for clockwise rotation and
x_new = 1 - (y_old - (me - 2))
y_new = x_old
for counter-clockwise rotation. me is the maximum extent of the block, i.e. 4 for I-blocks, 2 for O-blocks and 3 for all other blocks.
If you're doing this in python, cell-based instead of coordinate pairs it's very simple to rotate a nested list.
rotate = lambda tetrad: zip(*tetrad[::-1])
# S Tetrad
tetrad = rotate([[0,0,0,0], [0,0,0,0], [0,1,1,0], [1,1,0,0]])
If we assume that the central square of the tetromino has coordinates (x0, y0) which remains unchanged then the rotation of the other 3 squares in Java will look like this:
private void rotateClockwise()
{
if(rotatable > 0) //We don't rotate tetromino O. It doesn't have central square.
{
int i = y1 - y0;
y1 = (y0 + x1) - x0;
x1 = x0 - i;
i = y2 - y0;
y2 = (y0 + x2) - x0;
x2 = x0 - i;
i = y3 - y0;
y3 = (y0 + x3) - x0;
x3 = x0 - i;
}
}
private void rotateCounterClockwise()
{
if(rotatable > 0)
{
int i = y1 - y0;
y1 = (y0 - x1) + x0;
x1 = x0 + i;
i = y2 - y0;
y2 = (y0 - x2) + x0;
x2 = x0 + i;
i = y3 - y0;
y3 = (y0 - x3) + x0;
x3 = x0 + i;
}
}
for 3x3 sized tetris pieces
flip x and y of your piece
then swap the outer columns
that's what I figured out some time
I have used a shape position and set of four coordinates for the four points in all the shapes. Since it's in 2D space, you can easy apply a 2D rotational matrice to the points.
The points are divs so their css class is turned from off to on. (this is after clearing the css class of where they were last turn.)
If array size is 3*3 ,than the simplest way to rotate it for example in anti-clockwise direction is:
oldShapeMap[3][3] = {{1,1,0},
{0,1,0},
{0,1,1}};
bool newShapeMap[3][3] = {0};
int gridSize = 3;
for(int i=0;i<gridSize;i++)
for(int j=0;j<gridSize;j++)
newShapeMap[i][j] = oldShapeMap[j][(gridSize-1) - i];
/*newShapeMap now contain:
{{0,0,1},
{1,1,1},
{1,0,0}};
*/
Python:
pieces = [
[(0,0),(0,1),(0,2),(0,3)],
[(0,0),(0,1),(1,0),(1,1)],
[(1,0),(0,1),(1,1),(1,2)],
[(0,0),(0,1),(1,0),(2,0)],
[(0,0),(0,1),(1,1),(2,1)],
[(0,1),(1,0),(1,1),(2,0)]
]
def get_piece_dimensions(piece):
max_r = max_c = 0
for point in piece:
max_r = max(max_r, point[0])
max_c = max(max_c, point[1])
return max_r, max_c
def rotate_piece(piece):
max_r, max_c = get_piece_dimensions(piece)
new_piece = []
for r in range(max_r+1):
for c in range(max_c+1):
if (r,c) in piece:
new_piece.append((c, max_r-r))
return new_piece
In Ruby, at least, you can actually use matrices. Represent your piece shapes as nested arrays of arrays like [[0,1],[0,2],[0,3]]
require 'matrix'
shape = shape.map{|arr|(Matrix[arr] * Matrix[[0,-1],[1,0]]).to_a.flatten}
However, I agree that hard-coding the shapes is feasible since there are 7 shapes and 4 states for each = 28 lines and it will never be any more than that.
For more on this see my blog post at
https://content.pivotal.io/blog/the-simplest-thing-that-could-possibly-work-in-tetris and a completely working implementation (with minor bugs) at https://github.com/andrewfader/Tetronimo
In Java:
private static char[][] rotateMatrix(char[][] m) {
final int h = m.length;
final int w = m[0].length;
final char[][] t = new char[h][w];
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
t[w - x - 1][y] = m[y][x];
}
}
return t;
}
A simple Tetris implementation as a single-page application in Java:
https://github.com/vadimv/rsp-tetris
Related
This question already has answers here:
Finding whether a point lies inside a rectangle or not
(10 answers)
Closed 2 years ago.
I have a rotated rectangle with these coordinates as vertices:
1 670273 4879507
2 677241 4859302
3 670388 4856938
4 663420 4877144
And I have points with these coordinates:
670831 4867989
675097 4869543
Using only the Python 2.7 standard library, I want to determine if the points fall within the rotated rectangle.
I am not able to add additional Python libraries to my Jython implementation
What would it take to do this?
A line equation of the form ax+by+c==0 can be constructed from 2 points. For a given point to be inside a convex shape, we need testing whether it lies on the same side of every line defined by the shape's edges.
In pure Python code, taking care of writing the equations avoiding divisions, this could be as follows:
def is_on_right_side(x, y, xy0, xy1):
x0, y0 = xy0
x1, y1 = xy1
a = float(y1 - y0)
b = float(x0 - x1)
c = - a*x0 - b*y0
return a*x + b*y + c >= 0
def test_point(x, y, vertices):
num_vert = len(vertices)
is_right = [is_on_right_side(x, y, vertices[i], vertices[(i + 1) % num_vert]) for i in range(num_vert)]
all_left = not any(is_right)
all_right = all(is_right)
return all_left or all_right
vertices = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]
The following plot tests the code visually for several shapes. Note that for shapes with horizontal and vertical lines usual line equations could provoke division by zero.
import matplotlib.pyplot as plt
import numpy as np
vertices1 = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]
vertices2 = [(680000, 4872000), (680000, 4879000), (690000, 4879000), (690000, 4872000)]
vertices3 = [(655000, 4857000), (655000, 4875000), (665000, 4857000)]
k = np.arange(6)
r = 8000
vertices4 = np.vstack([690000 + r * np.cos(k * 2 * np.pi / 6), 4863000 + r * np.sin(k * 2 * np.pi / 6)]).T
all_shapes = [vertices1, vertices2, vertices3, vertices4]
for vertices in all_shapes:
plt.plot([x for x, y in vertices] + [vertices[0][0]], [y for x, y in vertices] + [vertices[0][1]], 'g-', lw=3)
for x, y in zip(np.random.randint(650000, 700000, 1000), np.random.randint(4855000, 4880000, 1000)):
color = 'turquoise'
for vertices in all_shapes:
if test_point(x, y, vertices):
color = 'tomato'
plt.plot(x, y, '.', color=color)
plt.gca().set_aspect('equal')
plt.show()
PS: In case you are running a 32-bit version of numpy, with this size of integers it might be necessary to convert the values to float to avoid overflow.
If this calculation needs to happen very often, the a,b,c values can be precalculated and stored. If the direction of the edges is known, only one of all_left or all_right is needed.
When the shape is fixed, a text version of the function can be generated:
def generate_test_function(vertices, is_clockwise=True, function_name='test_function'):
ext_vert = list(vertices) + [vertices[0]]
unequality_sign = '>=' if is_clockwise else '<='
print(f'def {function_name}(x, y):')
parts = []
for (x0, y0), (x1, y1) in zip(ext_vert[:-1], ext_vert[1:]):
a = float(y1 - y0)
b = float(x0 - x1)
c = a * x0 + b * y0
parts.append(f'({a}*x + {b}*y {unequality_sign} {c})')
print(' return', ' and '.join(parts))
vertices = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]
generate_test_function(vertices)
This would generate a function as:
def test_function(x, y):
return (-20205.0*x + -6968.0*y >= -47543270741.0) and (-2364.0*x + 6853.0*y >= 31699798882.0) and (20206.0*x + 6968.0*y >= 47389003912.0) and (2363.0*x + -6853.0*y >= -31855406372.0)
This function then can be copy-pasted and optimized by the Jython compiler. Note that the shape doesn't need to be rectangular. Any convex shape will do, allowing to use a tighter box.
Take three consequent vertices A, B, C (your 1,2,3)
Find lengths of sides AB and BC
lAB = sqrt((B.x - A.x)^2+(B.y - A.y)^2)
Get unit (normalized) direction vectors
uAB = ((B.x - A.x) / lAB, (B.y - A.y) / lAB)
For tested point P get vector BP
BP = ((P.x - B.x), (P.y - B.y))
And calculate signed distances from sides to point using cross product
SignedDistABP = Cross(BP, uAB) = BP.x * uAB.y - BP.y * uAB.x
SignedDistBCP = - Cross(BP, uBC) = - BP.x * uBC.y + BP.y * uBC.x
For points inside rectangle both distances should have the same sign - either negative or positive depending on vertices order (CW or CCW), and their absolute values should not be larger than lBC and lAB correspondingly
Abs(SignedDistABP) <= lBC
Abs(SignedDistBCP) <= lAB
As the shape is an exact rectangle, the easiest is to rotate all points by the angle
-arctan((4859302-4856938)/(677241-670388))
Doing so, the rectangle becomes axis-aligned and you just have to perform four coordinate comparisons. Rotations are easy to compute with complex numbers.
In fact you can simply represent all points as complex numbers, compute the vector defined by some side, and multiply everything by the conjugate.
A slightly different approach is to consider the change of coordinate frame that brings some corner to the origin and two incident sides to (1,0) and (0,1). This is an affine transformation. Then your test boils down to checking insideness to the unit square.
(x - x0)^2/a^2 + (y - y0)^2/b^2 == 1
where (x0, y0) is the centre of the ellipse.
the centre point (x0, y0) is randomly located in the region [SIZE/4, 3*SIZE/4]x[SIZE/4, 3*SIZE/4],
and a, b in the interval [SIZE/4, SIZE/2] so that in most cases the complete curves can lie in figure.
I have to print out an ellipse on a X-Y axis of Size 15(0 to 14), using the given information.
I am using cout to print '.' on the entire graph, and have to print an ellipse using the given dimensions only on that portion of the graph using 'E'.
I have to use the equation to test which points are inside or outside of the curve.
I have to find the closest points to the curve.
For example, I can start from the center (x0, y0) and keep moving up until
I find a point (x0, y1), such as:
(x0 - x0)^2/a^2 + (y1 - y0)^2/b^2 >= 1
(x0 - x0)^2/a^2 + ((y1 - 1)- y0)^2/b^2 <= 1
Then (x0, y1 - 1) and (x0, y1) are candidates which are points closest to the curve on the line x = x0.
Not going to give you a full solution, but I think I see the general problem you are having. My comments were just some brainstorming. This is how I would actually approach it:
As you are using cout to print ., you should use a
std::array<std::string,N_ROWS> grid;
Initialize it properly with strings of correct size, and use a function std::pair<double,double> get_coordinates(int x,int y) to transform between discrete coordinates and "world coordinates".
To know if a grid point is part of the ellipsis or not, you have to realize that for a discrete grid, the equation
(x - x0)^2/a^2 + (y - y0)^2/b^2 == 1
will never be satisfied exactly for some grid coordinates x_i,y_i. However, this inequality:
(x - x0)^2/a^2 + (y - y0)^2/b^2 < 1
tells you if a point (x,y) is inside the area of the ellipsis. And if you consider the four neighboring points:
x_i,y_i
x_i+1,y_i
x_i,y_i+1
x_i+1,y_i+1
then eiter
all of them are inside -> grid[x_i][y_i] is inside of the ellipsis
none of them is inside -> grid[x_i][y_i] is not inside of the ellipsis
some are inside, some are outside -> grid[x_i][y_i] is part of the ellipsis, ie it gets a .
(In the last step i used grid[x_i][y_i] which should rather be grid[x_i + 1/2][y_i + 1/2], however this just shifts the whole grid by half a pixel and should not matter too much)
PS: in the meantime the question has been edited. It isnt perfeclty clear what is part of the requirements and what is part of your solution, so I don't know if this answer is of any help. I'll just leave it here...
First create a class to hold a Point:
struct Point
{
const int x;
const int y;
};
Next create a class to hold ellipse parameters and to check if a point is on that ellipse (or inside it):
struct Ellipse
{
const Point center;
const int a;
const int b;
bool is_on_ellipse(Point p) const
{
return std::pow(p.x - center.x, 2) / std::pow(a, 2) + std::pow(p.y - center.y, 2) / std::pow(b, 2) == 1.0;
}
bool is_inside_ellipse(Point p) const
{
return std::pow(p.x - center.x, 2) / std::pow(a, 2) + std::pow(p.y - center.y, 2) / std::pow(b, 2) < 1.0;
}
};
Then you can create an ellipse like this:
Ellipse e = {{0, 0}, 2, 3};
And check if the point is on the ellipse by calling: e.is_on_ellipse({px, py})
With C++20 it can be a little more verbose (but easier to verify correctness) :
Ellipse e = {.center = {.x = 0, .y = 0}, .a = 2, .b = 3};
//....
e.is_on_ellipse({.x = px, .y = py})
I have a hexagon grid:
with template type coordinates T. How I can calculate distance between two hexagons?
For example:
dist((3,3), (5,5)) = 3
dist((1,2), (1,4)) = 2
First apply the transform (y, x) |-> (u, v) = (x, y + floor(x / 2)).
Now the facial adjacency looks like
0 1 2 3
0*-*-*-*
|\|\|\|
1*-*-*-*
|\|\|\|
2*-*-*-*
Let the points be (u1, v1) and (u2, v2). Let du = u2 - u1 and dv = v2 - v1. The distance is
if du and dv have the same sign: max(|du|, |dv|), by using the diagonals
if du and dv have different signs: |du| + |dv|, because the diagonals are unproductive
In Python:
def dist(p1, p2):
y1, x1 = p1
y2, x2 = p2
du = x2 - x1
dv = (y2 + x2 // 2) - (y1 + x1 // 2)
return max(abs(du), abs(dv)) if ((du >= 0 and dv >= 0) or (du < 0 and dv < 0)) else abs(du) + abs(dv)
Posting here after I saw a blog post of mine had gotten referral traffic from another answer here. It got voted down, rightly so, because it was incorrect; but it was a mischaracterization of the solution put forth in my post.
Your 'squiggly' axis - in terms of your x coordinate being displaced every other row - is going to cause you all sorts of headaches with trying to determine distances or doing pathfinding later on, if this is for a game of some sort. Hexagon grids lend themselves to three axes naturally, and a 'squared off' grid of hexagons will optimally have some negative coordinates, which allows for simpler math around distances.
Here's a grid with (x,y) mapped out, with x increasing to the lower right, and y increasing upwards.
By straightening things out, the third axis becomes obvious.
The neat thing about this, is that the three coordinates become interlinked - the sum of all three coordinates will always be 0.
With such a consistent coordinate system, the atomic distance between any two hexes is the largest change between the three coordinates, or:
d = max( abs(x1 - x2), abs(y1 -y2), abs( (-x1 + -y1) - (-x2 + -y2) )
Pretty straightforward. But you must fix your grid first!
The correct explicit formula for the distance, with your coordinate system, is given by:
d((x1,y1),(x2,y2)) = max( abs(x1 - x2),
abs((y1 + floor(x1/2)) - (y2 + floor(x2/2)))
)
Here is what a did:
Taking one cell as center (it is easy to see if you choose 0,0), cells at distance dY form a big hexagon (with “radius” dY). One vertices of this hexagon is (dY2,dY). If dX<=dY2 the path is a zig-zag to the ram of the big hexagon with a distance dY. If not, then the path is the “diagonal” to the vertices, plus an vertical path from the vertices to the second cell, with add dX-dY2 cells.
Maybe better to understand: led:
dX = abs(x1 - x2);
dY = abs(y1 - y2);
dY2= floor((abs(y1 - y2) + (y1+1)%2 ) / 2);
Then:
d = d((x1,y1),(x2,y2))
= dX < dY2 ? dY : dY + dX-dY2 + y1%2 * dY%2
First, you need to transform your coordinates to a "mathematical" coordinate system. Every two columns you shift your coordinates by 1 unit in the y-direction. The "mathamatical" coordinates (s, t) can be calculated from your coordinates (u,v) as follows:
s = u + floor(v/2)
t = v
If you call one side of your hexagons a, the basis vectors of your coordinate system are (0, -sqrt(3)a) and (3a/2, sqrt(3)a/2). To find the minimum distance between your points, you need to calculate the manhattan distance in your coordinate system, which is given by |s1-s2|+|t1-t2| where s and t are the coordinates in your system. The manhattan distance only covers walking in the direction of your basis vectors so it only covers walking like that: |/ but not walking like that: |\. You need to transform your vectors into another coordinate system with basis vectors (0, -sqrt(3)a) and (3a/2, -sqrt(3)a/2). The coordinates in this system are given by s'=s-t and t'=t so the manhattan distance in this coordinate system is given by |s1'-s2'|+|t1'-t2'|. The distance you are looking for is the minimum of the two calculated manhattan distances. Your code would look like this:
struct point
{
int u;
int v;
}
int dist(point const & p, point const & q)
{
int const ps = p.u + (p.v / 2); // integer division!
int const pt = p.v;
int const qs = q.u + (q.v / 2);
int const qt = q.v;
int const dist1 = abs(ps - qs) + abs(pt - qt);
int const dist2 = abs((ps - pt) - (qs - qt)) + abs(pt - qt);
return std::min(dist1, dist2);
}
(odd-r)(without z, only x,y)
I saw some problems with realizations above. Sorry, I didn't check it all but. But maybe my solution will be helpful for someone and maybe it's a bad and not optimized solution.
The main idea to go by diagonal and then by horizontal. But for that we need to note:
1) For example, we have 0;3 (x1=0;y1=3) and to go to the y2=6 we can handle within 6 steps to each point (0-6;6)
so: 0-left_border , 6-right_border
2)Calculate some offsets
#include <iostream>
#include <cmath>
int main()
{
//while(true){
int x1,y1,x2,y2;
std::cin>>x1>>y1;
std::cin>>x2>>y2;
int diff_y=y2-y1; //only up-> bottom no need abs
int left_x,right_x;
int path;
if( y1>y2 ) { // if Down->Up then swap
int temp_y=y1;
y1=y2;
y2=temp_y;
//
int temp_x=x1;
x1=x2;
x2=temp_x;
} // so now we have Up->Down
// Note that it's odd-r horizontal layout
//OF - Offset Line (y%2==1)
//NOF -Not Offset Line (y%2==0)
if( y1%2==1 && y2%2==0 ){ //OF ->NOF
left_x = x1 - ( (y2 - y1 + 1)/2 -1 ); //UP->DOWN no need abs
right_x = x1 + (y2 - y1 + 1)/2; //UP->DOWN no need abs
}
else if( y1%2==0 && y2%2==1 ){ // OF->NOF
left_x = x1 - (y2 - y1 + 1)/2; //UP->DOWN no need abs
right_x = x1 + ( (y2 - y1 + 1)/2 -1 ); //UP->DOWN no need abs
}
else{
left_x = x1 - (y2 - y1 + 1)/2; //UP->DOWN no need abs
right_x = x1 + (y2 - y1 + 1)/2; //UP->DOWN no need abs
}
/////////////////////////////////////////////////////////////
if( x2>=left_x && x2<=right_x ){
path = y2 - y1;
}
else {
int min_1 = std::abs( left_x - x2 );
int min_2 = std::abs( right_x - x2 );
path = y2 - y1 + std::min(min_1, min_2);
}
std::cout<<"Path: "<<path<<"\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n";
//}
return 0;
}
I believe the answer you seek is:
d((x1,y1),(x2,y2))=max(abs(x1-x2),abs(y1-y2));
You can find a good explanation on hexagonal grid coordinate-system/distances here:
http://keekerdc.com/2011/03/hexagon-grids-coordinate-systems-and-distance-calculations/
I have a class tetronimo (a tetris block) that has four QRect types (named first, second, third, fourth respectively). I draw each tetronimo using a build_tetronimo_L type functions.
These build the tetronimo in a certain direction, but as in tetris you're supposed to be able to rotate the tetronimo's, I'm trying to rotate a tetronimo by rotating each individual square of the tetronimo.
I have found the following formula to apply to each (x, y) coordinate of a particular square.
newx = cos(angle) * oldx - sin(angle) * oldy
newy = sin(angle) * oldx + cos(angle) * oldy
Now, the QRect type of Qt, does only seem to have a setCoords function that takes the (x, y) coordinates of top-left and bottom-right points of the respective square.
I have here an example (which doesn't seem to produce the correct result) of rotating the first two squares in my tetronimo.
Can anyone tell me how I'm supposed to rotate these squares correctly, using runtime rotation calculation?
void tetromino::rotate(double angle) // angle in degrees
{
std::map<std::string, rect_coords> coords = get_coordinates();
// FIRST SQUARE
rect_coords first_coords = coords["first"];
//top left x and y
int newx_first_tl = (cos(to_radians(angle)) * first_coords.top_left_x) - (sin(to_radians(angle)) * first_coords.top_left_y);
int newy_first_tl = (sin(to_radians(angle)) * first_coords.top_left_x) + (cos(to_radians(angle)) * first_coords.top_left_y);
//bottom right x and y
int newx_first_bl = (cos(to_radians(angle)) * first_coords.bottom_right_x) - (sin(to_radians(angle)) * first_coords.bottom_right_y);
int newy_first_bl = (cos(to_radians(angle)) * first_coords.bottom_right_x) + (sin(to_radians(angle)) * first_coords.bottom_right_y);
//CHANGE COORDINATES
first->setCoords( newx_first_tl, newy_first_tl, newx_first_tl + tetro_size,newy_first_tl - tetro_size);
//SECOND SQUARE
rect_coords second_coords = coords["second"];
int newx_second_tl = (cos(to_radians(angle)) * second_coords.top_left_x) - (sin(to_radians(angle)) * second_coords.top_left_y);
int newy_second_tl = (sin(to_radians(angle)) * second_coords.top_left_x) + (cos(to_radians(angle)) * second_coords.top_left_y);
//CHANGE COORDINATES
second->setCoords(newx_second_tl, newy_second_tl, newx_second_tl - tetro_size, newy_second_tl + tetro_size);
first and second are QRect types. rect_coords is just a struct with four ints in it, that store the coordinates of the squares.
The first square and second square calculations are different, as I was playing around trying to figure it out.
I hope someone can help me figure this out?
(Yes, I can do this much simpler, but I'm trying to learn from this)
It seems more like a math question than a programming question. Just plug in values like 90 degrees for the angle to figure this out. For 90 degrees, a point (x,y) is mapped to (-y, x). You probably don't want to rotate around the origin but around a certain pivot point c.x, c.y. For that you need to translate first, then rotate, then translate back:
(x,y) := (x-c.x, y-c.y) // translate into coo system w/ origin at c
(x,y) := (-y, x) // rotate
(x,y) := (x+c.x, y+c.y) // translate into original coo system
Before rotating you have to translate so that the piece is centered in the origin:
Translate your block centering it to 0, 0
Rotate the block
Translate again the center of the block to x, y
If you rotate without translating you will rotate always around 0, 0 but since the block is not centered it will be rotated around the center. To center your block is quite simple:
For each point, compute the median of X and Y, let's call it m
Subtract m.X and m.Y to the coordinates of all points
Rotate
Add again m.X and m.Y to points.
Of course you can use linear algebra and vector * matrix multiplication but maybe it is too much :)
Translation
Let's say we have a segment with coordinates A(3,5) B(10,15).
If you want to rotate it around its center, we first translate it to our origin. Let's compute mx and my:
mx = (10 - 3) / 2
my = (15 - 5) / 2
Now we compute points A1 and B1 translating the segment so it is centered to the origin:
A1(A.X - mx, A.Y - my)
B1(B.X - mx, B.Y - my)
Now we can perform our rotation of A1 and B1 (you know how).
Then we have to translate again to the original position:
A = (rotatedA1.X + mx, rotatedA1.y + my)
B = (rotatedB1.X + mx, rotatedB1.y + my)
If instead of having two points you have n points you have of course do everything for n points.
You could use Qt Graphics View which does all the geometric calculations for you.
Or are you just wanting to learn basic linear geometrical transformations? Then reading a math textbook would probably be more appropriate than coding.
I'm creating a 2D game and want to test for collision between an OBB (Oriented Bounding Box) and a Circle. I'm unsure of the maths and code to do this. I'm creating the game in c++ and opengl.
Since both your shapes are convex, you can use the Separating Axis Theorem. Here's a tutorial on how to implement an algorithm to do this.
Essentially, you try to find if it's possible to put a line somewhere that's between the two shapes and if you can't find one, then you know they're colliding.
References and general answer taken from this question.
Here's what I would do, in pseudocode:
function does_line_go_through_circle (original_line, circle_centerpoint, radius):
original_slope = get_slope_of_line (original_line)
perpendicular_slope = 1/original_slope
perpendicular_line = create_line_with_slope_through_point (perpendicular_slope, circle_centerpoint)
intersect_point = intersection_of_infinite_lines (perpendicular_line, original_line)
if point_is_on_line (intersect_point, original_line):
finite_line_along_radius = create_finite_line_between_points (circle_centerpoint, intersect_point)
if length_of_line (finite_line_along_radius) < length_of_line (radius):
return true
end
end
return false
end
function does_box_intersect_with_circle (bounding_box, circle):
for each side in bounding_box:
if does_line_go_through_circle (side, circle.center, circle.radius):
return true
end
end
return false
end
Keep in mind, I'm a little rusty on this stuff, I might be wrong.
Anyway, it should be trivial to implement this in C++.
We will divide the rectangle into 4 finite lines.
We can construct the line equation ax + by + c = 0 connecting the points (x1, y1) and (x2, y2) as follows:
mx - y + c = 0
where m = (y2-y1)/(x2-x1)
Shortest (perpendicular) distance from a line ax + by + c = 0 to a point (xc, yc) is given by the expression:
d = (a*xc + b*yc + c) / sqrt(a*a + b*b)
or d = (m*xc - yc + c) / sqrt(m*m + 1) according the the above equation
For infinite lines, you can check if 'd' is less than the radius of the circle.
But for finite lines, you'd also have to make sure whether the point of contact is within the line.
Now m = tan(angle). You can have
cos = 1 / sqrt(m*m + 1); sin = m / sqrt(m*m + 1)
Then you can calculate the point of contact as
xp = xc + d*cos; yp = yc + d*sin
And to check whether (xp, yp) lies in between the points connecting line, you can do a simple check as
if(x1 < xp < x2 && y1 < yp < y2)
return true
depending upon which is greater among x1 x2 and y1 y2.
You can repeat the same algorithm for all the four lines of a rectangle by passing the points.
Please correct me if I'm wrong somewhere.