Last active
May 2, 2016 01:29
-
-
Save jonvaldes/6065213 to your computer and use it in GitHub Desktop.
Function that generates a discrete spiral from consecutive numbers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Needed a function would spiral out to cover a tiled world, then remembered these posts by John Carmack: | |
* https://twitter.com/ID_AA_Carmack/status/308678512099852288 | |
* https://twitter.com/ID_AA_Carmack/status/308676302079160320 | |
Derived the solution with an initial implementation that uses several arrays to calculate positions, | |
then removed those array values for a more compact implementation. There's probably still room for | |
improvement, though. | |
It uses uniform initialisation from c++11, so you'll have to enable compiler support for it if you want | |
to try it out. | |
Program output: | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256, -1, | |
-1, -1,239,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,257, -1, | |
-1, -1,238,181,132,133,134,135,136,137,138,139,140,141,142,143,144,197,258, -1, | |
-1, -1,237,180,131, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,145,198,259, -1, | |
-1, -1,236,179,130, 89, 56, 57, 58, 59, 60, 61, 62, 63, 64,101,146,199,260, -1, | |
-1, -1,235,178,129, 88, 55, 30, 31, 32, 33, 34, 35, 36, 65,102,147,200,261, -1, | |
-1,299,234,177,128, 87, 54, 29, 12, 13, 14, 15, 16, 37, 66,103,148,201,262, -1, | |
-1,298,233,176,127, 86, 53, 28, 11, 2, 3, 4, 17, 38, 67,104,149,202,263, -1, | |
-1,297,232,175,126, 85, 52, 27, 10, 1, 0, 5, 18, 39, 68,105,150,203,264, -1, | |
-1,296,231,174,125, 84, 51, 26, 9, 8, 7, 6, 19, 40, 69,106,151,204,265, -1, | |
-1,295,230,173,124, 83, 50, 25, 24, 23, 22, 21, 20, 41, 70,107,152,205,266, -1, | |
-1,294,229,172,123, 82, 49, 48, 47, 46, 45, 44, 43, 42, 71,108,153,206,267, -1, | |
-1,293,228,171,122, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72,109,154,207,268, -1, | |
-1,292,227,170,121,120,119,118,117,116,115,114,113,112,111,110,155,208,269, -1, | |
-1,291,226,169,168,167,166,165,164,163,162,161,160,159,158,157,156,209,270, -1, | |
-1,290,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,271, -1, | |
-1,289,288,287,286,285,284,283,282,281,280,279,278,277,276,275,274,273,272, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
struct ivec2 | |
{ | |
int x ,y ; | |
ivec2 operator*(int i) const { return {x*i,y*i}; } | |
ivec2 operator+(const ivec2 &other) const { return {x + other.x, y+other.y} ; } | |
}; | |
/* | |
// Original implementation, easier to understand | |
ivec2 pointOnDiscreteSpiral( int x ) | |
{ | |
const int n = ceil(sqrtf(4*x+1)*0.5f-1 + 0.5f) ; // 'n' is side-length of L-shaped spiral block | |
const int even = !(n & 0x1) ; // Odd-length blocks are bottom-left, even-length blocks are top-right | |
const int lastx = n*(n+1) ; // Which was the last tile covered by block of length n? | |
const int firstBlockSection = ( lastx - x < n ) ; // Each block has two sections. First sections are top and bottom, second sections are left and right | |
const int sectionIdx = even * 2 + firstBlockSection ; // 0:bottom , 1:left , 2: top , 3 :right | |
const ivec2 startPositions[4] = { {0,0},{-1,0},{0,0},{0,0} } ; | |
const ivec2 spreadDirs[4] = { {1,-1},{-1,-1},{-1,1},{1,1} } ; | |
const ivec2 directions[4] = { {-1,0},{0,1},{1,0},{0,-1} } ; | |
const ivec2 startPos = startPositions[sectionIdx] + spreadDirs[sectionIdx] * (n/2); | |
const int offset = x - (lastx - 2*n) - firstBlockSection*n ; | |
return startPos + directions[sectionIdx] * offset ; | |
} | |
*/ | |
// Compact implementation | |
ivec2 pointOnDiscreteSpiral( int x ) | |
{ | |
const int n = ceil(sqrtf(4*x+1)*0.5f-1 + 0.5f) ; | |
const int even = !(n & 0x1) ; | |
const int lastx = n*(n+1) ; | |
const int firstBlockSection = ( lastx - x < n ) ; | |
const int topLeft = 2*(even ^ firstBlockSection) - 1 ; | |
const ivec2 spreadDir{ -topLeft , 2*even - 1 } ; | |
const ivec2 startPos = ivec2{ 0 - (!even && firstBlockSection) ,0} + spreadDir * (n/2) ; | |
const ivec2 dir = ivec2{!firstBlockSection, firstBlockSection} * topLeft ; | |
const int offset = x - lastx + 2*n - firstBlockSection * n ; | |
return startPos + dir * offset ; | |
} | |
int main() | |
{ | |
const int RADIUS= 10; | |
const int ARR_SIZE = RADIUS*2 ; | |
int arr[ARR_SIZE*ARR_SIZE]; | |
memset(arr , -1 , sizeof(int) * ARR_SIZE*ARR_SIZE) ; | |
for( int i = 0 ; i< 300 ; ++i ){ | |
ivec2 pos = pointOnDiscreteSpiral(i); | |
arr[(pos.x+RADIUS) + (pos.y+RADIUS) * ARR_SIZE] = i ; | |
} | |
for( int y = ARR_SIZE -1 ; y >= 0 ; y-- ) { | |
for( int x = 0 ; x < ARR_SIZE ; ++x ) { | |
printf( "%3d,%s" , arr[x + y*ARR_SIZE] , x==ARR_SIZE-1?"\n":"" ) ; | |
} | |
} | |
return 0 ; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment