Sierpinski curve: Difference between revisions
(Added solution for Action!) |
|||
Line 1,359: | Line 1,359: | ||
var Game = SierpinskiCurve.new(770, 770, 5, Color.blue, Color.yellow)</lang> |
var Game = SierpinskiCurve.new(770, 770, 5, Color.blue, Color.yellow)</lang> |
||
=={{header|Yabasic}}== |
|||
<lang Yabasic>// Rosetta Code problem: http://rosettacode.org/wiki/Sierpinski_curve |
|||
// Adapted from https://www.ocg.at/sites/ocg.at/files/EuroLogo2001/P74Batagelj.pdf to Yabasic by Galileo, 01/2022 |
|||
import turtle |
|||
sub Sierp(n, a, h, k) |
|||
if n = 0 move(k) : return |
|||
turn(a) : Sierp(n - 1, -a, h, k) : turn(-a) : move(h) |
|||
turn(-a) : Sierp(n - 1, -a, h, k) : turn(a) |
|||
end sub |
|||
sub Sierpinski(n, d) |
|||
local i |
|||
pen(false) |
|||
goxy(10, 680) |
|||
pen(true) |
|||
color 255, 255, 0 |
|||
for i = 1 to 4 |
|||
Sierp(n, 45, d/sqrt(2), 5*d/6) |
|||
turn(45) |
|||
move(d/sqrt(2)) |
|||
turn(45) |
|||
next |
|||
end sub |
|||
startTurtle() |
|||
Sierpinski(9, 12) </lang> |
|||
=={{header|zkl}}== |
=={{header|zkl}}== |
Revision as of 08:58, 11 January 2022
- Task
Produce a graphical or ASCII-art representation of a Sierpinski curve of at least order 3.
Action!
Action! language does not support recursion. Therefore an iterative approach with a stack has been proposed. <lang Action!>DEFINE C_="10+" DEFINE N_="20+" DEFINE E_="30+" DEFINE S_="40+" DEFINE W_="50+" DEFINE SafePlot="BYTE inside inside=InsideScreen() IF inside THEN Plot(x,y) FI" DEFINE SafeDrawTo="IF inside=1 AND InsideScreen()=1 THEN DrawTo(x,y) FI" DEFINE Next="Push(state+1,level)" DEFINE DrawN="Push(21,level-1)" DEFINE DrawE="Push(31,level-1)" DEFINE DrawS="Push(41,level-1)" DEFINE DrawW="Push(51,level-1)"
INT x,y,stackSize
DEFINE MAX_COUNT="100" BYTE ARRAY stack(MAX_COUNT)
PROC InitStack()
stackSize=0
RETURN
BYTE FUNC IsEmpty()
IF stackSize=0 THEN RETURN (1) FI
RETURN (0)
PROC Push(BYTE state,level)
stack(stackSize)=state stackSize==+1 stack(stackSize)=level stackSize==+1
RETURN
PROC Pop(BYTE POINTER state,level)
stackSize==-1 level^=stack(stackSize) stackSize==-1 state^=stack(stackSize)
RETURN
BYTE FUNC InsideScreen()
IF x<0 OR y<0 OR x>319 OR y>191 THEN RETURN (0) FI
RETURN (1)
PROC LineN()
SafePlot y==-4 SafeDrawTo
RETURN
PROC LineNE()
SafePlot x==+2 y==-2 SafeDrawTo
RETURN
PROC LineE()
SafePlot x==+4 SafeDrawTo
RETURN
PROC LineSE()
SafePlot x==+2 y==+2 SafeDrawTo
RETURN
PROC LineS()
SafePlot y==+4 SafeDrawTo
RETURN
PROC LineSW()
SafePlot x==-2 y==+2 SafeDrawTo
RETURN
PROC LineW()
SafePlot x==-4 SafeDrawTo
RETURN
PROC LineNW()
SafePlot x==-2 y==-2 SafeDrawTo
RETURN
PROC SierpinskiCurve(BYTE level)
BYTE state InitStack() Push(C_ 1,level+1) WHILE IsEmpty()=0 DO Pop(@state,@level) IF state=C_ 1 THEN Next DrawN ELSEIF state=C_ 2 THEN LineNE() Next DrawE ELSEIF state=C_ 3 THEN LineSE() Next DrawS ELSEIF state=C_ 4 THEN LineSW() Next DrawW ELSEIF state=C_ 5 THEN LineNW() ELSEIF state=N_ 1 THEN IF level=1 THEN LineNE() LineN() LineNW() ELSE Next DrawN FI ELSEIF state=N_ 2 THEN LineNE() Next DrawE ELSEIF state=N_ 3 THEN LineN() Next DrawW ELSEIF state=N_ 4 THEN LineNW() DrawN ELSEIF state=E_ 1 THEN IF level=1 THEN LineSE() LineE() LineNE() ELSE Next DrawE FI ELSEIF state=E_ 2 THEN LineSE() Next DrawS ELSEIF state=E_ 3 THEN LineE() Next DrawN ELSEIF state=E_ 4 THEN LineNE() DrawE ELSEIF state=S_ 1 THEN IF level=1 THEN LineSW() LineS() LineSE() ELSE Next DrawS FI ELSEIF state=S_ 2 THEN LineSW() Next DrawW ELSEIF state=S_ 3 THEN LineS() Next DrawE ELSEIF state=S_ 4 THEN LineSE() DrawS ELSEIF state=W_ 1 THEN IF level=1 THEN LineNW() LineW() LineSW() ELSE Next DrawW FI ELSEIF state=W_ 2 THEN LineNW() Next DrawN ELSEIF state=W_ 3 THEN LineW() Next DrawS ELSEIF state=W_ 4 THEN LineSW() DrawW ELSE Break() FI OD
RETURN
PROC Main()
BYTE CH=$02FC,COLOR1=$02C5,COLOR2=$02C6
Graphics(8+16) Color=1 COLOR1=$0C COLOR2=$02
x=1 y=187 SierpinskiCurve(6)
DO UNTIL CH#$FF OD CH=$FF
RETURN</lang>
- Output:
Screenshot from Atari 8-bit computer
AutoHotkey
Requires Gdip Library <lang AutoHotkey>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 </lang>
C++
Output is a file in SVG format. The curve is generated using the Lindenmayer system method. <lang cpp>// 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;
}</lang>
- Output:
See: sierpinski_curve.svg (offsite SVG image)
Factor
<lang factor>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</lang>
When using the L-system visualizer, the following controls apply:
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 |
Button | Command |
---|---|
x | iterate L-system |
Fōrmulæ
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
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. <lang go>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")
}</lang>
Java
<lang java>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;
}</lang>
- Output:
See: sierpinski_curve.svg (offsite SVG image)
Julia
Turtle procedural (lineto) version
Modified from Craft of Coding blog, Processing version <lang Julia>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() </lang>
LSystem version
<lang julia>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
) </lang>
Mathematica / Wolfram Language
<lang Mathematica>Graphics[SierpinskiCurve[3]]</lang>
Nim
We produce a SVG file using same algorithm as the one of C++ version. <lang Nim>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()</lang>
- Output:
Same as C++ output.
Perl
<lang perl>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;</lang> See: sierpinski-curve.svg (offsite SVG image)
Phix
<lang Phix>-- 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()</lang>
Quackery
<lang Quackery> [ $ "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</lang>
- Output:
Raku
(formerly Perl 6)
<lang perl6>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>, ], ],
);</lang> See: Sierpinski-curve-perl6.svg (offsite SVG image)
Rust
Program output is a file in SVG format. <lang rust>// [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();
}</lang>
- Output:
See: sierpinski_curve.svg (offsite SVG image)
Sidef
Uses the LSystem() class from Hilbert curve. <lang ruby>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)</lang> Output image: Sierpiński curve
Wren
<lang ecmascript>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)</lang>
Yabasic
<lang Yabasic>// Rosetta Code problem: http://rosettacode.org/wiki/Sierpinski_curve // Adapted from https://www.ocg.at/sites/ocg.at/files/EuroLogo2001/P74Batagelj.pdf to Yabasic by Galileo, 01/2022
import turtle
sub Sierp(n, a, h, k)
if n = 0 move(k) : return turn(a) : Sierp(n - 1, -a, h, k) : turn(-a) : move(h) turn(-a) : Sierp(n - 1, -a, h, k) : turn(a)
end sub
sub Sierpinski(n, d)
local i pen(false) goxy(10, 680) pen(true) color 255, 255, 0 for i = 1 to 4 Sierp(n, 45, d/sqrt(2), 5*d/6) turn(45) move(d/sqrt(2)) turn(45) next
end sub
startTurtle() Sierpinski(9, 12) </lang>
zkl
Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl <lang 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");
}</lang>
- Output:
Offsite image at Sierpinski curve order 5