"Turtle Graphics" in Batch files!

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
Aacini
Expert
Posts: 1730
Joined: 06 Dec 2011 22:15
Location: México City, México

"Turtle Graphics" in Batch files!

#1 Post by Aacini » 06 Sep 2015 20:15

Turtle Graphics is a method to draw graphics introduced by Logo programming language more than 40 years ago that have a series of virtues, particularly in the areas of teaching graphics and/or programming. You may review a very simple introduction to Turtle Graphics in Logo at this site. The Turtle Graphics feature that I most like is its ability to draw certain complex graphics (like spirals or recursive curves) in a much simpler way when compared vs. traditional methods that use cartesian coordinates.

Turtle Graphics requires a simpler interface than other graphics schemes, so its implementation is easier. I developed a series of subroutines for Batch files that provides the equivalent graphics capabilities of Logo programming language; I called this work "Batch Turtle Graphics" or BTG. The method to show the graphics is based on Batch-BGI: a *standard* graphics library for Batch files, my previous development that used HTML5 canvas tag to draw graphics in a .HTA window.

Code: Select all

@if (@CodeSection == @Batch) @then


@echo off

:: TurtleGraphics.bat: Turtle Graphics in Batch files
:: Written by Antonio Perez Ayala aka Aacini
:: Turtle Graphics reference: http://www.logointerpreter.com/logo-reference/turtle-graphics-canvas.php
:: Canvas reference: http://www.w3schools.com/tags/ref_canvas.asp

setlocal EnableDelayedExpansion
if "%~1" equ "TurtleGraphics" goto %1

cls
echo/
echo                  Batch Turtle Graphics, by Antonio Perez Ayala
echo/
echo call :newWindow t x y      Open a new graphics window as specified; the Turtle
echo call :nw title maxX maxY   is placed at center of window, pointing upwards.
echo/
echo call :penDown              Start a new drawing at current Turtle position;
echo call :pd                   you *must* put the pen down to start drawing.
echo/
echo call :forward expr         Moves the Turtle forward expr pixels;
echo call :fd expr              negative values go backwards.
echo/
echo call :back expr            Moves the Turtle backward expr pixels;
echo call :bk expr              negative values go forward.
echo/
echo call :right expr           Rotates the Turtle expr degrees clockwise;
echo call :rt expr              negative values rotate counterclockwise.
echo/
echo call :left expr            Rotates the Turtle expr degrees counterclockwise;
echo call :lt expr              negative values rotate clockwise.
echo/
echo call :penUp                End the current drawing and show it in the window;
echo call :pu                   you *must* call this subroutine to show the drawing^^!
echo/
echo call :setcolor color       Set the color of the drawing. Colors are specified by
echo call :setpc color          HTML standard, like "white" or "#FFFF00".
echo/
echo call :setwidth expr        Set the width of the drawing to expr pixels.
echo call :setpw expr
echo/
echo call :setbgcolor color     Set the background color of the graphics window.
echo/
echo call :clearScreen          Clears the graphics window and resets all parameters
echo call :cs                   to their initial values, including Turtle position.
echo/
echo call :home                 Moves Turtle to center of window, pointing upwards.
echo/
echo call :setxy expr expr      Moves the Turtle to the given location (no drawing).
echo/
echo call :closeWindow          Close (unlink) the current graphics window from cmd.exe
echo call :cw                   and activate another (cmd.exe) window if its optional
echo call :closeWindowAndActivate title                            title is given.
echo/
echo Two simple examples:
echo call :penDown ^& call :forward 100 ^& call :right 90 ^& call :fd 50 ^& call :penUp
echo call :pd ^& (for /L %%i in (1,1,12) do call :fd 190 ^& call :rt 150) ^& call :pu
echo/
echo Enter Batch Turtle Graphics commands; nothing to end:

:begin
rem Place here commands to execute before drawing graphics

rem Start Turtle Graphics
"%~F0" TurtleGraphics 3>&1 1>&2 | CScript //nologo //E:JScript "%~F0"
rem End Turtle Graphics

rem Place here commands to execute after drawing graphics
goto :EOF


:TurtleGraphics
rem Start of drawing commands
call :newwindow "Batch Turtle Graphics" 400 400

:nextCommand
   echo/
   echo Ready...
   set "command="
   set /P "command="
   if not defined command goto endCommands
   %command%
goto nextCommand
:endCommands

rem End of drawing commands
goto :EOF

============================================================

:newWindow title width height
:nw
set "_title=Batch Turtle Graphics"
set /A "_width=400, _height=400"
if "%~1" neq "" set "_title=%~1"
if "%~2" neq "" set /A "_width=%~2, _height=%~3"
echo openwindow;%_width%;%_height%;%_title% >&3
goto resetAll

:clearScreen
:cs
echo ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,%_width%,%_height%); ctx.translate(%_width%/2,%_height%/2); >&3
echo ctx.strokeStyle="white"; ctx.lineWidth=1; document.body.style.background="black"; >&3
echo/>&3
:resetAll
set /A _pencolor=15, _pensize=1, _bgcolor=0
set "_fillcolor="
exit /B

:home
:ct = CenterTurtle
echo ctx.setTransform(1,0,0,1,0,0); ctx.translate(%_width%/2,%_height%/2); >&3
echo/>&3
exit /B

:setxy x y
:xy
echo ctx.moveTo(%~1,-(%~2)); ctx.translate(%~1,-(%~2)); >&3
exit /B

============================================================

:penDown
:pd
echo ctx.beginPath(); ctx.moveTo(0,0); >&3
exit /B

:penUp
:pu
if defined _fillcolor echo ctx.fill(); >&3
if defined _pencolor echo ctx.stroke(); >&3
echo/>&3
exit /B

:forward pixels
:fd
echo ctx.lineTo(0,-(%~1)); ctx.translate(0,-(%~1)); >&3
exit /B

:back pixels
:bk
echo ctx.lineTo(0,%~1); ctx.translate(0,%~1); >&3
exit /B

:right degrees
:rt
echo ctx.rotate((%~1)*Math.PI/180); >&3
exit /B

:left degrees
:lt
echo ctx.rotate(-(%~1)*Math.PI/180); >&3
exit /B

============================================================

New (version 2) subroutines

:setcolor color
:setpc
echo ctx.strokeStyle="%~1"; >&3
exit /B

:setwidth value
:setpw
echo ctx.lineWidth=%1; >&3
exit /B

:setbgcolor color
echo document.body.style.backgroundColor="%~1"; >&3
exit /B

:closeWindowAndActivate [title]
:closeWindow
:cw
echo closewindow;%~1; >&3
exit /B

============================================================

:: End of Batch section


@end


// Start of JScript section

var fso      = new ActiveXObject("Scripting.FileSystemObject"),
    WshShell = new ActiveXObject("WScript.Shell"),
    HTA      = { Win: false }, command;

// Create the HTA file
HTA.FullName = WScript.ScriptFullName.replace(WScript.ScriptName,"BatchTurtleGraphics.hta");
HTA.File     = fso.CreateTextFile(HTA.FullName,true);
HTA.File.WriteLine(
   "<meta http-equiv='x-ua-compatible' content='ie=edge'/>\r\n" +
   "<html> <head><HTA:APPLICATION></head>\r\n" +
   "<script language='JavaScript'>\r\n" +
   "var fso   = new ActiveXObject('Scripting.FileSystemObject'),\r\n" +
   "    stdin = fso.GetStandardStream(0), command, canvas, ctx;\r\n" +
   "fso.GetStandardStream(1).WriteLine();  // send confirmation\r\n" +
   "eval(stdin.ReadLine());  // get initialization\r\n" +
   "function evalCommands() {while (command=stdin.ReadLine()) eval(command);}\r\n" +
   "window.onkeydown = evalCommands;\r\n" +
   "</script>\r\n" +
   "</html>\r\n"
);
HTA.File.Close();

// Receive commands from Batch section and send they to the HTA window
while ( ! WScript.Stdin.AtEndOfStream ) {

   command = WScript.Stdin.ReadLine();

   if ( command.substr(0,11) == "openwindow;" ) {

      // command = "openwindow;width;height;title" - create a new mshta.exe .HTA window

      // If is there a previous open window: leave it in the screen
      if ( HTA.Win ) {
         HTA.Win.Stdin.WriteLine("ignorekey=function () { }; window.onkeydown=ignorekey;\r\n");
         WshShell.AppActivate(HTA.Win.ProcessID);
         WshShell.SendKeys(" ");
      }

      // Open the HTA window and wait for confirmation
      HTA.Win = WshShell.Exec('mshta.exe "'+HTA.FullName+'"');
      HTA.Win.Stdout.ReadLine();

      // Initialize the contents of the HTA window
      var p = command.split(";"), width = p[1], height = p[2], title=p[3];
      command = "\"<title>"+title+"</title> " +
                  "<style type='text/css'>body {color:white; background:black;}</style> " +
                  "<canvas id='BTGcanvas' width='"+width+"' height='"+height+"' style='border:1px solid;'>" +
                     "Your browser don't support the HTML5 Canvas tag" +
                  "</canvas>\"";
      HTA.Win.Stdin.WriteLine(
         "window.resizeTo("+width+"+34,"+height+"+64); "   +   // try: 34..40 64..82
         "document.body.innerHTML = "+command+"; "         +
         "canvas = document.getElementById('BTGcanvas'); " +
         "ctx = canvas.getContext('2d'); "                 +
         "ctx.translate("+width+"/2,"+height+"/2);"        +
         "ctx.strokeStyle = 'white';"
      );

   } else if ( command.substr(0,11) == "closewindow" ) {

   // Close the active HTA window
   HTA.Win.Stdin.WriteLine("ignorekey=function () { }; window.onkeydown=ignorekey;\r\n");
   WshShell.AppActivate(HTA.Win.ProcessID);
   WshShell.SendKeys(" ");
   HTA.Win = false;

   // ... and activate the application indicated in the optional parameter
   p = command.split(";"); title=p[1];
   if ( title ) { WshShell.AppActivate(title); }

   } else {

      // Read and send commands to the (active) HTA window
      // until an "end of block" mark (empty line) is received
      while ( command ) {
         HTA.Win.Stdin.WriteLine(command);
         command = WScript.Stdin.ReadLine();
      }

      // Raise the event for "window.onkeydown" function in the HTA window
      HTA.Win.Stdin.WriteLine();			//    Send the "end of block" mark (empty line)
      WshShell.AppActivate(HTA.Win.ProcessID);		//    Set focus on the HTA window
      WshShell.SendKeys(" ");				//    And do a "key down"

   }

}

// Close the HTA window                            Or v
// HTA.Win.Stdin.WriteLine("window.close();\r\n");
// Leave the last window on the screen             Or ^
HTA.Win.Stdin.WriteLine("ignorekey=function () { }; window.onkeydown=ignorekey;\r\n");

WshShell.AppActivate(HTA.Win.ProcessID);
WshShell.SendKeys(" ");

// Delete the HTA file
command = HTA.FullName;
HTA = { Win: false };
fso.DeleteFile(command);
Previous Batch file allows an interactive execution of Batch code with BTG subroutines in a way rather similar to the one originally provided by Logo programming language, so this program may be taken as a learning tool to teach graphics (or to teach Batch file programming using graphics as base). You must note that this is a preliminary version of BTG so there are some aspects of Logo Turtle Graphics that are not yet implemented, like show the Turtle itself or manage line and fill colors. In the development of applications of this type we are limited by the usual restrictions of Batch files combined with the method used to emulate Logo behaviour under the scheme required by HTML5 canvas tag; this means that there are several details we must pay attention when using BTG subroutines:
  • You must call :penDown subroutine in order to start a drawing.
  • You must call :penUp subroutine to end the current drawing and show it on the screen. Nothing will appear in the graphics window until :penUp subroutine be called.
  • The number of graphics operations between call:penDown and call:penUp subroutines can not be very high. For example, in the last drawing example shown below, the most internal loop have 5 iterations and the next one have 19. If we move the call:pd/call:pu subroutines from the first loop to the second one, the number of iterations change from 5 to 19*5=95 and in this case, the program just freezes. Check this detail in first place if your drawings fails.
  • After the graphics are updated the graphics window keep the focus, so you must manually select the cmd.exe window in order to enter the next command.
Some of these restrictions will be solved in future BTG versions.

Below there are several one-line BTG drawings taken from interesting Logo Turtle Graphics examples that I converted to Batch. You may copy anyone of these lines and paste they in the cmd.exe BTG program input in order to produce the graphics; to do that, run the TurtleGraphics.bat program, select and copy the complete line of the desired example from the list below, right-click on the TurtleGraphics.bat cmd.exe window and select "Paste". Remember to first clear previous graphics with call :clear or open a new window with call :newWindow "Any title".

Code: Select all

The following examples were taken from: http://www.mathcats.com/gallery/15wordcontest.html
--------------------------------------------------------------------------------
Hypercube:
call :setxy -120 50 & for /L %i in (1,1,8) do call :pd & (for /L %j in (1,1,4) do call :rt 90 & call :fd 100) & call :bk 100 & call :lt 45 & call :pu
Compare how the graphics are displayed when the position of call:pd/call:pu subroutines is changed this way:
call :setxy -120 50 & call :pd & (for /L %i in (1,1,8) do (for /L %j in (1,1,4) do call :rt 90 & call :fd 100) & call :bk 100 & call :lt 45 ) & call :pu
In this case the 8*4=32 iterations are supported between call :pd and call :pu,
but this point also depends on the number and type of commands in the loops.
--------------------------------------------------------------------------------
Hexagon:
for %i in (100 50) do (for /L %j in (1,1,6) do call :pd & (for /L %k in (1,1,6) do call :fd %i & call :lt 60) & call :lt 60 & call :pu)
--------------------------------------------------------------------------------
Hexagon Variation:
for /L %i in (15,15,90) do call :pd & (for /L %j in (1,1,6) do (for /L %k in (1,1,6) do call :fd %i & call :rt 60) & call :rt 60) & call :pu
--------------------------------------------------------------------------------
Octa-star Spiral:
for /L %i in (0,4,104) do call :pd & (for /L %j in (1,1,8) do call :fd %i & call :rt 135) & call :fd %i & call :rt 30 & call :pu
--------------------------------------------------------------------------------
Penta-star Spiral:
for /L %i in (0,3,96) do call :pd & (for /L %j in (1,1,5) do call :fd %i & call :rt 144) & call :fd %i & call :rt 30 & call :pu
--------------------------------------------------------------------------------
Simple Flower:
for /L %j in (1,1,11) do ( for /L %i in (0,1,359) do call :pd & call :fd 1 & call :rt Math.sin(%i*Math.PI/360^) & call :pu )
--------------------------------------------------------------------------------
Five Rose:
for /L %i in (1,1,1800) do call :pd & call :fd 10 & call :rt %i+.1 & call :pu
The .1 can be replaced with other values to produce different figures, but the 1800 number must be adjusted accordingly;
for example, .2 generates a figure with 10 "roses" so it requires a value of 10*360=3600.
Other values are: 0.125=8 roses, 0.15=20 roses.
More details at: http://www.mathcats.com/gallery/fiverosedetails.html
--------------------------------------------------------------------------------
Dahlia: this drawing is amazing!
for /L %i in (1,1,8) do call :rt 45 & (for /L %j in (1,1,4) do call :rt 90 & (for /L %k in (1,1,90) do call :pd & call :fd 2 & call :rt 2 & call :pu ) )
The 4 can be replaced with 1 to 7 for other flowers.
--------------------------------------------------------------------------------
My own design:
call :rt 18 & for /L %i in (1,1,5) do call :rt 72 & (for /L %j in (190,-10,10) do call :pd & (for /L %k in (1,1,5) do call :fd %j & call :rt 144) & call :pu)
--------------------------------------------------------------------------------
Another simple one:
set /A s=1,a=s/2 & call :lt 18 & (for /L %n in (1,1,7) do call :pd & (for /L %i in (1,1,5) do call :fd !s! & call :lt 144 & call :fd !s! & call :rt 72) & call :pu & call :fd !s! & call :lt 36 & set /A s0=s,s=s*2+a,a+=s0)
When there are several FOR commands nested in a long line, is convenient to change the position of the parentheses in order to clearly delimit the group of elements in each FOR (Logo style). For example, instead of "for /L %i in (1,1,N) do (call :first & call :second)" we may use "(for /L %i in (1,1,N) do call :first & call :second)".

This is the output that appeared in my screen after I executed four of the previous examples in a new graphics window each, and manually arranged the lay out:

Image

If you want to run a larger multi-line Batch Turtle Graphics program, just remove the part that appear between "rem Start of drawing commands" and "rem End of drawing commands" lines in previous Batch file and place your own code there. More examples coming soon...

Enjoy it! :D

Antonio

Meerkat
Posts: 89
Joined: 19 Jul 2015 02:27
Location: Philippines

Re: "Turtle Graphics" in Batch files!

#2 Post by Meerkat » 06 Sep 2015 23:07

It's simply amazing! :shock:

Now let me do some Batch art!!! :twisted:

Code: Select all

set "no=20" & for /l %i in (1,1,!no!) do (call :rt 360/!no! & for /L %k in (1,1,60) do call :pd & call :rt 6  & call :fd 7 & call :pu )

Image

You can change the value of !no! to change the number of circles to be displayed.

Meerkat

Aacini
Expert
Posts: 1730
Joined: 06 Dec 2011 22:15
Location: México City, México

Re: "Turtle Graphics" in Batch files!

#3 Post by Aacini » 15 Oct 2021 22:30

I updated the Batch Turtle Graphics package. I implemented the following subroutines:

Code: Select all

:setcolor color
:setpc color
echo ctx.strokeStyle="%~1"; >&3
exit /B

:setwidth value
:setpw value
echo ctx.lineWidth=%1; >&3
exit /B

:setbgcolor color
echo document.body.style.backgroundColor="%~1"; >&3
exit /B

:closeWindowAndActivate title
:closeWindow
:cw
echo closewindow;%~1; >&3
exit /B
The first three are Logo Turtle Graphics standard subroutines. closeWindow/closeWindowAndActivate is a new subroutine that closes the graphics window separating (unlinking) it from the cmd.exe father window. This point avoids some problems that happened when the graphics window didn't received graphics commands from the cmd.exe window for a long time period.

This subroutine have an optional parameter that is the title of a window that get the focus (be activated) after the graphics window is closed. This feature allows to get the control back to the cmd.exe window (the Batch file) after a graphic was completed.

These new subroutines are now included in the code posted at the first page of this thread, as usual.

NOTE: I discovered a subtle bug that happened when the last command sent to a graphics window is not a drawing command; for example, just rotate the turtle. In theses cases the HTA engine marks the ";" expected error or a similar one. Some graphics terminate in a rotate that may or may not be followed by a drawing. In these cases the cure is simple: enclose the rotate command between PenDown/PenUp commands like if it was a drawing command. For example:

Code: Select all

rem The following rotate is the last command in this graphic
rem (that could be used by *the next* graphic, if any)
call :penDown & call :right 90 & call :penUp
exit /B
Antonio

Aacini
Expert
Posts: 1730
Joined: 06 Dec 2011 22:15
Location: México City, México

Re: "Turtle Graphics" in Batch files!

#4 Post by Aacini » 16 Oct 2021 09:10

Drawing open Recursive Curves with Turtle Graphics

A Recursive or Fractal Curve is a figure generated from a base shape comprised of several straight line segments of the same size, like this one:

Image

For the purposes of this description, consider that this base figure is made up of 8 lines of the same size (two of which are drawn in sequence in the same direction), not by 6 short lines plus a seventh larger one. It is funny that a figure formed by straight lines be called "curve", but that is the nomenclature...

The basic rule to generate a Recursive Curve is simple: replace each segment of the base shape by a smaller copy of itself. For example, using previous figure:

Image

Perhaps the replacement could be appreciated in an easier way in the next figures that use the same color for the replacement of the same segments:

Image Image

The replacement rule can be applied again in the generated figure (in a recursive way) and so on for many times, with every time smaller lines. The number of times that the replacements are made is called recursive level or just level: the base figure is at level 1, the figure created after the first replacement is at recursive level 2, etc. All the figures created in this way are self-similar: the final curve resembles the original small curve in its distribution and parts. There are many different Recursive Curves, they are named after the person that first studied they or because the shape of the figure; the previous one is Minkowski.

Recursive Curves have interesting and unusual features. For example, when the recursive level is very high ("tends to infinity" is the mathematical term), no small piece of the figure is line-like, but rather it is composed of an infinite number of segments joined at different angles. For this reason, the length of the curve is infinite (it has countless number of points) and its dimension have a value between 1 and 2 (where a straigth line have dimension 1 and a plane a 2). This concept is called fractal dimension and reflects the fact that "a fractal line is an object too detailed to be one-dimensional, but too simple to be two-dimensional". Recursive curves may produce surprising figures at high recursive levels. For example, this is Minkowski at level 4:

Image

Another Recursive Curve is Peano. The next images show the base shape of Peano curve at level 1 with numbers indicating the order at which the segments are drawn, and after it the Peano curve at levels 2 and 4:

Image Image Image

The following images may help to understand how Peano level 2 is generated from level 1; they were drawn with the same color in the same replacement segments.

Image Image

Another interesting Recursive Curve is Hilbert:

Image

In Hilbert curve the base shape is like an upside-down "U" letter. In this case is not entirely clear how each segment of the figure must be replaced by a smaller and complete copy of the original curve. Also, note that the level 2 curve must fit in the same space of the level 1 one. For example, Peano level 2 fits with smaller lines in the same square than Peano level 1, and the same thing happens with Peano level 4 that, with more numerous and smaller lines, cover a larger area of the same square! As a matter of fact, if we increase the recursive level of Peano until the lines be 1 pixel long, then Peano at such a level will cover the entire plane (this is a feature of the space-filling family of recursive curves).

In order for Hilbert curve to change from level 1 to level 2 we introduce some additional lines in the replacement that aids the figure to conform the rules of Recursive Curves. The image below is Hilbert level 2 with such additional lines in white color and the "base" figures in yellow and blue. There is also a standard black and white Hilbert level 3:

Image Image

Besides the additional lines introduced, the change from Hilbert level 1 to level 2 requires to manage two different versions of the original shape: one for each starting point of the base shape. This is necessary because the method used to draw graphics is based on a "Turtle" that points towards a certain direction and such a Turtle draws a line when it walks "forward". This way, the "original" (left-to-right) figure could be traced by these steps: LINE, Right Turn 90°, LINE, Right Turn 90°, LINE, but the "reversed" (traced in the right-to-left opposite direction) version is this: LINE, Left Turn 90°, LINE, Left Turn 90°, LINE. In the above image the "original" figures are shown in yellow and the "reversed" ones in blue.

We could describe the change of Hilbert curve from level 1 to level 2 with these steps/definitions:

Code: Select all

"Right" = Right Turn 90°
"Left"  = Left Turn 90°

Original: Line, Right, Line, Right, Line
Reversed: Line, Left , Line, Left , Line

Hilbert level 1 = Original

Hilbert level 2: Right, Reversed, Right, Line, Original, Left, Line, Left, Original, Line, Right, Reversed, Right
You may follow these steps and match they with the corresponding lines in Hilbert curve level 2 image above. Remember that when Turtle graphic begins, the Turtle is pointing upwards (north direction).

Succesive replacements in higher recursive levels can be easily achieved if the definitions are based on the current level and two signs that control/interchange turn directions: + to right, or - to left. In this case is important that each "U" sub-graphic leave the Turtle pointing in the same direction it has when the sub-graphic begins (that is the reason for the "Turn 180°" step in the example below). In this way no matters how many recursive sub-graphic levels be executed, the general Turtle direction is always preserved and processed in the right way.

:Hilbert is a subroutine that is started with the desired recursive level and that will be called recursively with a smaller level value each time until it reaches 1. Also, the initial values for the signs are "+" and "-" of the original Hilbert shape. The shape in the opposite direction is selected with "-" and "+" (changed) signs.

Code: Select all

:Hilbert level sign1 sign2

Draw first "sub-U" (reversed)
if level > 1:  Turn sign1;  :Hilbert level-1 sign2 sign1;  Turn sign1

Draw line from first to second "sub-U":
LINE

Draw second "sub-U" (original)
if level > 1:  :Hilbert level-1 sign1 sign2;  Turn 180

rem Draw line from second to third "sub-U"'
Turn sign1;  LINE

Draw third "sub-U" (original)
if level > 1:  Turn sign2;  :Hilbert level-1 sign1 sign2;  Turn sign2

rem Draw line from third to fourth "sub-U"'
Turn sign1;  LINE

rem Draw fourth "sub-U" (reversed)
if level > 1:  Turn sign1;  :Hilbert level-1 sign2 sign1;  Turn sign1
If we review this pseudo-code we'll realize that when :Hilbert subroutine is invoked with level=1, the LINE segments draw the original Hilbert shape, but when the level is greater than 1, then the same LINE segments draw the (supposedly) additional lines we introduced before, and they are drawing in the same place of the original lines! This means that such a lines are in no way "additional", they are an inherent element of the recursive curve. (Recursive code is surprising! ;) )

You may review the complete Batch code implementation of this method in the attached program.

---------------------------------------------------------------

This is a variation of Peano curve, at levels 1 and 2:

Image Image

The same adjustment details of Hilbert curve apply in this Peano variation, but in this case an additional point is introduced: because the topology of the base shape, it is necessary to replicate the base figure in a 3 x 3 square when pass from level 1 to level 2 (instead of the 2 x 2 square used in Hilbert curve). Of course, this point introduces several operative details in the implementation of the method: the definition of the recursive subroutine consist of 9 "sub-figures" connected by 8 "additional" lines. You may review the complete code in the attached Batch file.


A very different Recursive Curve is Gosper. The base shape of this curve does not have 90° right angles, but 60° and 120° ones. Besides, the base shape is not simmetrical and looks incomplete and "broken" at level 1. However, it was specifically designed in order to generate an hexagonal figure from recursive levels 2 and higher. This means that the base shape is just 1/6 of an hexagonal figure that must be auto-replaced several times in order to generate a complete figure.

Image Image Image

Two of the most famous Recursive Curves are Dragon and Lévy-C curves. These are "slow growth" curves where you can reach very high recursive levels (like 12) and the curve is still clear, with many small details. Although these curves are created in the same way (replacing each segment with a copy of the complete curve), in the case of Dragon and Lévy-C it may be easier to go visually from one recursive level to the next by taking the curve, "unfolding" a full copy and flipping it around one end.

Image Image Image

You can physically build a Dragon curve out of a strip of paper. It is enough to put the strip on a table and fold it in half, and continue folding it like this several times always in the same direction: the right end over the left (or vice versa, but always the same); the number of folds is equal to the recursive level. If you want to reach a high recursive level, you must use very thin paper. Strongly mark the folds each time. At the end, unfold the strip and carefully arrange the folds so that they are all at 90 degrees. That's it!


The Lévy-C curve is my favorite:

Image Image Image

=======================================================


The next 3 curves are variations of Lévy-C that were developed by myself. The first one have one turn, the middle one, inverted with respect to standard Lévy-C. The result has "spines" facing out (instead of facing in, like in standard Lévy-C) and "crosses" instead of "squares". I like the result:

Image Image Image


These figures were traced with the same recursive levels of previous standard Lévy-C images, so you can compare both.

The next figure is comprised of two standard Lévy-C curves traced as mirror-images of each other and joined at the central point. I called "Doggy" the resulting figure:

Image Image

The last curve is two mirror-images facing each other like in previous "Doggy" curve, but using above Lévy-C variation this time. I called "Bulldog" the resulting figure:

Image Image

=======================================================


This is the Batch file program that allows to draw all the open Recursive Curves previously described (and many more):

Code: Select all

@if (@CodeSection == @Batch) @then

@echo off
setlocal EnableDelayedExpansion

:: TurtleGraphics.bat: Turtle Graphics in Batch files
:: Written by Antonio Perez Ayala aka Aacini
:: Turtle Graphics reference: http://www.logointerpreter.com/logo-reference/turtle-graphics-canvas.php
:: Canvas reference: http://www.w3schools.com/tags/ref_canvas.asp

:: RecursiveCurves.bat: Draw Recursive Curves using Turtle Graphics engine
:: - Bulldog (APA), Doggy (APA), Dragon, Gosper, Hilbert, Lévy-C, Lévy-var (APA), Minkowski, Peano, Sierpinski, Wirth

if "%~1" equ "TurtleGraphics" goto %1
"%~F0" TurtleGraphics 3>&1 1>&2 | CScript //nologo //E:JScript "%~F0"
goto :EOF



:TurtleGraphics
Title Draw Recursive Curves

set graphics="Minkowski=4" "Peano=4" "Hilbert=7" "peanO_var=4" "Gosper=5" "Dragon=12" ^
             "Levy-C=13" "levy-C_var=12" "doggY=12" "Bulldog=12" "Wirth=6" "Sierpinski=6"

set "spaces= " & for /L %%i in (1,1,6) do set "spaces=!spaces!!spaces!"
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
set "upcase=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
set "N=0" & set "graph[0]=:EOF" & set "opts="
for %%a in (%graphics%) do for /F "tokens=1,2 delims==" %%g in (%%a) do (
   set /A "N+=1"
   set "graph[!N!]=%%g"
   set "level[!N!]=%%h"
   set "opt="
   for /F "delims=" %%c in ('cmd /U /C echo %%g^| find /V ""') do if not defined opt (
      if "!upcase:%%c=%%c!" equ "%upcase%" set "opt=%%c" & if "%%c" equ "-" set "opt="
   )
   if not defined opt echo ERROR: Menu options *must* have an upcase letter & goto :EOF
   set "opts=!opts!!opt!"
)

:nextGraphic
(
cls
echo/
echo Draw Recursive Curves via Turtle Graphics
echo ==== ========= ====== === ====== ========
echo/
for /L %%i in (1,1,%N%) do echo   - !graph[%%i]!
echo/
echo   - eXit
echo/
)
choice /C X%opts% /N /M "Select curve: "
set /A "option=%errorlevel%-1, lines=N-option+3"
echo/
rem Move cursor home: https://www.dostips.com/forum/viewtopic.php?f=3&t=6760
timeout /T 1 > CON | cmd /Q /C for /F %%C in ('copy /Z "%~F0" NUL') do set /P "=.%%C%spaces%%%C"
echo/
echo/
for /L %%i in (1,1,%option%) do echo/   & REM 3 spaces here
echo ==^>
for /L %%i in (1,1,%lines%) do echo/   & REM 3 spaces here
echo/
echo/
echo Curve selected:  !graph[%option%]!  Maximum recursive level:  !level[%option%]!
goto !graph[%option%]!

============================================================

:Minkowski
echo/
choice /C 01234 /N /M "Enter level (0 to end): "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "winSize=562, size=winSize-50, size2=size/2"
for /L %%i in (1,1,%level%) do set /A "size/=4"
call :newWindow "MINKOWSKI  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%size2% 0 & call :rt 90
call :MinkowskiSub %level%
call :closeWindowAndActivate "Draw Recursive Curves"
goto Minkowski

:MinkowskiSub level
setlocal 
set /A "level=%1"

if %level% lss 1 (
   call :pd & call :fd %size% & call :pu
) else (
   for %%a in (+90 -90 -90 0 +90 +90 -90) do call :MinkowskiSub %level%-1 & call :rt %%a
   call :MinkowskiSub %level%-1
)
exit /B

============================================================

:Hilbert
echo/
choice /C 01234567 /N /M "Enter level (0 to end): "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "winSize=562, size=winSize-50, size2=size/2, size=size/((1<<level)-1)"
call :newWindow "HILBERT  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%size2% -%size2%
call :HilbertSub %level% + -
call :closeWindowAndActivate "Draw Recursive Curves"
goto Hilbert


:HilbertSub level sign1 sign2
setlocal
set /A "level=%1"

rem Draw first "sub-U"
if %level% gtr 1 (
   call :rt %290
   call :HilbertSub %level%-1 %3 %2
   call :rt %290
)

rem Draw line from first to second "sub-U"'s
call :pd & call :fd %size% & call :pu

rem Draw second "sub-U"
if %level% gtr 1 (
   call :HilbertSub %level%-1 %2 %3
   call :rt 180
)

rem Draw line from second to third "sub-U"'s
call :rt %290 & call :pd & call :fd %size% & call :pu

rem Draw third "sub-U"
if %level% gtr 1 (
   call :rt %390
   call :HilbertSub %level%-1 %2 %3
   call :rt %390
)

rem Draw line from third to fourth "sub-U"'s
call :rt %290 & call :pd & call :fd %size% & call :pu

rem Draw fourth "sub-U"
if %level% gtr 1 (
   call :rt %290
   call :HilbertSub %level%-1 %3 %2
   call :pd & call :rt %290 & call :pu
)

exit /B

============================================================

:Peano
echo/
choice /C 01234 /N /M "Enter level (0 to end): "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "winSize=508, size=winSize-50, size2=size/2, size=648" & REM size=sqrt(2*size*size) (and multiple of 3)
for /L %%i in (1,1,%level%) do set /A "size/=3"
call :newWindow "PEANO  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%size2% -%size2% & call :rt 45
call :PeanoSub %level%
call :closeWindowAndActivate "Draw Recursive Curves"
goto Peano

:PeanoSub level
setlocal 
set /A "level=%1"

if %level% lss 1 (
   call :pd & call :fd %size% & call :pu
) else (
   call :PeanoSub %level%-1
   for %%a in (-90 90 90 90 -90 -90 -90 ) do call :rt %%a & call :PeanoSub %level%-1
   call :rt 90
   call :PeanoSub %level%-1
)

exit /B

============================================================

:Peano_var
echo/
choice /C 01234 /N /M "Enter level (0 to end): "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "winSize=538, size=winSize-50, size2=size/2"
for /F "tokens=%level%" %%a in ("2 8 25 80") do set /A "size=size/%%a"
call :newWindow "PEANO_var  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%size2% -%size2%
call :Peano_varSub %level% + -
call :closeWindowAndActivate "Draw Recursive Curves"
goto Peano_var


:Peano_varSub level sign1 sign2
setlocal
set /A "level=%1"

rem Draw "sub-N" at bottom-left
if %level% gtr 1 call :Peano_varSub %level%-1 %2 %3

rem Draw first left vertical to-up line
call :pd & call :fd %size% & call :pu

rem Draw "sub-N" at middle-left
if %level% gtr 1 call :Peano_varSub %level%-1 %3 %2

rem Draw second left vertical to-up line
call :pd & call :fd %size% & call :pu

rem Draw "sub-N" at top-left
if %level% gtr 1 call :Peano_varSub %level%-1 %2 %3

rem Draw top horizontal to-right line
call :rt %290 & call :pd & call :fd %size% & call :pu & call :rt %290

rem Draw "sub-N" at top-middle
if %level% gtr 1 call :Peano_varSub %level%-1 %3 %2

rem Draw first middle vertical to-down line
call :pd & call :fd %size% & call :pu

rem Draw "sub-N" at middle-middle
if %level% gtr 1 call :Peano_varSub %level%-1 %2 %3

rem Draw second middle vertical to-down line
call :pd & call :fd %size% & call :pu

rem Draw "sub-N" at bottom-middle
if %level% gtr 1 call :Peano_varSub %level%-1 %3 %2

rem Draw bottom horizontal to-right line
call :rt %390 & call :pd & call :fd %size% & call :pu & call :rt %390

rem Draw "sub-N" at bottom-right
if %level% gtr 1 call :Peano_varSub %level%-1 %2 %3

rem Draw first right vertical to-up line
call :pd & call :fd %size% & call :pu

rem Draw "sub-N" at middle-right
if %level% gtr 1 call :Peano_varSub %level%-1 %3 %2

rem Draw second right vertical to-up line
call :pd & call :fd %size% & call :pu

rem Draw "sub-N" at top-right
if %level% gtr 1 call :Peano_varSub %level%-1 %2 %3

exit /B

============================================================

:Gosper
echo/
choice /C 012345 /N /M "Enter level (0 to end): "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "winSize=608, size=winSize-90, posX=size/2"
for /L %%i in (1,1,%level%) do set /A "size=(size+1)*1000000/2645751" & REM size=size/sqrt(7) (2.645751311)
if %level% equ 1 (set /A posY=-posX/4) else set /A "posY=posX/4*(level-1)" & if %level% equ 3 set /A "posY+=posX/12"
if %level% equ 4 (set /A posX-=posX/4) else if %level% equ 5 set /A "posX=posX/2+4, posY+=posY/12"
call :newWindow "GOSPER  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%posX% %posY% & call :rt 30
call :GosperSub %level% R
call :closeWindowAndActivate "Draw Recursive Curves"
goto Gosper

Version in Logo: https://en.wikipedia.org/wiki/Gosper_curve

to rg :st :ln
 make "st :st - 1
 make "ln :ln / sqrt 7
 if :st > 0 [rg :st :ln rt 60 gl :st :ln  rt 120 gl :st :ln lt 60 rg :st :ln lt 120 rg :st :ln rg :st :ln lt 60 gl :st :ln rt 60]
 if :st = 0 [fd :ln rt 60 fd :ln rt 120 fd :ln lt 60 fd :ln lt 120 fd :ln fd :ln lt 60 fd :ln rt 60]
end
 
to gl :st :ln
 make "st :st - 1
 make "ln :ln / sqrt 7
 if :st > 0 [lt 60 rg :st :ln rt 60 gl :st :ln gl :st :ln rt 120 gl :st :ln rt 60 rg :st :ln lt 120 rg :st :ln lt 60 gl :st :ln]
 if :st = 0 [lt 60 fd :ln rt 60 fd :ln fd :ln rt 120 fd :ln rt 60 fd :ln lt 120 fd :ln lt 60 fd :ln]
end

:GosperSub level type (R or L)
setlocal 
set /A "level=%1"

if %level% equ 1 (
   if %2 equ R (
      for %%a in (+60 +120 -60 -120 0 -60 +60) do call :pd & call :fd %size% & call :rt %%a & call :pu 
   ) else (
      for %%a in (-60 +60 0 +120 +60 -120 -60) do call :rt %%a & call :pd & call :fd %size% & call :pu
   )
) else (
   if %2 equ R (
      call :GosperSub %level%-1 R & call :rt 60 & call :GosperSub %level%-1 L & call :rt 120 & call :GosperSub %level%-1 L & call :rt -60 & call :GosperSub %level%-1 R & call :rt -120 & call :GosperSub %level%-1 R & call :rt 0 & call :GosperSub %level%-1 R & call :rt -60 & call :GosperSub %level%-1 L & CALL :pd & call :rt 60 & CALL :pu
   ) else (
      call :rt -60 & call :GosperSub %level%-1 R & call :rt 60 & call :GosperSub %level%-1 L & call :rt 0 & call :GosperSub %level%-1 L & call :rt 120 & call :GosperSub %level%-1 L & call :rt 60 & call :GosperSub %level%-1 R & call :rt -120 & call :GosperSub %level%-1 R & call :rt -60 & call :GosperSub %level%-1 L
   )
)

exit /B

============================================================

:Dragon
echo/
choice /C 0123456789ABC /N /M "Enter level; use A,B,C letters for 10,11,12: "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "odd=level&1, lev2=level+odd, p=0"
for /L %%i in (2,2,12) do set /A "parts[%%i]=p=p*2+2"
set /A "winSize=558, size=winSize-50, posX=size/3, posY=size/8, size=(odd*1414213+^!odd)*size/parts[%lev2%]/(odd*1000000+^!odd)"
if %level% leq 4 set /A "size=size*3/4"
call :newWindow "DRAGON  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%posX% %posY% & call :rt 90
call :DragonSub %level% + -
call :closeWindowAndActivate "Draw Recursive Curves"
goto Dragon


:DragonSub level sign1 sign2
setlocal
set /A "level=%1"

rem At last level: draw segment
if %level% equ 0 (
   call :pd & call :fd %size% & call :pu
) else (

   rem Turn 45° in one direction and draw left side
   call :rt %245 & call :DragonSub %level%-1 - +

   rem Turn 90° in the opposite direction and draw right side
   call :rt %390 & call :DragonSub %level%-1 + -

   rem Re-Turn 45° to the original direction
   CALL :pd & call :rt %245 & CALL :pu

)

exit /B

============================================================

:Levy-C
echo/
choice /C 0123456789ABCD /N /M "Enter level; use A,B,C,D letters for 10,11,12,13: "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "odd=level&1, lev2=level+odd, p=0"
for /L %%i in (2,2,13) do set /A "parts[%%i]=p=p*2+2"
set /A "winSize=558, size=winSize-50, posY=size/3, size=(odd*1414213+^!odd)*size/parts[%lev2%]/(odd*1000000+^!odd)"
if %level% leq 2 set /A "size=size*3/4"
if %level% gtr 3 set /A "posY-=posY/7"
call :newWindow "LEVY-C  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy 0 -%posY%
call :Levy-CSub %level%
call :closeWindowAndActivate "Draw Recursive Curves"
goto Levy-C


:Levy-CSub level
setlocal
set /A "level=%1"

rem At last level: draw segment
if %level% equ 0 (
   call :pd & call :fd %size% & call :pu
) else (

   rem Turn 45° left and draw left side
   call :rt -45 & call :Levy-CSub %level%-1

   rem Turn 90° right and draw right side
   call :rt 90 & call :Levy-CSub %level%-1

   rem Re-Turn 45° left to the original direction
   CALL :pd & call :rt -45 & CALL :pu

)

exit /B

============================================================

:Levy-C_var   Variation of Lévy-C by Antonio Perez Ayala
echo/
choice /C 0123456789ABCD /N /M "Enter level; use A,B,C letters for 10,11,12: "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "odd=level&1, lev2=level+odd, p=0"
for /L %%i in (2,2,12) do set /A "parts[%%i]=p=p*2+2"
set /A "winSize=558, size=winSize-50, posY=size/3, size=(odd*1414213+^!odd)*size/parts[%lev2%]/(odd*1000000+^!odd)"
if %level% leq 2 set /A "size=size*3/4"
if %level% gtr 3 set /A "posY-=posY/7"
call :newWindow "LEVY-C_var  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy 0 -%posY% & call :rt 90
call :Levy-C_varSub %level%
call :closeWindowAndActivate "Draw Recursive Curves"
goto Levy-C_var


:Levy-C_varSub level
setlocal
set /A "level=%1"

if %level% equ 0 (
   call :pd & call :fd %size% & call :pu
) else (

   call :rt -45 & call :Levy-C_varSub %level%-1

   rem Variation: middle turn is in the opposite direction
   call :rt -90 & call :Levy-C_varSub %level%-1

   CALL :pd & call :rt -45 & CALL :pu

)

exit /B

============================================================

:Doggy   Created by Antonio Perez Ayala
echo/
choice /C 0123456789ABC /N /M "Enter level; use A,B,C letters for 10,11,12: "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "odd=level&1, lev2=level+odd, p=0"
for /L %%i in (2,2,12) do set /A "parts[%%i]=p=p*2+2"
set /A "winSize=558, size=winSize-50, posX=size/4, size=(odd*1414213+^!odd)*size/parts[%lev2%]/(odd*1000000+^!odd)"
if %level% leq 4 set /A "size=size*3/4"
call :newWindow "DOGGY  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%posX% 0 & call :rt 90
call :DoggySub %level% + -
call :closeWindowAndActivate "Draw Recursive Curves"
goto Doggy


:DoggySub level sign1 sign2
setlocal
set /A "level=%1"

if %level% equ 0 (
   call :pd & call :fd %size% & call :pu
) else (

   call :rt %245 & call :DoggySub %level%-1 - +

   call :rt %390 & call :DoggySub %level%-1 - +

   CALL :pd & call :rt %245 & CALL :pu

)

exit /B

============================================================

:Bulldog   Created by Antonio Perez Ayala
echo/
choice /C 0123456789ABC /N /M "Enter level; use A,B,C letters for 10,11,12: "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "odd=level&1, lev2=level+odd, p=0"
for /L %%i in (2,2,12) do set /A "parts[%%i]=p=p*2+2"
set /A "winSize=558, size=winSize-50, posX=size/4, size=(odd*1414213+^!odd)*size/parts[%lev2%]/(odd*1000000+^!odd)"
if %level% leq 4 set /A "size=size*3/4"
call :newWindow "BULLDOG  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy -%posX% 0 & call :rt 180
call :BulldogSub %level% + -
call :closeWindowAndActivate "Draw Recursive Curves"
goto Bulldog


:BulldogSub level sign1 sign2
setlocal
set /A "level=%1"

rem At last level: draw segment
if %level% equ 0 (
   call :pd & call :fd %size% & call :pu
) else (

   call :rt %245 & call :BulldogSub %level%-1 - +

   rem Variation: middle turn is in the opposite direction
   call :rt %290 & call :BulldogSub %level%-1 - +

   CALL :pd & call :rt %245 & CALL :pu

)

exit /B

============================================================

:Wirth
echo/
choice /C 0123456 /N /M "Enter level (0 to end): "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "winSize=562, size=winSize-50, posY=size/2, size=size/((1<<(level+1))-1), posX=posY-size"
call :newWindow "WIRTH  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy %posX% %posY%
for /L %%i in (1,1,4) do call :WirthSub %level% & call :pd & call :rt -90 & call :fd %size% & call :rt 90 & call :fd %size% & call :pu
call :closeWindowAndActivate "Draw Recursive Curves"
goto Wirth


:WirthSub level
setlocal
set /A "level=%1"

rem At last level: draw last segment
if %level% equ 1 (
   call :pd & call :rt -90 & call :fd %size% & call :pu
   exit /B
)

rem Draw first sub-side
call :WirthSub %level%-1

rem Draw first connection corner
call :pd & call :rt -90 & call :fd %size% & call :rt 90 & call :fd %size% & call :pu

rem Draw second sub-side
call :WirthSub %level%-1

rem Draw middle connection line
call :pd & call :rt 90 & call :fd %size% & call :pu

rem Set-up and draw third sub-side
call :rt 180 & call :WirthSub %level%-1

rem Draw last connection corner
call :pd & call :rt -90 & call :fd %size% & call :rt 90 & call :fd %size% & call :pu

rem Draw fourth sub-side
call :WirthSub %level%-1

exit /B

============================================================

:Sierpinski
echo/
choice /C 0123456 /N /M "Enter level (0 to end): "
set /A "level=%errorlevel%-1"
echo/
if %level% equ 0 goto nextGraphic

set /A "winSize=572, size=winSize-60, posX=size/2+10, size=size/((1<<(level+1))-1), diag=size*3/4, posY=posX-diag*3/4"
call :newWindow "SIERPINSKI  @  Level=%level%  Line=%size%" %winSize% %winSize%
call :setxy %posX% -%posY% & call :rt 45
for /L %%i in (1,1,4) do call :SierpinskiSub %level% & call :pd & call :rt -90 & call :fd %diag% & call :pu
call :closeWindowAndActivate "Draw Recursive Curves"
goto Sierpinski

:SierpinskiSub level
setlocal
set /A "level=%1"

rem Draw first segment
if %level% gtr 1 call :SierpinskiSub %level%-1

rem Draw first connection diagonal
call :pd & call :rt -90 & call :fd %diag% & call :pu

rem Draw second segment
if %level% gtr 1 call :SierpinskiSub %level%-1

rem Draw horiz/vert connection line
call :pd & call :rt 45 & call :fd %size% & call :pu

rem Set-up and draw third segment
if %level% gtr 1 call :rt 135 & call :SierpinskiSub %level%-1 & call :rt -135

rem Draw second connection diagonal
call :pd & call :rt 45 & call :fd %diag% & call :pu

rem Draw fourth segment
if %level% gtr 1 call :SierpinskiSub %level%-1 

exit /B

============================================================

Turtle Graphics support subroutines, simplified version for this Recursive Curves application

:newWindow title width height
:nw
echo openwindow;%~2;%~3;%~1 >&3
exit /B

:penDown
:pd
echo ctx.beginPath(); ctx.moveTo(0,0); >&3
exit /B

:forward pixels
:fd
echo ctx.lineTo(0,-(%~1)); ctx.translate(0,-(%~1)); >&3
exit /B

:right degrees
:rt
echo ctx.rotate((%~1)*Math.PI/180); >&3
exit /B

:penUp
:pu
echo ctx.stroke(); >&3
echo/>&3
exit /B

:setxy x y
:xy
echo ctx.moveTo(%~1,-(%~2)); ctx.translate(%~1,-(%~2)); >&3
exit /B

===== New version 2 subroutines =====

:closeWindowAndActivate [title]
:closeWindow
:cw
echo closewindow;%~1; >&3
exit /B

:setcolor color
:setpc
echo ctx.strokeStyle="%~1"; >&3
exit /B

:setwidth value
:setpw
echo ctx.lineWidth=%1; >&3
exit /B

:setbgcolor color
echo document.body.style.backgroundColor="%~1"; >&3
exit /B

:setheading degrees   NOT WORKS YET!!!
:seth
echo ctx.transform(1,0,0,1,0,0); sinAng=Math.sin((%~1)*Math.PI/180); ctx.transform(1,sinAng,-sinAng,1,0,0); >&3
exit /B


============================================================

:: End of Batch section


@end


// Start of JScript section

var fso      = new ActiveXObject("Scripting.FileSystemObject"),
    WshShell = new ActiveXObject("WScript.Shell"),
    HTA      = { Win: false }, command;

// Create the HTA file
HTA.FullName = WScript.ScriptFullName.replace(WScript.ScriptName,"BatchTurtleGraphics.hta");
HTA.File     = fso.CreateTextFile(HTA.FullName,true);
HTA.File.WriteLine(
   "<meta http-equiv='x-ua-compatible' content='ie=edge'/>\r\n" +
   "<html> <head><HTA:APPLICATION></head>\r\n" +
   "<script language='JavaScript'>\r\n" +
   "var fso   = new ActiveXObject('Scripting.FileSystemObject'),\r\n" +
   "    stdin = fso.GetStandardStream(0), command, canvas, ctx;\r\n" +
   "fso.GetStandardStream(1).WriteLine();  // send confirmation\r\n" +
   "eval(stdin.ReadLine());  // get initialization\r\n" +
   "function evalCommands() {while (command=stdin.ReadLine()) eval(command);}\r\n" +
   "window.onkeydown = evalCommands;\r\n" +
   "</script>\r\n" +
   "</html>\r\n"
);
HTA.File.Close();

// Receive commands from Batch section and send they to the HTA window
while ( ! WScript.Stdin.AtEndOfStream ) {

   command = WScript.Stdin.ReadLine();

   if ( command.substr(0,11) == "openwindow;" ) {

      // command = "openwindow;width;height;title" - create a new mshta.exe .HTA window

      // If is there a previous open window: leave it in the screen
      if ( HTA.Win ) {
         HTA.Win.Stdin.WriteLine("ignorekey=function () { }; window.onkeydown=ignorekey;\r\n");
         WshShell.AppActivate(HTA.Win.ProcessID);
         WshShell.SendKeys(" ");
      }

      // Open the HTA window and wait for confirmation
      HTA.Win = WshShell.Exec('mshta.exe "'+HTA.FullName+'"');
      HTA.Win.Stdout.ReadLine();

      // Initialize the contents of the HTA window
      var p = command.split(";"), width = p[1], height = p[2], title=p[3];
      command = "\"<title>"+title+"</title> " +
                  "<style type='text/css'>body {color:white; background:black;}</style> " +
                  "<canvas id='BTGcanvas' width='"+width+"' height='"+height+"' style='border:1px solid;'>" +
                     "Your browser don't support the HTML5 Canvas tag" +
                  "</canvas>\"";
      HTA.Win.Stdin.WriteLine(
         "window.resizeTo("+width+"+34,"+height+"+64); "   +   // try: 34..40 64..82
         "document.body.innerHTML = "+command+"; "         +
         "canvas = document.getElementById('BTGcanvas'); " +
         "ctx = canvas.getContext('2d'); "                 +
         "ctx.translate("+width+"/2,"+height+"/2);"        +
         "ctx.strokeStyle = 'white';"
      );

   } else if ( command.substr(0,11) == "closewindow" ) {

   // Close the active HTA window
   HTA.Win.Stdin.WriteLine("ignorekey=function () { }; window.onkeydown=ignorekey;\r\n");
   WshShell.AppActivate(HTA.Win.ProcessID);
   WshShell.SendKeys(" ");
   HTA.Win = false;

   // ... and activate the application indicated in the optional parameter
   p = command.split(";"); title=p[1];
   if ( title ) { WshShell.AppActivate(title); }

   } else {

      // Read and send commands to the (active) HTA window
      // until an "end of block" mark (empty line) is received
      while ( command ) {
         HTA.Win.Stdin.WriteLine(command);
         command = WScript.Stdin.ReadLine();
      }

      // Raise the event for "window.onkeydown" function in the HTA window
      HTA.Win.Stdin.WriteLine();			//    Send the "end of block" mark (empty line)
      WshShell.AppActivate(HTA.Win.ProcessID);		//    Set focus on the HTA window
      WshShell.SendKeys(" ");				//    And do a "key down"

   }

}

// Close the HTA window                            Or v
// HTA.Win.Stdin.WriteLine("window.close();\r\n");
// Leave the last window on the screen             Or ^
HTA.Win.Stdin.WriteLine("ignorekey=function () { }; window.onkeydown=ignorekey;\r\n");

WshShell.AppActivate(HTA.Win.ProcessID);
WshShell.SendKeys(" ");

// Delete the HTA file
command = HTA.FullName;
HTA = { Win: false };
fso.DeleteFile(command);
Enjoy it!

Antonio

PS - In my humble opinion, one aspect that allows to enjoy the construction of a Recursive Curve is to see the way it is drawn: see how the convoluted line advances for unexpected paths or get back to fill the gaps we think were left missing... If a Recursive Curve drawing program is written in a programming language that run fast, then this aspect of the curve construction is lost. Batch-file programming language is inherently slow, so I think is ideal for this application. If you have a very fast computer, it is suggested to insert some delays in the code.

Squashman
Expert
Posts: 4356
Joined: 23 Dec 2011 13:59

Re: "Turtle Graphics" in Batch files!

#5 Post by Squashman » 16 Oct 2021 10:34

Trurtle Graphics was my first computer class I took as a child many decades ago. Probably on the original Apple computers. This does peak my interest from an athlete perspective.

I am thinking I could write a script to read the .FIT file from my cycling computer and call out to the Turtle Graphics script to plot heart rate, cadence and power as line graphs. It really wouldn't serve any practical purpose as there are already dozens of free programs that do this. It would be a fun and challenging exercise.

Post Reply