Related
The Question
Okay so basically what I'm trying to do, is calculating the Y location on a Cubic Curve / Bezier Curve / Spline when the X location is given.
I've searched everywhere on Stack Overflow and Google and I could find anything that actually worked!
The Curve Points
x1 = 50d;
y1 = 400d / 2d + 100d;
x2 = 400d;
y2 = 400d / 2d + 100d;
x3 = 600d - 400d;
y3 = 400d / 2d - 100d;
x4 = 600d - 50d;
y4 = 400d / 2d - 100d;
The reason that I calculate "600 - 400" and I don't just write "200" is because in my code "600" is actually the width of the window, which the Cubic Curve is rendered in. Thereby it actually says "width - 400" in my code.
So the following code after, can calculate the X & Y on a Cubic Curve when T is given!
t = 0.5d;
cx = 3d * (x2 - x1);
cy = 3d * (y2 - y1);
bx = 3d * (x3 - x2) - cx;
by = 3d * (y3 - y2) - cy;
ax = x4 - x1 - cx - bx;
ay = y4 - y1 - cy - by;
point_x = ax * (t * t * t) + bx * (t * t) + cx * t + x1;
point_y = ay * (t * t * t) + by * (t * t) + cy * t + y1;
So again, what I'm trying to calculate is the Y location of a Curve when you know an X location. But the only thing that I'm able to calculate is both an X & Y location on the Curve when T is given.
This is my first post, so if something isn't written 100% correctly, I apologize!
A cubic curve can have multiple 'y' values for an 'x' value, so you're going to have to perform root finding, after rotating the cubic curve so that it's aligned with the x/y axes. http://pomax.github.io/bezierinfo/#intersections covers this concept, but the idea is this:
take your curve, defined by points {p1,p2,p3,p4}, and take your line at x=X, defined by points {p5,p6} (where p5 is some coordinate (x,...) and p6 is another coordinate(x,...). The important part is that it's a vertical line, so both points have the same x value).
translate and rotate your cubic curve and your line, together, so that your line becomes horizontal, at new height y = 0.
you can now perform cubic root finding on the curve's y function. This may generate 0, 1, 2, or 3 distinct t values.
for each of these t values: in your normal, unrotated curve, compute the (x,y) coordinate given that t value. You will now have all the y values for a given x (all points will have the same x value, too).
In the article linked, have a look at the source for the cubic curve/line example, and to see how the rotation/rootfinding works, see the BezierCurve align(Point start, Point end) {... and float[] findAllRoots(int derivative, float[] values) {... functions. (especially note that after rotation, we're only finding the roots for the y-function! the x-function has become irrelevant for what we want to do)
If your formulas are correct, theoretically one might use Formula for cubic function roots to express t dependency from x. Then from x you find t, from t you find y.
I am given 2 points inside a circle. I have to find a point on the circle (not inside, not outside) so that the sum of the distances between the given 2 points and the point i found is minimum. I only have to find the minimum distance, not the position of the point.
It is a minimization problem:
minimize sqrt((x1-x0)^2 + (y1-y0)^2) + sqrt((x2-x0)^2 + (y2-y0)^2)
^ ^
(distance from point1) (distance from point 2)
subject to constraints:
x1 = C1
y1 = C2
x2 = C3
x4 = C4
x0^2 + y0^2 = r^2
(assuming the coordinates are already aligned to the center of the circle as (0,0)).
(C1,C2,C3,C4,r) are given constants, just need to assign them.
After assigning x1,y1,x2,y2 - you are given a minimization problem with 2 variables (x0,y0) and a constraint. The minimization problem can be solved using lagrange multiplier.
Let (x1, y1) and (x2, y2) be the points inside the circle, (x, y) be the point on the circle, r be the radius of the circle.
Your problem reduces to a Lagrange conditional extremum problem, namely:
extremum function:
f(x, y) = sqrt((x-x1)^2 + (y-y1)^2) + sqrt((x-x2)^2 + (y-y2)^2)
condition:
g(x, y) = x^2 + y^2 = r^2 (1)
Introduce an auxiliary function(reference):
Λ(x, y, λ) = f(x, y) + λ(g(x, y) - r^2)
Let ∂Λ / ∂x = 0, we have:
(x-x1)/sqrt((x-x1)^2 + (y-y1)^2) + (x-x2)/sqrt((x-x2)^2 + (y-y2)^2) + 2λx = 0 (2)
Let ∂Λ / ∂y = 0, we have:
(y-y1)/sqrt((x-x1)^2 + (y-y1)^2) + (y-y2)/sqrt((x-x2)^2 + (y-y2)^2) + 2λy = 0 (3)
Now we have 3 variables(i.e. x, y and λ) and 3 equations(i.e. (1), (2) and (3)), so it's solvable.
Please be noted that there should be two solutions(unless two inside points happen to be the center of the circle). One is the minimal value(which is what you need), the other is the maximal value(which will be ignored).
just another approach(which is more directly):
for simplicity, assume the circle is at (0,0) with radius r
and suppose two points are P1(x1,y1) and P2(x2,y2)
we can calculate the polar angle of these two points, suppose they are alpha1 and alpha2
obviously, the point which is on the circle and having the minimum sum of distance to P1 and P2 is within the circular sector composed of alpha1 and alpha2
meanwhile, the sum of distance between the point on the circle within that sector and P1 and P2 is a quadratic function. so, the minimum distance can be found by using trichotomy.
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