Sierpinski square curve

From Rosetta Code
Task
Sierpinski square curve
You are encouraged to solve this task according to the task description, using any language you may know.


Task

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

11l

Translation of: C++
F sierpinski_square(fname, size, length, order)
   V x = (size - length) / 2
   V y = Float(length)
   V angle = 0.0

   V outfile = File(fname, ‘w’)
   outfile.write(‘<svg xmlns='http://www.w3.org/2000/svg' width='’size‘' height='’size"'>\n")
   outfile.write("<rect width='100%' height='100%' fill='white'/>\n")
   outfile.write(‘<path stroke-width='1' stroke='black' fill='none' d='’)
   V s = ‘F+XF+F+XF’
   L 0 .< order
      s = s.replace(‘X’, ‘XF-F+F-XF+F+XF-F+F-X’)

   outfile.write(‘M’x‘,’y)
   L(c) s
      S c
         ‘F’
            x += length * cos(radians(angle))
            y += length * sin(radians(angle))
            outfile.write(‘ L’x‘,’y)
         ‘+’
            angle = (angle + 90) % 360
         ‘-’
            angle = (angle - 90 + 360) % 360

   outfile.write("'/>\n</svg>\n")

sierpinski_square(‘sierpinski_square.svg’, 635, 5, 5)
Output:

Output is similar to C++.

ALGOL W

Draws an ASCII art Sierpinski square curve. For orders greater than 6, the value of CANVAS_WIDTH must be increased.
The resolution of the canvas is, of course fairly small, so for orders > 4, to avoid the curve overwriting itself, the connecting lines between the segments of the curve are made longer.

begin % draw a Sierpinski curve 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
                    asciiX := asciiX + 1;
                    canvas( asciiX, asciiY ) := "_";
                    updateCoordinateRange;
                    end
                else if heading =  90 then begin
                    updateCoordinateRange;
                    canvas( asciiX, asciiY ) := "|";
                    asciiY := asciiY - 1;
                    end
                else if heading = 180 then begin
                    asciiX := asciiX - 1;
                    canvas( asciiX, asciiY ) := "_";
                    updateCoordinateRange;
                    end
                else if heading = 270 then begin
                    asciiY := asciiY + 1;
                    updateCoordinateRange;
                    canvas( asciiX - 1, asciiY ) := "|";
                end if_various_headings
            end drawLine ;
        % changes the heading by the specified angle ( in degrees ) - angle must be +/- 90 %
        % the position is updated, if necessary as the horizontal lines are at the bottom  %
        % of a character but the vertical lines are in the middle pf a character           %
        procedure turn( integer value angle ) ;
            begin
                integer prevHeading;
                prevHeading  := heading;
                heading      := heading + angle;
                while heading < 0 do heading := heading + 360;
                heading := heading rem 360;
                if      heading =   0 and prevHeading = 270 then asciiX := asciiX - 1
                else if heading =  90 then begin
                    if      prevHeading = 180 then asciiX := asciiX - 1
                    else if prevHeading =   0 then asciiX := asciiX + 1
                    end
                else if heading = 180 and prevHeading = 270 then asciiX := asciiX - 1
                else if heading = 270 and prevHeading =   0 then asciiX := asciiX + 2
            end turn ;
        % initialises the ascii art canvas %
        procedure initArt ( integer value initHeading ) ;
            begin
                heading := initHeading;;
                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 square curve of the specified order %
        procedure sierpinskiSquareCurve( integer value order ) ;
            begin
                % draw a line connecting segments %
                procedure extendedLine ;
                    if actualOrder > 4 then begin
                        % for higher orders, the segments can touch %
                        % so space the segments further apart       %
                        if heading rem 180 = 0 then drawline( 1 );
                        drawline( 1 );
                        drawline( 1 )
                    end extendedLine ;
                % draw a corner of an element of the curve %
                procedure corner ;
                    begin
                        drawline( 1 );
                        turn( - 90 );
                        drawline( 1 )
                    end corner ;
                % recursively draws a part of a sierpinski square curve %
                procedure subCurve( integer value order; logical value threeSubCurves ) ;
                    begin
                        corner;
                        turn( + 90 );
                        drawline( 1 );
                        if order < 1 then begin
                            turn( - 90 );
                            drawline( 1 );
                            turn( - 90 )
                            end
                        else begin
                            extendedLine;;
                            turn( + 90 );
                            curve( order, threeSubCurves );
                            turn( + 90 );
                            extendedLine
                        end if_order_lt_1 ;
                        drawline( 1 );
                        turn( + 90 )
                    end subCurve;
                % recursively draws a segment of the sierpinski curve %
                procedure curve( integer value order; logical value threeSubCurves ) ;
                    begin
                        subCurve( if threeSubCurves then order - 1 else 0, not threeSubCurves );
                        subCurve( order - 1, not threeSubCurves );
                        subCurve( if threeSubCurves then order - 1 else 0, not threeSubCurves );
                        corner
                    end curve ;
                integer actualOrder;
                actualOrder := order;
                if order = 1 then begin
                    for c := 1 until 4 do corner
                    end
                else if order = 2 then begin
                    for c := 1 until 4 do subCurve( 0, false )
                    end
                else begin
                    for c := 1 until 4 do subCurve( ( 2 * order ) - 5, false )
                end if_order_eq_1__2__
            end sierpinskiSquareCurve ;
        % draw curves %
        begin
            integer order;
            i_w := 1; s_w := 0; % set output formatting %
            write( "order> " );
            read( order );
            write( "Sierpinski curve of order ", order );
            write( "===========================" );
            write();
            initArt( 0 );
            sierpinskiSquareCurve( order );
            drawArt
        end
    end
end.
Output:
order> 4

Sierpinski square curve of order 4
==================================

                             _
                           _| |_
                         _|     |_
                        |_       _|
                     _    |_   _|    _
                   _| |_   _| |_   _| |_
                 _|     |_|     |_|     |_
                |_       _       _       _|
             _    |_   _| |_   _| |_   _|    _
           _| |_    |_|    _| |_    |_|    _| |_
         _|     |_       _|     |_       _|     |_
        |_       _|     |_       _|     |_       _|
     _    |_   _|    _    |_   _|    _    |_   _|    _
   _| |_   _| |_   _| |_   _| |_   _| |_   _| |_   _| |_
 _|     |_|     |_|     |_|     |_|     |_|     |_|     |_
|_       _       _       _       _       _       _       _|
  |_   _| |_   _| |_   _| |_   _| |_   _| |_   _| |_   _|
    |_|    _| |_    |_|    _| |_    |_|    _| |_    |_|
         _|     |_       _|     |_       _|     |_
        |_       _|     |_       _|     |_       _|
          |_   _|    _    |_   _|    _    |_   _|
            |_|    _| |_   _| |_   _| |_    |_|
                 _|     |_|     |_|     |_
                |_       _       _       _|
                  |_   _| |_   _| |_   _|
                    |_|    _| |_    |_|
                         _|     |_
                        |_       _|
                          |_   _|
                            |_|

C++

Output is a file in SVG format.

// See https://en.wikipedia.org/wiki/Sierpi%C5%84ski_curve#Representation_as_Lindenmayer_system
#include <cmath>
#include <fstream>
#include <iostream>
#include <string>

class sierpinski_square {
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_square::write(std::ostream& out, int size, int length, int order) {
    length_ = length;
    x_ = (size - length)/2;
    y_ = length;
    angle_ = 0;
    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_square::rewrite(const std::string& s) {
    std::string t;
    for (char c : s) {
        if (c == 'X')
            t += "XF-F+F-XF+F+XF-F+F-X";
        else
            t += c;
    }
    return t;
}

void sierpinski_square::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_square::execute(std::ostream& out, const std::string& s) {
    out << 'M' << x_ << ',' << y_;
    for (char c : s) {
        switch (c) {
        case 'F':
            line(out);
            break;
        case '+':
            angle_ = (angle_ + 90) % 360;
            break;
        case '-':
            angle_ = (angle_ - 90) % 360;
            break;
        }
    }
}

int main() {
    std::ofstream out("sierpinski_square.svg");
    if (!out) {
        std::cerr << "Cannot open output file\n";
        return 1;
    }
    sierpinski_square s;
    s.write(out, 635, 5, 5);
    return 0;
}
Output:

Media:Sierpinski_square_cpp.svg

Factor

Works with: Factor version 0.99 2020-08-14
USING: accessors kernel L-system sequences ui ;

: square-curve ( L-system -- L-system )
    L-parser-dialect >>commands
    [ 90 >>angle ] >>turtle-values
    "F+XF+F+XF" >>axiom
    {
        { "X" "XF-F+F-XF+F+XF-F+F-X" }
    } >>rules ;

[
    <L-system> square-curve
    "Sierpinski square 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

FreeBASIC

Translation of: 11l

Output is a file in SVG format.

#define pi  4 * Atn(1)

Sub sierpinski_square(fname As String, size As Integer, length As Integer, order As Integer)
    Dim As Single x = (size - length) / 2
    Dim As Single y = length
    Dim As Single angle = 0.0
    
    Dim As Integer i, j
    Dim As String t, s = "F+XF+F+XF"
    
    For i = 1 To order
        t = ""
        For j = 1 To Len(s)
            Select Case Mid(s, j, 1)
            Case "X"
                t += "XF-F+F-XF+F+XF-F+F-X"
            Case Else
                t += Mid(s, j, 1)
            End Select
        Next j
        s = t
    Next i
    
    Open fname For Output As #1
    Print #1, "<svg xmlns='http://www.w3.org/2000/svg' width='" ; size ; "' height='" ; size ; "'>"
    Print #1, "<rect width='100%' height='100%' fill='white'/>"
    Print #1, "<path stroke-width='1' stroke='black' fill='none' d='";
    
    Print #1, "M" ; x ; "," ; y;
    For i = 1 To Len(s)
        Select Case Mid(s, i, 1)
        Case "F"
            x += length * Cos(angle * pi / 180)
            y += length * Sin(angle * pi / 180)
            Print #1, " L" ; x ; "," ; y;
        Case "+"
            angle = (angle + 90) Mod 360
        Case "-"
            angle = (angle - 90 + 360) Mod 360
        End Select
    Next i
    
    Print #1, "'/>"
    Print #1, "</svg>"
    Close #1
End Sub

sierpinski_square("sierpinski_square.svg", 635, 5, 5)
Windowtitle "Hit any key to end program"
Output:
Output is similar to C++.

Go

Library: Go Graphics

The following uses the Lindenmayer system with the appropriate parameters from the Wikipedia article and produces a similar image (apart from the colors, yellow on blue) to the Sidef and zkl entries.

package main

import (
    "github.com/fogleman/gg"
    "github.com/trubitsyn/go-lindenmayer"
    "log"
    "math"
)

const twoPi = 2 * math.Pi

var (
    width  = 770.0
    height = 770.0
    dc     = gg.NewContext(int(width), int(height))
)

var cx, cy, h, theta float64

func main() {
    dc.SetRGB(0, 0, 1) // blue background
    dc.Clear()
    cx, cy = 10, height/2+5
    h = 6
    sys := lindenmayer.Lsystem{
        Variables: []rune{'X'},
        Constants: []rune{'F', '+', '-'},
        Axiom:     "F+XF+F+XF",
        Rules: []lindenmayer.Rule{
            {"X", "XF-F+F-XF+F+XF-F+F-X"},
        },
        Angle: math.Pi / 2, // 90 degrees in radians
    }
    result := lindenmayer.Iterate(&sys, 5)
    operations := map[rune]func(){
        'F': func() {
            newX, newY := cx+h*math.Sin(theta), cy-h*math.Cos(theta)
            dc.LineTo(newX, newY)
            cx, cy = newX, newY
        },
        '+': func() {
            theta = math.Mod(theta+sys.Angle, twoPi)
        },
        '-': func() {
            theta = math.Mod(theta-sys.Angle, twoPi)
        },
    }
    if err := lindenmayer.Process(result, operations); err != nil {
        log.Fatal(err)
    }
    // needed to close the square at the extreme left
    operations['+']()
    operations['F']()

    // create the image and save it
    dc.SetRGB255(255, 255, 0) // yellow curve
    dc.SetLineWidth(2)
    dc.Stroke()
    dc.SavePNG("sierpinski_square_curve.png")
}

J

It looks like there's two different (though similar) concepts implemented here, of what a "Sierpinski square curve" looks like (the wikipedia writeup shows 45 degree angles -- like j:File:Sierpinski_curve.png but many of the implementations here show only right angles). And, the wikipedia writeup is obtuse about some of the details of the structure. And, we've got some dead links here. So, for now, a quickie ascii art implementation:
   1j1#"1' #'{~{{l,(1,~0{.~#y),l=.y,.0,.y}}^:3,.1
#   #   #   #   #   #   #   # 
  #       #       #       #   
#   #   #   #   #   #   #   # 
      #               #       
#   #   #   #   #   #   #   # 
  #       #       #       #   
#   #   #   #   #   #   #   # 
              #               
#   #   #   #   #   #   #   # 
  #       #       #       #   
#   #   #   #   #   #   #   # 
      #               #       
#   #   #   #   #   #   #   # 
  #       #       #       #   
#   #   #   #   #   #   #   #

Java

Translation of: C++
import java.io.*;

public class SierpinskiSquareCurve {
    public static void main(final String[] args) {
        try (Writer writer = new BufferedWriter(new FileWriter("sierpinski_square.svg"))) {
            SierpinskiSquareCurve s = new SierpinskiSquareCurve(writer);
            int size = 635, length = 5;
            s.currentAngle = 0;
            s.currentX = (size - length)/2;
            s.currentY = length;
            s.lineLength = length;
            s.begin(size);
            s.execute(rewrite(5));
            s.end();
        } catch (final Exception ex) {
            ex.printStackTrace();
        }
    }

    private SierpinskiSquareCurve(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':
                    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-F+F-XF+F+XF-F+F-X";
    private static final int ANGLE = 90;
}
Output:

Media:Sierpinski_square_java.svg

jq

Works with: jq

Works with gojq, the Go implementation of jq

The program given here generates SVG code that can be viewed directly in a browser, at least if the file suffix is .svg.

See Simple Turtle Graphics for the simple-turtle.jq module used in this entry. The `include` statement assumes the file is in the pwd.

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

def rules: {"X": "XF-F+F-XF+F+XF-F+F-X"};

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

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

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

sierpinski_curve(5)
| path("none"; "red"; 1) | svg(1000)


Julia

using Lindenmayer # https://github.com/cormullion/Lindenmayer.jl

scurve = LSystem(Dict("X" => "XF-F+F-XF+F+XF-F+F-X"), "F+XF+F+XF")

drawLSystem(scurve,
    forward = 3,
    turn = 90,
    startingy = -400,
    iterations = 6,
    filename = "sierpinski_square_curve.png",
    showpreview = true
)

Mathematica/Wolfram Language

Graphics[SierpinskiCurve[3]]
Output:

Outputs a graphical version of a 3rd order Sierpinski curve.

Nim

Translation of: C++

We produce a SVG file.

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': sc.line()
    of '+': sc.angle = floorMod(sc.angle + 90, 360)
    of '-': sc.angle = floorMod(sc.angle - 90, 360)
    else: discard


func rewrite(s: string): string =
  for c in s:
    if c == 'X':
      result.add "XF-F+F-XF+F+XF-F+F-X"
    else:
      result.add c


proc write(sc: var SierpinskiCurve; size, length, order: int) =
  sc.length = length
  sc.x = (size - length) / 2
  sc.y = length.toFloat
  sc.angle = 0
  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_square.svg", fmWrite)
var sc = SierpinskiCurve(file: outfile)
sc.write(635, 5, 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+F-XF+F+XF-F+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/2; }
    elsif (/\-/) { $theta -= pi/2; }
}

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-square-curve.svg';
print $fh  $svg->xmlify(-namespace=>'svg');
close $fh;

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

Phix

Library: Phix/pGUI
Library: Phix/online

You can run this online here.

--
-- demo\rosetta\Sierpinski_square_curve.exw
-- ========================================
--
-- My second atempt at a Lindenmayer system. The first 
--  is now saved in demo\rosetta\Penrose_tiling.exw
--
with javascript_semantics
include pGUI.e

Ihandle dlg, canvas
cdCanvas cddbuffer, cdcanvas

function redraw_cb(Ihandle /*canvas*/)
    string s = "F+F+XF+F+XF"
    for n=1 to 4 do
        string next = ""
        for i=1 to length(s) do
            integer ch = s[i]
            next &= iff(ch='X'?"XF-F+F-XF+F+XF-F+F-X":ch)
        end for
        s = next
    end for
 
    cdCanvasActivate(cddbuffer)
    cdCanvasBegin(cddbuffer, CD_CLOSED_LINES)
    atom x=0, y=0, theta=PI/4, r = 6
    for i=1 to length(s) do
        integer ch = s[i]
        switch ch do
            case 'F':   x += r*cos(theta)
                        y += r*sin(theta)
                        cdCanvasVertex(cddbuffer, x+270, y+270)
            case '+':   theta += PI/2
            case '-':   theta -= PI/2
        end switch
    end for
    cdCanvasEnd(cddbuffer)
    cdCanvasFlush(cddbuffer)
    return IUP_DEFAULT
end function

function map_cb(Ihandle canvas)
    cdcanvas = cdCreateCanvas(CD_IUP, canvas)
    cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)
    cdCanvasSetBackground(cddbuffer, CD_WHITE)
    cdCanvasSetForeground(cddbuffer, CD_BLUE)
    return IUP_DEFAULT
end function

IupOpen()
canvas = IupCanvas("RASTERSIZE=290x295")
IupSetCallbacks(canvas, {"MAP_CB", Icallback("map_cb"),
                         "ACTION", Icallback("redraw_cb")})
dlg = IupDialog(canvas,`TITLE="Sierpinski square curve"`)
IupSetAttribute(dlg,`DIALOGFRAME`,`YES`)
IupShow(dlg)
if platform()!=JS then
    IupMainLoop()
    IupClose()
end if

and an svg-creating version:

without js -- (file i/o)
constant rule = "XF-F+F-XF+F+XF-F+F-X"
string s = "F+F+XF+F+XF"
for n=1 to 4 do
    string next = ""
    for i=1 to length(s) do
        integer ch = s[i]
        next &= iff(ch='X'?rule:ch)
    end for
    s = next
end for
 
sequence X = {}, Y= {}
atom x=0, y=0, theta=PI/4, r = 6
string svg = ""
for i=1 to length(s) do
    integer ch = s[i]
    switch ch do
        case 'F':   X &= x; x += r*cos(theta)
                    Y &= y; y += r*sin(theta)
        case '+':   theta += PI/2
        case '-':   theta -= PI/2
    end switch
end for
constant svgfmt = """
<svg xmlns="http://www.w3.org/2000/svg" height="%d" width="%d">
 <rect height="100%%" width="100%%" style="fill:black" />
 <polyline points="%s" style="stroke: orange; stroke-width: 1" transform="translate(%d,%d)" />
</svg>"""
string points = ""
for i=1 to length(X) do
    points &= sprintf("%.2f,%.2f ",{X[i],Y[i]})
end for
integer fn = open("sierpinski_square_curve.svg","w")
atom xt = -min(X)+10,
     yt = -min(Y)+10
printf(fn,svgfmt,{max(X)+xt+10,max(Y)+yt+10,points,xt,yt})
close(fn)

Python

import matplotlib.pyplot as plt
import math


def nextPoint(x, y, angle):
    a = math.pi * angle / 180
    x2 = (int)(round(x + (1 * math.cos(a))))
    y2 = (int)(round(y + (1 * math.sin(a))))
    return x2, y2


def expand(axiom, rules, level):
    for l in range(0, level):
        a2 = ""
        for c in axiom:
            if c in rules:
                a2 += rules[c]
            else:
                a2 += c
        axiom = a2
    return axiom


def draw_lsystem(axiom, rules, angle, iterations):
    xp = [1]
    yp = [1]
    direction = 0
    for c in expand(axiom, rules, iterations):
        if c == "F":
            xn, yn = nextPoint(xp[-1], yp[-1], direction)
            xp.append(xn)
            yp.append(yn)
        elif c == "-":
            direction = direction - angle
            if direction < 0:
                direction = 360 + direction
        elif c == "+":
            direction = (direction + angle) % 360

    plt.plot(xp, yp)
    plt.show()


if __name__ == '__main__':
    # Sierpinski Square L-System Definition
    s_axiom = "F+XF+F+XF"
    s_rules = {"X": "XF-F+F-XF+F+XF-F+F-X"}
    s_angle = 90

    draw_lsystem(s_axiom, s_rules, s_angle, 3)

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          ( $ --> $ )
 
  [ $ "AFRFLFRAFLFLAFRFLFRA" ]   is A          ( $ --> $ )
 
  $ "FLAFLFLAF"
  
  4 times expand
  
  turtle
  10 frames
  witheach
    [ switch
        [ char L case [ -1 4 turn ]
          char R case [  1 4 turn ]
          char F case [  5 1 walk ] 
          otherwise ( ignore ) ] ]
  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 = 'X' but Lindenmayer( { X => 'XF-F+F-XF+F+XF-F+F-X' } );

$sierpinski++ xx 5;

my $dim = 600;
my $scale = 6;

my @points = (-80, 298);

for $sierpinski.comb {
    state ($x, $y) = @points[0,1];
    state $d = $scale + 0i;
    when 'F' { @points.append: ($x += $d.re).round(1), ($y += $d.im).round(1) }
    when /< + - >/ { $d *= "{$_}1i" }
    default { }
}

my @t = @points.tail(2).clone;

my $out = './sierpinski-square-curve-perl6.svg'.IO;

$out.spurt: SVG.serialize(
    svg => [
        :width($dim), :height($dim),
        :rect[:width<100%>, :height<100%>, :fill<black>],
        :polyline[
          :points((@points, map {(@t »+=» $_).clone}, ($scale,0), (0,$scale), (-$scale,0)).join: ','),
          :fill<black>, :transform("rotate(45, 300, 300)"), :style<stroke:#61D4FF>,
        ],
        :polyline[
          :points(@points.map( -> $x,$y { $x, $dim - $y + 1 }).join: ','),
          :fill<black>, :transform("rotate(45, 300, 300)"), :style<stroke:#61D4FF>,
        ],
    ],
);

See: Sierpinski-square-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 SierpinskiSquareCurve {
    current_x: f64,
    current_y: f64,
    current_angle: i32,
    line_length: f64,
}

impl SierpinskiSquareCurve {
    fn new(x: f64, y: f64, length: f64, angle: i32) -> SierpinskiSquareCurve {
        SierpinskiSquareCurve {
            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-F+F-XF+F+XF-F+F-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 SierpinskiSquareCurve::rewrite(order).chars() {
            match ch {
                'F' => data = self.draw_line(data),
                '+' => self.turn(90),
                '-' => self.turn(-90),
                _ => {}
            }
        }
        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, length: f64, order: usize) -> std::io::Result<()> {
        use svg::node::element::Rectangle;
        let x = (size as f64 - length) / 2.0;
        let y = length;
        let rect = Rectangle::new()
            .set("width", "100%")
            .set("height", "100%")
            .set("fill", "white");
        let mut s = SierpinskiSquareCurve::new(x, y, length, 0);
        let document = svg::Document::new()
            .set("width", size)
            .set("height", size)
            .add(rect)
            .add(s.execute(order));
        svg::save(file, &document)
    }
}

fn main() {
    SierpinskiSquareCurve::save("sierpinski_square_curve.svg", 635, 5.0, 5).unwrap();
}
Output:

Media:Sierpinski_square_curve_rust.svg

Sidef

Uses the LSystem() class from Hilbert curve.

var rules = Hash(
    x => 'xF-F+F-xF+F+xF-F+F-x',
)

var lsys = LSystem(
    width:  510,
    height: 510,

    xoff: -505,
    yoff: -254,

    len:   4,
    angle: 90,
    color: 'dark green',
)

lsys.execute('F+xF+F+xF', 5, "sierpiński_square_curve.png", rules)

Output image: Sierpiński square curve

VBScript

Output to html (svg) displayed in the default browser. A turtle graphics class helps to keep the curve definition simple

option explicit
'outputs turtle graphics to svg file and opens it

const pi180= 0.01745329251994329576923690768489 ' pi/180 
const pi=3.1415926535897932384626433832795 'pi
class turtle
   
   dim fso
   dim fn
   dim svg
   
   dim iang  'radians
   dim ori   'radians
   dim incr
   dim pdown
   dim clr
   dim x
   dim y

   public property let orient(n):ori = n*pi180 :end property
   public property let iangle(n):iang= n*pi180 :end property
   public sub pd() : pdown=true: end sub 
   public sub pu()  :pdown=FALSE :end sub 
   
   public sub rt(i)  
     ori=ori - i*iang:
     'if ori<0 then ori = ori+pi*2
   end sub 
   public sub lt(i):  
     ori=(ori + i*iang) 
     'if ori>(pi*2) then ori=ori-pi*2
   end sub
   
   public sub bw(l)
      x= x+ cos(ori+pi)*l*incr
      y= y+ sin(ori+pi)*l*incr
     ' ori=ori+pi '?????
   end sub 
   
   public sub fw(l)
      dim x1,y1 
      x1=x + cos(ori)*l*incr
      y1=y + sin(ori)*l*incr
      if pdown then line x,y,x1,y1
      x=x1:y=y1
   end sub
   
   Private Sub Class_Initialize()  
      setlocale "us" 
      initsvg
      x=400:y=400:incr=100
      ori=90*pi180
      iang=90*pi180
      clr=0
      pdown=true
   end sub
   
   Private Sub Class_Terminate()   
      disply
   end sub
   
   private sub line (x,y,x1,y1)
      svg.WriteLine "<line x1=""" & x & """ y1= """& y & """ x2=""" & x1& """ y2=""" & y1 & """/>"
   end sub 

   private sub disply()
       dim shell
       svg.WriteLine "</svg></body></html>"
       svg.close
       Set shell = CreateObject("Shell.Application") 
       shell.ShellExecute fn,1,False
   end sub 

   private sub initsvg()
     dim scriptpath
     Set fso = CreateObject ("Scripting.Filesystemobject")
     ScriptPath= Left(WScript.ScriptFullName, InStrRev(WScript.ScriptFullName, "\"))
     fn=Scriptpath & "SIERP.HTML"
     Set svg = fso.CreateTextFile(fn,True)
     if SVG IS nothing then wscript.echo "Can't create svg file" :vscript.quit
     svg.WriteLine "<!DOCTYPE html>" &vbcrlf & "<html>" &vbcrlf & "<head>"
     svg.writeline "<style>" & vbcrlf & "line {stroke:rgb(255,0,0);stroke-width:.5}" &vbcrlf &"</style>"
     svg.writeline "</head>"&vbcrlf & "<body>"
     svg.WriteLine "<svg xmlns=""http://www.w3.org/2000/svg"" width=""800"" height=""800"" viewBox=""0 0 800 800"">" 
   end sub 
end class

'to half.sierpinski :size :level
' if :level = 0 [forward :size stop]
' half.sierpinski :size :level - 1
' left 45
' forward :size * sqrt 2 
' left 45
' half.sierpinski :size :level - 1
' right 90
' forward :size 
' right 90
' half.sierpinski :size :level - 1
' left 45
' forward :size * sqrt 2 
' left 45
' half.sierpinski :size :level - 1
'end
const raiz2=1.4142135623730950488016887242097
sub media_sierp (niv,sz)
   if niv=0 then x.fw sz: exit sub 
   media_sierp niv-1,sz
   x.lt 1
   x.fw sz*raiz2
   x.lt 1
    media_sierp niv-1,sz
   x.rt 2
   x.fw sz
   x.rt 2
  media_sierp niv-1,sz
   x.lt 1
   x.fw sz*raiz2
   x.lt 1 
    media_sierp niv-1,sz
end sub    

'to sierpinski :size :level
' half.sierpinski :size :level
' right 90
' forward :size
' right 90
' half.sierpinski :size :level
' right 90
' forward :size
' right 90
'end

sub sierp(niv,sz)
   media_sierp niv,sz
   x.rt 2
   x.fw sz
   x.rt 2
   media_sierp niv,sz
   x.rt 2
   x.fw sz
   x.rt 2
end sub   
     
dim x
set x=new turtle
x.iangle=45
x.orient=0
x.incr=1
x.x=100:x.y=270
'star5
sierp 5,4
set x=nothing

Wren

Translation of: Go
Library: DOME
Library: Wren-lsystem
import "graphics" for Canvas, Color
import "dome" for Window
import "math" for Math
import "./lsystem" for LSystem, Rule

var TwoPi = Num.pi * 2

class SierpinskiSquareCurve {
    construct new(width, height, back, fore) {
        Window.title = "Sierpinski Square Curve"
        Window.resize(width, height)
        Canvas.resize(width, height)
        _w = width
        _h = height
        _bc = back
        _fc = fore
    }

    init() {
        Canvas.cls(_bc)
        var cx = 10
        var cy = (_h/2).floor + 5
        var theta = 0
        var h = 6
        var lsys = LSystem.new(
            ["X"],                                    //  variables
            ["F", "+", "-"],                          //  constants
            "F+XF+F+XF",                              //  axiom
            [Rule.new("X", "XF-F+F-XF+F+XF-F+F-X")],  //  rules
            Num.pi / 2                                //  angle (90 degrees in radians)
        )
        var result = lsys.iterate(5)
        var operations = {
            "F": Fn.new {
                var newX = cx + h*Math.sin(theta)
                var newY = cy - h*Math.cos(theta)
                Canvas.line(cx, cy, newX, newY, _fc, 2)
                cx = newX
                cy = newY
            },
            "+": Fn.new {
                theta = (theta + lsys.angle) % TwoPi
            },
            "-": Fn.new {
                theta = (theta - lsys.angle) % TwoPi
            }
        }
        LSystem.execute(result, operations)
    }

    update() {}

    draw(alpha) {}
}

var Game = SierpinskiSquareCurve.new(770, 770, Color.blue, Color.yellow)
Output:

File:Wren-Sierpinski square curve.png

zkl

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

sierpinskiSquareCurve(4) : turtle(_);

fcn sierpinskiSquareCurve(n){	// Lindenmayer system --> Data of As
   var [const] A="AF-F+F-AF+F+AF-F+F-A", B="";  // Production rules
   var [const] Axiom="F+AF+F+AF";
   buf1,buf2 := Data(Void,Axiom).howza(3), Data().howza(3);  // characters
   do(n){
      buf1.pump(buf2.clear(),fcn(c){ if(c=="A") A else if(c=="B") B else c });
      t:=buf1; buf1=buf2; buf2=t;	// swap buffers
   }
   buf1		// n=4 --> 3,239 characters
}

fcn turtle(curve){	// a "square" turtle, directions are +-90*
   const D=10;
   ds,dir := T( T(D,0), T(0,-D), T(-D,0), T(0,D) ), 2; // turtle offsets
   dx,dy := ds[dir];
   img,color := PPM(650,650), 0x00ff00;  // green on black
   x,y := img.w/2, 10;
   curve.replace("A","").replace("B","");  // A & B are no-op during drawing
   foreach c in (curve){
      switch(c){
	 case("F"){ img.line(x,y, (x+=dx),(y+=dy), color) }  // draw forward
	 case("+"){ dir=(dir+1)%4; dx,dy = ds[dir] } // turn right 90*
	 case("-"){ dir=(dir-1)%4; dx,dy = ds[dir] } // turn left 90*
      }
   }
   img.writeJPGFile("sierpinskiSquareCurve.zkl.jpg");
}
Output:

Offsite image at Sierpinski square curve of order 4