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

Translation of: Go

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

  1. include <cmath>
  2. include <fstream>
  3. include <iostream>
  4. 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

Works with: Factor version 0.99 2020-08-14

<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:

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

Go

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. <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

Translation of: C++

<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

Translation of: C++

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>

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

Library: Phix/pGUI

<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>

Raku

(formerly Perl 6)

Works with: Rakudo version 2020.02

<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

Translation of: Go
Library: DOME

<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>

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