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.

11l

Translation of: C++
T SierpinskiCurve
   . Float x, y
   . Int angle, length

   . F line(out)
      V theta = radians(Float(.angle))
      .x += .length * cos(theta)
      .y -= .length * sin(theta)
      out.write(‘ L’gconvfmt(.x)‘,’gconvfmt(.y))

   . F execute(out, s)
      out.write(‘M’gconvfmt(.x)‘,’gconvfmt(.y))
      L(c) s
         S c
            ‘F’, ‘G’
               .line(out)
            ‘+’
               .angle = (.angle + 45) % 360
            ‘-’
               .angle = (.angle - 45) % 360

   . F :rewrite(s)
      V t = ‘’
      L(c) s
         I c == ‘X’
            t ‘’= ‘XF+G+XF--F--XF+G+X’
         E
            t ‘’= c
      R t

   F write(out, size, length, order)
      .length = length
      .x = length / sqrt(2)
      .y = .x * 2
      .angle = 45
      out.write(‘<svg xmlns='http://www.w3.org/2000/svg' width='’size‘' height='’size"'>\n")
      out.write("<rect width='100%' height='100%' fill='white'/>\n")
      out.write(‘<path stroke-width='1' stroke='black' fill='none' d='’)
      V s = ‘F--XF--F--XF’
      L 0 .< order
         s = .:rewrite(s)
      .execute(out, s)
      out.write("'/>\n</svg>\n")

V out = File(‘sierpinski_curve.svg’, ‘w’)
SierpinskiCurve().write(out, 545, 7, 5)
Output:

Same as C++ output.

Action!

Action! language does not support recursion. Therefore an iterative approach with a stack has been proposed.

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

Screenshot from Atari 8-bit computer

ALGOL W

Using code from the Sierpinski arrowhead curve task.
Curve algorithm based on the XPL0 sample.

begin % draw sierpinski curves using ascii art %
    integer CANVAS_WIDTH;
    CANVAS_WIDTH := 200;
    begin
        % the ascii art canvas and related items %
        string(1) array canvas ( 1 :: CANVAS_WIDTH, 1 :: CANVAS_WIDTH );
        integer         heading, asciiX, asciiY, width, maxX, maxY, minX, minY;
        % draw a line using ascii art - the length is ignored and the heading determines the %
        %                               character to use                                     %
        % the position is updated                                                            %
        procedure drawLine( real value length ) ;
            begin
                % stores the min and max coordinates %
                procedure updateCoordinateRange ;
                    begin
                        if asciiX > maxX then maxX := asciiX;
                        if asciiY > maxY then maxY := asciiY;
                        if asciiX < minX then minX := asciiX;
                        if asciiY < minY then minY := asciiY
                    end updateCoordinateRange ;
                if      heading =   0 then begin
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "_";
                    asciiX := asciiX + 1
                    end
                else if heading =  45 then begin
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "/";
                    asciiY := asciiY - 1;
                    asciiX := asciiX + 1
                    end
                else if heading =  90 then begin
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "|";
                    asciiY := asciiY - 1
                    end
                else if heading = 135 then begin
                    asciiX := asciiX - 1;
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "\";
                    asciiY := asciiY - 1
                    end
                else if heading = 180 then begin
                    asciiX := asciiX - 1;
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "_"
                    end
                else if heading = 225 then begin
                    asciiX := asciiX - 1;
                    asciiY := asciiY + 1;
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "/"
                    end
                else if heading = 270 then begin
                    asciiY := asciiY + 1;
                    updateCoordinateRange;
                    canvas( asciiX - 1, asciiY ) := "|";
                    end
                else if heading = 315 then begin
                    asciiY := asciiY + 1;
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "\";
                    asciiX := asciiX + 1
                end if_various_headings
            end drawLine ;
        % changes the heading by the specified angle ( in degrees ) - angle must be +/- 45 %
        procedure turn( integer value angle ) ;
            if angle > 0
            then heading := ( heading + angle ) rem 360
            else begin
                 heading := heading + angle;
                 if heading < 0 then heading := heading + 360
            end tuen ;
        % initialises the ascii art canvas %
        procedure initArt ;
            begin
                heading :=   0;
                asciiX  :=  CANVAS_WIDTH div 2;
                asciiY  := asciiX;
                maxX    := asciiX;
                maxY    := asciiY;
                minX    := asciiX;
                minY    := asciiY;
                for x := 1 until CANVAS_WIDTH do for y := 1 until CANVAS_WIDTH do canvas( x, y ) := " "
            end initArt ;
        % shows the used parts of the canvas %
        procedure drawArt ;
            begin
                for y := minY until maxY do begin
                    write();
                    for x := minX until maxX do writeon( canvas( x, y ) )
                end for_y ;
                write()
            end drawIArt ;
        % draws a sierpinski curve of the specified order and line length %
        procedure sierpinskiCurve( integer value order ) ;
            begin
                % recursively draws a segment of the sierpinski curve %
                procedure curve( integer value order; integer value angle ) ;
                    if 0 not = order then begin
                        turn( + angle );
                        curve( order - 1, - angle );
                        turn( - angle );
                        drawline( 1 );
                        if heading rem 180 = 0 then drawline( 1 );
                        turn( - angle );
                        curve( order - 1, - angle );
                        turn( + angle );
                    end curve ;
                for Quad := 1 until 4 do begin
                    curve( order * 2, 45 );
                    turn( 45 );
                    drawline( 1 );
                    if heading rem 180 = 0 then drawline( 1 );
                    turn( 45 );
                end for_Quad
            end sierpinskiCurve ;
        % draw curves %
        i_w := 1; s_w := 0; % set output formatting %
        for order := 3 do begin
            write( "Sierpinski curve of order ", order );
            write( "===========================" );
            write();
            initArt;
            sierpinskiCurve( order );
            drawArt
        end for_order
    end
end.
Output:
Sierpinski curve of order 3
===========================

/\__/\  /\__/\  /\__/\  /\__/\
\    /  \    /  \    /  \    /
|    |  |    |  |    |  |    |
/ __ \__/ __ \  / __ \__/ __ \
\/  \    /  \/  \/  \    /  \/
    |    |          |    |
/\__/ __ \__/\  /\__/ __ \__/\
\    /  \    /  \    /  \    /
|    |  |    |  |    |  |    |
/ __ \  / __ \__/ __ \  / __ \
\/  \/  \/  \    /  \/  \/  \/
            |    |
/\__/\  /\__/ __ \__/\  /\__/\
\    /  \    /  \    /  \    /
|    |  |    |  |    |  |    |
/ __ \__/ __ \  / __ \__/ __ \
\/  \    /  \/  \/  \    /  \/
    |    |          |    |
/\__/ __ \__/\  /\__/ __ \__/\
\    /  \    /  \    /  \    /
|    |  |    |  |    |  |    |
/ __ \  / __ \  / __ \  / __ \
\/  \/  \/  \/  \/  \/  \/  \/

AutoHotkey

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

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:

Media:Sierpinski_curve_cpp.svg

Factor

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æ

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.

In this page you can see and run the program(s) related to this task and their results. You can also change either the programs or the parameters they are called with, for experimentation, but remember that these programs were created with the main purpose of showing a clear solution of the task, and they generally lack any kind of validation.

Solution

File:Fōrmulæ - Sierpiński curve 01.png

Test cases

File:Fōrmulæ - Sierpiński curve 02.png

File:Fōrmulæ - Sierpiński curve 03.png

FreeBASIC

Translation of: XPL0
#define pi  4 * Atn(1)
#define yellow  Rgb(255,255,0)

Dim Shared As Integer posX, posY
Dim Shared As Single direc

Sub Dibuja(largo As Integer)
    posX += Fix(largo * Cos(direc))
    posY -= Fix(largo * Sin(direc))
    Line - (posX, posY), yellow
End Sub

Sub Curva(orden As Integer, angulo As Single, long1 As Single, long2 As Single)
    If orden <> 0 Then
        direc += angulo
        Curva(orden-1, -angulo, long1, long2)
        direc -= angulo
        Dibuja(long1)
        direc -= angulo
        Curva(orden-1, -angulo, long1, long2)
        direc += angulo
    End If
End Sub

Screenres 640, 480, 32

Dim As Single ang45 = pi / 4
Dim As Byte orden = 3
Dim As Byte tam = 20
direc = 0
posX = 640/4  
posY = 3*480/4
Pset (posX, posY)

For c As Byte = 1 To 4
    Curva(orden*2, ang45, tam/Sqr(2), 5*tam/6)
    direc += ang45
    Dibuja(tam/Sqr(2))
    direc += ang45
Next

Windowtitle "Hit any key to end program"
Sleep

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.

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

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:

Media:Sierpinski_curve_java.svg

jq

Works with: jq

Works with gojq, the Go implementation of jq

This entry uses an L-system and turtle graphics to generate an SVG file which can be viewed using a web browser, at least if the file type is `.svg`. The SVG viewBox is dynamically sized.

See Category_talk:Jq-turtle for the turtle.jq module used here. Please note that the `include` directive may need to be modified depending on the location of the included file, and the command-line options used.

include "turtle" {search: "."};

# Compute the curve using a Lindenmayer system of rules
def rules:
  { X: "XF+G+XF--F--XF+G+X",
   "": "F--XF--F--XF" };

def sierpinski($count):
  rules as $rules
  | def p($count):
      if $count == 0 then .
      else gsub("X"; $rules["X"]) | p($count-1)
      end;
  $rules[""] | p($count) ;

def interpret($x):
  if   $x == "+" then turtleRotate(45)
  elif $x == "-" then turtleRotate(-45)
  elif $x == "F" or $x == "G" then turtleForward(5)
  else .
  end;

def sierpinski_curve($n):
  sierpinski($n)
  | split("") 
  | reduce .[] as $action (
      turtle([100,100]) | turtleDown;
      interpret($action) ) ;

# viewBox = <min-x> <min-y> <width> <height>
# Input: {svg, minx, miny, maxx, maxy}
def svg:
  "<svg viewBox='\(.minx|floor) \(.miny - 2 |floor) \(.maxx - .minx|ceil) \(2 + .maxy - .miny|ceil)'",
  "     preserveAspectRatio='xMinYmin meet'",
  "     xmlns='http://www.w3.org/2000/svg' >",
  path("none"; "red"; 1),
  "</svg>";

sierpinski_curve(5)
| svg

Julia

Turtle procedural (lineto) version

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

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
)

Lambdatalk

{def sierp
 {def sierp.r
  {lambda {:order :length :angle} 
   {if {= :order 0}
    then M:length                            // move :length
    else {sierp.r {- :order 1}               // recurse
                  {/ :length 2}
                  {- :angle}}
         T:angle                             // turn :angle
         {sierp.r {- :order 1}               // recurse
                  {/ :length 2} 
                  {+ :angle}}
         T:angle                             // turn :angle
         {sierp.r {- :order 1}               // recurse
                  {/ :length 2} 
                  {- :angle}}
 }}}
 {lambda {:order :length}
  {if {= {% :order 2} 0}                     // if :order is even
   then {sierp.r :order :length 60}          // recurse with 60°
   else T60                                  // else turn 60°
        {sierp.r :order :length -60}         // recurse with -60°
}}}

Four curves drawn using the turtle promitive.

{svg {@ width="580" height="580" style="box-shadow:0 0 8px #000;"}

 {polyline  {@ points="{turtle 50 5 0 {sierp 1 570}}"  
               stroke="#ccc" fill="transparent" stroke-width="7"}}
 {polyline  {@ points="{turtle 50 5 0 {sierp 3 570}}"  
               stroke="#8ff" fill="transparent" stroke-width="5"}}
 {polyline  {@ points="{turtle 50 5 0 {sierp 5 570}}"  
               stroke="#f88" fill="transparent" stroke-width="3"}}
 {polyline  {@ points="{turtle 50 5 0 {sierp 7 570}}"  
               stroke="#000" fill="transparent" stroke-width="1"}}
}

See the result in http://lambdaway.free.fr/lambdawalks/?view=sierpinsky

Mathematica / Wolfram Language

Graphics[SierpinskiCurve[3]]

Nim

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

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

Library: Phix/pGUI
Library: Phix/online

You can run this online here.

--
-- 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, ctrl +/- for both.
--  ("=_" are also mapped to "+-", for the non-numpad +/-)
--
with javascript_semantics
include pGUI.e

Ihandle dlg, canvas
cdCanvas cddbuffer, cdcanvas

integer width, height,
        lm = 0, tm = 0, -- left and top margins
        lo = 1, hi = 1
atom cx, cy, h

procedure lineTo(atom newX, newY)
    cdCanvasVertex(cddbuffer, newX-width/2+h+lm, height-newY+2*h+tm)
    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

forward procedure sierN(integer level)
forward procedure sierE(integer level)
forward procedure sierS(integer level)
forward procedure sierW(integer level)

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*/)
    {width, height} = IupGetIntInt(canvas, "DRAWSIZE")
    if width>height then
        lm = floor((width-height)/2)
        tm = 0
        width = height
    else
        lm = 0
        tm = floor((height-width)/2)
        height = width
    end if
    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

procedure set_dlg_title()
    IupSetStrAttribute(dlg, "TITLE", "Sierpinski curve (%d..%d)",{lo,hi})
end procedure

function key_cb(Ihandle /*ih*/, atom c)
    if c=K_ESC then return IUP_CLOSE end if
    if c=K_F5 then return IUP_DEFAULT end if -- (let browser reload work)
    c = iup_XkeyBase(c)
    if find(c,"+=-_") then
        bool bCtrl = IupGetInt(NULL,"CONTROLKEY"),
             bShift = IupGetInt(NULL,"SHIFTKEY")
        if c='+' or c='=' then
            if bCtrl or not bShift then hi = min(8,hi+1) end if
            if bCtrl or     bShift then lo = min(lo+1,hi) end if
        elsif c='-' or c='_' then
            if bCtrl or     bShift then lo = max(1,lo-1) end if
            if bCtrl or not bShift then hi = max(lo,hi-1) end if
        end if
        set_dlg_title()
        cdCanvasClear(cddbuffer)
        IupUpdate(canvas)
    end if
    return IUP_CONTINUE
end function

procedure main()
    IupOpen()
    canvas = IupCanvas("RASTERSIZE=770x770")
    IupSetCallbacks(canvas, {"MAP_CB", Icallback("map_cb"),
                             "ACTION", Icallback("redraw_cb")})
    dlg = IupDialog(canvas)
    IupSetCallback(dlg, "KEY_CB", Icallback("key_cb"))
    set_dlg_title()
    IupShow(dlg)
    IupSetAttribute(canvas, "RASTERSIZE", NULL)
    if platform()!=JS then
        IupMainLoop()
        IupClose()
    end if
end procedure

main()

Processing

Translation of: Go
// https://rosettacode.org/wiki/Sierpinski_curve#C.2B.2B
// translation of the GO code https://rosettacode.org/wiki/Sierpinski_curve#Go
// output on github at: https://github.com/rupertrussell/sierpinski_curve
// animated gif created using: https://ezgif.com/
int width = 770;
int height = 770;
int level = 6;
float cx = width / 2;
float cy = height;

float oldx = width/2;
float oldy = height;
 
float h = cx / pow(2, (level+1));
int count = 0;

void setup() {
  size(770, 770);
    noLoop();  // stop draw from looping repeatedly
}

void draw() {
  background(0, 0, 255);  // blue background
  stroke(255, 255, 0); // yellow curve
  squareCurve(level);
  print("count = " , count);
  save("Sierpinski_Curve_Level" + level + ".png");
}
void squareCurve(int 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
}

void lineTo(float newX, float newY) {
  if (count == 0) {
    oldx = newX-width/2+h;
    oldy = height-newY+2*h;
  }
  line(oldx, oldy, newX-width/2+h, height-newY+2*h);
  // save("line-" + count + ".png");  // save each step in drawing the curve
  cx = newX;
  cy = newY;

  oldx = newX-width/2+h;
  oldy = height-newY+2*h;
  count ++;
}

void lineN() { 
  lineTo(cx, cy-2*h);
}
void lineS() { 
  lineTo(cx, cy+2*h);
}
void lineE() { 
  lineTo(cx+2*h, cy);
}
void lineW() { 
  lineTo(cx-2*h, cy);
}
void lineNW() { 
  lineTo(cx-h, cy-h);
}
void lineNE() { 
  lineTo(cx+h, cy-h);
}
void lineSE() { 
  lineTo(cx+h, cy+h);
}
void lineSW() { 
  lineTo(cx-h, cy+h);
}

void sierN(int level) {
  if (level == 1) {
    lineNE();
    lineN();
    lineNW();
  } else {
    sierN(level - 1);
    lineNE();
    sierE(level - 1);
    lineN();
    sierW(level - 1);
    lineNW();
    sierN(level - 1);
  }
}

void sierE(int level) {
  if (level == 1) {
    lineSE();
    lineE();
    lineNE();
  } else {
    sierE(level - 1);
    lineSE();
    sierS(level - 1);
    lineE();
    sierN(level - 1);
    lineNE();
    sierE(level - 1);
  }
}

void sierS(int level) {
  if (level == 1) {
    lineSW();
    lineS();
    lineSE();
  } else {
    sierS(level - 1);
    lineSW();
    sierW(level - 1);
    lineS();
    sierE(level - 1);
    lineSE();
    sierS(level - 1);
  }
}

void sierW(int level) {
  if ( level == 1) {
    lineNW();
    lineW();
    lineSW();
  } else {
    sierW(level - 1);
    lineNW();
    sierN(level - 1);
    lineW();
    sierS(level - 1);
    lineSW();
    sierW(level - 1);
  }
}
Output:

Offsite image at Sierpinski_Curve_Level5.png Offsite image at Sierpinski_Curve_Level6.png Offsite image at level 5 animated gif

Python

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import hsv_to_rgb as hsv

def curve(axiom, rules, angle, depth):
    for _ in range(depth):
        axiom = ''.join(rules[c] if c in rules else c for c in axiom)

    a, x, y = 0, [0], [0]
    for c in axiom:
        match c:
            case '+':
                a += 1
            case '-':
                a -= 1
            case 'F' | 'G':
                x.append(x[-1] + np.cos(a*angle*np.pi/180))
                y.append(y[-1] + np.sin(a*angle*np.pi/180))

    l = len(x)
    # this is very slow, but pretty colors
    for i in range(l - 1):
        plt.plot(x[i:i+2], y[i:i+2], color=hsv([i/l, 1, .7]))
    plt.gca().set_aspect(1)
    plt.show()

curve('F--XF--F--XF', {'X': 'XF+G+XF--F--XF+G+X'}, 45, 5)
#curve('F+XF+F+XF', {'X': 'XF-F+F-XF+F+XF-F+F-X'}, 90, 5)
#curve('F', {'F': 'G-F-G', 'G': 'F+G+F'}, 60, 7)
#curve('A', {'A': '+BF-AFA-FB+', 'B': '-AF+BFB+FA-'}, 90, 6)
#curve('FX+FX+', {'X': 'X+YF', 'Y': 'FX-Y'}, 90, 12)

Output in the plot window.

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
  

  [ $ "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"
  
  4 times expand
  
  turtle
  10 frames
  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
  1 frames
Output:

Raku

(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

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:

Media:Sierpinski_curve_rust.svg

Sidef

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

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

File:Wren-Sierpinski curve.png

XPL0

int  PosX, PosY;
real Dir;

proc Draw(Len);
real Len;
[PosX:= PosX + fix(Len*Cos(Dir));
 PosY:= PosY - fix(Len*Sin(Dir));
Line(PosX, PosY, $E \yellow\);
];

proc Curve(Lev, Ang, Len1, Len2);
int  Lev;  real Ang, Len1, Len2;
[if Lev # 0 then
    [Dir:= Dir + Ang;
    Curve(Lev-1, -Ang, Len1, Len2);
    Dir:= Dir - Ang;
    Draw(Len1);
    Dir:= Dir - Ang;
    Curve(Lev-1, -Ang, Len1, Len2);
    Dir:= Dir + Ang;
    ];
];

def Order=3, Pi=3.141592654, Ang45=Pi/4.0, Size=20.;
int Quad;
[SetVid($12);   \VGA graphics: 640x480x8
PosX:= 640/4;  PosY:= 3*480/4;
Move(PosX, PosY);
Dir:= 0.;
for Quad:= 1 to 4 do
    [Curve(Order*2, Ang45, Size/Sqrt(2.), 5.*Size/6.);
    Dir:= Dir + Ang45;
    Draw(Size/Sqrt(2.));
    Dir:= Dir + Ang45;
    ];
]

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)

zkl

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