Saturday, June 25, 2022

Day 12/100 Graphic with SVG

 Day 12/100 Graphic with SVG

It makes a fine line!


So far I have been doing graphic using text only console character, and although it works, it's not really suitable for fine graphics. Of course, having only 80x25 grid doesn't help matters. But what if there's a way to do an ultra high resolution graphic, and do it via text terminal only programs? Turns out there is such a way, albeit not directly. Introducing Scalable Vector Graphic (SVG)!


The tutorial that is out there somewhat skimps on the installation process, and proper formatting. I had trouble setting things up. It's not until I finally gave in and installed Inkscape, that I figured out what's going on. The trick is that I play around with the various settings that Inkscape has, and there are several options to do. I chose the simplest, and go from there.


The SVG header has viewBox, resolution, and size. There's also default line and fill specification. So far, I mostly do monochrome graphics. Lines and rectangles, so to speak. But I checked out the specification, and it has curves as well, and that is something I'm interested in for future exercises.


The point is that you can do really nice graphic using SVG. Check out these pictures:


Line Graph:



Bar Graph:




Right now, I don't have a vector based font handy. Sure there is a True Type Font converter, but I'd rather make my own font. After all, this is all for learning purposes. Here is the actual, relevant SVG code, which I later inserted into the SVG framework file.


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

void SVGDrawRect(int x0,int y0,int x1,int y1) {
  printf("<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" />\n",
         x0,y0,(x1-x0),(y1-y0));
}


That's all there is to it. It's not something mysterious at all. If you can do XML, then you can do SVG. All that's left is to fill in the blanks, so to speak. I generated the SVG instructions by modifying my old stock/frequency analysis program. The output is intermingled with ASCII graphic. So, I have to filter them out. I simply use GREP command for that. It's a good thing Raspberry Pi is set up for learning computer programming rather well. I really enjoy using it to create computer programs.


Here is the SVG template that I use. Don't ask me what all this stuff do because I don't know! The most important bit is the one in the middle: width, height, and viewBox. I use millimeter because it's more precise than inches, and I don't have to use floating points so much. I inserted the SVG output between the <g> </g> markups, just below the 4 line commands. 


<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<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:1; 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="118.0" y1="180.0" x2="10.0" y2="180.0" />
<line x1="10.0" y1="180.0" x2="10.0" y2="10.0" />
</g>
</svg>


This is just beginning of my journey to do computer graphic using C programming language. Would you believe that there's nobody who can answer the extremely simple question of "How do I draw a dot on screen using C?" I mean, there's tremendous amount of computer graphic libraries, but if you just want to draw a dot on screen, then you're just plain out of luck! Unbelievable!


Anyway, enough rambling around. The somewhat confusing and raw experiment using the old, traditional hack-and-slash method is below. That's not a clean software engineering, but like I said, this is only the beginning. Technological research does tend to be messy, since by definition, you don't know what you're doing! That's right. Learning new things involves having your ignorance exposed, at least for a little while. Later on, as you progress, you can refine things better. For now, though, it's just raw.


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

#define MAXENTRY 10000
#define MAXSTR 2560
#define sx0 28
#define sx1 108
#define sy0 20
#define sy1 170
#define cwidth 40.0

char Liner[MAXSTR];
int Data[MAXENTRY];
int minEntry=MAXENTRY;
int maxEntry=0;
int minValue=MAXENTRY;
int maxValue=0;

int LoadData() {
  int num;
  char *buf;
  int i=0;
  while (buf=fgets(Liner,MAXSTR,stdin)) {
    num=atoi(buf); Data[i]=num;
    if (minEntry>i) minEntry=i;
    if (maxEntry<i) maxEntry=i;
    i++;
  }
  return 0;
}


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

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

void SVGDrawRect(int x0,int y0,int x1,int y1) {
  printf("<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" />\n",
         x0,y0,(x1-x0),(y1-y0));
}

void DrawHLine(int start, int stop, char mark) {
  int i;
  if (start>stop) { i=start; start=stop; stop=i; }
  for (i=start;i<=stop;i++) putchar(mark);
}

void DrawHeader() {
  printf("\n\n\n");
  DrawHLine(0,5,' ');
  printf("%5d",minValue);
  DrawHLine(15,(72-5),'-');
  printf("%5d\n",maxValue);
}

void GraphBar(char LineMark) {
  int i,j;
  int spacer;
  DrawHeader();
  for (i=minEntry;i<=maxEntry;i++) {
    printf("%4d%5d  ",i,Data[i]);
    spacer=(int)map((float)minValue,
      (float)Data[i],(float)maxValue,0.0,cwidth);
    DrawHLine(0,spacer,'X'); putchar('\n');
    SVGDrawRect(
                sx0,
                sy0+(i*(sy1-sy0)/(maxEntry-minEntry)),
                sx0+(int)((float)spacer*2.0),
                (sy0+2)+(i*(sy1-sy0)/(maxEntry-minEntry))
      );
  }
}

void GraphLine(char LineMark) {
  int i,j;
  int spacer;
  int g0,g1;
  int l0,l1;
  DrawHeader();
  for (i=minEntry;i<=maxEntry;i++) {
    if (i==minEntry)
      g0=(int)map((float)minValue,
         (float)Data[i],(float)maxValue,0.0,cwidth);
    g1=(int)map((float)minValue,
       (float)Data[i],(float)maxValue,0.0,cwidth);
    printf("%4d%5d  ",i,g1); l0=g0; l1=g1;
    if (l0>l1) { j=l0;l0=l1;l1=j; }
    DrawHLine(0,l0,' '); DrawHLine(l0,l1,'+');
    putchar('\n');
    if (i>minEntry) SVGDrawLine(
                sx0+(g0*2),
                sy0+((i-1)*(sy1-sy0)/(maxEntry-minEntry)),
                sx0+(g1*2),
                sy0+((i)*(sy1-sy0)/(maxEntry-minEntry))
      );
    g0=g1;
  }
}

int main (int argc, char *argv[] ) {
  int n,i;
  for (i=0;i<MAXENTRY;i++) Data[i]=0;
  LoadData();
  for (i=minEntry;i<=maxEntry;i++) {
    if (minValue>Data[i]) minValue=Data[i];
    if (maxValue<Data[i]) maxValue=Data[i];
    printf("%d\t%d\n",i,Data[i]);
  }
  printf("\n");
  printf("Entry: %d\t%d\n",minEntry,maxEntry);
  printf("Value: %d\t%d\n",minValue,maxValue);
  GraphBar('X');
  GraphLine('+');
  return 0;
}


One more thing: I'm using C for this, but if you're really into Graphic, then I suggest Learning Processing. Dan Shiffman is great at that. You should seek him out!

No comments:

Post a Comment