I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

Sierpinski curve

From Rosetta Code
Sierpinski curve is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.


Task

Produce a graphical or ASCII-art representation of a Sierpinski curve of at least order 3.

AutoHotkey[edit]

Translation of: Go

Requires Gdip Library

SierpinskiW	:= 500
SierpinskiH := 500
level := 5
cx := SierpinskiW/2
cy := SierpinskiH
h := cx / (2**(level+1))
Arr := []
squareCurve(level)
xmin := xmax := ymin := ymax := 0
for i, point in Arr
{
xmin := A_Index = 1 ? point.x : xmin < point.x ? xmin : point.x
xmax := point.x > xmax ? point.x : xmax
ymin := A_Index = 1 ? point.y : ymin < point.y ? ymin : point.y
ymax := point.y > ymax ? point.y : ymax
}
SierpinskiX := A_ScreenWidth/2 - (xmax-xmin)/2 , SierpinskiY := A_ScreenHeight/2 - (ymax-ymin)/2
for i, point in Arr
points .= point.x - xmin + SierpinskiX "," point.y - ymin + SierpinskiY "|"
points := Trim(points, "|")
gdip1()
Gdip_DrawLines(G, pPen, Points)
UpdateLayeredWindow(hwnd1, hdc, 0, 0, Width, Height)
return
 
; ---------------------------------------------------------------
lineTo(newX, newY) {
global
Arr[Arr.count()+1, "x"] := newX-SierpinskiW/2+h
Arr[Arr.count() , "y"] := SierpinskiH-newY+2*h
cx := newX
cy := newY
}
; ---------------------------------------------------------------
sierN(level) {
global
if (level = 1) {
lineTo(cx+h, cy-h) ; lineNE()
lineTo(cx, cy-2*h) ; lineN()
lineTo(cx-h, cy-h) ; lineNW()
}
else{
sierN(level - 1)
lineTo(cx+h, cy-h) ; lineNE()
sierE(level - 1)
lineTo(cx, cy-2*h) ; lineN()
sierW(level - 1)
lineTo(cx-h, cy-h) ; lineNW()
sierN(level - 1)
}
}
; ---------------------------------------------------------------
sierE(level) {
global
if (level = 1) {
lineTo(cx+h, cy+h) ; lineSE()
lineTo(cx+2*h, cy) ; lineE()
lineTo(cx+h, cy-h) ; lineNE()
}
else {
sierE(level - 1)
lineTo(cx+h, cy+h) ; lineSE()
sierS(level - 1)
lineTo(cx+2*h, cy) ; lineE()
sierN(level - 1)
lineTo(cx+h, cy-h) ; lineNE()
sierE(level - 1)
}
}
; ---------------------------------------------------------------
sierS(level) {
global
if (level = 1) {
lineTo(cx-h, cy+h) ; lineSW()
lineTo(cx, cy+2*h) ; lineS()
lineTo(cx+h, cy+h) ; lineSE()
}
else {
sierS(level - 1)
lineTo(cx-h, cy+h) ; lineSW()
sierW(level - 1)
lineTo(cx, cy+2*h) ; lineS()
sierE(level - 1)
lineTo(cx+h, cy+h) ; lineSE()
sierS(level - 1)
}
}
; ---------------------------------------------------------------
sierW(level) {
global
if (level = 1) {
lineTo(cx-h, cy-h) ; lineNW()
lineTo(cx-2*h, cy) ; lineW()
lineTo(cx-h, cy+h) ; lineSW()
}
else {
sierW(level - 1)
lineTo(cx-h, cy-h) ; lineNW()
sierN(level - 1)
lineTo(cx-2*h, cy) ; lineW()
sierS(level - 1)
lineTo(cx-h, cy+h) ; lineSW()
sierW(level - 1)
}
}
; ---------------------------------------------------------------
squareCurve(level) {
global
sierN(level)
lineTo(cx+h, cy-h) ; lineNE()
sierE(level)
lineTo(cx+h, cy+h) ; lineSE()
sierS(level)
lineTo(cx-h, cy+h) ; lineSW()
sierW(level)
lineTo(cx-h, cy-h) ; lineNW()
lineTo(cx+h, cy-h) ; lineNE()
}
 
; ---------------------------------------------------------------
gdip1(){
global
If !pToken := Gdip_Startup()
{
MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
ExitApp
}
OnExit, Exit
Width := A_ScreenWidth, Height := A_ScreenHeight
Gui, 1: -Caption +E0x80000 +LastFound +OwnDialogs +Owner +AlwaysOnTop
Gui, 1: Show, NA
hwnd1 := WinExist()
hbm := CreateDIBSection(Width, Height)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)
Gdip_SetSmoothingMode(G, 4)
pPen := Gdip_CreatePen(0xFFFF0000, 2)
}
; ---------------------------------------------------------------
gdip2(){
global
Gdip_DeleteBrush(pBrush)
Gdip_DeletePen(pPen)
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)
}
; ---------------------------------------------------------------
Exit:
gdip2()
Gdip_Shutdown(pToken)
ExitApp
Return
 

C++[edit]

Output is a file in SVG format. The curve is generated using the Lindenmayer system method.

// See https://en.wikipedia.org/wiki/Sierpi%C5%84ski_curve#Representation_as_Lindenmayer_system
#include <cmath>
#include <fstream>
#include <iostream>
#include <string>
 
class sierpinski_curve {
public:
void write(std::ostream& out, int size, int length, int order);
private:
static std::string rewrite(const std::string& s);
void line(std::ostream& out);
void execute(std::ostream& out, const std::string& s);
double x_;
double y_;
int angle_;
int length_;
};
 
void sierpinski_curve::write(std::ostream& out, int size, int length, int order) {
length_ = length;
x_ = length/std::sqrt(2.0);
y_ = 2 * x_;
angle_ = 45;
out << "<svg xmlns='http://www.w3.org/2000/svg' width='"
<< size << "' height='" << size << "'>\n";
out << "<rect width='100%' height='100%' fill='white'/>\n";
out << "<path stroke-width='1' stroke='black' fill='none' d='";
std::string s = "F--XF--F--XF";
for (int i = 0; i < order; ++i)
s = rewrite(s);
execute(out, s);
out << "'/>\n</svg>\n";
}
 
std::string sierpinski_curve::rewrite(const std::string& s) {
std::string t;
for (char c : s) {
if (c == 'X')
t += "XF+G+XF--F--XF+G+X";
else
t += c;
}
return t;
}
 
void sierpinski_curve::line(std::ostream& out) {
double theta = (3.14159265359 * angle_)/180.0;
x_ += length_ * std::cos(theta);
y_ -= length_ * std::sin(theta);
out << " L" << x_ << ',' << y_;
}
 
void sierpinski_curve::execute(std::ostream& out, const std::string& s) {
out << 'M' << x_ << ',' << y_;
for (char c : s) {
switch (c) {
case 'F':
case 'G':
line(out);
break;
case '+':
angle_ = (angle_ + 45) % 360;
break;
case '-':
angle_ = (angle_ - 45) % 360;
break;
}
}
}
 
int main() {
std::ofstream out("sierpinski_curve.svg");
if (!out) {
std::cerr << "Cannot open output file\n";
return 1;
}
sierpinski_curve s;
s.write(out, 545, 7, 5);
return 0;
}
Output:

See: sierpinski_curve.svg (offsite SVG image)

Factor[edit]

Works with: Factor version 0.99 2020-08-14
USING: accessors kernel L-system sequences ui ;
 
: curve ( L-system -- L-system )
L-parser-dialect
{ "G" [ dup length>> draw-forward ] } suffix >>commands
[ 45 >>angle ] >>turtle-values
"F--XF--F--XF" >>axiom
{
{ "X" "XF+G+XF--F--XF+G+X" }
} >>rules ;
 
[ <L-system> curve "Sierpinski curve" open-window ] with-ui


When using the L-system visualizer, the following controls apply:

Camera controls
Button Command
a zoom in
z zoom out
left arrow turn left
right arrow turn right
up arrow pitch down
down arrow pitch up
q roll left
w roll right
Other controls
Button Command
x iterate L-system

Fōrmulæ[edit]

Fōrmulæ programs are not textual, visualization/edition of programs is done showing/manipulating structures but not text. Moreover, there can be multiple visual representations of the same program. Even though it is possible to have textual representation —i.e. XML, JSON— they are intended for storage and transfer purposes more than visualization and edition.

Programs in Fōrmulæ are created/edited online in its website, However they run on execution servers. By default remote servers are used, but they are limited in memory and processing power, since they are intended for demonstration and casual use. A local server can be downloaded and installed, it has no limitations (it runs in your own computer). Because of that, example programs can be fully visualized and edited, but some of them will not run if they require a moderate or heavy computation/memory resources, and no local server is being used.

In this page you can see the program(s) related to this task and their results.

Go[edit]

Library: Go Graphics
Translation of: Phix

A partial translation anyway which produces a static image of a SC of level 5, yellow on blue, which can be viewed with a utility such as EOG.

package main
 
import (
"github.com/fogleman/gg"
"math"
)
 
var (
width = 770.0
height = 770.0
dc = gg.NewContext(int(width), int(height))
)
 
var cx, cy, h float64
 
func lineTo(newX, newY float64) {
dc.LineTo(newX-width/2+h, height-newY+2*h)
cx, cy = newX, newY
}
 
func lineN() { lineTo(cx, cy-2*h) }
func lineS() { lineTo(cx, cy+2*h) }
func lineE() { lineTo(cx+2*h, cy) }
func lineW() { lineTo(cx-2*h, cy) }
 
func lineNW() { lineTo(cx-h, cy-h) }
func lineNE() { lineTo(cx+h, cy-h) }
func lineSE() { lineTo(cx+h, cy+h) }
func lineSW() { lineTo(cx-h, cy+h) }
 
func sierN(level int) {
if level == 1 {
lineNE()
lineN()
lineNW()
} else {
sierN(level - 1)
lineNE()
sierE(level - 1)
lineN()
sierW(level - 1)
lineNW()
sierN(level - 1)
}
}
 
func sierE(level int) {
if level == 1 {
lineSE()
lineE()
lineNE()
} else {
sierE(level - 1)
lineSE()
sierS(level - 1)
lineE()
sierN(level - 1)
lineNE()
sierE(level - 1)
}
}
 
func sierS(level int) {
if level == 1 {
lineSW()
lineS()
lineSE()
} else {
sierS(level - 1)
lineSW()
sierW(level - 1)
lineS()
sierE(level - 1)
lineSE()
sierS(level - 1)
}
}
 
func sierW(level int) {
if level == 1 {
lineNW()
lineW()
lineSW()
} else {
sierW(level - 1)
lineNW()
sierN(level - 1)
lineW()
sierS(level - 1)
lineSW()
sierW(level - 1)
}
}
 
func squareCurve(level int) {
sierN(level)
lineNE()
sierE(level)
lineSE()
sierS(level)
lineSW()
sierW(level)
lineNW()
lineNE() // needed to close the square in the top left hand corner
}
 
func main() {
dc.SetRGB(0, 0, 1) // blue background
dc.Clear()
level := 5
cx, cy = width/2, height
h = cx / math.Pow(2, float64(level+1))
squareCurve(level)
dc.SetRGB255(255, 255, 0) // yellow curve
dc.SetLineWidth(2)
dc.Stroke()
dc.SavePNG("sierpinski_curve.png")
}

Java[edit]

Translation of: C++
import java.io.*;
 
public class SierpinskiCurve {
public static void main(final String[] args) {
try (Writer writer = new BufferedWriter(new FileWriter("sierpinski_curve.svg"))) {
SierpinskiCurve s = new SierpinskiCurve(writer);
s.currentAngle = 45;
s.currentX = 5;
s.currentY = 10;
s.lineLength = 7;
s.begin(545);
s.execute(rewrite(5));
s.end();
} catch (final Exception ex) {
ex.printStackTrace();
}
}
 
private SierpinskiCurve(final Writer writer) {
this.writer = writer;
}
 
private void begin(final int size) throws IOException {
write("<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>\n", size, size);
write("<rect width='100%%' height='100%%' fill='white'/>\n");
write("<path stroke-width='1' stroke='black' fill='none' d='");
}
 
private void end() throws IOException {
write("'/>\n</svg>\n");
}
 
private void execute(final String s) throws IOException {
write("M%g,%g\n", currentX, currentY);
for (int i = 0, n = s.length(); i < n; ++i) {
switch (s.charAt(i)) {
case 'F':
case 'G':
line(lineLength);
break;
case '+':
turn(ANGLE);
break;
case '-':
turn(-ANGLE);
break;
}
}
}
 
private void line(final double length) throws IOException {
final double theta = (Math.PI * currentAngle) / 180.0;
currentX += length * Math.cos(theta);
currentY -= length * Math.sin(theta);
write("L%g,%g\n", currentX, currentY);
}
 
private void turn(final int angle) {
currentAngle = (currentAngle + angle) % 360;
}
 
private void write(final String format, final Object... args) throws IOException {
writer.write(String.format(format, args));
}
 
private static String rewrite(final int order) {
String s = AXIOM;
for (int i = 0; i < order; ++i) {
final StringBuilder sb = new StringBuilder();
for (int j = 0, n = s.length(); j < n; ++j) {
final char ch = s.charAt(j);
if (ch == 'X')
sb.append(PRODUCTION);
else
sb.append(ch);
}
s = sb.toString();
}
return s;
}
 
private final Writer writer;
private double lineLength;
private double currentX;
private double currentY;
private int currentAngle;
 
private static final String AXIOM = "F--XF--F--XF";
private static final String PRODUCTION = "XF+G+XF--F--XF+G+X";
private static final int ANGLE = 45;
}
Output:

See: sierpinski_curve.svg (offsite SVG image)

Julia[edit]

Turtle procedural (lineto) version[edit]

Modified from Craft of Coding blog, Processing version

using Luxor
 
function sierpinski_curve(x0, y0, h, level)
x1, y1 = x0, y0
lineto(x, y) = begin line(Point(x1, y1), Point(x, y), :stroke); x1, y1 = x, y end
lineN() = lineto(x1,y1-2*h)
lineS() = lineto(x1,y1+2*h)
lineE() = lineto(x1+2*h,y1)
lineW() = lineto(x1-2*h,y1)
lineNW() = lineto(x1-h,y1-h)
lineNE() = lineto(x1+h,y1-h)
lineSE() = lineto(x1+h,y1+h)
lineSW() = lineto(x1-h,y1+h)
function drawN(i)
if i == 1
lineNE(); lineN(); lineNW()
else
drawN(i-1); lineNE(); drawE(i-1); lineN(); drawW(i-1); lineNW(); drawN(i-1)
end
end
function drawE(i)
if i == 1
lineSE(); lineE(); lineNE()
else
drawE(i-1); lineSE(); drawS(i-1); lineE(); drawN(i-1); lineNE(); drawE(i-1)
end
end
function drawS(i)
if i == 1
lineSW(); lineS(); lineSE()
else
drawS(i-1); lineSW(); drawW(i-1); lineS(); drawE(i-1); lineSE(); drawS(i-1)
end
end
function drawW(i)
if i == 1
lineNW(); lineW(); lineSW()
else
drawW(i-1); lineNW(); drawN(i-1); lineW(); drawS(i-1); lineSW(); drawW(i-1)
end
end
function draw_curve(levl)
drawN(levl); lineNE(); drawE(levl); lineSE()
drawS(levl); lineSW(); drawW(levl); lineNW()
end
draw_curve(level)
end
 
Drawing(800, 800)
sierpinski_curve(10, 790, 3, 6)
finish()
preview()
 

LSystem version[edit]

using Lindenmayer # https://github.com/cormullion/Lindenmayer.jl
 
sierpcurve = LSystem(Dict("X" => "XF+G+XF--F--XF+G+X"), "F--XF--F--XF")
 
drawLSystem(sierpcurve,
forward = 10,
turn = 45,
startingpen= (0.2, 0.8, 0.8),
startingx = -380,
startingy = 380,
startingorientation = π/4,
iterations = 5,
filename = "sierpinski_curve.png",
showpreview = true
)
 

Mathematica / Wolfram Language[edit]

Graphics[SierpinskiCurve[3]]

Nim[edit]

Translation of: C++

We produce a SVG file using same algorithm as the one of C++ version.

import math
 
type
 
SierpinskiCurve = object
x, y: float
angle: float
length: int
file: File
 
 
proc line(sc: var SierpinskiCurve) =
let theta = degToRad(sc.angle)
sc.x += sc.length.toFloat * cos(theta)
sc.y -= sc.length.toFloat * sin(theta)
sc.file.write " L", sc.x, ',', sc.y
 
 
proc execute(sc: var SierpinskiCurve; s: string) =
sc.file.write 'M', sc.x, ',', sc.y
for c in s:
case c
of 'F', 'G': sc.line()
of '+': sc.angle = floorMod(sc.angle + 45, 360)
of '-': sc.angle = floorMod(sc.angle - 45, 360)
else: discard
 
 
func rewrite(s: string): string =
for c in s:
if c == 'X':
result.add "XF+G+XF--F--XF+G+X"
else:
result.add c
 
 
proc write(sc: var SierpinskiCurve; size, length, order: int) =
sc.length = length
sc.x = length.toFloat / sqrt(2.0)
sc.y = 2 * sc.x
sc.angle = 45
sc.file.write "<svg xmlns='http://www.w3.org/2000/svg' width='", size, "' height='", size, "'>\n"
sc.file.write "<rect width='100%' height='100%' fill='white'/>\n"
sc.file.write "<path stroke-width='1' stroke='black' fill='none' d='"
var s = "F--XF--F--XF"
for _ in 1..order: s = s.rewrite()
sc.execute(s)
sc.file.write "'/>\n</svg>\n"
 
 
let outfile = open("sierpinski_curve.svg", fmWrite)
var sc = SierpinskiCurve(file: outfile)
sc.write(545, 7, 5)
outfile.close()
Output:

Same as C++ output.

Perl[edit]

use strict;
use warnings;
use SVG;
use List::Util qw(max min);
 
use constant pi => 2 * atan2(1, 0);
 
my $rule = 'XF+F+XF--F--XF+F+X';
my $S = 'F--F--XF--F--XF';
$S =~ s/X/$rule/g for 1..5;
 
my (@X, @Y);
my ($x, $y) = (0, 0);
my $theta = pi/4;
my $r = 6;
 
for (split //, $S) {
if (/F/) {
push @X, sprintf "%.0f", $x;
push @Y, sprintf "%.0f", $y;
$x += $r * cos($theta);
$y += $r * sin($theta);
}
elsif (/\+/) { $theta += pi/4; }
elsif (/\-/) { $theta -= pi/4; }
}
 
my ($xrng, $yrng) = ( max(@X) - min(@X), max(@Y) - min(@Y));
my ($xt, $yt) = (-min(@X) + 10, -min(@Y) + 10);
 
my $svg = SVG->new(width=>$xrng+20, height=>$yrng+20);
my $points = $svg->get_path(x=>\@X, y=>\@Y, -type=>'polyline');
$svg->rect(width=>"100%", height=>"100%", style=>{'fill'=>'black'});
$svg->polyline(%$points, style=>{'stroke'=>'orange', 'stroke-width'=>1}, transform=>"translate($xt,$yt)");
 
open my $fh, '>', 'sierpinski-curve.svg';
print $fh $svg->xmlify(-namespace=>'svg');
close $fh;

See: sierpinski-curve.svg (offsite SVG image)

Phix[edit]

Library: Phix/pGUI
-- demo\rosetta\Sierpinski_curve.exw
--
-- Draws curves lo to hi (simultaneously), initially {1,1}, max {8,8}
-- Press +/- to change hi, shift +/- to change lo.
-- ("=_" are also mapped to "+-", for the non-numpad +/-)
--
include pGUI.e
 
Ihandle dlg, canvas
cdCanvas cddbuffer, cdcanvas
 
integer width, height,
lo = 1, hi = 1
atom cx, cy, h
 
procedure lineTo(atom newX, newY)
cdCanvasVertex(cddbuffer, newX-width/2+h, height-newY+2*h)
cx = newX
cy = newY
end procedure
 
procedure lineN() lineTo(cx,cy-2*h) end procedure
procedure lineS() lineTo(cx,cy+2*h) end procedure
procedure lineE() lineTo(cx+2*h,cy) end procedure
procedure lineW() lineTo(cx-2*h,cy) end procedure
 
procedure lineNW() lineTo(cx-h,cy-h) end procedure
procedure lineNE() lineTo(cx+h,cy-h) end procedure
procedure lineSE() lineTo(cx+h,cy+h) end procedure
procedure lineSW() lineTo(cx-h,cy+h) end procedure
 
procedure sierN(integer level)
if level=1 then
lineNE() lineN()
lineNW()
else
sierN(level-1) lineNE()
sierE(level-1) lineN()
sierW(level-1) lineNW()
sierN(level-1)
end if
end procedure
 
procedure sierE(integer level)
if level=1 then
lineSE() lineE()
lineNE()
else
sierE(level-1) lineSE()
sierS(level-1) lineE()
sierN(level-1) lineNE()
sierE(level-1)
end if
end procedure
 
procedure sierS(integer level)
if level=1 then
lineSW() lineS()
lineSE()
else
sierS(level-1) lineSW()
sierW(level-1) lineS()
sierE(level-1) lineSE()
sierS(level-1)
end if
end procedure
 
procedure sierW(integer level)
if level=1 then
lineNW() lineW()
lineSW()
else
sierW(level-1) lineNW()
sierN(level-1) lineW()
sierS(level-1) lineSW()
sierW(level-1)
end if
end procedure
 
procedure sierpinskiCurve(integer level)
sierN(level) lineNE()
sierE(level) lineSE()
sierS(level) lineSW()
sierW(level) lineNW()
end procedure
 
function redraw_cb(Ihandle /*ih*/, integer /*posx*/, integer /*posy*/)
{width, height} = IupGetIntInt(canvas, "DRAWSIZE")
cdCanvasActivate(cddbuffer)
for level=lo to hi do
cx = width/2
cy = height
h = cx/power(2,level+1)
cdCanvasBegin(cddbuffer, CD_CLOSED_LINES)
sierpinskiCurve(level)
cdCanvasEnd(cddbuffer)
end for
cdCanvasFlush(cddbuffer)
return IUP_DEFAULT
end function
 
function map_cb(Ihandle ih)
cdcanvas = cdCreateCanvas(CD_IUP, ih)
cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)
cdCanvasSetBackground(cddbuffer, CD_WHITE)
cdCanvasSetForeground(cddbuffer, CD_BLUE)
return IUP_DEFAULT
end function
 
function key_cb(Ihandle /*ih*/, atom c)
if c=K_ESC then return IUP_CLOSE end if
if find(c,"+=-_") then
bool bShift = IupGetInt(NULL,"SHIFTKEY")
if c='+' or c='=' then
if bShift then
lo = min(lo+1,hi)
else
hi = min(8,hi+1)
end if
elsif c='-' or c='_' then
if bShift then
lo = max(1,lo-1)
else
hi = max(lo,hi-1)
end if
end if
IupSetStrAttribute(dlg, "TITLE", "Sierpinski curve (%d..%d)",{lo,hi})
cdCanvasClear(cddbuffer)
IupUpdate(canvas)
end if
return IUP_CONTINUE
end function
 
procedure main()
IupOpen()
 
canvas = IupCanvas(NULL)
IupSetAttribute(canvas, "RASTERSIZE", "770x770")
IupSetCallback(canvas, "MAP_CB", Icallback("map_cb"))
IupSetCallback(canvas, "ACTION", Icallback("redraw_cb"))
 
dlg = IupDialog(canvas)
IupSetAttribute(dlg, "TITLE", "Sierpinski curve (1..1)")
IupSetCallback(dlg, "K_ANY", Icallback("key_cb"))
 
IupMap(dlg)
IupShowXY(dlg,IUP_CENTER,IUP_CENTER)
IupMainLoop()
IupClose()
end procedure
 
main()

Quackery[edit]

  [ $ "turtleduck.qky" loadfile ] now!
 
[ stack ] is switch.arg ( --> [ )
 
[ switch.arg put ] is switch ( x --> )
 
[ switch.arg release ] is otherwise ( --> )
 
[ switch.arg share
 != iff ]else[ done
otherwise ]'[ do ]done[ ] is case ( x --> )
 
[ $ "" swap witheach
[ nested quackery join ] ] is expand ( $ --> $ )
 
[ $ "L" ] is L ( $ --> $ )
 
[ $ "R" ] is R ( $ --> $ )
 
[ $ "F" ] is F ( $ --> $ )
 
[ $ "G" ] is G ( $ --> $ )
 
[ $ "AFLGLAFRRFRRAFLGLA" ] is A ( $ --> $ )
 
$ "FRRAFRRFRRAF"
 
5 times expand
 
turtle 1 8 turn
witheach
[ switch
[ char L case [ -1 8 turn ]
char R case [ 1 8 turn ]
char A case [ ( ignore ) ]
otherwise [ 5 1 walk ] ] ]
-1 8 turn
Output:

https://imgur.com/bDBjJzb

Raku[edit]

(formerly Perl 6)

Works with: Rakudo version 2020.02
use SVG;
 
role Lindenmayer {
has %.rules;
method succ {
self.comb.map( { %!rules{$^c} // $c } ).join but Lindenmayer(%!rules)
}
}
 
my $sierpinski = 'F--XF--F--XF' but Lindenmayer( { X => 'XF+G+XF--F--XF+G+X' } );
 
$sierpinski++ xx 5;
 
my $dim = 640;
my $scale = 8;
my $dir = pi/4;
my @points = (316, -108);
 
for $sierpinski.comb {
state ($x, $y) = @points[0,1];
state $d = 0;
when 'F'|'G' { @points.append: ($x += $scale * $d.cos).round(1), ($y += $scale * $d.sin).round(1) }
when '+' { $d -= $dir }
when '-' { $d += $dir }
default { }
}
 
my $out = './sierpinski-curve-perl6.svg'.IO;
 
$out.spurt: SVG.serialize(
svg => [
:width($dim), :height($dim),
:rect[:width<100%>, :height<100%>, :fill<black>],
:polyline[
:points(@points.join: ','), :fill<black>,
:transform("rotate(45, 320, 320)"), :style<stroke:#F7DF1E>,
],
],
);

See: Sierpinski-curve-perl6.svg (offsite SVG image)

Rust[edit]

Program output is a file in SVG format.

// [dependencies]
// svg = "0.8.0"
 
use svg::node::element::path::Data;
use svg::node::element::Path;
 
struct SierpinskiCurve {
current_x: f64,
current_y: f64,
current_angle: i32,
line_length: f64,
}
 
impl SierpinskiCurve {
fn new(x: f64, y: f64, length: f64, angle: i32) -> SierpinskiCurve {
SierpinskiCurve {
current_x: x,
current_y: y,
current_angle: angle,
line_length: length,
}
}
fn rewrite(order: usize) -> String {
let mut str = String::from("F--XF--F--XF");
for _ in 0..order {
let mut tmp = String::new();
for ch in str.chars() {
match ch {
'X' => tmp.push_str("XF+G+XF--F--XF+G+X"),
_ => tmp.push(ch),
}
}
str = tmp;
}
str
}
fn execute(&mut self, order: usize) -> Path {
let mut data = Data::new().move_to((self.current_x, self.current_y));
for ch in SierpinskiCurve::rewrite(order).chars() {
match ch {
'F' => data = self.draw_line(data),
'G' => data = self.draw_line(data),
'+' => self.turn(45),
'-' => self.turn(-45),
_ => {}
}
}
Path::new()
.set("fill", "none")
.set("stroke", "black")
.set("stroke-width", "1")
.set("d", data)
}
fn draw_line(&mut self, data: Data) -> Data {
let theta = (self.current_angle as f64).to_radians();
self.current_x += self.line_length * theta.cos();
self.current_y -= self.line_length * theta.sin();
data.line_to((self.current_x, self.current_y))
}
fn turn(&mut self, angle: i32) {
self.current_angle = (self.current_angle + angle) % 360;
}
fn save(file: &str, size: usize, order: usize) -> std::io::Result<()> {
use svg::node::element::Rectangle;
let x = 5.0;
let y = 10.0;
let rect = Rectangle::new()
.set("width", "100%")
.set("height", "100%")
.set("fill", "white");
let mut s = SierpinskiCurve::new(x, y, 7.0, 45);
let document = svg::Document::new()
.set("width", size)
.set("height", size)
.add(rect)
.add(s.execute(order));
svg::save(file, &document)
}
}
 
fn main() {
SierpinskiCurve::save("sierpinski_curve.svg", 545, 5).unwrap();
}
Output:

See: sierpinski_curve.svg (offsite SVG image)

Sidef[edit]

Uses the LSystem() class from Hilbert curve.

var rules = Hash(
x => 'xF+G+xF--F--xF+G+x',
)
 
var lsys = LSystem(
width: 550,
height: 550,
 
xoff: -9,
yoff: -271,
 
len: 5,
angle: 45,
color: 'dark green',
)
 
lsys.execute('F--xF--F--xF', 5, "sierpiński_curve.png", rules)

Output image: Sierpiński curve

Wren[edit]

Translation of: Go
Library: DOME
import "graphics" for Canvas, Color
import "dome" for Window
 
var PX = 0
var PY = 0
var CX = 0
var CY = 0
var H = 0
 
class SierpinskiCurve {
construct new(width, height, level, back, fore) {
Window.title = "Sierpinski Curve"
Window.resize(width, height)
Canvas.resize(width, height)
_w = width
_h = height
_l = level
_bc = back
_fc = fore
}
 
init() {
Canvas.cls(Color.blue)
CX = _w /2
CY = _h
H = CX / 2.pow(_l + 1)
PX = CX - _w/2 + 2*H
PY = _h - CY + 3*H
squareCurve(_l)
}
 
lineTo(newX, newY) {
Canvas.line(PX, PY, PX = newX - _w/2 + H, PY = _h - newY + 2*H, _fc, 2)
CX = newX
CY = newY
}
 
lineN() { lineTo(CX, CY - 2*H) }
lineS() { lineTo(CX, CY + 2*H) }
lineE() { lineTo(CX + 2*H, CY) }
lineW() { lineTo(CX - 2*H, CY) }
 
lineNW() { lineTo(CX - H, CY - H) }
lineNE() { lineTo(CX + H, CY - H) }
lineSE() { lineTo(CX + H, CY + H) }
lineSW() { lineTo(CX - H, CY + H) }
 
sierN(level) {
if (level == 1) {
lineNE()
lineN()
lineNW()
} else {
sierN(level - 1)
lineNE()
sierE(level - 1)
lineN()
sierW(level - 1)
lineNW()
sierN(level - 1)
}
}
 
sierE(level) {
if (level == 1) {
lineSE()
lineE()
lineNE()
} else {
sierE(level - 1)
lineSE()
sierS(level - 1)
lineE()
sierN(level - 1)
lineNE()
sierE(level - 1)
}
}
 
sierS(level) {
if (level == 1) {
lineSW()
lineS()
lineSE()
} else {
sierS(level - 1)
lineSW()
sierW(level - 1)
lineS()
sierE(level - 1)
lineSE()
sierS(level - 1)
}
}
 
sierW(level) {
if (level == 1) {
lineNW()
lineW()
lineSW()
} else {
sierW(level - 1)
lineNW()
sierN(level - 1)
lineW()
sierS(level - 1)
lineSW()
sierW(level - 1)
}
}
 
squareCurve(level) {
sierN(level)
lineNE()
sierE(level)
lineSE()
sierS(level)
lineSW()
sierW(level)
lineNW()
lineNE() // needed to close the square in the top left hand corner
}
 
update() {}
 
draw(alpha) {}
}
 
var Game = SierpinskiCurve.new(770, 770, 5, Color.blue, Color.yellow)

zkl[edit]

Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl

sierpinskiCurve(5) : turtle(_,45,45);	// n=5 --> 11,606 characters
 
fcn sierpinskiCurve(order){
LSystem("F--XF--F--XF",Dictionary("X","XF+G+XF--F--XF+G+X"), order)
}
fcn LSystem(axiom,rules,order){ // Lindenmayer system
buf1,buf2 := Data(Void,axiom).howza(3), Data().howza(3); // characters
do(order){
buf1.pump(buf2.clear(),'wrap(c){ rules.find(c,c) }); // change if rule
t:=buf1; buf1=buf2; buf2=t; // swap buffers
}
buf1
}
 
fcn turtle(curve,angle,startAngle){ // angles in degrees
const D=10.0;
dir:=startAngle;
img,color := PPM(800,800), 0x00ff00; // green on black
x,y := 15, img.h - x;
foreach c in (curve){
switch(c){
case("F","G"){ // draw forward
a,b := D.toRectangular(dir.toFloat().toRad());
img.line(x,y, (x+=a.round()),(y+=b.round()), color)
}
case("+"){ dir=(dir + angle)%360; } // turn left angle
case("-"){ dir=(dir - angle)%360; } // turn right angle
}
}
img.writeJPGFile("sierpinskiCurve.zkl.jpg");
}
Output:

Offsite image at Sierpinski curve order 5