Sunday, July 3, 2022

Day 18/100 Turtle2 (Improved)

 Day 18/100 Turtle 2 (Improved)

Do turtles come in red?


If you remember Day 14, I did a simple turtle graphic program. Well, I revisited the program, and add a bunch of features. A little too much, maybe. It has over 300 lines on it. Quite a program. Yet, it is not complete yet. There's still quite a few capabilities I want to put it in, but it suffices for now.


The code is interesting because of the function Turtle() actually acts like an object. Structurally, it has 4 parameters: 2 strings and 2 numbers. The first string is actually a command for the turtle. Object methods, so to speak. The rest are the parameters, except it's not object oriented. This is just plain old C, instead of C++.


If I want to, I can do it as a graphic library, and I still have an option to do that. Just do it as an abstraction layer, or something. But it's getting complicated, and I'm thinking maybe I should stop here for now, and build some kind of documentation. As it is, it's good enough for some fancy graphic:



A picture of a random tree


Most commands comes in pairs. Usually the point is obvious if you look at the code. The function Turtle() is rather large, but if you look at individual commands, it's actually rather simple for each. Something to remember that doing graphic is usually a library call away. Well, not this one. Here, I simply put out SVG commands, and that's a simple text output.


  //TreeMaker with Spark?
  if (r>30) {
  for (i=3+(rand()%n);i;i--) {
    x1=x0+(rand()%r-(r/2));
    y1=y0+(rand()%r-(r/3));
    Turtle("G","",x0,y0);
    Turtle("C","",0x102030,0);
    Turtle("G","",x1,y1);
    Spark1(x1,y1,n/2,r/2);
  }}



  Turtle("SC","",0,0);
  srand(3333);Turtle("C","",0x010101,0);
  Turtle("G","",60,20);Spark1(60,90,32,75);
  srand(6666);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,70);
  srand(9999);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,60);
  srand(18100);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark2(60,90,32,60);
  Turtle("sc","",0,0);


This is the main code for drawing the tree. It uses 2 Spark functions. Originally, I used a recursive function, but then I added color, and well, there you go. I'd say the tree looks good enough for something that is random.


I also added some SVG capabilities, such as Fonts. I'm thinking that maybe I should just change the name from Turtle to SVG Library. Oh, well, that is something to do in the future. I may as well do it, too. It's no longer a turtle graphic program when I eliminate turn left and turn right. Those has been replaced with Heading(relative) and Heading(absolute). 


Furthermore, I changed the program so that it can handle multiple character command. So, with that, it has greatly expanded the capability of taking in numerous commands. BTW, most commands comes in upper case and lower case versions.


SC: prints out SVG file header
sc: prints out SVG file footer
F - n1: Forward, absolute distance
f - n1: forward, relative distance
G - n1,n2: Goto XY, absolute
g - n1,n2: Goto XY, relative
H - n1: Heading, absolute (0-360)
h - n1: Heading, relative
C - n1: Stroke (pen) color. 0 for pen up
c - n1: Fill color. 0 for "none"
T - s2,n1: Set Font, Font name, size
t - s2,n1,n2: Draw Text. Text,X,Y
M1 - EvenOdd Fill mode. Uses background color
m1 - disable fill mode.


These command set is enough to allow for some sophisticated graphic. I was even able to include the SVG code in my blog! So, that's good.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdlib.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;
int Debug=0;

//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 color
int  bg; //background color
int mode=0; //0-normal 1-fill
char FontFam[MAXSTR];
int FontSiz;

 

float map(float x0,float x1, float x2,float y0, float y2) {

  return y0+((x1-x0)*(y2-y0)/(x2-x0)); //y1

}


void SVGHeader() {
  puts("<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\" 
style=\"fill-opacity:1; color-rendering:auto; 
color-interpolation:auto; text-rendering:auto; 
stroke:black; stroke-linecap:square; 
stroke-miterlimit:10; shape-rendering:auto; 
stroke-opacity:0.4; fill:black; 
stroke-dasharray:none; font-weight:normal; 
stroke-width:0.25; font-family:'Dialog'; font-style:normal; 
stroke-linejoin:miter; font-size:12px; 
stroke-dashoffset:0; image-rendering:auto;\" 

width=\"128.0mm\" height=\"190.0mm\" 
viewBox=\"0 0 128.0 190.0\" 

xmlns=\"http://www.w3.org/2000/svg\">
<!--Template generated by the Batik Graphics2D SVG Generator-->
<defs id=\"genericDefs\" />

<g>
<line x1=\"10.0\" y1=\"10.0\" x2=\"118.0\" y2=\"10.0\" />
<line x1=\"118.0\" y1=\"10.0\" x2=\"118.0\" y2=\"180.0\" />
<line x1=\"10.0\" y1=\"180.0\" x2=\"118.0\" y2=\"180.0\" />
<line x1=\"10.0\" y1=\"180.0\" x2=\"10.0\" y2=\"10.0\" />
");
}


void SVGFooter() {
  puts("
</g>
</svg>
");
}


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

  if (mode==1) {
    if (pe) {
      printf("L %f,%f ",x1,y1);
    } else {
      printf("M %f,%f ",x1,y1);
    }
  } else if (pe) {
    printf("<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" />\n",
           x0,y0,x1,y1);
  }
}

int Turtle(char s1[], char s2[], int n1, int n2) {

  if (!strncmp("SC",s1,2)) { //Capture SVG
    SVGHeader();
    if (Debug) puts("SVGStart");
  }
  if (!strncmp("sc",s1,2)) { //Close SVG
    SVGFooter();
    if (Debug) puts("SVGEnd");
  }
  if (!strncmp("F",s1,1)) { //forward absolute
    di=n1;
    if (Debug) printf("Forward Absolute %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);
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("f",s1,1)) { //forward relative
    di+=n1;
    if (Debug) printf("Forward Relative %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);
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("G",s1,1)) { //Goto XY Absolute
    tx1=n1; ty1=n2;
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("g",s1,1)) { //Goto XY Relative
    tx1=tx0+n1; ty1=ty0+n2;
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("H",s1,1)) { //Heading Absolute
    he=n1;
    if (Debug) printf("Heading Absolute %d\n",he);
  }
  if (!strncmp("h",s1,1)) { //Heading Relative
    he+=n1;
    if (Debug) printf("Heading Relative %d\n",he);
  }
  if (!strncmp("C",s1,1)) { //Color Stroke
    pe=n1;
    if (Debug) printf("Pen color %d\n",pe);
  }
  if (!strncmp("c",s1,1)) { //Color Fill
    bg=n1;
    if (Debug) printf("Background color %d\n",pe);
  }
  if (!strncmp("T",s1,1)) { //Font Fam+Siz
    strcpy(FontFam,s2);
    FontSiz=n1;
  }
  if (!strncmp("t",s1,1)) { //Draw text
    printf("<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d%%\" >"
            ,n1,n2,FontFam,FontSiz);
    printf("%s",s2);
    printf("</text>\n");
  }
  if (!strncmp("M1",s1,2)) { //Mode 1 start
    mode=1;
    if (bg) {
      printf("<path stroke=\"#%6X\" fill-rule=\"evenodd\" fill=\"#%6X\" d=\"\n"
              ,pe,bg);
    } else {
      printf("<path stroke=\"#%6X\" fill-rule=\"evenodd\" fill=\"none\" d=\"\n"
              ,pe,bg);
    }
    printf("M %f,%f ",tx0,ty0);
  }
  if (!strncmp("m1",s1,2)) { //Mode 1 end
    mode=0;
    puts("\" />");
  }
  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
  strcpy(FontFam,"PibotoThin");
  FontSiz=10;
  return 0;
}

int ProcessData() {
  char Command[MAXSTR];
  char Value[MAXSTR];
  int  Num1=0;
  int  Num2=0;
  int i,j;


  Turtle("C","",0x112233,0);
  Turtle("c","",0xCCBBAA,0);
  Turtle("SC","",0,0);
//  Turtle("M1","",0,0);
  for (i=10;i<170;i+=5) {
    Turtle("C","",0,0);
    Turtle("G","",10,i);
    Turtle("C","",0x102030,0);
    Turtle("G","",113,i);
  }
//  Turtle("m1","",0,0);
  Turtle("T","FreeMono",40,0);
  Turtle("t","FreeMono 40",15,20);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,25);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,30);
  Turtle("T","FreeSans",40,0);
  Turtle("t","FreeSans 40",15,35);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,40);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,45);
  Turtle("T","FreeSerif",40,0);
  Turtle("t","FreeSerif 40",15,50);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,55);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,60);

  Turtle("T","Quicksand Light",50,0);
  Turtle("t","Quicksand Light 50",15,70);
  Turtle("t","The quick brown fox jumps over the",15,75);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,80);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,85);

  Turtle("T","Quicksand Light",40,0);
  Turtle("t","Quicksand Light 40",15,95);
  Turtle("t","The quick brown fox jumps over the lazy dog",15,100);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,105);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,110);

  Turtle("T","Quicksand Light",30,0);
  Turtle("t","Quicksand Light 30",15,120);
  Turtle("t","The quick brown fox jumps over the lazy dog",15,125);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,130);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,135);

  Turtle("T","Quicksand Light",20,0);
  Turtle("t","Quicksand Light 20",15,145);
  Turtle("t","The quick brown fox jumps over the lazy dog",15,150);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,155);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,160);

  Turtle("sc","",0,0);
  return 0;
}

int Spark1(int x0,int y0, int n, int r) {
  int i;
  int x1,y1;

  if (r>30) {
  for (i=3+(rand()%n);i;i--) {
    x1=x0+(rand()%r-(r/2));
    y1=y0+(rand()%r-(r/3));
    Turtle("G","",x0,y0);
    Turtle("C","",0x102030,0);
    Turtle("G","",x1,y1);
    Spark1(x1,y1,n/2,r/2);
  }}

  if (r<=30) {
    Turtle("c","",0x99FF99,0);
    Turtle("M1","",0,0);
    for (i=3+(rand()%n);i;i--) {
      x1=x0+(rand()%r-(r/4));
      y1=y0+(rand()%r-(r/3));
      Turtle("G","",x1,y1);
    }
    Turtle("m1","",0,0);
  }
  return 0;
}

 

int Spark2(int x0,int y0, int n, int r) {

  int i;

  int x1,y1;


  if (r>15) {
  for (i=3+(rand()%n);i;i--) {
    x1=x0+(rand()%r-(r/2));
    y1=y0+(rand()%r-(r/3));
    Turtle("G","",x0,y0);
    Turtle("C","",0x102030,0);
    Turtle("G","",x1,y1);
    Spark2(x1,y1,n/2,r/2);
  }}

  if (r<=15) {
    Turtle("c","",0x338833,0);
    Turtle("M1","",0,0);
    for (i=3+(rand()%n);i;i--) {
      x1=x0+(rand()%r-(r/4));
      y1=y0+(rand()%r-(r/3));
      Turtle("G","",x1,y1);
    }
    Turtle("m1","",0,0);
  }
  return 0;
}


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

  Init();
//  ProcessData();
  //TreeMaker with Spark?
  Turtle("SC","",0,0);
  srand(3333);Turtle("C","",0x010101,0);
  Turtle("G","",60,20);Spark1(60,90,32,75);
  srand(6666);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,70);
  srand(9999);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,60);
  srand(18100);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark2(60,90,32,60);
  Turtle("sc","",0,0);
  return 0;
}


One more thing: Should I actually just make this an SVG library with Turtle layer?


No comments:

Post a Comment