Tuesday, June 28, 2022

Day 14/100 Turtle Graphic (Simple)

 Day 14/100 Turtle Graphic (Simple)

These turtles are surprisingly fast



It annoys me to no end that people are claiming that computer programming is hard and takes years to do. Even more so when I know for sure, that the programming in question is so easy, I have done it with no direction or guidance in an hour. That remains the longest time I spent with this program. I have done it with SmileBasic 3, on Nintendo 3DS, in as little as 10 minutes! Yes, it's that easy!


Yet, when I suggested that this problem is good for beginners, people resisted. "That's too complicated!" they say, "It involves a lot of math!" What can I say? The only math involved is sin() and cos(), and you don't really need to understand how they work. You just need to understand how to use it, and that's not really difficult at all. Really, the math is so simple, I was doing it (without properly understanding them until much later) when I was 13 years old! How's that for being a "difficult" problem for kids?


I asked as many teachers as I can possibly find to have them teach this, and not only they resisted, some were even felt insulted! I wonder why? Especially noticeable are python users where they insist that importing a turtle graphic library is the only acceptable answer in teaching children how to program this! The only exception is Dan Shiffman, who is an excellent Processing programming language, who featured it in one of his videos. Thanks to him, I have a 'clear' command, that unfortunately does nothing in this case. 


But really, is the problem really that hard? If you think about it, there's only a few variables:

Command, Number, Heading (degree), Distance, Angle(radians), and (x0,y0)-(x1,y1) when drawing a line. Include Pen variable if you want to be able to lift the pen off paper and not draw a line when moving the turtle.


The Commands are: Clear, Forward, TurnLeft, TurnRight, Pen, and Quit. Only six commands! How long can it be? Clear does nothing, so that's done immediately. Quit simply ends the function, so that's easy!


Pen command basically takes the Number variable, 0 for false (don't draw), 1 for true (draw). This can double as color if desired. So, that's easy, too!


TurnLeft and TurnRight is basically Heading-=Number and Heading+=Number. How hard is that? Not hard at all!


All in all, six commands, and I just did 5 of them really quick and easy! How can people say it's hard? I don't know. Let this be a lesson to you: If you working on a problem, don't worry whether it's hard or not, simply because you don't know how hard it is until you solve it, and once you solve it, you know how to solve it, so how can it be hard?


Anyway, here's the core of turtle graphic code:


an=map(0.0,(float)he,360.0,0,2*PI);
tx1=tx0+(float)di*sin(an);
ty1=ty0-(float)di*cos(an);
if (pe) SVGDrawLine(tx0,ty0,tx1,ty1);
tx0=tx1;ty0=ty1;


Not that hard, is it? The first line simply maps degree to radians. The only trigonometric functions used are on the second and third line. And this can be confusing. But it's not your fault!


You see, I'm using 0 degree as to point North, or upwards to 12 o'clock direction. Mathematicians, however, applied the 0 degree direction to point East, or toward 3 o'clock direction! So, there's a disjoint of application there. I'm no mathematician, so I use the layman's convention. You can use either. It's doesn't really matter at the end, since you can just rotate the turtle 90 degrees.


As far as sin() and cos() are concerned, you don't really need to know much more than the curve, and how they map according to distance. The function takes radians, rather annoyingly so, but the math calculations does take radians and converting degree to radians is just a one-liner, so I don't mind it that much.


As long as you can see that sin() goes (0)-(1)-(0)-(-1)-(0) for every 90 degrees or PI/2, then all is well. cos() goes (1)-(0)-(-1)-(0)-(1), or basically just shift sin() 90 degrees. That's not difficult at all.


Next you want to match the x and y axis with sin() and cos(), and it depends on which direction you want 0 degree to point at! Well, you know my choice. So there.


The rest is just taking the scaling the Number to each axis by multiplying it with the values of sin() and cos() for each appropriate axis. Note that in this case, y axis features substraction. This is because in mathematic, the (0,0) origin is at lower left corner whereas in computing, the origin is at upper left corner. If you're confused by this, and I certainly was confused in the beginning, just try it both ways, and you'll see.


I'm sorry to say that I have never seen this code shown anywhere else. The two codes that I have seen aren't like mine. The first one limits the angles to 90 degree increment. The second one does allow arbitrary angle, but forgot to subtract the y values! Or maybe that was intentional, since the person who did it was a mathematicians, and he chose East as his zero.


Once you have the values of x0,y0 and x1,y1 then all you have to do is draw the line. Put a check to see if the pen variable (pe) is non-zero. After that, you update the values of x0,y0 to that of x1,y1.


And that's all there is to it. I hope you will decide to implement this yourself, using any language you wish. Just don't use somebody else's turtle graphic library! Create your own library. Are you a computer programmer, or aren't you?


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define MAXENTRY 10000
#define MAXSTR 2560
#define sx0 0
#define sx1 118
#define sy0 0
#define sy1 180
#define PI 3.141528


char Liner[MAXSTR];
char Data[MAXENTRY][MAXSTR];
int numentry;

//turtle
char co; //command
int  nu; //Number
int  he; //heading
int  di; //distance
float  an; //angle (rad)
float  tx0,ty0,tx1,ty1; //line
int  pe; //pen

int LoadData() {
  int num;
  char *buf;
  int i=0;


  for (i=0;buf=fgets(Liner,MAXSTR,stdin);i++) {
    strcpy(Data[i],Liner);
  }
  numentry=i;
  return 0;
}

float map(float x0,float x1, float x2,float y0, float y2) {
  return y0+((x1-x0)*(y2-y0)/(x2-x0)); //y1
}

void SVGDrawLine(float x0,float y0,float x1,float y1) {
  int t;

//  if (y0>y1) { t=y0; y0=y1; y1=t;
//               t=x0; x0=x1; x1=t; }
  printf("<line x1=\"%f\" y1=\"%f.0\" x2=\"%f.0\" y2=\"%f.0\" />\n",
         x0,y0,x1,y1);
}

int ProcessData() {
  int i,j;
  char c;
  int n1,n2;
  char nums[99];

  i=j=0;
  while (1) {
    c=Data[i][j++];
    switch (c) {
      case '\n': //newline
        i++;j=0;
        break;
      case 'c': //clearscreen
        puts("ClearScreen");
        break;
      case 'f': //forward
        strncpy(nums,Data[i]+j,3); j+=3;
        n1=atoi(nums); di=n1;
        printf("Forward %d\n",di);
        an=map(0.0,(float)he,360.0,0,2*PI);
        tx1=tx0+(float)di*sin(an);
        ty1=ty0-(float)di*cos(an);
        if (pe) SVGDrawLine(tx0,ty0,tx1,ty1);
        tx0=tx1;ty0=ty1;
        break;
      case 'l': //turn left
        strncpy(nums,Data[i]+j,3); j+=3;
        n1=atoi(nums); he-=n1;
        printf("Turn Left %d\n",he);
        break;
      case 'r': //turn right
        strncpy(nums,Data[i]+j,3); j+=3;
        n1=atoi(nums); he+=n1;
        printf("Turn Right %d\n",he);
        break;
      case 'p': //pen
        strncpy(nums,Data[i]+j,3); j+=3;
        n1=atoi(nums); pe=n1;
        printf("Pen %d\n",pe);
        break;
      case 'q': //quit
        puts("Quit");
        return 0;
      default:
        break;
    }
  }
  return 0;
}


int Init() {
  co=' ';
  nu=0; //Number
  he=0; //heading
  di=0; //distance
  an=0; //angle (deg)
  tx0=59;ty0=90; //line from
  tx1=tx0;ty1=ty0; //line to
  pe=1; //pen
  return 0;
}

int main (int argc, char *argv[] ) {
  int n,i;


  Init();
  LoadData();
  for (i=0;i<numentry;i++) {
    printf("%d\t%s",i,Data[i]);
  }
  ProcessData();
  return 0;
}




No comments:

Post a Comment