Sierpinski curve: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Fōrmulæ solution)
(Added Quackery.)
Line 859: Line 859:


main()</lang>
main()</lang>

=={{header|Quackery}}==

<lang Quackery> [ $ "turtleduck.qky" loadfile ] now!
[ stack ] is switch.arg ( --> [ )
[ switch.arg put ] is switch ( x --> )
[ switch.arg release ] is otherwise ( --> )
[ switch.arg share
!= iff ]else[ done
otherwise ]'[ do ]done[ ] is case ( x --> )
[ $ "" swap witheach
[ nested quackery join ] ] is expand ( $ --> $ )
[ $ "L" ] is L ( $ --> $ )
[ $ "R" ] is R ( $ --> $ )
[ $ "F" ] is F ( $ --> $ )

[ $ "G" ] is G ( $ --> $ )
[ $ "AFLGLAFRRFRRAFLGLA" ] is A ( $ --> $ )
$ "FRRAFRRFRRAF"
4 times expand
turtle 1 8 turn
witheach
[ switch
[ char L case [ -1 8 turn ]
char R case [ 1 8 turn ]
char A case [ ( ignore ) ]
otherwise [ 5 1 walk ] ] ]
-1 8 turn</lang>


=={{header|Raku}}==
=={{header|Raku}}==

Revision as of 13:36, 29 July 2021

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


Task

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

AutoHotkey

Translation of: Go

Requires Gdip Library <lang AutoHotkey>SierpinskiW := 500 SierpinskiH := 500 level := 5 cx := SierpinskiW/2 cy := SierpinskiH h := cx / (2**(level+1)) Arr := [] squareCurve(level) xmin := xmax := ymin := ymax := 0 for i, point in Arr { xmin := A_Index = 1 ? point.x : xmin < point.x ? xmin : point.x xmax := point.x > xmax ? point.x : xmax ymin := A_Index = 1 ? point.y : ymin < point.y ? ymin : point.y ymax := point.y > ymax ? point.y : ymax } SierpinskiX := A_ScreenWidth/2 - (xmax-xmin)/2 , SierpinskiY := A_ScreenHeight/2 - (ymax-ymin)/2 for i, point in Arr points .= point.x - xmin + SierpinskiX "," point.y - ymin + SierpinskiY "|" points := Trim(points, "|") gdip1() Gdip_DrawLines(G, pPen, Points) UpdateLayeredWindow(hwnd1, hdc, 0, 0, Width, Height) return

---------------------------------------------------------------

lineTo(newX, newY) { global Arr[Arr.count()+1, "x"] := newX-SierpinskiW/2+h Arr[Arr.count() , "y"] := SierpinskiH-newY+2*h cx := newX cy := newY }

---------------------------------------------------------------

sierN(level) { global if (level = 1) { lineTo(cx+h, cy-h) ; lineNE() lineTo(cx, cy-2*h) ; lineN() lineTo(cx-h, cy-h) ; lineNW() } else{ sierN(level - 1) lineTo(cx+h, cy-h) ; lineNE() sierE(level - 1) lineTo(cx, cy-2*h) ; lineN() sierW(level - 1) lineTo(cx-h, cy-h) ; lineNW() sierN(level - 1) } }

---------------------------------------------------------------

sierE(level) { global if (level = 1) { lineTo(cx+h, cy+h) ; lineSE() lineTo(cx+2*h, cy) ; lineE() lineTo(cx+h, cy-h) ; lineNE() } else { sierE(level - 1) lineTo(cx+h, cy+h) ; lineSE() sierS(level - 1) lineTo(cx+2*h, cy) ; lineE() sierN(level - 1) lineTo(cx+h, cy-h) ; lineNE() sierE(level - 1) } }

---------------------------------------------------------------

sierS(level) { global if (level = 1) { lineTo(cx-h, cy+h) ; lineSW() lineTo(cx, cy+2*h) ; lineS() lineTo(cx+h, cy+h) ; lineSE() } else { sierS(level - 1) lineTo(cx-h, cy+h) ; lineSW() sierW(level - 1) lineTo(cx, cy+2*h) ; lineS() sierE(level - 1) lineTo(cx+h, cy+h) ; lineSE() sierS(level - 1) } }

---------------------------------------------------------------

sierW(level) { global if (level = 1) { lineTo(cx-h, cy-h) ; lineNW() lineTo(cx-2*h, cy) ; lineW() lineTo(cx-h, cy+h) ; lineSW() } else { sierW(level - 1) lineTo(cx-h, cy-h) ; lineNW() sierN(level - 1) lineTo(cx-2*h, cy) ; lineW() sierS(level - 1) lineTo(cx-h, cy+h) ; lineSW() sierW(level - 1) } }

---------------------------------------------------------------

squareCurve(level) { global sierN(level) lineTo(cx+h, cy-h) ; lineNE() sierE(level) lineTo(cx+h, cy+h) ; lineSE() sierS(level) lineTo(cx-h, cy+h) ; lineSW() sierW(level) lineTo(cx-h, cy-h) ; lineNW() lineTo(cx+h, cy-h) ; lineNE() }

---------------------------------------------------------------

gdip1(){ global If !pToken := Gdip_Startup() { MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system ExitApp } OnExit, Exit Width := A_ScreenWidth, Height := A_ScreenHeight Gui, 1: -Caption +E0x80000 +LastFound +OwnDialogs +Owner +AlwaysOnTop Gui, 1: Show, NA hwnd1 := WinExist() hbm := CreateDIBSection(Width, Height) hdc := CreateCompatibleDC() obm := SelectObject(hdc, hbm) G := Gdip_GraphicsFromHDC(hdc) Gdip_SetSmoothingMode(G, 4) pPen := Gdip_CreatePen(0xFFFF0000, 2) }

---------------------------------------------------------------

gdip2(){ global Gdip_DeleteBrush(pBrush) Gdip_DeletePen(pPen) SelectObject(hdc, obm) DeleteObject(hbm) DeleteDC(hdc) Gdip_DeleteGraphics(G) }

---------------------------------------------------------------

Exit: gdip2() Gdip_Shutdown(pToken) ExitApp Return </lang>

C++

Output is a file in SVG format. The curve is generated using the Lindenmayer system method. <lang cpp>// See https://en.wikipedia.org/wiki/Sierpi%C5%84ski_curve#Representation_as_Lindenmayer_system

  1. include <cmath>
  2. include <fstream>
  3. include <iostream>
  4. include <string>

class sierpinski_curve { public:

   void write(std::ostream& out, int size, int length, int order);

private:

   static std::string rewrite(const std::string& s);
   void line(std::ostream& out);
   void execute(std::ostream& out, const std::string& s);
   double x_;
   double y_;
   int angle_;
   int length_;

};

void sierpinski_curve::write(std::ostream& out, int size, int length, int order) {

   length_ = length;
   x_ = length/std::sqrt(2.0);
   y_ = 2 * x_;
   angle_ = 45;
   out << "<svg xmlns='http://www.w3.org/2000/svg' width='"
       << size << "' height='" << size << "'>\n";
   out << "<rect width='100%' height='100%' fill='white'/>\n";
   out << "<path stroke-width='1' stroke='black' fill='none' d='";
   std::string s = "F--XF--F--XF";
   for (int i = 0; i < order; ++i)
       s = rewrite(s);
   execute(out, s);
   out << "'/>\n</svg>\n";

}

std::string sierpinski_curve::rewrite(const std::string& s) {

   std::string t;
   for (char c : s) {
       if (c == 'X')
           t += "XF+G+XF--F--XF+G+X";
       else
           t += c;
   }
   return t;

}

void sierpinski_curve::line(std::ostream& out) {

   double theta = (3.14159265359 * angle_)/180.0;
   x_ += length_ * std::cos(theta);
   y_ -= length_ * std::sin(theta);
   out << " L" << x_ << ',' << y_;

}

void sierpinski_curve::execute(std::ostream& out, const std::string& s) {

   out << 'M' << x_ << ',' << y_;
   for (char c : s) {
       switch (c) {
       case 'F':
       case 'G':
           line(out);
           break;
       case '+':
           angle_ = (angle_ + 45) % 360;
           break;
       case '-':
           angle_ = (angle_ - 45) % 360;
           break;
       }
   }

}

int main() {

   std::ofstream out("sierpinski_curve.svg");
   if (!out) {
       std::cerr << "Cannot open output file\n";
       return 1;
   }
   sierpinski_curve s;
   s.write(out, 545, 7, 5);
   return 0;

}</lang>

Output:

See: sierpinski_curve.svg (offsite SVG image)

Factor

Works with: Factor version 0.99 2020-08-14

<lang factor>USING: accessors kernel L-system sequences ui ;

curve ( L-system -- L-system )
   L-parser-dialect
   { "G" [ dup length>> draw-forward ] } suffix >>commands
   [ 45 >>angle ] >>turtle-values
   "F--XF--F--XF" >>axiom
   {
       { "X" "XF+G+XF--F--XF+G+X" }
   } >>rules ;

[ <L-system> curve "Sierpinski curve" open-window ] with-ui</lang>


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

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

Fōrmulæ

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

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

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

Go

Library: Go Graphics
Translation of: Phix

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

import (

   "github.com/fogleman/gg"
   "math"

)

var (

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

)

var cx, cy, h float64

func lineTo(newX, newY float64) {

   dc.LineTo(newX-width/2+h, height-newY+2*h)
   cx, cy = newX, newY

}

func lineN() { lineTo(cx, cy-2*h) } func lineS() { lineTo(cx, cy+2*h) } func lineE() { lineTo(cx+2*h, cy) } func lineW() { lineTo(cx-2*h, cy) }

func lineNW() { lineTo(cx-h, cy-h) } func lineNE() { lineTo(cx+h, cy-h) } func lineSE() { lineTo(cx+h, cy+h) } func lineSW() { lineTo(cx-h, cy+h) }

func sierN(level int) {

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

}

func sierE(level int) {

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

}

func sierS(level int) {

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

}

func sierW(level int) {

   if level == 1 {
       lineNW()
       lineW()
       lineSW()
   } else {
       sierW(level - 1)
       lineNW()
       sierN(level - 1)
       lineW()
       sierS(level - 1)
       lineSW()
       sierW(level - 1)
   }

}

func squareCurve(level int) {

   sierN(level)
   lineNE()
   sierE(level)
   lineSE()
   sierS(level)
   lineSW()
   sierW(level)
   lineNW()
   lineNE() // needed to close the square in the top left hand corner

}

func main() {

   dc.SetRGB(0, 0, 1) // blue background
   dc.Clear()
   level := 5
   cx, cy = width/2, height
   h = cx / math.Pow(2, float64(level+1))
   squareCurve(level)
   dc.SetRGB255(255, 255, 0) // yellow curve
   dc.SetLineWidth(2)
   dc.Stroke()
   dc.SavePNG("sierpinski_curve.png")

}</lang>

Java

Translation of: C++

<lang java>import java.io.*;

public class SierpinskiCurve {

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

}</lang>

Output:

See: sierpinski_curve.svg (offsite SVG image)

Julia

Turtle procedural (lineto) version

Modified from Craft of Coding blog, Processing version <lang Julia>using Luxor

function sierpinski_curve(x0, y0, h, level)

   x1, y1 = x0, y0
   lineto(x, y) = begin line(Point(x1, y1), Point(x, y), :stroke); x1, y1 = x, y end
   lineN() = lineto(x1,y1-2*h)
   lineS() = lineto(x1,y1+2*h)
   lineE() = lineto(x1+2*h,y1)
   lineW() = lineto(x1-2*h,y1)
   lineNW() = lineto(x1-h,y1-h)
   lineNE() = lineto(x1+h,y1-h)
   lineSE() = lineto(x1+h,y1+h)
   lineSW() = lineto(x1-h,y1+h)
   function drawN(i)
       if i == 1
           lineNE(); lineN(); lineNW()
       else
           drawN(i-1); lineNE(); drawE(i-1); lineN(); drawW(i-1); lineNW(); drawN(i-1)
       end
   end
   function drawE(i)
       if i == 1
           lineSE(); lineE(); lineNE()
       else
           drawE(i-1); lineSE(); drawS(i-1); lineE(); drawN(i-1); lineNE(); drawE(i-1)
       end
   end
   function drawS(i)
       if i == 1
           lineSW(); lineS(); lineSE()
       else
           drawS(i-1); lineSW(); drawW(i-1); lineS(); drawE(i-1); lineSE(); drawS(i-1)
       end
   end
   function drawW(i)
       if i == 1
           lineNW(); lineW(); lineSW()
       else
           drawW(i-1); lineNW(); drawN(i-1); lineW(); drawS(i-1); lineSW(); drawW(i-1)
       end
   end
   function draw_curve(levl)
       drawN(levl); lineNE(); drawE(levl); lineSE()
       drawS(levl); lineSW(); drawW(levl); lineNW()
   end
   draw_curve(level)

end

Drawing(800, 800) sierpinski_curve(10, 790, 3, 6) finish() preview() </lang>

LSystem version

<lang julia>using Lindenmayer # https://github.com/cormullion/Lindenmayer.jl

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

drawLSystem(sierpcurve,

   forward = 10,
   turn = 45,
   startingpen= (0.2, 0.8, 0.8),
   startingx = -380,
   startingy = 380,
   startingorientation = π/4,
   iterations = 5,
   filename = "sierpinski_curve.png",
   showpreview = true

) </lang>

Mathematica / Wolfram Language

<lang Mathematica>Graphics[SierpinskiCurve[3]]</lang>

Nim

Translation of: C++

We produce a SVG file using same algorithm as the one of C++ version. <lang Nim>import math

type

 SierpinskiCurve = object
   x, y: float
   angle: float
   length: int
   file: File


proc line(sc: var SierpinskiCurve) =

 let theta = degToRad(sc.angle)
 sc.x += sc.length.toFloat * cos(theta)
 sc.y -= sc.length.toFloat * sin(theta)
 sc.file.write " L", sc.x, ',', sc.y


proc execute(sc: var SierpinskiCurve; s: string) =

 sc.file.write 'M', sc.x, ',', sc.y
 for c in s:
   case c
   of 'F', 'G': sc.line()
   of '+': sc.angle = floorMod(sc.angle + 45, 360)
   of '-': sc.angle = floorMod(sc.angle - 45, 360)
   else: discard


func rewrite(s: string): string =

 for c in s:
   if c == 'X':
     result.add "XF+G+XF--F--XF+G+X"
   else:
     result.add c


proc write(sc: var SierpinskiCurve; size, length, order: int) =

 sc.length = length
 sc.x = length.toFloat / sqrt(2.0)
 sc.y = 2 * sc.x
 sc.angle = 45
 sc.file.write "<svg xmlns='http://www.w3.org/2000/svg' width='", size, "' height='", size, "'>\n"
 sc.file.write "<rect width='100%' height='100%' fill='white'/>\n"
 sc.file.write "<path stroke-width='1' stroke='black' fill='none' d='"
 var s = "F--XF--F--XF"
 for _ in 1..order: s = s.rewrite()
 sc.execute(s)
 sc.file.write "'/>\n</svg>\n"


let outfile = open("sierpinski_curve.svg", fmWrite) var sc = SierpinskiCurve(file: outfile) sc.write(545, 7, 5) outfile.close()</lang>

Output:

Same as C++ output.

Perl

<lang perl>use strict; use warnings; use SVG; use List::Util qw(max min);

use constant pi => 2 * atan2(1, 0);

my $rule = 'XF+F+XF--F--XF+F+X'; my $S = 'F--F--XF--F--XF'; $S =~ s/X/$rule/g for 1..5;

my (@X, @Y); my ($x, $y) = (0, 0); my $theta = pi/4; my $r = 6;

for (split //, $S) {

   if (/F/) {
       push @X, sprintf "%.0f", $x;
       push @Y, sprintf "%.0f", $y;
       $x += $r * cos($theta);
       $y += $r * sin($theta);
   }
   elsif (/\+/) { $theta += pi/4; }
   elsif (/\-/) { $theta -= pi/4; }

}

my ($xrng, $yrng) = ( max(@X) - min(@X), max(@Y) - min(@Y)); my ($xt, $yt) = (-min(@X) + 10, -min(@Y) + 10);

my $svg = SVG->new(width=>$xrng+20, height=>$yrng+20); my $points = $svg->get_path(x=>\@X, y=>\@Y, -type=>'polyline'); $svg->rect(width=>"100%", height=>"100%", style=>{'fill'=>'black'}); $svg->polyline(%$points, style=>{'stroke'=>'orange', 'stroke-width'=>1}, transform=>"translate($xt,$yt)");

open my $fh, '>', 'sierpinski-curve.svg'; print $fh $svg->xmlify(-namespace=>'svg'); close $fh;</lang> See: sierpinski-curve.svg (offsite SVG image)

Phix

Library: Phix/pGUI

<lang Phix>-- demo\rosetta\Sierpinski_curve.exw -- -- Draws curves lo to hi (simultaneously), initially {1,1}, max {8,8} -- Press +/- to change hi, shift +/- to change lo. -- ("=_" are also mapped to "+-", for the non-numpad +/-) -- include pGUI.e

Ihandle dlg, canvas cdCanvas cddbuffer, cdcanvas

integer width, height,

       lo = 1, hi = 1

atom cx, cy, h

procedure lineTo(atom newX, newY)

   cdCanvasVertex(cddbuffer, newX-width/2+h, height-newY+2*h)
   cx = newX
   cy = newY

end procedure

procedure lineN() lineTo(cx,cy-2*h) end procedure procedure lineS() lineTo(cx,cy+2*h) end procedure procedure lineE() lineTo(cx+2*h,cy) end procedure procedure lineW() lineTo(cx-2*h,cy) end procedure

procedure lineNW() lineTo(cx-h,cy-h) end procedure procedure lineNE() lineTo(cx+h,cy-h) end procedure procedure lineSE() lineTo(cx+h,cy+h) end procedure procedure lineSW() lineTo(cx-h,cy+h) end procedure

procedure sierN(integer level)

  if level=1 then
     lineNE()  lineN()
     lineNW()
  else
     sierN(level-1)  lineNE()
     sierE(level-1)  lineN()
     sierW(level-1)  lineNW()
     sierN(level-1) 
  end if

end procedure

procedure sierE(integer level)

  if level=1 then
     lineSE()  lineE()
     lineNE() 
  else
     sierE(level-1)  lineSE()
     sierS(level-1)  lineE()
     sierN(level-1)  lineNE()
     sierE(level-1) 
  end if

end procedure

procedure sierS(integer level)

  if level=1 then
     lineSW()  lineS()
     lineSE() 
  else
     sierS(level-1)  lineSW()
     sierW(level-1)  lineS()
     sierE(level-1)  lineSE()
     sierS(level-1) 
  end if

end procedure

procedure sierW(integer level)

  if level=1 then
     lineNW()  lineW()
     lineSW() 
  else
     sierW(level-1)  lineNW()
     sierN(level-1)  lineW()
     sierS(level-1)  lineSW()
     sierW(level-1) 
  end if

end procedure

procedure sierpinskiCurve(integer level)

  sierN(level)     lineNE()
  sierE(level)     lineSE()
  sierS(level)     lineSW()
  sierW(level)     lineNW()

end procedure

function redraw_cb(Ihandle /*ih*/, integer /*posx*/, integer /*posy*/)

   {width, height} = IupGetIntInt(canvas, "DRAWSIZE")
   cdCanvasActivate(cddbuffer)
   for level=lo to hi do
       cx = width/2
       cy = height
       h = cx/power(2,level+1)
       cdCanvasBegin(cddbuffer, CD_CLOSED_LINES)
       sierpinskiCurve(level)
       cdCanvasEnd(cddbuffer)
   end for
   cdCanvasFlush(cddbuffer)
   return IUP_DEFAULT

end function

function map_cb(Ihandle ih)

   cdcanvas = cdCreateCanvas(CD_IUP, ih)
   cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)
   cdCanvasSetBackground(cddbuffer, CD_WHITE)
   cdCanvasSetForeground(cddbuffer, CD_BLUE)
   return IUP_DEFAULT

end function

function key_cb(Ihandle /*ih*/, atom c)

   if c=K_ESC then return IUP_CLOSE end if
   if find(c,"+=-_") then
       bool bShift = IupGetInt(NULL,"SHIFTKEY")
       if c='+' or c='=' then
           if bShift then
               lo = min(lo+1,hi)
           else
               hi = min(8,hi+1)
           end if
       elsif c='-' or c='_' then
           if bShift then
               lo = max(1,lo-1)
           else
               hi = max(lo,hi-1)
           end if
       end if
       IupSetStrAttribute(dlg, "TITLE", "Sierpinski curve (%d..%d)",{lo,hi})
       cdCanvasClear(cddbuffer)
       IupUpdate(canvas)
   end if
   return IUP_CONTINUE

end function

procedure main()

   IupOpen()
   
   canvas = IupCanvas(NULL)
   IupSetAttribute(canvas, "RASTERSIZE", "770x770")
   IupSetCallback(canvas, "MAP_CB", Icallback("map_cb"))
   IupSetCallback(canvas, "ACTION", Icallback("redraw_cb"))
   dlg = IupDialog(canvas)
   IupSetAttribute(dlg, "TITLE", "Sierpinski curve (1..1)")
   IupSetCallback(dlg, "K_ANY", Icallback("key_cb"))
   IupMap(dlg)
   IupShowXY(dlg,IUP_CENTER,IUP_CENTER)
   IupMainLoop()
   IupClose()

end procedure

main()</lang>

Quackery

<lang Quackery> [ $ "turtleduck.qky" loadfile ] now!

 [ stack ]                      is switch.arg (   --> [ )
 
 [ switch.arg put ]             is switch     ( x -->   )

 [ switch.arg release ]         is otherwise  (   -->   )

 [ switch.arg share 
   != iff ]else[ done  
   otherwise ]'[ do ]done[ ]    is case       ( x -->   )
 
 [ $ "" swap witheach 
     [ nested quackery join ] ] is expand     ( $ --> $ )
   
 [ $ "L" ]                      is L          ( $ --> $ )

 [ $ "R" ]                      is R          ( $ --> $ )

 [ $ "F" ]                      is F          ( $ --> $ )
 [ $ "G" ]                      is G          ( $ --> $ )

 [ $ "AFLGLAFRRFRRAFLGLA" ]     is A          ( $ --> $ )

 $ "FRRAFRRFRRAF"
 
 4 times expand
 
 turtle 1 8 turn 
 witheach
   [ switch
       [ char L case [ -1 8 turn  ]
         char R case [  1 8 turn  ]
         char A case [ ( ignore ) ] 
         otherwise [ 5 1 walk ] ] ]
 -1 8 turn</lang>

Raku

(formerly Perl 6)

Works with: Rakudo version 2020.02

<lang perl6>use SVG;

role Lindenmayer {

   has %.rules;
   method succ {
       self.comb.map( { %!rules{$^c} // $c } ).join but Lindenmayer(%!rules)
   }

}

my $sierpinski = 'F--XF--F--XF' but Lindenmayer( { X => 'XF+G+XF--F--XF+G+X' } );

$sierpinski++ xx 5;

my $dim = 640; my $scale = 8; my $dir = pi/4; my @points = (316, -108);

for $sierpinski.comb {

   state ($x, $y) = @points[0,1];
   state $d = 0;
   when 'F'|'G' { @points.append: ($x += $scale * $d.cos).round(1), ($y += $scale * $d.sin).round(1) }
   when '+' { $d -= $dir }
   when '-' { $d += $dir }
   default { }

}

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

$out.spurt: SVG.serialize(

   svg => [
       :width($dim), :height($dim),
       :rect[:width<100%>, :height<100%>, :fill<black>],
       :polyline[
         :points(@points.join: ','), :fill<black>,
         :transform("rotate(45, 320, 320)"), :style<stroke:#F7DF1E>,
       ],
   ],

);</lang> See: Sierpinski-curve-perl6.svg (offsite SVG image)

Rust

Program output is a file in SVG format. <lang rust>// [dependencies] // svg = "0.8.0"

use svg::node::element::path::Data; use svg::node::element::Path;

struct SierpinskiCurve {

   current_x: f64,
   current_y: f64,
   current_angle: i32,
   line_length: f64,

}

impl SierpinskiCurve {

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

}

fn main() {

   SierpinskiCurve::save("sierpinski_curve.svg", 545, 5).unwrap();

}</lang>

Output:

See: sierpinski_curve.svg (offsite SVG image)

Sidef

Uses the LSystem() class from Hilbert curve. <lang ruby>var rules = Hash(

   x => 'xF+G+xF--F--xF+G+x',

)

var lsys = LSystem(

   width:  550,
   height: 550,
   xoff: -9,
   yoff: -271,
   len:   5,
   angle: 45,
   color: 'dark green',

)

lsys.execute('F--xF--F--xF', 5, "sierpiński_curve.png", rules)</lang> Output image: Sierpiński curve

Wren

Translation of: Go
Library: DOME

<lang ecmascript>import "graphics" for Canvas, Color import "dome" for Window

var PX = 0 var PY = 0 var CX = 0 var CY = 0 var H = 0

class SierpinskiCurve {

   construct new(width, height, level, back, fore) {
       Window.title = "Sierpinski Curve"
       Window.resize(width, height)
       Canvas.resize(width, height)
       _w = width
       _h = height
       _l = level
       _bc = back
       _fc = fore
   }
   init() {
       Canvas.cls(Color.blue)
       CX = _w /2 
       CY = _h
       H  = CX / 2.pow(_l + 1)
       PX = CX - _w/2 + 2*H
       PY = _h - CY + 3*H
       squareCurve(_l)
   }
   lineTo(newX, newY) {
       Canvas.line(PX, PY, PX = newX - _w/2 + H, PY = _h - newY + 2*H, _fc, 2)
       CX = newX
       CY = newY
   }
   lineN() { lineTo(CX, CY - 2*H) }
   lineS() { lineTo(CX, CY + 2*H) }
   lineE() { lineTo(CX + 2*H, CY) }
   lineW() { lineTo(CX - 2*H, CY) }
   lineNW() { lineTo(CX - H, CY - H) }
   lineNE() { lineTo(CX + H, CY - H) }
   lineSE() { lineTo(CX + H, CY + H) }
   lineSW() { lineTo(CX - H, CY + H) }
   sierN(level) {
       if (level == 1) {
           lineNE()
           lineN()
           lineNW()
       } else {
           sierN(level - 1)
           lineNE()
           sierE(level - 1)
           lineN()
           sierW(level - 1)
           lineNW()
           sierN(level - 1)
       }
   }

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

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

   sierW(level) {
       if (level == 1) {
           lineNW()
           lineW()
           lineSW()
       } else {
           sierW(level - 1)
           lineNW()
           sierN(level - 1)
           lineW()
           sierS(level - 1)
           lineSW()
           sierW(level - 1)
       }
   }
   squareCurve(level) {
       sierN(level)
       lineNE()
       sierE(level)
       lineSE()
       sierS(level)
       lineSW()
       sierW(level)
       lineNW()
       lineNE() // needed to close the square in the top left hand corner
   }
   update() {}
   draw(alpha) {}

}

var Game = SierpinskiCurve.new(770, 770, 5, Color.blue, Color.yellow)</lang>

zkl

Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl <lang zkl>sierpinskiCurve(5) : turtle(_,45,45); // n=5 --> 11,606 characters

fcn sierpinskiCurve(order){

  LSystem("F--XF--F--XF",Dictionary("X","XF+G+XF--F--XF+G+X"), order)

} fcn LSystem(axiom,rules,order){ // Lindenmayer system

  buf1,buf2 := Data(Void,axiom).howza(3), Data().howza(3);  // characters
  do(order){
     buf1.pump(buf2.clear(),'wrap(c){ rules.find(c,c) });   // change if rule
     t:=buf1; buf1=buf2; buf2=t;	// swap buffers
  }
  buf1

}

fcn turtle(curve,angle,startAngle){ // angles in degrees

  const D=10.0;
  dir:=startAngle;
  img,color := PPM(800,800), 0x00ff00;  // green on black
  x,y := 15, img.h - x;
  foreach c in (curve){
     switch(c){

case("F","G"){ // draw forward a,b := D.toRectangular(dir.toFloat().toRad()); img.line(x,y, (x+=a.round()),(y+=b.round()), color) } case("+"){ dir=(dir + angle)%360; } // turn left angle case("-"){ dir=(dir - angle)%360; } // turn right angle

     }
  }
  img.writeJPGFile("sierpinskiCurve.zkl.jpg");

}</lang>

Output:

Offsite image at Sierpinski curve order 5