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:
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:
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:
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:
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:
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.
Another interesting Recursive Curve is
Hilbert:
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:
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:
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.
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.
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:
=======================================================
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:
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:
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:
=======================================================
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.