Related
The problem is as follows:
Given two different points P1 = (x1,y1), P2 = (x2,y2) and point G=(a,b), find c and d such that G'=(c,d) is the reflection of G about the line P1P2
What I am looking for is a method to do this quickly. Since I am working on floating point numbers, I'd also like to use a method which minimizes the absolute value of the exponent in scientific notation, but that is second priority.
What I have tried: let R be the vector which is the projection of vector (G-P1) onto vector (P2-P1). Then, the reflection is achieved by taking Q = P1 + R, which is the projection of G onto the line, and then G' = 2Q-G. Now this is all cool and dandy, but calculating the projection is the hard part here.
How I calculate the projection of vector A onto B:
The scalar product of A and B is |A|*|B|*cos(theta), where theta is the directed angle from A to B. You can obtain the value of the scalar product by taking xAxB + yAyB. But the projection is of length |A|*cos(theta), so we have to divide the scalar product by |B|. Now, we have the length, but not the direction. The direction is along vector B, so we must multiply by the unit vector along B, which is B/|B|. Ultimately, we get the formula (xAxB + yAyB)*B/|B|2.
The actual problem:
This is kind of a roundabout way to do this, and I am looking for a more direct formula from the coordinates. Additionally (although less important), calculating the length of a vector as I need to do in computing the projection and scalar product is problematic, when the numbers I am working on are big, because I may get a floating point overflow or something like that.
If this is of any significance, I am working in OCaml.
Thanks in advance
Formulas are really very simple.
Projection (for line AB and point P) seems similar to yours:
L = A + AB * ScalarProduct(AB, AP) / ScalarProduct(AB, AB)
Reflection point
P' = P + 2*(L-P) = 2*L-P
Working Python example:
def refl(x1, y1, x2, y2, xp, yp):
x12 = x2 - x1
y12 = y2 - y1
xxp = xp - x1
yyp = yp - y1
dotp = x12 * xxp + y12 * yyp
dot12 = x12 * x12 + y12 * y12
coeff = dotp / dot12
lx = x1 + x12 * coeff
ly = y1 + y12 * coeff
return 2*lx-xp, 2*ly-yp
print(refl(0, 0, 2, 2, 0, 1))
>>> (1.0, 0.0)
For a square grid the euclidean distance between tile A and B is:
distance = sqrt(sqr(x1-x2)) + sqr(y1-y2))
For an actor constrained to move along a square grid, the Manhattan Distance is a better measure of actual distance we must travel:
manhattanDistance = abs(x1-x2) + abs(y1-y2))
How do I get the manhattan distance between two tiles in a hexagonal grid as illustrated with the red and blue lines below?
I once set up a hexagonal coordinate system in a game so that the y-axis was at a 60-degree angle to the x-axis. This avoids the odd-even row distinction.
(source: althenia.net)
The distance in this coordinate system is:
dx = x1 - x0
dy = y1 - y0
if sign(dx) == sign(dy)
abs(dx + dy)
else
max(abs(dx), abs(dy))
You can convert (x', y) from your coordinate system to (x, y) in this one using:
x = x' - floor(y/2)
So dx becomes:
dx = x1' - x0' - floor(y1/2) + floor(y0/2)
Careful with rounding when implementing this using integer division. In C for int y floor(y/2) is (y%2 ? y-1 : y)/2.
I assume that you want the Euclidean distance in the plane between the centers of two tiles that are identified as you showed in the figure. I think this can be derived from the figure. For any x and y, the vector from the center of tile (x, y) to the center of tile (x + dx, y) is (dx, 0). The vector from the center of tile (x, y) and (x, y + dy) is (-dy / 2, dy*sqrt(3) / 2). A simple vector addition gives a vector of (dx - (dy / 2), dy * sqrt(3) / 2) between (x, y) and (x + dx, y + dy) for any x, y, dx, and dy. The total distance is then the norm of the vector: sqrt((dx - (dy / 2)) ^ 2 + 3 * dy * dy / 4)
If you want the straight-line distance:
double dy = y2 - y1;
double dx = x2 - x1;
// if the height is odd
if ((int)dy & 1){
// whether the upper x coord is displaced left or right
// depends on whether the y1 coordinate is odd
dx += ((y1 & 1) ? -0.5 : 0.5);
}
double dis = sqrt(dx*dx + dy*dy);
What I'm trying to say is, if dy is even, it's just a rectangular space. If dy is odd, the position of the upper right corner is 1/2 unit to the left or to the right.
A straight forward answer for this question is not possible. The answer of this question is very much related to how you organize your tiles in the memory. I use odd-q vertical layout and with the following matlab code gives me the right answer always.
function f = offset_distance(x1,y1,x2,y2)
ac = offset_to_cube(x1,y1);
bc = offset_to_cube(x2,y2);
f = cube_distance(ac, bc);
end
function f = offset_to_cube(row,col)
%x = col - (row - (row&1)) / 2;
x = col - (row - mod(row,2)) / 2;
z = row;
y = -x-z;
f = [x,z,y];
end
function f= cube_distance(p1,p2)
a = abs( p1(1,1) - p2(1,1));
b = abs( p1(1,2) - p2(1,2));
c = abs( p1(1,3) - p2(1,3));
f = max([a,b,c]);
end
Here is a matlab testing code
sx = 6;
sy = 1;
for i = 0:7
for j = 0:5
k = offset_distance(sx,sy,i,j);
disp(['(',num2str(sx),',',num2str(sy),')->(',num2str(i),',',num2str(j),')=',num2str(k)])
end
end
For mathematical details of this solution visit: http://www.redblobgames.com/grids/hexagons/ . You can get a full hextile library at: http://www.redblobgames.com/grids/hexagons/implementation.html
This sounds like a job for the Bresenham line algorithm. You can use that to count the number of segments to get from A to B, and that will tell you the path distance.
If you define the different hexagons as a graph, you can get the shortest path from node A to node B. Since the distance from the hexagon centers is constant, set that as the edge weight.
This will probably be inefficient for large fields though.
This is a common interview question (according to some interview sites) but I can find no normal answers on the Internet - some are wrong and some point to complex theory I expect not to be required in an interview (like the Bresenham algorithm).
The question is simple:
The circle equation is: x2 + y2 = R2.
Given R, draw 0,0-centered circle as best as possible without using any
floating point (no trig, square roots, and so on, only integers)
Bresenham-like algorithms are probably the expected answer, and can be derived without "complex theory". Start from a point (x,y) on the circle: (R,0) and maintain the value d=x^2+y^2-R^2, initially 0. D is the squared distance from the current point to the circle. We increment Y, and decrement X as needed to keep D minimal:
// Discretize 1/8 circle:
x = R ; y = 0 ; d = 0
while x >= y
print (x,y)
// increment Y, D must be updated by (Y+1)^2 - Y^2 = 2*Y+1
d += (2*y+1) ; y++
// now if we decrement X, D will be updated by -2*X+1
// do it only if it keeps D closer to 0
if d >= 0
d += (-2*x+1) ; x--
Honestly, isn't the Midpoint circle algorithm enough? Just mirror it in all quadrants. And by all means no -- unless you're trying to get a job as a window application tester, Bresenham's Line Algorithm isn't complex theory.
From the second method on this page:
for each pixel, evaluate
x2+y2 and see if
it is in the range from
R2-R+1 to R2+R
inclusive. If so, color the pixel on
the screen, and if not, don't.
Further details and explanation given on the aforementioned page, but the crux is that you are looking for pixels that are a distance between R-0.5 and R+0.5 from the origin, so the distance squared is x2+y2 and the threshold distances squared are R2-R+0.25 and R2+R+0.25.
For other methods, Google "draw a circle using integer arithmetic only".
Pretty old question but I will try to provide the end solution with visual tests in python as an alternative to Bresenham's algorithm - the best and the shortest solution for this task. I think this idea also can have a place and perhaps is simpler to understand but needs more code. Someone maybe also end up with this solution.
The idea is based on the following facts:
Every point on circle lies on the same distance to circle central point
A circle contains 4 quadrant which starts and ends in points (r, 0), (2r, r), (r, 2r) and (0, r) if r is radius and central point is in (r, r) point.
A circle is a continues figure and every point can have 8 neighbor points. If move on circle in one direction only three points are interesting for us - 3 lie in opposite direction and 2 are too far from center. For example for point (r, 0) with direction to (2r, r) interesting points will be (r + 1, 1), (r, 1) and (r + 1, 0)
import matplotlib.pyplot as plt
from itertools import chain
def get_distance(x1, y1, x2, y2):
"""
Calculates squared distance between (x1, y1) and (x2, y2) points
"""
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
def get_next_point(x, y, dx, dy, cx, cy, r):
"""
Returns the next circle point base on base point (x, y),
direction (dx, dy), circle central point (cx, cy) and radius r
"""
r2 = r * r
# three possible points
x1, y1 = x + dx, y + dy
x2, y2 = x, y + dy
x3, y3 = x + dx, y
# calculate difference between possible point distances
# with central point and squared radius
dif1 = abs(get_distance(x1, y1, cx, cy) - r2)
dif2 = abs(get_distance(x2, y2, cx, cy) - r2)
dif3 = abs(get_distance(x3, y3, cx, cy) - r2)
# choosing the point with minimum distance difference
diff_min = min(dif1, dif2, dif3)
if diff_min == dif1:
return x1, y1
elif diff_min == dif2:
return x2, y2
else:
return x3, y3
def get_quadrant(bx, by, dx, dy, cx, cy, r):
"""
Returns circle quadrant starting from base point (bx, by),
direction (dx, dy), circle central point (cx, cy) and radius r
"""
x = bx
y = by
# maximum or minimum quadrant point (x, y) values
max_x = bx + dx * r
max_y = by + dy * r
# choosing only quadrant points
while (dx * (x - max_x) <= 0) and (dy * (y - max_y) <= 0):
x, y = get_next_point(x, y, dx, dy, cx, cy, r)
yield x, y
def get_circle(r, cx, cy):
"""
Returns circle points (list) with radius r and center point (cx, cy)
"""
north_east_quadrant = get_quadrant(cx, cy - r, 1, 1, cx, cy, r)
south_east_quadrant = get_quadrant(cx + r, cy, -1, 1, cx, cy, r)
south_west_quadrant = get_quadrant(cx, cy + r, -1, -1, cx, cy, r)
north_west_quadrant = get_quadrant(cy - r, cy, 1, -1, cx, cy, r)
return chain(north_east_quadrant, south_east_quadrant,
south_west_quadrant, north_west_quadrant)
# testing
r = 500
circle_points = get_circle(r, r, r)
for x, y in circle_points:
plt.plot([x], [y], marker='o', markersize=3, color="red")
plt.show()
I will use the Bresenham's Circle drawing algorithm or the Midpoint Circle drawing algorithm. Both produce the same coordinate points. And with the symmetry between the eight octants of the circle, we just need to generate one octant and reflect and copy it to all the other positions.
Here would be my interview answer (no research, this is on the spot)...
Set up two nested for loops that collectively loop over the square defined by {-R, -R, 2R, 2R}. For each pixel, calculate (i^2 + j^2) where i and j are your loop variables. If this is within some tolerance to R^2, then color that pixel black, if not then leave that pixel alone.
I'm too lazy to determine what that tolerance should be. You may need to store the last calculated value to zero-in on which pixel best represents the circle... But this basic method should work pretty well.
Has anyone considered they might be looking for a lateral answer such as "with a compass and pencil" or "use the inside of a roll of sellotape as a template".
Everyone assumes all problems have to be solved with a computer.
You can easily calculate the x in x^2= r^2- y^2 using the first order Taylor approximation
sqrt(u^2 + a) = u + a / 2u
This is a program for that in Mathematica (short, but perhaps not nice)
rad=87; (* Example *)
Calcy[r_,x_]:= (
y2 = rad^2 - x^2;
u = Ordering[Table[ Abs[n^2-y2], {n,1,y2}]] [[1]]; (* get the nearest perfect square*)
Return[ u-(u^2-y2)/(2 u) ]; (* return Taylor approx *)
)
lista = Flatten[Table[{h Calcy[rad, x], j x}, {x, 0, rad}, {h, {-1, 1}}, {j, {-1, 1}}], 2];
ListPlot[Union[lista, Map[Reverse, lista]], AspectRatio -> 1];
This is the result
Not too bad IMHO ... I don't know anything about graphic algorithms ...
I am trying to determine whether a line segment (i.e. between two points) intersects a sphere. I am not interested in the position of the intersection, just whether or not the segment intersects the sphere surface. Does anyone have any suggestions as to what the most efficient algorithm for this would be? (I'm wondering if there are any algorithms that are simpler than the usual ray-sphere intersection algorithms, since I'm not interested in the intersection position)
If you are only interested if knowing if it intersects or not then your basic algorithm will look like this...
Consider you have the vector of your ray line, A -> B.
You know that the shortest distance between this vector and the centre of the sphere occurs at the intersection of your ray vector and a vector which is at 90 degrees to this which passes through the centre of the sphere.
You hence have two vectors, the equations of which fully completely defined. You can work out the intersection point of the vectors using linear algebra, and hence the length of the line (or more efficiently the square of the length of the line) and test if this is less than the radius (or the square of the radius) of your sphere.
I don't know what the standard way of doing it is, but if you only want to know IF it intersects, here is what I would do.
General rule ... avoid doing sqrt() or other costly operations. When possible, deal with the square of the radius.
Determine if the starting point is inside the radius of the sphere. If you know that this is never the case, then skip this step. If you are inside, your ray will intersect the sphere.
From here on, your starting point is outside the sphere.
Now, imagine the small box that will fit sphere. If you are outside that box, check the x-direction, y-direction and z-direction of the ray to see if it will intersect the side of the box that your ray starts at. This should be a simple sign check, or comparison against zero. If you are outside the and moving away from it, you will never intersect it.
From here on, you are in the more complicated phase. Your starting point is between the imaginary box and the sphere. You can get a simplified expression using calculus and geometry.
The gist of what you want to do is determine if the shortest distance between your ray and the sphere is less than radius of the sphere.
Let your ray be represented by (x0 + it, y0 + jt, z0 + kt), and the centre of your sphere be at (xS, yS, zS). So, we want to find t such that it would give the shortest of (xS - x0 - it, yS - y0 - jt, zS - z0 - kt).
Let x = xS - x0, y = yX - y0, z = zS - z0, D = magnitude of the vector squared
D = x^2 -2*xit + (i*t)^2 + y^2 - 2*yjt + (j*t)^2 + z^2 - 2*zkt + (k*t)^2
D = (i^2 + j^2 + k^2)t^2 - (xi + yj + zk)*2*t + (x^2 + y^2 + z^2)
dD/dt = 0 = 2*t*(i^2 + j^2 + k^2) - 2*(xi + yj + z*k)
t = (xi + yj + z*k) / (i^2 + j^2 + k^2)
Plug t back into the equation for D = .... If the result is less than or equal the square of the sphere's radius, you have an intersection. If it is greater, then there is no intersection.
This page has an exact solution for this problem. Essentially, you are substituting the equation for the line into the equation for the sphere, then computes the discriminant of the resulting quadratic. The values of the discriminant indicate intersection.
Are you still looking for an answer 13 years later? Here is a complete and simple solution
Assume the following:
the line segment is defined by endpoints as 3D vectors v1 and v2
the sphere is centered at vc with radius r
Ne define the three side lengths of a triangle ABC as:
A = v1-vc
B = v2-vc
C = v1-v2
If |A| < r or |B| < r, then we're done; the line segment intersects the sphere
After doing the check above, if the angle between A and B is acute, then we're done; the line segment does not intersect the sphere.
If neither of these conditions are met, then the line segment may or may not intersect the sphere. To find out, we just need to find H, which is the height of the triangle ABC taking C as the base. First we need φ, the angle between A and C:
φ = arccos( dot(A,C) / (|A||C|) )
and then solve for H:
sin(φ) = H/|A|
===> H = |A|sin(φ) = |A| sqrt(1 - (dot(A,C) / (|A||C|))^2)
and we are done. The result is
if H < r, then the line segment intersects the sphere
if H = r, then the line segment is tangent to the sphere
if H > r, then the line segment does not intersect the sphere
Here that is in Python:
import numpy as np
def unit_projection(v1, v2):
'''takes the dot product between v1, v2 after normalization'''
u1 = v1 / np.linalg.norm(v1)
u2 = v2 / np.linalg.norm(v2)
return np.dot(u1, u2)
def angle_between(v1, v2):
'''computes the angle between vectors v1 and v2'''
return np.arccos(np.clip(unit_projection(v1, v2), -1, 1))
def check_intersects_sphere(xa, ya, za, xb, yb, zb, xc, yc, zc, radius):
'''checks if a line segment intersects a sphere'''
v1 = np.array([xa, ya, za])
v2 = np.array([xb, yb, zb])
vc = np.array([xc, yc, zc])
A = v1 - vc
B = v2 - vc
C = v1 - v2
if(np.linalg.norm(A) < radius or np.linalg.norm(B) < radius):
return True
if(angle_between(A, B) < np.pi/2):
return False
H = np.linalg.norm(A) * np.sqrt(1 - unit_projection(A, C)**2)
if(H < radius):
return True
if(H >= radius):
return False
Note that I have written this so that it returns False when either endpoint is on the surface of the sphere, or when the line segment is tangent to the sphere, because it serves my purposes better.
This might be essentially what user Cruachan suggested. A comment there suggests that other answers are "too elaborate". There might be a more elegant way to implement this that uses more compact linear algebra operations and identities, but I suspect that the amount of actual compute required boils down to something like this. If someone sees somewhere to save some effort please do let us know.
Here is a test of the code. The figure below shows several trial line segments originating from a position (-1, 1, 1) , with a unit sphere at (1,1,1). Blue line segments have intersected, red have not.
And here is another figure which verifies that line segments that stop just short of the sphere's surface do not intersect, even if the infinite ray that they belong to does:
Here is the code that generates the image:
import matplotlib.pyplot as plt
radius = 1
xc, yc, zc = 1, 1, 1
xa, ya, za = xc-2, yc, zc
nx, ny, nz = 4, 4, 4
xx = np.linspace(xc-2, xc+2, nx)
yy = np.linspace(yc-2, yc+2, ny)
zz = np.linspace(zc-2, zc+2, nz)
n = nx * ny * nz
XX, YY, ZZ = np.meshgrid(xx, yy, zz)
xb, yb, zb = np.ravel(XX), np.ravel(YY), np.ravel(ZZ)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(n):
if(xb[i] == xa): continue
intersects = check_intersects_sphere(xa, ya, za, xb[i], yb[i], zb[i], xc, yc, zc, radius)
color = ['r', 'b'][int(intersects)]
s = [0.3, 0.7][int(intersects)]
ax.plot([xa, xb[i]], [ya, yb[i]], [za, zb[i]], '-o', color=color, ms=s, lw=s, alpha=s/0.7)
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = np.outer(np.cos(u), np.sin(v)) + xc
y = np.outer(np.sin(u), np.sin(v)) + yc
z = np.outer(np.ones(np.size(u)), np.cos(v)) + zc
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='k', linewidth=0, alpha=0.25, zorder=0)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.tight_layout()
plt.show()
you sorta have to work that the position anyway if you want accuracy. The only way to improve speed algorithmically is to switch from ray-sphere intersection to ray-bounding-box intersection.
Or you could go deeper and try and improve sqrt and other inner function calls
http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
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