Skip to content

Instantly share code, notes, and snippets.

@jonvaldes
Last active May 2, 2016 01:29
Show Gist options
  • Save jonvaldes/6065213 to your computer and use it in GitHub Desktop.
Save jonvaldes/6065213 to your computer and use it in GitHub Desktop.
Function that generates a discrete spiral from consecutive numbers.
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,
#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