"Turtle Graphics" in Batch files!
Posted: 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.
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:
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".
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:
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!
Antonio
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);
- 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.
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)
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:
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!
Antonio