Sierpinski curve: Difference between revisions

From Rosetta Code
Content added Content deleted
Line 707: Line 707:
<lang jq>include "turtle" {search: "."};
<lang jq>include "turtle" {search: "."};


# Compute the curve using a Lindemayer system of rules
# Compute the curve using a Lindenmayer system of rules
def rules:
def rules:
{ X: "XF+G+XF--F--XF+G+X",
{ X: "XF+G+XF--F--XF+G+X",
Line 746: Line 746:
| svg
| svg
</lang>
</lang>

=={{header|Julia}}==
=={{header|Julia}}==
===Turtle procedural (lineto) version===
===Turtle procedural (lineto) version===

Revision as of 07:06, 17 January 2022

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.

Action!

Action! language does not support recursion. Therefore an iterative approach with a stack has been proposed. <lang Action!>DEFINE C_="10+" DEFINE N_="20+" DEFINE E_="30+" DEFINE S_="40+" DEFINE W_="50+" DEFINE SafePlot="BYTE inside inside=InsideScreen() IF inside THEN Plot(x,y) FI" DEFINE SafeDrawTo="IF inside=1 AND InsideScreen()=1 THEN DrawTo(x,y) FI" DEFINE Next="Push(state+1,level)" DEFINE DrawN="Push(21,level-1)" DEFINE DrawE="Push(31,level-1)" DEFINE DrawS="Push(41,level-1)" DEFINE DrawW="Push(51,level-1)"

INT x,y,stackSize

DEFINE MAX_COUNT="100" BYTE ARRAY stack(MAX_COUNT)

PROC InitStack()

 stackSize=0

RETURN

BYTE FUNC IsEmpty()

 IF stackSize=0 THEN
   RETURN (1)
 FI

RETURN (0)

PROC Push(BYTE state,level)

 stack(stackSize)=state stackSize==+1
 stack(stackSize)=level stackSize==+1

RETURN

PROC Pop(BYTE POINTER state,level)

 stackSize==-1 level^=stack(stackSize)
 stackSize==-1 state^=stack(stackSize)

RETURN

BYTE FUNC InsideScreen()

 IF x<0 OR y<0 OR x>319 OR y>191 THEN
   RETURN (0)
 FI

RETURN (1)

PROC LineN()

 SafePlot y==-4 SafeDrawTo

RETURN

PROC LineNE()

 SafePlot x==+2 y==-2 SafeDrawTo

RETURN

PROC LineE()

 SafePlot x==+4 SafeDrawTo

RETURN

PROC LineSE()

 SafePlot x==+2 y==+2 SafeDrawTo

RETURN

PROC LineS()

 SafePlot y==+4 SafeDrawTo

RETURN

PROC LineSW()

 SafePlot x==-2 y==+2 SafeDrawTo

RETURN

PROC LineW()

 SafePlot x==-4 SafeDrawTo

RETURN

PROC LineNW()

 SafePlot x==-2 y==-2 SafeDrawTo

RETURN

PROC SierpinskiCurve(BYTE level)

 BYTE state
 
 InitStack()
 Push(C_ 1,level+1)
 WHILE IsEmpty()=0
 DO
   Pop(@state,@level)
   IF state=C_ 1 THEN
     Next DrawN
   ELSEIF state=C_ 2 THEN
     LineNE() Next DrawE
   ELSEIF state=C_ 3 THEN
     LineSE() Next DrawS
   ELSEIF state=C_ 4 THEN
     LineSW() Next DrawW
   ELSEIF state=C_ 5 THEN
     LineNW() 
   ELSEIF state=N_ 1 THEN
     IF level=1 THEN
       LineNE() LineN() LineNW()
     ELSE
       Next DrawN
     FI
   ELSEIF state=N_ 2 THEN
     LineNE() Next DrawE
   ELSEIF state=N_ 3 THEN
     LineN() Next DrawW
   ELSEIF state=N_ 4 THEN
     LineNW()
     DrawN
   ELSEIF state=E_ 1 THEN
     IF level=1 THEN
       LineSE() LineE() LineNE()
     ELSE
       Next DrawE
     FI
   ELSEIF state=E_ 2 THEN
     LineSE() Next DrawS
   ELSEIF state=E_ 3 THEN
     LineE() Next DrawN
   ELSEIF state=E_ 4 THEN
     LineNE() DrawE
   ELSEIF state=S_ 1 THEN
     IF level=1 THEN
       LineSW() LineS() LineSE()
     ELSE
       Next DrawS
     FI
   ELSEIF state=S_ 2 THEN
     LineSW() Next DrawW
   ELSEIF state=S_ 3 THEN
     LineS() Next DrawE
   ELSEIF state=S_ 4 THEN
     LineSE() DrawS
   ELSEIF state=W_ 1 THEN
     IF level=1 THEN
       LineNW() LineW() LineSW()
     ELSE
       Next DrawW
     FI
   ELSEIF state=W_ 2 THEN
     LineNW() Next DrawN
   ELSEIF state=W_ 3 THEN
     LineW() Next DrawS
   ELSEIF state=W_ 4 THEN
     LineSW() DrawW
   ELSE
     Break()
   FI
 OD

RETURN

PROC Main()

 BYTE CH=$02FC,COLOR1=$02C5,COLOR2=$02C6
 Graphics(8+16)
 Color=1
 COLOR1=$0C
 COLOR2=$02
 x=1 y=187
 SierpinskiCurve(6)
 DO UNTIL CH#$FF OD
 CH=$FF

RETURN</lang>

Output:

Screenshot from Atari 8-bit computer

AutoHotkey

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)

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. <lang jq>include "turtle" {search: "."};

  1. 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) ) ;
  1. viewBox = <min-x> <min-y> <width> <height>
  2. 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 </lang>

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>


Processing

Translation of: Go

<lang Processing>

// 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);
 }

} </lang>

Output:

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

Quackery

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

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

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

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

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

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

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

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

https://imgur.com/bDBjJzb

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>

Yabasic

<lang Yabasic>// Rosetta Code problem: http://rosettacode.org/wiki/Sierpinski_curve // Adapted from https://www.ocg.at/sites/ocg.at/files/EuroLogo2001/P74Batagelj.pdf to Yabasic by Galileo, 01/2022

import turtle

sub Sierp(n, a, h, k)

   if n = 0 move(k) : return
   turn(a) : Sierp(n - 1, -a, h, k) : turn(-a) : move(h) 
   turn(-a) : Sierp(n - 1, -a, h, k) : turn(a) 

end sub

sub Sierpinski(n, d)

   local i
   
   pen(false)
   goxy(10, 680)
   pen(true)
   color 255, 255, 0
   for i = 1 to 4
       Sierp(n, 45, d/sqrt(2), 5*d/6)
       turn(45)
       move(d/sqrt(2))
       turn(45) 
   next

end sub

startTurtle() Sierpinski(9, 12) </lang>

zkl

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

fcn sierpinskiCurve(order){

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

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

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

}

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

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

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

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

}</lang>

Output:

Offsite image at Sierpinski curve order 5