1 /*
   2  * This will be my attempt to build a graphical application with AEC,
   3  * specifically, one that will draw a discrete mathematical curve using SVG.
   4  * A few years ago, I've made a similar program in JavaScript
   5  * ( https://flatassembler.github.io/fraktal.html ). I've never before
   6  * attempted to write a program that will draw the Dragon Curve in AEC,
   7  * because there is no obvious way to do it using console graphics (and my
   8  * old AEC compiler, targeting x86, was basically limited to console
   9  * graphics). Also, to do that, I'll need to do a bit of string manipulation,
  10  * and the old version of AEC didn't support string manipulation at all (The
  11  * only data types available were Decimal32 and Decimal32Array, and there
  12  * were no functions. It's because I wanted the code my compiler generates
  13  * not to favor one operating system over another, and x86 OS-es do those
  14  * things very differently). This version of AEC is about at good at string
  15  * manipulation as C is without a standard library. JavaScript has many
  16  * useful string manipulation functions, however, since they are actually
  17  * methods of the "String" class, there is no standardized way to call them
  18  * from a WebAssembly program, even though it's running on JavaScript
  19  * Virtual Machine. That's because WebAssembly standard tries to be agnostic
  20  * about other programming languages running on the same virtual machine,
  21  * which may be a good thing (Why make it necessary for a JavaScript program
  22  * to be running on the same virtual machine for any program that's compiled
  23  * to WebAssembly to make sense?).
  24  */
  25 
  26 //Let's import some JavaScript functions...
  27 Function drawLine(Integer32 x1,
  28                   Integer32 y1,
  29                   Integer32 x2,
  30                   Integer32 y2,
  31                   CharacterPointer color,
  32                   Integer32 lineWidth) Which Returns Nothing Is External;
  33 Function applyTurtleTransformation(CharacterPointer svgDirective)
  34     Which Returns Nothing Is External; //We won't use the turtle for actual
  35                                        //drawing, but we will move it and
  36                                        //rotate it.
  37 
  38 Integer32 directionX[4]    := { 0, 1, 0, -1},
  39           directionY[4]    := {-1, 0, 1,  0},
  40           currentX         := 10,
  41           currentY         := 250 + 490 - 410, //When set on 250, the turtle
  42                                                //reaches 410 and then turns
  43                                                //back (I know this by
  44                                                //experimenting).
  45           currentDirection := 0,
  46           lineLength       := 5,
  47           lineWidth        := 2,
  48           currentStep      := 0;
  49 
  50 Character path[16384], reversedPath[16384];
  51 
  52 //Again, we need to implement string manipulation functions. Like I've said,
  53 //even though this program will be running on JavaScript Virtual Machine, it
  54 //can't call the methods of the JavaScript "String" class.
  55 Function strlen(CharacterPointer str) Which Returns Integer32 Does
  56     //We can't implement this recursively, like we did in earlier AEC
  57     //programs, because we will be dealing with large strings which will
  58     //cause stack overflow.
  59     Integer32 length := 0;
  60     While ValueAt(str + length) Loop
  61         length := length + 1;
  62     EndWhile
  63     Return length;
  64 EndFunction
  65 
  66 Function strcpy(CharacterPointer dest,
  67                 CharacterPointer src) Which Returns Nothing Does
  68     While ValueAt(src) Loop
  69         ValueAt(dest) := ValueAt(src);
  70         dest          :=     dest + 1;
  71         src           :=      src + 1;
  72     EndWhile
  73     ValueAt(dest) := 0;
  74 EndFunction
  75 
  76 Function reverseString(CharacterPointer string) Which Returns Nothing Does
  77     CharacterPointer pointerToLastCharacter := string + strlen(string) - 1;
  78     While pointerToLastCharacter - string > 0 Loop
  79         Character tmp                   := ValueAt(string);
  80         ValueAt(string)                 := ValueAt(pointerToLastCharacter);
  81         ValueAt(pointerToLastCharacter) := tmp;
  82         string                          := string + 1;
  83         pointerToLastCharacter          := pointerToLastCharacter - 1;
  84     EndWhile
  85 EndFunction
  86 
  87 Function strcat(CharacterPointer dest,
  88                 CharacterPointer src) Which Returns Nothing Does
  89     strcpy(dest + strlen(dest), src);
  90 EndFunction
  91 
  92 Function convertIntegerToString(CharacterPointer string,
  93                                 Integer32 number)
  94     Which Returns Integer32 Does //Returns the length of the string.
  95     Integer32 isNumberNegative := 0;
  96     If number < 0 Then
  97         number           := -number;
  98         isNumberNegative :=       1;
  99     EndIf
 100     Integer32 i := 0;
 101     While number > 9 Loop
 102         ValueAt(string + i) := '0' + mod(number, 10);
 103         number              :=           number / 10;
 104         i                   :=                 i + 1;
 105     EndWhile
 106     ValueAt(string + i) := '0' + number;
 107     i                   :=        i + 1;
 108     If isNumberNegative Then
 109         ValueAt(string + i) :=   '-';
 110         i                   := i + 1;
 111     EndIf
 112     ValueAt(string + i) := 0;
 113     reverseString(string);
 114     Return i;
 115 EndFunction
 116 
 117 //This is the function that's supposed to be called by JavaScript as soon
 118 //as it is ready.
 119 Function init() Which Returns Nothing Does
 120     CharacterPointer path         :=         AddressOf(path[0]);
 121     CharacterPointer reversedPath := AddressOf(reversedPath[0]);
 122     strcpy(path, "R");
 123     Integer32 counter := 0;
 124     While strlen(path) < 8192 Loop
 125         strcpy(reversedPath, path);
 126         If mod(counter, 4) = 0 Then
 127             reverseString(reversedPath);
 128         EndIf
 129         strcat(path, reversedPath);
 130         strcat(path, not(mod(counter, 4))?
 131                         "L"
 132                      :  "LLL");
 133         counter := counter + 1;
 134     EndWhile
 135 EndFunction
 136 
 137 //This function is supposed to be periodically called by JavaScript:
 138 Function step() Which Returns Nothing Does
 139     If not(path[currentStep] = 0) and currentX > 0 and currentX < 500
 140        and currentY > 0 and currentY < 500 Then
 141         Integer32 nextX := currentX +
 142                            directionX[currentDirection] * lineLength,
 143                   nextY := currentY +
 144                            directionY[currentDirection] * lineLength;
 145         drawLine(currentX, currentY, nextX, nextY,
 146                  currentStep = 0?
 147                     "lightYellow"
 148                  :path[currentStep] = 'R'?
 149                     "red"
 150                  :path[currentStep] = 'L'?
 151                     "lightBlue"
 152                  :  "lightYellow",
 153                  lineWidth);
 154         currentX    := nextX;
 155         currentY    := nextY;
 156         If path[currentStep] = 'R' Then
 157             currentDirection := mod(currentDirection + 1, 4);
 158         ElseIf not(currentDirection = 0) and path[currentStep] = 'L' Then
 159             currentDirection := currentDirection - 1;
 160         ElseIf path[currentStep] = 'L' Then
 161             currentDirection := 3;
 162         EndIf
 163         Integer32 tmp; //I had no idea WebAssembly would behave that way,
 164                        //that the assembler will complain about not storing
 165                        //the result of a function. This is very different
 166                        //from x86 assembly. So, I modified the compiler
 167                        //to warn about that.
 168         currentStep := currentStep + 1;
 169         Character turtleTranformation[64]    := {0};
 170         CharacterPointer turtleTranformation := AddressOf(
 171                                                     turtleTranformation[0]);
 172         strcat(turtleTranformation, "translate(");
 173         tmp := convertIntegerToString(turtleTranformation +
 174                                          strlen(turtleTranformation),
 175                                       currentX);
 176         strcat(turtleTranformation, " ");
 177         tmp := convertIntegerToString(turtleTranformation +
 178                                          strlen(turtleTranformation),
 179                                       currentY);
 180         strcat(turtleTranformation, ") rotate(");
 181         tmp := convertIntegerToString(turtleTranformation +
 182                                          strlen(turtleTranformation),
 183                                       currentDirection * 90);
 184         strcat(turtleTranformation, ")");
 185         applyTurtleTransformation(turtleTranformation);
 186     EndIf
 187 EndFunction
 188