Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot rotate Koch snowflake

Tags:

c++

math

EDIT: I wasn't sure if this should be a new question or not so I am just updating this one for now. The snowflake is now being generated properly, unless I change the original coordinates. For example, if my original triangle is like picture 1, the result after 5 iterations is picture 2:

enter image description hereenter image description here

However, if my original triangle is anything different, for example picture 3, the results are skewed:

enter image description hereenter image description here

I again think the problem is with my normals but I am really lost. I have tried for hours to figure out the correct formulas to do this but I am not really making any progress. Since it seems that a lot of times the problem is with the new triangles being inverted, I suspect that atan is giving me a negative value when it should be giving me a positive value. Is there a mathematical way I can get around this? Thank you so much for your help!

My code (minus the openGL portions because I don't think they are the problem) is:

const int NumTimesToSubdivide = 5;
const int NumSegments = 3072;  // 3x4^n lines generated {3, 12, 48, 192, 768, 3072..}
const int NumVertices = NumSegments * 2; // 2 vertices for each segment

vec2 arrayA[NumVertices];
vec2 arrayB[NumVertices];

//----------------------------------------------------------------------------
void koch( const vec2 &a, const vec2 &b, vec2 * verts) {        

/* Calculate new segments.
 *         v1      
 *         /\
 *        /  \
 *       /    \
 * a----/      \----b
 *     v0      v2
 */
vec2 v0;
vec2 v1;
vec2 v2;

GLfloat distance(sqrt(pow(b.x - a.x, 2) + pow(b.y - a.y,2)));
GLfloat deltaX = abs(b.x - a.x);
GLfloat deltaY = abs(b.y - a.y);
GLfloat normalX((b.x - a.x) / deltaX);
GLfloat normalY((b.y - a.y) / deltaY);
GLfloat theta = atan2(b.y - a.y, b.x - a.x) - M_PI / 3.0;
cout << " theta = " << theta << endl;

/*************************
 * Find trisection points
 *************************/
// horizontal line _____________
if(a.y == b.y) {
    vec2 temp0(a.x + (deltaX * normalX / 3) , a.y);
    vec2 temp2(a.x + (deltaX * normalX * 2/3) , a.y);
    vec2 temp1((a.x + b.x) / 2, a.y + distance * sin(theta) / 3);
    v0 = temp0;
    v2 = temp2;
    v1 = temp1;
}

//                 |
// vertical line   |
//                 |
else if(a.x == b.x){
    vec2 temp0(a.x , (a.y + (deltaY * normalY / 3)));
    vec2 temp2(a.x , (a.y + (deltaY * normalY * 2/3)));
    vec2 temp1(a.x + distance * cos(theta) / 3 , (a.y + b.y) / 2);
    v0 = temp0;
    v2 = temp2;
    v1 = temp1;
}

// slope != 0 && slope != 1
else {
    vec2 temp0(a.x + (deltaX * normalX / 3), a.y + (deltaY * normalY / 3));
    vec2 temp2(a.x + (deltaX * normalX * 2/3), a.y + (deltaY * normalY * 2/3));
    // Andrew is the greatest!
    vec2 temp1(temp0.x + distance * cos(theta) / 3, 
               temp0.y + distance * sin(theta) / 3);        
    v0 = temp0;
    v2 = temp2;
    v1 = temp1;
}

verts[0] = a;
verts[1] = v0;
verts[2] = v0;
verts[3] = v1;
verts[4] = v1;
verts[5] = v2;
verts[6] = v2;
verts[7] = b;

}

//----------------------------------------------------------------------------

void divide_line( const vec2& a, const vec2& b, const vec2& c, int n )
{ 

// arrayA = {a, b, b, c, c, a} i.e., the sides of the initial triangle
arrayA[0] = a;
arrayA[1] = b;
arrayA[2] = b;
arrayA[3] = c;
arrayA[4] = c;
arrayA[5] = a;

// If at least one iteration:
if(n > 0) {
    // The current iteration, starting with 0
    int currentIteration = 1;
    // Do for every iteration from 0 - n:
    while (currentIteration <= n) {
        int i;
        int j = 0;
        int size = 3 * 2 * (pow(4,currentIteration-1));
        // Call koch() for each pair of vertices in arrayA
        for(i = 0; i < size; i = i+2) {
            vec2 verts[8];
            koch(arrayA[i], arrayA[i+1], verts);
            // Store each vertex in arrayB
            int k;
            for(k = 0; k <= 7; k++)
                arrayB[j++] = verts[k];
        }
        // Copy arrayB to arrayA for next iteration.
        size = 3 * 2 * pow(4, currentIteration);
        for(i = 0; i < NumVertices; i++) {
            arrayA[i] = arrayB[i];
        }
        // Increase count of currentIteration.
        currentIteration++;
    }
} else
    printf("The number of iterations must be >= 0.\n");
}

I am currently trying to implement the Koch curve in c++. I have it almost working correctly. There are basically three different cases that I am addressing: line segment that I need to break into four segments is horizontal, vertical, or other.

The problem is that when the program calculates what the new vertices are for the line segments, there are two possibilities: the triangle can face "up" or the triangle can face "down". I tried to take this into account by normalizing the vectors, but I either did this incorrectly or there is something else slightly off. The pictures below are two examples; both have 3 iterations of the the fractal.

The code for splitting a non-horizontal and non-vertical line segment is below, since the triangles seem like the face the right way for vertical/horizontal segments:

if(a.x == b.x) {
  ...
}
else if (a.y == b.y) {
  ...
}
// slope != 0 && slope != 1
else {
    GLfloat deltaX = abs(b.x - a.x);
    GLfloat deltaY = abs(b.y - a.y);
    vec2 temp0(a.x + (deltaX * normalX / 3), a.y + (deltaY * normalY / 3));
    vec2 temp2(a.x + (deltaX * normalX * 2/3), a.y + (deltaY * normalY * 2/3));
    GLfloat dist(sqrt(pow(temp2.x - temp0.x, 2) + pow(temp2.y - temp0.y,2)));
    GLfloat theta = (a.x - b.x)/ (b.y - a.y);
    vec2 temp1((a.x + b.x) / 2 + dist * cos(atan(theta)) ,
               (a.y + b.y) / 2 + dist * sin(atan(theta)));
    v0 = temp0;
    v2 = temp2;
    v1 = temp1;
}

a and b are the vectors of the segment. The normalX and normalY are:

GLfloat normalX((b.x - a.x) / (abs(b.x - a.x)));
GLfloat normalY((b.y - a.y) / (abs(b.y - a.y)));

Any ideas of what I can do to fix this problem?

like image 601
A D Avatar asked Sep 21 '11 03:09

A D


People also ask

Why is the perimeter of the Koch snowflake infinite?

times the area of the original triangle, while the perimeters of the successive stages increase without bound. Consequently, the snowflake encloses a finite area, but has an infinite perimeter.

Is the Koch snowflake infinite?

The length of the boundary of S(n) at the nth iteration of the construction is 3(43)ns 3 ( 4 3 ) n s , where s denotes the length of each side of the original equilateral triangle. Therefore the Koch snowflake has a perimeter of infinite length.

What happens to the area of a Koch snowflake?

The Koch Snowflake has an infinite perimeter, but all its squiggles stay crumpled up in a finite area.


1 Answers

Your problem is probably atan(theta). It's domain is too small to be used for generic angle determination. You should instead use atan2(y,x):

GLfloat theta = atan2(b.y - a.y, a.x - b.x);
vec2 temp1((a.x + b.x) / 2 + dist * cos(theta) ,
           (a.y + b.y) / 2 + dist * sin(theta));

(I've not tested this code, so there could be some sign errors.)

Read more: Atan2 (Wikipedia)


EDIT:

My guess is that you are giving the coordinates in the wrong order. If you think of one segment from the perspective of the start point facing the end point, the curve will always extrude to your right. If you give the coordinates in the opposite order, the curve will be mirrored.

like image 154
Markus Jarderot Avatar answered Sep 20 '22 00:09

Markus Jarderot