I'm making a hexagon tile based game, which generates a honeycomb arrangement of hexagons in a spiral shape. My goal is to convert a world position (e.g. a mouse click) to a spiral coordinate (index / layer / position around layer). I can't think of how to do this, so instead I've been looking to simplify the problem by converting to/from axial coordinates first. How can this be done?
My configuration is pointy-topped as follows:
And here are the spiral indexes:
Axial Coordinates for reference:
I already have these equations for spiral coordinates:
const Layer = (index: number): number => {
return Math.floor((3 + Math.sqrt(12 * index - 3)) / 6);
}
const Position = (index: number, layer: number): number => {
return index - 3 * layer * (layer - 1) - 1;
}
const Index = (layer: number, position: number): number => {
return 3 * layer * (layer - 1) + 1 + position;
}
You can check the individual cases as follows. Here L is layer, P is position, (x, y) are the axial coordinates; coordinates are in agreement with your images.
From (L, P) to (x, y):
if L == 0:
return (0,0)
otherwise:
k = floor(P/L) mod 6
j = P mod L
k x y z
-------------------
0 j L-j -L
1 L -j j-L
2 L-j -L j
3 -j j-L L
4 -L j L-j
5 j-L L -j
From (x, y) to (L, P):
z = -x-y
L = max(abs(x), abs(y), abs(z))
x y z P
------------------------
>=0 >=0 x
<0 <0 L - y
>=0 >=0 2L + z
<0 <0 3L - x
>=0 >=0 4L + y
<0 <0 5L - z
Disclaimer: I didn't test this.
You may also be able to fold some of the cases by exploiting the symmetry, but coding these directly may be easier, albeit a bit more verbose.
You already coded the functions to go between (L, P) and spiral.
Related
I want to calculate the length of every full rotation of an Archimedean Spiral given the spacing between each arm and the total length are known. The closest to a solution I've been able to find is here, but this is for finding an unknown length.
I can't interpret math notation so am unable to extrapolate from the info in the link above. The closest I've been able to achieve is:
Distance between each spiral arm:
ArmSpace <- 7
Total length of spiral:
TotalLength <- 399.5238
Create empty df to accommodate TotalLength (note that sum(df[,2]) can be > TotalLength):
df <- data.frame(matrix(NA, nrow=0, ncol=2))
colnames(df) <- c("turn_num", "turn_len_m")
df[1,1] <- 0 # Start location of spiral
df[1,2] <- pi*1/1000
Return length of every turn:
i <- 0
while(i < TotalLength) {
df[nrow(df)+1,1] <- nrow(df) # Add turn number
df[nrow(df),2] <- pi*(df[nrow(df)-1,2] +
(2*df[nrow(df),1])*ArmSpace)/1000
i <- sum(df[,2])
}
An annotated example explaining the steps would be most appreciated.
I used approximation Clackson formula
t = 2 * Pi * Sqrt(2 * s / a)
to get theta angle corresponding to arc length s.
Example in Delphi, I hope idea is clear enough to implement in R
var
i, cx, cy, x, y: Integer;
s, t, a, r : Double;
begin
cx := 0;
cy := 0;
a := 10; //spiral size parameter
Canvas.MoveTo(cx, cy);
for i := 1 to 1000 do begin
s := 0.07 * i; //arc length
t := 2 * Pi * Sqrt(2 * s / a); //theta
r := a * t; //radius
x := Round(cx + r * cos(t)); //rounded coordinates
y := Round(cy + r * sin(t));
Memo1.Lines.Add(Format('len %5.3f theta %5.3f r %5.3f x %d y %d', [s, t, r, x, y]));
Canvas.LineTo(x, y);
if i mod 10 = 1 then //draw some points as small circles
Canvas.Ellipse(x-2, y-2, x+3, y+3);
end;
Some generated points
len 0.070 theta 0.743 r 7.434 x 5 y 5
len 0.140 theta 1.051 r 10.514 x 5 y 9
len 0.210 theta 1.288 r 12.877 x 4 y 12
len 0.280 theta 1.487 r 14.869 x 1 y 15
len 0.350 theta 1.662 r 16.624 x -2 y 17
len 0.420 theta 1.821 r 18.210 x -5 y 18
Link gives exact formula for ac length,
s(t) = 1/(2*a) * (t * Sqrt(1 + t*t) + ln(t + Sqrt(1+t*t)))
but we cannot calculate inverse (t for given s) using simple formula, so one need to apply numerical methods to find theta for arc length value.
Addition: length of k-th turn. Here we can use exact formula. Python code:
import math
def arch_sp_len(a, t):
return a/2 * (t * math.sqrt(1 + t*t) + math.log(t + math.sqrt(1+t*t)))
def arch_sp_turnlen(a, k):
return arch_sp_len(a, k*2*math.pi) - arch_sp_len(a, (k-1)*2*math.pi)
print(arch_sp_turnlen(1, 1))
print(arch_sp_turnlen(1, 2))
print(arch_sp_turnlen(10, 3))
I am trying to determine if a point lies between two bearings from a central point.
The diagram below attempts to explain things
I have a central point labelled A
I have two points (labelled B & C) which provide the boundaries of the search area (based on bearing only - there is no distance element required).
I'm trying to determine if point D is within the sector formed by A-B and A-C
I've calculated the bearings from A to each B & C
In my real scenario the angle created between the bearings can be anything from 0 to 360.
There are some similar questions & answers
however in my case I'm not interested in restricting my search to the radius of a circle. And there seems to be some implementation issues around angle size and the location of the points in terms of clockwise vs counter-clockwise
It seems so simple in theory but my maths is clearly not up to scratch :(
Any advice or pseudo-code would be greatly appreciated.
Here would be my approach:
calculate first bearing angle X
calculate second bearing angle Y
calculate angle Z towards point D
if X < Z < Y, return true; otherwise, return false
In your example it looks like you'd calculate Z ~ 90deg and find 45 < 90 < 135 (is your picture wrong? is says 315).
You can use something like the "atan2" function in whatever language you're using. This is an extension of the basic arctangent function which takes not just the slope but both the rise and run and instead of returning an angle from only a 180-degree range, it returns the true angle from a 360-degree range. So
Z = atan2(Dy, Dx)
Should give you the angle (possibly in radians; be careful) that you can compare to your bearings to tell whether you're inside the search. Note that the order of X and Y matter since the order is what defines which of the two sections is in the search area (X to Y gives ~90 deg in your picture, but Y to X gives ~270 deg).
You can calculate and compare the cross products of the vectors (AB X BD), and (AC X CD).
if (AB X BD) > 0, you have a counter clock wise turn
if (AC X CD) < 0, you have a clock wise turn
If both above tests are true, then the point D is in the sector BAC
This allows you to completely avoid using expensive trig functions.
class Point:
"""small class for point arithmetic convenience
"""
def __init__(self, x: float = 0, y: float = 0) -> None:
self.x = x
self.y = y
def __sub__(self, other: 'Point') -> 'Vector':
return Vector(self.x - other.x, self.y - other.y)
class Vector:
"""small class for vector arithmetic convenience
"""
def __init__(self, x: float = 0, y: float = 0) -> None:
self.x = x
self.y = y
def cross(self, other: 'Vector') -> float:
return (self.x * other.y) - (self.y * other.x)
def in_sector(A: Point, B: Point, C: Point, D: Point) -> bool:
# construct vectors:
ab = B - A
bd = D - B
ac = C - A
cd = D - C
print(f'ab x bc = {ab.cross(bd)}, ac x cd = {ac.cross(cd)}')
return ab.cross(bd) > 0 and ac.cross(cd) < 0
if __name__ == '__main__':
A = Point(0, 0)
B = Point(1, 1)
C = Point(-1, 1)
D = Point(0, 1)
print(f'D in sector ABC: {in_sector(A, B, C, D)}', end='\n\n')
print(f'D in sector ACB: {in_sector(A, C, B, D)}') # inverting the sector definition so D is now outside
Output:
ab x bc = 1, ac x cd = -1
D in sector ABC: True
ab x bc = -1, ac x cd = 1
D in sector ACB: False
This question already has answers here:
Knight's Shortest Path on Chessboard
(18 answers)
Closed 8 years ago.
Is there a mathematical formula one can use to compute the minimum number of knight moves to get between two points in a infinite 2D grid? I can figure it out using a breadth-first search, but is there a closed-form expression we can use instead?
Thanks!
I dont think there is one formula that generates the minimum distands for all pairs of points.
But for some special points there are.
Let A,B be points on a 2D - Grid with A = (0,0) and B = (x,y) and dist(x,y) the minimum number of knight moves.
First of all, the distance is symmetric:
dist(x,y) = dist(-x,y) = dist(x,-y) = dist(-x,-y) = dist(y,x)
Case: 2x=y -> dist(x,2x) = x
Case: x = 0
Subcase 1: y = 4k (k is a natural number)
-> dist(x,y) = 2k
Subcase 2: y = 4k+1 or y = 4k+3
-> dist(x,y) = 2k + 3
Subcase 3: y = 4k+2
-> dist(x,y) = 2k + 2
Case: x = y
Subcase 1: x = 3k (k is a natural number)
-> dist(x,y) = 2k
Subcase 2: x = 3k+1
-> dist(x,y) = 2k + 2
Subcase 3: y = 3k+2
-> dist(x,y) = 2k + 4
If B (with 0 <= x <= y) fits in no case, you know at least
dist(x,y) <= dist(x-k,y-2k) + dist(k,2k) = dist(0,y-2k) + k
and
dist(x,y) <= dist(x-z,y-z) + dist(z,z) = dist(0,y-z) + dist(z,z)
EDIT:
I have thought about it a little more. I think the following algorithm computs the minimum moves (Maple Code):
dist := proc(x,y)
global d;
local temp;
if x < 0 then x:= -x; fi;
if y < 0 then y:= -y; fi;
if x > y then temp := x; x:= y; y:= temp; fi;
if y = 2*x then return x; fi;
if x = y then
if x mod 3 = 0 then return 2*(x/3); fi;
if x mod 3 = 1 then return 2+2*(x-1)/3 fi;
if x mod 3 = 1 then return 4+2*(x-2)/3 fi;
fi;
if x = 0 then
if y mod 4 = 0 then return y/2; fi;
if y mod 4 = 1 or y mod 4 = 3 then return 3+(y - (y mod 4))/2; fi;
if y mod 4 = 2 then return 2+(y-2)/2; fi;
fi;
if y > 2*x then
return dist(0,y-2*x) + dist(x,2*x);
else
return dist(2*x-y,2*x-y) + dist(y-x,2*(y-x));
fi;
end proc:
NOTE: this is only correct on a infinite 2D grid.
EDIT2: This (recursive) algorithm runs in O(1) (time and space) cause it has a constant number of O(1) operations and calls it self at most one more time.
EDIT3: I thought a littel further and I think this is also correkt on a finite 2D grid, if A or B are at least 1 row/column away from at least one border.
I have 3 angles a b c
a=315
b=20
c=45
ok so would like to know giving all three if b is in between a and c
i have the long way of doing this adding and subtracting that's seems to work. I would just like to get something smaller and maybe more efficient.
thanks
EDIT
Here is a picture what i am trying to say.
Ok I have angle L(currently 0) i add 45(or any angle) and subtract 45(or any angle) to get a and b (my view angle).
Now i need to know if the green dot is between a and b
(g> a || g > 0) && (g < b)
so in this picture only the top green dot will be true..
Sorry if I am not making my self clear my first language is not English
I had a similar problem. I got it. All the calculations are in degrees.
I needed to calculate id a gps location is inside a rectangle.
Or, I needed to see if an angle x is between angle check+r and angle check-r.
check-r<x<check+r.
If you need a<x<b, find the angle check in the middle of a and b and then the distance (r) of check from a or b.
The method normalize, changes the angles from -infinity...infinity to -180...180.
The method check, takes the arguments
x: the angle that we need to see if it is between the angles check-r and check+r.
check: the angle to check with.
r: the radius around angle check.
private static double normalize(double x) {
x = x % 360;
if (x>=180) {
return x-360;
}
if (x<-180) {
return x+360;
}
return x;
}
public static boolean check(double x, double check, double r) {
x = x - check;
x = normalize(x);
return x<r && x>-r;
}
1st off, every angle is between 2 other angles, what you're really asking is:
For given angles: a, b, and g, is g outside the reflex angle between a and b?
You can just go ahead and define a as the leftmost angle and b as the rightmost angle or you can solve for that, for example if either of these statements are true a is your leftmost angle:
a ≤ b ∧ b - a ≤ π
a > b ∧ a - b ≥ π
For simplicity let's say that your leftmost angle is l and your rightmost angle is r and you're trying to find if g is between them.
The problem here is the seem. There are essentially 3 positive cases that we're looking for:
l ≤ g ≤ r
l ≤ g ∧ r < l
g ≤ r ∧ r < l
If you're just defining a to be leftmost and b to be rightmost you're done here and your condition will look like:
a <= g && g <= b ||
a <= g && b < a ||
g <= b && b < a
If however you calculated the l and r you'll notice there is an optimization opportunity here in doing both processes at once. Your function will look like:
if(a <= b) {
if(b - a <= PI) {
return a <= g && g <= b;
} else {
return b <= g || g <= a;
}
} else {
if(a - b <= PI) {
return b <= g && g <= a;
} else {
return a <= g || g <= b;
}
}
Or if you need it you could expand into this nightmare condition:
a <= b ?
(b - a <= PI && a <= g && g <= b) || (b - a > PI && (b <= g || g <= a)) :
(a - b <= PI && b <= g && g <= a) || (a - b > PI && (a <= g || g <= b))
Note that all this math presumes that your input is in radians and in the range [0 : 2π].
Live Example
I personally had the same problem recently and found counterexamples for all the answers posted yet, so I will share my own approach.
Let a be the start angle and b the end angle and we are checking whether c is between them clockwise, that means when you go from a to b you must pass c. The approach of checking whether c is in the range from a to b gives you false positives when b is greater than a numerically. For example:
a=80°, b=320° and c=150°: a <= c <= b which would mean that c is between a and b.
But it isn't.
The approach that's working is to subtract 360 from b if it is greater than a and also subtract 360 from c if c is greater than a. Then check whether a <= c <= b.
In Java:
public static boolean isBetween(double c, double a, double b) {
if (b > a) b -= 360;
if (c > a) c -= 360;
return a <= c && c <= b;
}
This assumes that a, b and c are in range 0 to 360.
Some example:
isBetween(150, 80, 320) => false
isBetween(30, 80, 320) => true
isBetween(340, 80, 320) => true
isBetween(140, 0, 160) => true
isBetween(180, 0, 160) => false
There is an issue with the suggested solutions when handling negative angles (e.g. from=30 to=-29)
The suggested (kotlin) fix should be:
fun isBetween(from:Float,to:Float,check:Float,inclusive:Boolean = true):Boolean{
var a1 = to - from
a1 = (a1 + 180f).mod(360f) - 180f
if(a1<0f) a1+=360f
var a2 = check - from
a2 = (a2 + 180f).mod(360f) - 180f
if(a2<0f) a2+=360f
val between = if(inclusive) a2<=a1 else a2<a1 && a2>0f
println("$from->$to, $check, $between ($a1,$a2)")
return between }
Assuming a > c, you would actually use:
( b < a ) && ( b > c )
This is the same as checking if a value is between a lower and upper bound. Them being angles makes no difference, unless you want to take into account the fact that as you go round a circle, an angle of 405 is the same as an angle of 45. In which case you can just use a % 360 to get the angle betweeen 0 and 360.
I have a square bitmap of a circle and I want to compute the normals of all the pixels in that circle as if it were a sphere of radius 1:
The sphere/circle is centered in the bitmap.
What is the equation for this?
Don't know much about how people program 3D stuff, so I'll just give the pure math and hope it's useful.
Sphere of radius 1, centered on origin, is the set of points satisfying:
x2 + y2 + z2 = 1
We want the 3D coordinates of a point on the sphere where x and y are known. So, just solve for z:
z = ±sqrt(1 - x2 - y2).
Now, let us consider a unit vector pointing outward from the sphere. It's a unit sphere, so we can just use the vector from the origin to (x, y, z), which is, of course, <x, y, z>.
Now we want the equation of a plane tangent to the sphere at (x, y, z), but this will be using its own x, y, and z variables, so instead I'll make it tangent to the sphere at (x0, y0, z0). This is simply:
x0x + y0y + z0z = 1
Hope this helps.
(OP):
you mean something like:
const int R = 31, SZ = power_of_two(R*2);
std::vector<vec4_t> p;
for(int y=0; y<SZ; y++) {
for(int x=0; x<SZ; x++) {
const float rx = (float)(x-R)/R, ry = (float)(y-R)/R;
if(rx*rx+ry*ry > 1) { // outside sphere
p.push_back(vec4_t(0,0,0,0));
} else {
vec3_t normal(rx,sqrt(1.-rx*rx-ry*ry),ry);
p.push_back(vec4_t(normal,1));
}
}
}
It does make a nice spherical shading-like shading if I treat the normals as colours and blit it; is it right?
(TZ)
Sorry, I'm not familiar with those aspects of C++. Haven't used the language very much, nor recently.
This formula is often used for "fake-envmapping" effect.
double x = 2.0 * pixel_x / bitmap_size - 1.0;
double y = 2.0 * pixel_y / bitmap_size - 1.0;
double r2 = x*x + y*y;
if (r2 < 1)
{
// Inside the circle
double z = sqrt(1 - r2);
.. here the normal is (x, y, z) ...
}
Obviously you're limited to assuming all the points are on one half of the sphere or similar, because of the missing dimension. Past that, it's pretty simple.
The middle of the circle has a normal facing precisely in or out, perpendicular to the plane the circle is drawn on.
Each point on the edge of the circle is facing away from the middle, and thus you can calculate the normal for that.
For any point between the middle and the edge, you use the distance from the middle, and some simple trig (which eludes me at the moment). A lerp is roughly accurate at some points, but not quite what you need, since it's a curve. Simple curve though, and you know the beginning and end values, so figuring them out should only take a simple equation.
I think I get what you're trying to do: generate a grid of depth data for an image. Sort of like ray-tracing a sphere.
In that case, you want a Ray-Sphere Intersection test:
http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter1.htm
Your rays will be simple perpendicular rays, based off your U/V coordinates (times two, since your sphere has a diameter of 2). This will give you the front-facing points on the sphere.
From there, calculate normals as below (point - origin, the radius is already 1 unit).
Ripped off from the link above:
You have to combine two equations:
Ray: R(t) = R0 + t * Rd , t > 0 with R0 = [X0, Y0, Z0] and Rd = [Xd, Yd, Zd]
Sphere: S = the set of points[xs, ys, zs], where (xs - xc)2 + (ys - yc)2 + (zs - zc)2 = Sr2
To do this, calculate your ray (x * pixel / width, y * pixel / width, z: 1), then:
A = Xd^2 + Yd^2 + Zd^2
B = 2 * (Xd * (X0 - Xc) + Yd * (Y0 - Yc) + Zd * (Z0 - Zc))
C = (X0 - Xc)^2 + (Y0 - Yc)^2 + (Z0 - Zc)^2 - Sr^2
Plug into quadratic equation:
t0, t1 = (- B + (B^2 - 4*C)^1/2) / 2
Check discriminant (B^2 - 4*C), and if real root, the intersection is:
Ri = [xi, yi, zi] = [x0 + xd * ti , y0 + yd * ti, z0 + zd * ti]
And the surface normal is:
SN = [(xi - xc)/Sr, (yi - yc)/Sr, (zi - zc)/Sr]
Boiling it all down:
So, since we're talking unit values, and rays that point straight at Z (no x or y component), we can boil down these equations greatly:
Ray:
X0 = 2 * pixelX / width
Y0 = 2 * pixelY / height
Z0 = 0
Xd = 0
Yd = 0
Zd = 1
Sphere:
Xc = 1
Yc = 1
Zc = 1
Factors:
A = 1 (unit ray)
B
= 2 * (0 + 0 + (0 - 1))
= -2 (no x/y component)
C
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2 + (0 - 1) ^ 2 - 1
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2
Discriminant
= (-2) ^ 2 - 4 * 1 * C
= 4 - 4 * C
From here:
If discriminant < 0:
Z = ?, Normal = ?
Else:
t = (2 + (discriminant) ^ 1 / 2) / 2
If t < 0 (hopefully never or always the case)
t = -t
Then:
Z: t
Nx: Xi - 1
Ny: Yi - 1
Nz: t - 1
Boiled farther still:
Intuitively it looks like C (X^2 + Y^2) and the square-root are the most prominent figures here. If I had a better recollection of my math (in particular, transformations on exponents of sums), then I'd bet I could derive this down to what Tom Zych gave you. Since I can't, I'll just leave it as above.