Pionix
Pionix

Reputation: 475

Drawing koch curves only using line function

I am trying to draw a koch curve (line) with basic trigonometric conversions.

I couldn't figure out what is the correct angle for newly generated peak point.

Here is my logic: Points

Given the start point of the line, angle of the line and the length for every segment, create this scheme.

After creating the schemem, treat every sub-lines starting point as new koch curves and repeat the steps.

I suspect the problem is at point 'pt' angle value.

/* Angle for turning downwards after the peak point */
float angle = 2*PI - PI/6;

void koch(Point2D start, float alpha, int d, int noi) {

  Point2D p1 = new Point2D(start.x + d*cos(alpha), start.y + d*sin(alpha));
  Point2D pt = new Point2D(start.x + d*sqrt(3)*cos(alpha+PI/6), start.y + d*sqrt(3)*sin(alpha+PI/6));
  Point2D p2 = new Point2D(start.x + 2*d*cos(alpha), start.y + 2*d*sin(alpha));
  Point2D p3 = new Point2D(start.x + 3*d*cos(alpha), start.y + 3*d*sin(alpha));

  line(start.x, start.y, p1.x, p1.y);
  line(p1.x, p1.y, pt.x, pt.y);
  line(pt.x, pt.y, p2.x, p2.y);            
  line(p2.x, p2.y, p3.x, p3.y);

  if(noi != 0) {
    koch(start, alpha, d/3, noi-1);

    koch(p1, alpha + PI/3, d/3, noi-1);

    koch(pt, angle, d/3, noi-1);         //Problem is here i suspect

    koch(p2, alpha, d/3, noi-1);

  } 

  return;

}

Calling this function with alpha being PI/6 and noi is 2 i get:This

I want to get something like: This

Upvotes: 2

Views: 1347

Answers (2)

Spektre
Spektre

Reputation: 51845

I was reluctant to answer as I do not code in Unity but as your question after few days still did not have any valid answer here is mine:

I do not see what I would expect in turtle graphics code. See:

and look for turtle_draw in the code. This is what I would expect:

  1. initial string

    turtle fractals are represented by a string holding turtle commands. Usual commands are:

    • f go forward by predetermined step
    • l turn left (CCW) by predetermined angle in your case 60 deg
    • r turn right (CW) by predetermined angle in your case 60 deg

    For Koch snowflake you should start with triangle so "frrfrrf" the Koch curve starts with single line "f" instead.

  2. iteration/recursion

    for each level of iteration/recursion of the fractal you should replace each straight line command f by the triangular bump feature "flfrrflf" (make sure that last direction matches original f command). As the triangle tripled in size you should divide size of the f movement by 3 to stay at the same scale ...

  3. render the string

    simply process all the characters of the resulting string and render the lines. There are two approaches how to handle the rotations. Either remember direction angle and inc/dec it by rotation angle and compute the lines as polar coordinates increments (see the code below), or have direction in form of a 2D (or higher dimension) vector and apply rotation formula on it (see the link above).

Here small C++/VCL example of the Koch snowflake:

//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop

#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
Graphics::TBitmap *bmp=new Graphics::TBitmap;
int xs,xs2,ys,ys2,n=0;
AnsiString str;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void turtle(TCanvas *scr,float x,float y,float a,float dl,AnsiString s)
    {
    int i;
    char c;
    float da=60.0*M_PI/180.0;
    scr->MoveTo(x,y);
    for (i=1;i<=s.Length();i++)
        {
        c=s[i];
        if (c=='f')
            {
            x+=dl*cos(a);
            y+=dl*sin(a);
            scr->LineTo(x,y);
            }
        if (c=='l') a-=da;
        if (c=='r') a+=da;
        }
    }
//---------------------------------------------------------------------------
AnsiString replace(AnsiString s0,char find,AnsiString replace)
    {
    int i;
    char c;
    AnsiString s="";
    for (i=1;i<=s0.Length();i++)
        {
        c=s0[i];
        if (c==find) s+=replace;
        else s+=c;
        }
    return s;
    }
//---------------------------------------------------------------------------
void draw()
    {
    str="frrfrrf"; // initial string
    for (int i=0;i<n;i++) str=replace(str,'f',"flfrrflf"); // n times replacement

    bmp->Canvas->Brush->Color=0x00000000; // just clear screen ...
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));
    bmp->Canvas->Pen  ->Color=0x00FFFFFF; // and some info text
    bmp->Canvas->Font ->Color=0x00FFFFFF;
    bmp->Canvas->TextOutA(5,5,AnsiString().sprintf("n:%i",n));
    float nn=pow(3,n),a;
    a=xs; if (a>ys) a=ys; a=0.75*a/nn;

    turtle(bmp->Canvas,xs2-(0.5*nn*a),ys2-(0.33*nn*a),0.0,a,str); // render fractal
    Form1->Canvas->Draw(0,0,bmp); // swap buffers to avoid flickering
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
    {
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
    {
    bmp->Width=ClientWidth;
    bmp->Height=ClientHeight;
    xs=ClientWidth;
    ys=ClientHeight;
    xs2=xs>>1;
    ys2=ys>>1;
    draw();
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
    {
    draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
    {
    if (WheelDelta<0) if (n<8) n++;
    if (WheelDelta>0) if (n>0) n--;
    Handled=true;
    draw();
    }
//---------------------------------------------------------------------------

Ignore the VCL stuff. The important thing here are:

  1. void turtle(TCanvas *scr,float x,float y,float a,float dl,AnsiString s)

    which renders the string s on canvas scr (using VCL encapsulated GDI) where x,y is start position a is starting direction angle [rad] and dl is size of line.

  2. AnsiString replace(AnsiString s0,char find,AnsiString replace)

    which replace any find characters in s0 by replace pattern returned as a new string.

  3. void draw()

    which computes and render the fractal

Here few screenshots:

n=0 n=1 n=2 n=3

Now when I look at your code (just a quick look as I am too lazy to analyze your code in depth) you are generating points directly and without the incremental steps needed. Instead you are sort of hard-coding the triangular bump feature which will not work properly for next level of fractal recursion without clever indexing techniques. In your case it stop working properly even in the same level of recursion (on the next line because its oriented differently and you are not rotating but hard-coding the feature instead).

Upvotes: 2

CanCode
CanCode

Reputation: 154

As far as I know basic Koch curve starts with a line, divides the length of step into three and puts a equilateral triangle in the middle:

enter image description here

There are different variations out there if you are interested in, but for basic Koch curve you can start with either two of p1, p2, p3, pt and start points that you drawn and calculate the rest respectively. In each iteration, you can go one level deeper.

Upvotes: 1

Related Questions