# Sierpinski arrowhead curve

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

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

## ALGOL W

Produces an Ascii Art Sierpinski Arrowhead Curve using the algorithm from the Wikipedia page.
Note that the Wikipedia algotrithm draws even order curves with the base at the top. <lang algolw>begin % draw sierpinski arrowhead curves using ascii art %

```   integer CANVAS_WIDTH;
CANVAS_WIDTH := 200;
begin
% the ascii art canvas and related items %
string(1) array canvas ( 1 :: CANVAS_WIDTH, 1 :: CANVAS_WIDTH );
integer         heading, asciiX, asciiY, width, maxX, maxY, minX, minY;
% draw a line using ascii art - the length is ignored and the heading determines the %
%                               character to use                                     %
% the position is updated                                                            %
procedure drawLine( real value length ) ;
begin
% stores the min and max coordinates %
procedure updateCoordinateRange ;
begin
if asciiX > maxX then maxX := asciiX;
if asciiY > maxY then maxY := asciiY;
if asciiX < minX then minX := asciiX;
if asciiY < minY then minY := asciiY
end updateCoordinateRange ;
if      heading =   0 then begin
updateCoordinateRange;
canvas( asciiX, asciiY ) := "_";
asciiX := asciiX + 1
end
else if heading =  60 then begin
updateCoordinateRange;
canvas( asciiX, asciiY ) := "/";
asciiY := asciiY - 1;
asciiX := asciiX + 1
end
else if heading = 120 then begin
asciiX := asciiX - 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "\";
asciiY := asciiY - 1
end
else if heading = 180 then begin
asciiX := asciiX - 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "_"
end
else if heading = 240 then begin
asciiX := asciiX - 1;
asciiY := asciiY + 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "/"
end
else if heading = 300 then begin
asciiY := asciiY + 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "\";
asciiX := asciiX + 1
end drawLine ;
% changes the heading by the specified angle ( in degrees ) - angle must be +/- 60 %
procedure turn( integer value angle ) ;
if angle > 0
then heading := ( heading + angle ) rem 360
else begin
if heading < 0 then heading := heading + 360
end tuen ;
% initialises the ascii art canvas %
procedure initArt ;
begin
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 arrowhead curve of the specified order and line length %
procedure sierpinskiArrowheadCurve( integer value order; real value length ) ;
begin
% recursively draws a segment of the sierpinski arrowhead curve %
procedure curve( integer value order; real value length; integer value angle ) ;
if 0 = order then drawline( length )
else begin
curve( order - 1, length / 2, - angle );
turn(  angle );
curve( order - 1, length / 2,   angle );
turn(  angle );
curve( order - 1, length / 2, - angle )
end curve ;
if not odd( order ) then begin % order is even, we can just draw the curve. %
curve( order, length, +60 );
end
else begin % order is odd %
turn( +60 );
curve( order, length, -60 )
end if_not_odd_order__
% draw curves %
i_w := 1; s_w := 0; % set output formatting %
for order := 5 do begin
write( "Sierpinski arrowhead curve of order ", order );
write( "=====================================" );
write();
initArt;
sierpinskiArrowheadCurve( order, 1 );
drawArt
end for_order
end
```

end.</lang>

Output:
```Sierpinski arrowhead curve of order 5
=====================================

_
/ \
\ /
_/ \_
/     \
\_   _/
_  \ /  _
/ \_/ \_/ \
\         /
_/         \_
/  _       _  \
\_/ \     / \_/
_    /     \    _
/ \   \_   _/   / \
\ /  _  \ /  _  \ /
_/ \_/ \_/ \_/ \_/ \_
/                     \
\_                   _/
_  \                 /  _
/ \_/                 \_/ \
\    _               _    /
_/   / \             / \   \_
/  _  \ /             \ /  _  \
\_/ \_/ \_           _/ \_/ \_/
_          \         /          _
/ \        _/         \_        / \
\ /       /  _       _  \       \ /
_/ \_      \_/ \     / \_/      _/ \_
/     \    _    /     \    _    /     \
\_   _/   / \   \_   _/   / \   \_   _/
_  \ /  _  \ /  _  \ /  _  \ /  _  \ /  _
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \
```

## AutoHotkey

Translation of: Go

Requires Gdip Library <lang AutoHotkey>order := 7 theta := 0

curve := [] curve.curveW := 1000 curve.curveH := 1000 curve.iy := 1 curve.cx := curve.curveW/2 curve.cy := curve.curveH curve.ch := curve.cx/2

arrowhead(order, curve, theta, Arr :=[]) 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 } arrowheadX := A_ScreenWidth/2 - (xmax-xmin)/2 , arrowheadY := A_ScreenHeight/2 - (ymax-ymin)/2 for i, point in Arr points .= point.x - xmin + arrowheadX "," point.y - ymin + arrowheadY "|"

points := Trim(points, "|") gdip1() Gdip_DrawLines(G, pPen, Points) UpdateLayeredWindow(hwnd1, hdc, 0, 0, Width, Height) return

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

arrowhead(order, curve, theta, Arr) { length := curve.cx if (order&1 = 0) curve(order, length, theta, 60, Arr) else { theta := turn(theta, 60) theta := curve(order, length, theta, -60, Arr) } drawLine(length, theta, Arr) }

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

drawLine(length, theta, Arr) { global curve Arr[Arr.count()+1, "x"] := curve.cx-curve.curveW/2+curve.ch Arr[Arr.count(), "y"] := (curve.curveH-curve.cy)*curve.iy+2*curve.ch pi := 3.141592653589793 curve.cx := curve.cx + length * Cos(theta*pi/180) curve.cy := curve.cy + length * Sin(theta*pi/180) }

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

turn(theta, angle) { return theta := Mod(theta+angle, 360) }

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

curve(order, length, theta, angle, Arr) { if (order = 0) drawLine(length, theta, Arr) else { theta := curve(order-1, length/2, theta, -angle, Arr) theta := turn(theta, angle) theta := curve(order-1, length/2, theta, angle, Arr) theta := turn(theta, angle) theta := curve(order-1, length/2, theta, -angle, Arr) } return theta }

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

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

This code is based on the Phix and Go solutions, but produces a file in SVG format. <lang c>// See https://en.wikipedia.org/wiki/Sierpi%C5%84ski_curve#Arrowhead_curve

1. include <math.h>
2. include <stdio.h>
3. include <stdlib.h>

// Structure to keep track of current position and orientation typedef struct cursor_tag {

```   double x;
double y;
int angle;
```

} cursor_t;

void turn(cursor_t* cursor, int angle) {

```   cursor->angle = (cursor->angle + angle) % 360;
```

}

void draw_line(FILE* out, cursor_t* cursor, double length) {

```   double theta = (M_PI * cursor->angle)/180.0;
cursor->x += length * cos(theta);
cursor->y += length * sin(theta);
fprintf(out, "L%g,%g\n", cursor->x, cursor->y);
```

}

void curve(FILE* out, int order, double length, cursor_t* cursor, int angle) {

```   if (order == 0) {
draw_line(out, cursor, length);
} else {
curve(out, order - 1, length/2, cursor, -angle);
turn(cursor, angle);
curve(out, order - 1, length/2, cursor, angle);
turn(cursor, angle);
curve(out, order - 1, length/2, cursor, -angle);
}
```

}

void write_sierpinski_arrowhead(FILE* out, int size, int order) {

```   const double margin = 20.0;
const double side = size - 2.0 * margin;
cursor_t cursor;
cursor.angle = 0;
cursor.x = margin;
cursor.y = 0.5 * size + 0.25 * sqrt(3) * side;
if ((order & 1) != 0)
turn(&cursor, -60);
fprintf(out, "<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>\n",
size, size);
fprintf(out, "<rect width='100%%' height='100%%' fill='white'/>\n");
fprintf(out, "<path stroke-width='1' stroke='black' fill='none' d='");
fprintf(out, "M%g,%g\n", cursor.x, cursor.y);
curve(out, order, side, &cursor, 60);
fprintf(out, "'/>\n</svg>\n");
```

}

int main(int argc, char** argv) {

```   const char* filename = "sierpinski_arrowhead.svg";
if (argc == 2)
filename = argv[1];
FILE* out = fopen(filename, "w");
if (!out) {
perror(filename);
return EXIT_FAILURE;
}
fclose(out);
return EXIT_SUCCESS;
```

}</lang>

Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

## C++

The output of this program is an SVG file. <lang cpp>#include <fstream>

1. include <iostream>
2. include <vector>

constexpr double sqrt3_2 = 0.86602540378444; // sqrt(3)/2

struct point {

```   double x;
double y;
```

};

std::vector<point> sierpinski_arrowhead_next(const std::vector<point>& points) {

```   size_t size = points.size();
std::vector<point> output(3*(size - 1) + 1);
double x0, y0, x1, y1;
size_t j = 0;
for (size_t i = 0; i + 1 < size; ++i, j += 3) {
x0 = points[i].x;
y0 = points[i].y;
x1 = points[i + 1].x;
y1 = points[i + 1].y;
double dx = x1 - x0;
output[j] = {x0, y0};
if (y0 == y1) {
double d = dx * sqrt3_2/2;
if (d < 0) d = -d;
output[j + 1] = {x0 + dx/4, y0 - d};
output[j + 2] = {x1 - dx/4, y0 - d};
} else if (y1 < y0) {
output[j + 1] = {x1, y0};
output[j + 2] = {x1 + dx/2, (y0 + y1)/2};
} else {
output[j + 1] = {x0 - dx/2, (y0 + y1)/2};
output[j + 2] = {x0, y1};
}
}
output[j] = {x1, y1};
return output;
```

}

void write_sierpinski_arrowhead(std::ostream& out, int size, int iterations) {

```   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='";
const double margin = 20.0;
const double side = size - 2.0 * margin;
const double x = margin;
const double y = 0.5 * size + 0.5 * sqrt3_2 * side;
std::vector<point> points{{x, y}, {x + side, y}};
for (int i = 0; i < iterations; ++i)
for (size_t i = 0, n = points.size(); i < n; ++i)
out << (i == 0 ? "M" : "L") << points[i].x << ',' << points[i].y << '\n';
out << "'/>\n</svg>\n";
```

}

int main() {

```   std::ofstream out("sierpinski_arrowhead.svg");
if (!out) {
std::cerr << "Cannot open output file\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
```

}</lang>

Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

## Factor

Works with: Factor version 0.99 2020-08-14

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

arrowhead ( L-system -- L-system )
```   L-parser-dialect >>commands
[ 60 >>angle ] >>turtle-values
"XF" >>axiom
{
{ "X" "YF+XF+Y" }
{ "Y" "XF-YF-X" }
} >>rules ;
```

[ <L-system> arrowhead "Arrowhead" 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

## Forth

Works with: gforth version 0.7.3

### ASCII

<lang forth>( ASCII output with use of ANSI terminal control )

draw-line ( direction -- )
``` case
0 of  .\" _"              endof ( horizontal right:          _          )
1 of  .\" \e[B\\"         endof (       down right:     CUD  \          )
2 of  .\" \e[D\e[B/\e[D"  endof (        down left: CUB CUD  /  CUB     )
3 of  .\" \e[D_\e[D"      endof (  horizontal left:     CUB  _  CUB     )
4 of  .\" \e[D\\\e[A\e[D" endof (          up left:     CUB  \  CUU CUB )
5 of  .\" /\e[A"          endof (         up right:          /  CUU     )
endcase                         ( cursor is up-right of the last point  )
```
turn+ 1+ 6 mod ;
turn- 1- 6 mod ;

defer curve

A-rule ( order direction -- ) turn+ 2dup 'B curve turn- 2dup 'A curve turn- 'B curve ;
B-rule ( order direction -- ) turn- 2dup 'A curve turn+ 2dup 'B curve turn+ 'A curve ;
noname ( order direction type -- )
``` 2 pick 0 = if drop draw-line drop exit then \ draw line when order is 0
rot 1- rot rot
'A = if A-rule else B-rule then
```
is curve

arrowhead ( order -- )
``` page
s" Sierpinski arrowhead curve of order " type dup . cr
s" =====================================" type cr
0 'A curve
```

Output:
```Sierpinski arrowhead curve of order 5
=====================================
_   _   _   _   _   _   _   _   _   _    ok
\_/ \ / \_/ \ / \_/ \ / \_/ \ / \_/ \ / \_/
_/ \_    / \    _/ \_    / \    _/ \_
/     \   \_/   /     \   \_/   /     \
\_   _/      _  \     /  _      \_   _/
\ /       / \_/     \_/ \       \ /
/ \       \_           _/       / \
\_/         \         /         \_/
_   _   _/         \_   _   _
/ \_/ \ /             \ / \_/ \
\_    / \             / \    _/
\   \_/             \_/   /
/  _                   _  \
\_/ \                 / \_/
_/                 \_
/                     \
\_   _   _   _   _   _/
\ / \_/ \ / \_/ \ /
/ \    _/ \_    / \
\_/   /     \   \_/
_  \     /  _
/ \_/     \_/ \
\_           _/
\         /
/  _   _  \
\_/ \ / \_/
_/ \_
/     \
\_   _/
\ /
/ \
\_/
```

### SVG file

<lang forth>( SVG ouput )

draw-line ( direction -- ) \ line-length=10 ; sin(60)=0.87 ; cos(60)=0.5
``` case
0 of s" h 10"      type cr endof
1 of s" l  5  8.7" type cr endof
2 of s" l -5  8.7" type cr endof
3 of s" h -10"     type cr endof
4 of s" l -5 -8.7" type cr endof
5 of s" l  5 -8.7" type cr endof
endcase
```
turn+ 1+ 6 mod ;
turn- 1- 6 mod ;

defer curve

A-rule ( order direction -- ) turn+ 2dup 'B curve turn- 2dup 'A curve turn- 'B curve ;
B-rule ( order direction -- ) turn- 2dup 'A curve turn+ 2dup 'B curve turn+ 'A curve ;
noname ( order direction type -- )
``` 2 pick 0 = if drop draw-line drop exit then \ draw line when order is 0
rot 1- rot rot
'A = if A-rule else B-rule then
```
is curve
raw. ( u -- ) 0 <# #s #> type ;
svg-start
``` dup 1 swap lshift 10 * ( -- order image-width ) \ image-width is 2 power order
s" sierpinski_arrowhead.svg" w/o create-file throw to outfile-id
s" <svg xmlns='http://www.w3.org/2000/svg' width='" type dup raw.
87 * 100 / ( -- order image-height ) \ image-height; sin(60)=0.87
s" ' height='" type raw. s" '>" type cr
s" <rect width='100%' height='100%' fill='white'/>" type cr
s" <path stroke-width='1' stroke='black' fill='none' d='" type cr
s" M 0 0" type cr
```
svg-end
``` s" '/> </svg>" type cr
outfile-id close-file throw
```
arrowhead ( order -- )
``` outfile-id >r svg-start
0 'A curve
svg-end r> to outfile-id
```

## Go

Library: Go Graphics
Translation of: Phix

A partial translation anyway which produces a static image of a SAC of order 6, magenta on black, 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))
iy     = 1.0
theta  = 0
```

)

var cx, cy, h float64

func arrowhead(order int, length float64) {

```   // if order is even, we can just draw the curve
if order&1 == 0 {
curve(order, length, 60)
} else {
turn(60)
curve(order, length, -60)
}
drawLine(length) // needed to make base symmetric
```

}

func drawLine(length float64) {

```   dc.LineTo(cx-width/2+h, (height-cy)*iy+2*h)
cx += length * math.Cos(rads)
cy += length * math.Sin(rads)
```

}

func turn(angle int) {

```   theta = (theta + angle) % 360
```

}

func curve(order int, length float64, angle int) {

```   if order == 0 {
drawLine(length)
} else {
curve(order-1, length/2, -angle)
turn(angle)
curve(order-1, length/2, angle)
turn(angle)
curve(order-1, length/2, -angle)
}
```

}

func main() {

```   dc.SetRGB(0, 0, 0) // black background
dc.Clear()
order := 6
if order&1 == 0 {
iy = -1 // apex will point upwards
}
cx, cy = width/2, height
h = cx / 2
dc.SetRGB255(255, 0, 255) // magenta curve
dc.SetLineWidth(2)
dc.Stroke()
```

}</lang>

## Julia

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

scurve = LSystem(Dict("F" => "G+F+Gt", "G"=>"F-G-F"), "G")

drawLSystem(scurve,

```   forward = 3,
turn = 60,
startingy = -350,
iterations = 8,
startingorientation = π/3,
showpreview = true
```

) </lang>

## Mathematica / Wolfram Language

<lang Mathematica>ClearAll[DoStep] DoStep[Line[{x_, y_}]] := Module[{diff, perp, pts},

``` diff = y - x;
perp = Cross[diff] Sqrt[3]/2;
pts = {x, x + diff/4 + perp/2, x + 3 diff/4 + perp/2, y};
{Line[pts[[{2, 1}]]], Line[pts[[{2, 3}]]], Line[pts[[{4, 3}]]]}
]
```

lns = {Line[{{0.0, 0.0}, {1.0, 0.0}}]}; lns = Nest[Catenate[DoStep /@ #] &, lns, 5]; Graphics[lns]</lang>

## Nim

Translation of: C++

Output is an SVG file. <lang Nim>import math

const Sqrt3_2 = sqrt(3.0) / 2.0

type Point = tuple[x, y: float]

func sierpinskiArrowheadNext(points: seq[Point]): seq[Point] =

``` result.setLen(3 * (points.len - 1) + 1)
var j = 0
for i in 0..<points.high:
let (x0, y0) = points[i]
let (x1, y1) = points[i + 1]
let dx = x1 - x0
result[j] = (x0, y0)
if y0 == y1:
let d = abs(dx * Sqrt3_2 / 2)
result[j + 1] = (x0 + dx / 4, y0 - d)
result[j + 2] = (x1 - dx / 4, y0 - d)
elif y1 < y0:
result[j + 1] = (x1, y0)
result[j + 2] = (x1 + dx / 2, (y0 + y1) / 2)
else:
result[j + 1] = (x0 - dx / 2, (y0 + y1) / 2)
result[j + 2] = (x0, y1)
inc j, 3
result[j] = points[^1]
```

proc writeSierpinskiArrowhead(outfile: File; size, iterations: int) =

``` 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='"
const Margin = 20.0
let side = size.toFloat - 2 * Margin
let x = Margin
let y = 0.5 * size.toFloat + 0.5 * Sqrt3_2 * side
var points = @[(x: x, y: y), (x: x + side, y: y)]
for _ in 1..iterations:
for i, point in points:
outfile.write if i == 0: 'M' else: 'L', point.x, ',', point.y, '\n'
outfile.write "'/>\n</svg>\n"
```

let outfile = open("sierpinski_arrowhead.svg", fmWrite) outfile.writeSierpinskiArrowhead(600, 8) outfile.close()</lang>

Output:

See output of C++ program.

## Perl

<lang perl>use strict; use warnings; use SVG; use List::Util qw(max min); use constant pi => 2 * atan2(1, 0);

my %rules = (

```   X => 'YF+XF+Y',
Y => 'XF-YF-X'
```

); my \$S = 'Y'; \$S =~ s/([XY])/\$rules{\$1}/eg for 1..7;

my (@X, @Y); my (\$x, \$y) = (0, 0); my \$theta = 0; 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/3; }
elsif (/\-/) { \$theta -= pi/3; }
```

}

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-arrowhead-curve.svg'; print \$fh \$svg->xmlify(-namespace=>'svg'); close \$fh;</lang> See: sierpinski-arrowhead-curve.svg (offsite SVG image)

## Phix

Library: Phix/pGUI

<lang Phix>-- demo\rosetta\Sierpinski_arrowhead_curve.exw -- -- Draws curves lo to hi (simultaneously), initially {6,6}, max {10,10} -- 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 = 6, hi = 6
```

atom cx, cy, h, theta

integer iy = +1

procedure draw_line(atom l)

```   cdCanvasVertex(cddbuffer, cx-width/2+h, (height-cy)*iy+2*h)
```

end procedure

procedure turn(integer angle)

```   theta = mod(theta+angle,360)
```

end procedure

procedure curve(integer order, atom l, integer angle)

```   if order=0 then
draw_line(l)
else
curve(order-1, l/2, -angle)
turn(angle)
curve(order-1, l/2,  angle)
turn(angle)
curve(order-1, l/2, -angle)
end if
```

end procedure

procedure sierpinski_arrowhead_curve(integer order, atom l)

```   -- If order is even we can just draw the curve.
if and_bits(order,1)=0 then
curve(order, l, +60)
else -- order is odd
turn( +60)
curve(order, l, -60)
end if
draw_line(l)
```

end procedure

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

```   {width, height} = IupGetIntInt(canvas, "DRAWSIZE")
cdCanvasActivate(cddbuffer)
for order=lo to hi do
cx = width/2
cy = height
h = cx/2
theta = 0
iy = iff(and_bits(order,1)?-1:+1)
cdCanvasBegin(cddbuffer, CD_OPEN_LINES)
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(10,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 arrowhead 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 arrowhead curve (6..6)")
IupSetCallback(dlg, "K_ANY", Icallback("key_cb"))
```
```   IupMap(dlg)
IupShowXY(dlg,IUP_CENTER,IUP_CENTER)
IupMainLoop()
IupClose()
```

end procedure

main()</lang>

## Processing

<lang java>final PVector t = new PVector(20, 30, 60);

void setup() {

``` size(450, 400);
noLoop();
background(0, 0, 200);
stroke(-1);
sc(7, 400, -60, t);
```

}

PVector sc(int o, float l, final int a, final PVector s) {

``` if (o > 0) {
sc(--o, l *= .5, -a, s).z += a;
sc(o, l, a, s).z += a;
sc(o, l, -a, s);
} else line(s.x, s.y,
s.x += cos(radians(s.z)) * l,
s.y += sin(radians(s.z)) * l);
return s;
```

}</lang>The sketch can be run online :
here.

### Processing Python mode

<lang python>

t = { 'x': 20, 'y': 30, 'a': 60 }

def setup():

```   size(450, 400)
background(0, 0, 200)
stroke(-1)
sc(7, 400, -60)
```

def sc(o, l, a, s = t, X = 'x', Y = 'y', A = 'a', HALF = .5):

```   if o:
o -= 1
l *= HALF
sc(o, l, -a)[A] += a
sc(o, l, a)[A] += a
sc(o, l, -a)
else:
x, y = s[X], s[Y]
s[X] += cos(radians(s[A])) * l
s[Y] += sin(radians(s[A])) * l
line(x, y, s[X], s[Y])
```
```   return s
```

</lang>

The sketch can be run online :
here.

## Prolog

Works with: SWI Prolog
Translation of: C

<lang prolog>main:-

```   write_sierpinski_arrowhead('sierpinski_arrowhead.svg', 600, 8).
```

```   open(File, write, Stream),
format(Stream,
"<svg xmlns='http://www.w3.org/2000/svg' width='~d' height='~d'>\n",
[Size, Size]),
write(Stream, "<rect width='100%' height='100%' fill='white'/>\n"),
write(Stream, "<path stroke-width='1' stroke='black' fill='none' d='"),
Margin = 20.0,
Side is Size - 2.0 * Margin,
X = Margin,
Y is 0.5 * Size + 0.25 * sqrt(3) * Side,
Cursor = cursor(X, Y, 0),
(Order mod 2 == 1 -> turn(Cursor, -60, Cursor1) ; Cursor1 = Cursor),
format(Stream, "M~g,~g", [X, Y]),
curve(Stream, Order, Side, Cursor1, _, 60),
write(Stream, "'/>\n</svg>\n"),
close(Stream).
```

turn(cursor(X, Y, A), Angle, cursor(X, Y, A1)):-

```   A1 is (A + Angle) mod 360.
```

draw_line(Stream, cursor(X, Y, A), Length, cursor(X1, Y1, A)):-

```   Theta is (pi * A)/180.0,
X1 is X + Length * cos(Theta),
Y1 is Y + Length * sin(Theta),
format(Stream, "L~g,~g", [X1, Y1]).
```

curve(Stream, 0, Length, Cursor, Cursor1, _):-

```   !,
draw_line(Stream, Cursor, Length, Cursor1).
```

curve(Stream, Order, Length, Cursor, Cursor1, Angle):-

```   Order1 is Order - 1,
Angle1 is -Angle,
Length2 is Length/2.0,
curve(Stream, Order1, Length2, Cursor, Cursor2, Angle1),
turn(Cursor2, Angle, Cursor3),
curve(Stream, Order1, Length2, Cursor3, Cursor4, Angle),
turn(Cursor4, Angle, Cursor5),
curve(Stream, Order1, Length2, Cursor5, Cursor1, Angle1).</lang>
```
Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

## 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 \$arrow = 'X' but Lindenmayer( { X => 'YF+XF+Y', Y => 'XF-YF-X' } );

\$arrow++ xx 7;

my \$w = 800; my \$h = (\$w * 3**.5 / 2).round(1);

my \$scale = 6; my @points = (400, 15); my \$dir = pi/3;

for \$arrow.comb {

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

}

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

\$out.spurt: SVG.serialize(

```   svg => [
:width(\$w), :height(\$h),
:rect[:width<100%>, :height<100%>, :fill<black>],
:polyline[ :points(@points.join: ','), :fill<black>, :style<stroke:#FF4EA9> ],
],
```

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

## REXX

Translation of: Algol W

<lang rexx>/*REXX pgm computes and displays a Sierpinski Arrowhead Curve using the characters: \_/ */ parse arg order . /*obtain optional argument from the CL.*/ if order== | order=="," then order= 5 /*Not specified? Then use the default.*/ say ' Sierpinski arrowhead curve of order' order /*display the title. */ say '═════════════════════════════════════════' /* " " separator.*/ \$= init() /*initialize a bunch of variables. */ if order//2 then do; call turn +60; call curve order, len, -60; end /*CURVE odd? */

```            else                      call curve order, len, +60          /*CURVE even.*/
```
```      do    row=Ly  to Hy;   a=                 /*show arrowhead graph 1 row at a time.*/
do col=Lx  to Hx;   a= a || @.col.row  /*build a row of   "   " col  " "   "  */
end   /*col*/;  say strip(a, 'T')      /*show  "  "   "   "     row  " "   "  */
end      /*row*/
```

exit 0 /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ init: @.=" "; #=0; len=512; x=len; y=x;Hx=x;Hy=y;Lx=x;Ly=y; return '@. # Hx Hy Lx Ly x y' turn: parse arg angle; #= (#+angle)//360; if #<0 then #= #+360; return /*normalize.*/ /*──────────────────────────────────────────────────────────────────────────────────────*/ curve: procedure expose (\$); parse arg order,len,angle /*\$: list of exposed variables*/

```      if order==0  then call draw len                   /*Is ORDER zero?  Then draw it.*/
else do;  call curve order-1, len/2, -angle;      call turn angle
call curve order-1, len/2, +angle;      call turn angle
call curve order-1, len/2, -angle
end
return                                    /*The  CURVE  function is recursive.   */
```

/*──────────────────────────────────────────────────────────────────────────────────────*/ draw: select /*draw part of the curve using a char. */

```          when #==  0  then do;    @.x.y= '_';      x= x + 1;                        end
when #== 60  then do;    @.x.y= '/';      x= x + 1;        y= y - 1;       end
when #==120  then do;    x= x - 1;        @.x.y= '\';      y= y - 1;       end
when #==180  then do;    x= x - 1;        @.x.y= '_';                      end
when #==240  then do;    x= x - 1;        y= y + 1;        @.x.y= '/';     end
when #==300  then do;    y= y + 1;        @.x.y= '\';      x= x + 1;       end
end   /*select*/                      /*curve character is based on direction*/
Lx= min(Lx,x);  Hx= max(Hx,x);  Ly= min(Ly,y);  Hy= max(Hy,y)  /*min&max  of  x,y*/
return                                    /*#:  heading in degrees of the curve. */</lang>
```
output   when using the default input value of:     5
```  Sierpinski arrowhead curve of order 5
═════════════════════════════════════════
_
/ \
\ /
_/ \_
/     \
\_   _/
_  \ /  _
/ \_/ \_/ \
\         /
_/         \_
/  _       _  \
\_/ \     / \_/
_    /     \    _
/ \   \_   _/   / \
\ /  _  \ /  _  \ /
_/ \_/ \_/ \_/ \_/ \_
/                     \
\_                   _/
_  \                 /  _
/ \_/                 \_/ \
\    _               _    /
_/   / \             / \   \_
/  _  \ /             \ /  _  \
\_/ \_/ \_           _/ \_/ \_/
_          \         /          _
/ \        _/         \_        / \
\ /       /  _       _  \       \ /
_/ \_      \_/ \     / \_/      _/ \_
/     \    _    /     \    _    /     \
\_   _/   / \   \_   _/   / \   \_   _/
_  \ /  _  \ /  _  \ /  _  \ /  _  \ /  _
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \
```

## Ruby

Library: RubyGems
Library: JRubyArt

For grammar see Hilbert Curve <lang ruby> load_libraries :grammar attr_reader :points

def setup

``` sketch_title 'Sierpinski Arrowhead'
sierpinski = SierpinskiArrowhead.new(Vec2D.new(width * 0.15, height * 0.7))
production = sierpinski.generate 6 # 6 generations looks OK
@points = sierpinski.translate_rules(production)
no_loop
```

end

def draw

``` background(0)
render points
```

end

def render(points)

``` no_fill
stroke 200.0
stroke_weight 3
begin_shape
points.each_slice(2) do |v0, v1|
v0.to_vertex(renderer)
v1.to_vertex(renderer)
end
end_shape
```

end

def renderer

``` @renderer ||= GfxRender.new(g)
```

end

def settings

``` size(800, 800)
```

end

``` include Processing::Proxy
attr_reader :draw_length, :pos, :theta, :axiom, :grammar
DELTA = PI / 3 # 60 degrees
def initialize(pos)
@axiom = 'XF' # Axiom
rules = {
'X' => 'YF+XF+Y',
'Y' => 'XF-YF-X'
}
@grammar = Grammar.new(axiom, rules)
@theta = 0
@draw_length = 200
@pos = pos
end
```
``` def generate(gen)
@draw_length = draw_length * 0.6**gen
grammar.generate gen
end
```
``` def forward(pos)
pos + Vec2D.from_angle(theta) * draw_length
end
```
``` def translate_rules(prod)
[].tap do |pts| # An array to store line vertices as Vec2D
prod.scan(/./) do |ch|
case ch
when 'F'
new_pos = forward(pos)
pts << pos << new_pos
@pos = new_pos
when '+'
@theta += DELTA
when '-'
@theta -= DELTA
when 'X', 'Y'
else
puts("character #{ch} not in grammar")
end
end
end
end
```

end

</lang>

## Rust

Output is a file in SVG format. Another variation on the same theme as the C, Go and Phix solutions. <lang rust>// [dependencies] // svg = "0.8.0"

const SQRT3_2: f64 = 0.86602540378444;

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

struct Cursor {

```   x: f64,
y: f64,
angle: i32,
```

}

impl Cursor {

```   fn new(x: f64, y: f64) -> Cursor {
Cursor {
x: x,
y: y,
angle: 0,
}
}
fn turn(&mut self, angle: i32) {
self.angle = (self.angle + angle) % 360;
}
fn draw_line(&mut self, data: Data, length: f64) -> Data {
let theta = (self.angle as f64).to_radians();
self.x += length * theta.cos();
self.y += length * theta.sin();
data.line_to((self.x, self.y))
}
```

}

fn curve(mut data: Data, order: usize, length: f64, cursor: &mut Cursor, angle: i32) -> Data {

```   if order == 0 {
return cursor.draw_line(data, length);
}
data = curve(data, order - 1, length / 2.0, cursor, -angle);
cursor.turn(angle);
data = curve(data, order - 1, length / 2.0, cursor, angle);
cursor.turn(angle);
curve(data, order - 1, length / 2.0, cursor, -angle)
```

}

fn write_sierpinski_arrowhead(file: &str, size: usize, order: usize) -> std::io::Result<()> {

```   use svg::node::element::Path;
use svg::node::element::Rectangle;
```
```   let margin = 20.0;
let side = (size as f64) - 2.0 * margin;
let y = 0.5 * (size as f64) + 0.5 * SQRT3_2 * side;
let x = margin;
let mut cursor = Cursor::new(x, y);
if (order & 1) != 0 {
cursor.turn(-60);
}
let mut data = Data::new().move_to((x, y));
data = curve(data, order, side, &mut cursor, 60);
let rect = Rectangle::new()
.set("width", "100%")
.set("height", "100%")
.set("fill", "white");
let mut document = svg::Document::new()
.set("width", size)
.set("height", size)
let path = Path::new()
.set("fill", "none")
.set("stroke", "black")
.set("stroke-width", "1")
.set("d", data);
svg::save(file, &document)
```

}

fn main() {

```   write_sierpinski_arrowhead("sierpinski_arrowhead.svg", 600, 8).unwrap();
```

}</lang>

Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

## Sidef

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

```   x => 'yF+xF+y',
y => 'xF-yF-x',
```

)

var lsys = LSystem(

```   width:  550,
height: 500,
```
```   xoff: -20,
yoff: -30,
```
```   len:   4,
turn: -90,
angle: 60,
color: 'dark green',
```

)

lsys.execute('xF', 7, "sierpiński_arrowhead.png", rules)</lang> Output image: Sierpiński arrowhead

## Wren

Translation of: Go
Library: DOME

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

class Game {

```   static init() {
Window.title = "Sierpinski Arrowhead Curve"
__width = 770
__height = 770
Window.resize(__width, __height)
Canvas.resize(__width, __height)
var order = 6
__iy = (order&1 == 0) ? -1: 1  // apex will point upwards
__theta = 0
__cx = __width / 2
__cy = __height
__h  = __cx / 2
__prev = Point.new(__cx-__width/2 +__h, (__height-__cy)*__iy + 2*__h)
__col = Color.white
}
```
```   static update() {}
```
```   static draw(alpha) {}
```
```   static arrowhead(order, length) {
// if order is even, we can just draw the curve
if (order&1 == 0) {
curve(order, length, 60)
} else {
turn(60)
curve(order, length, -60)
}
drawLine(length) // needed to make base symmetric
}
```
```   static drawLine(length) {
var curr = Point.new(__cx-__width/2 +__h, (__height-__cy)*__iy + 2*__h)
Canvas.line(__prev.x, __prev.y, curr.x, curr.y, __col)
var rads = __theta * Num.pi / 180
__cx = __cx + length*(rads.cos)
__cy = __cy + length*(rads.sin)
__prev = curr
}
```
```   static turn(angle) { __theta = (__theta + angle) % 360 }
```
```   static curve(order, length, angle) {
if (order == 0) {
drawLine(length)
} else {
curve(order-1, length/2, -angle)
turn(angle)
curve(order-1, length/2, angle)
turn(angle)
curve(order-1, length/2, -angle)
}
}
```

}</lang>

## zkl

Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl <lang zkl>order:=7; sierpinskiArrowheadCurve(order) : turtle(_,order);

fcn sierpinskiArrowheadCurve(n){ // Lindenmayer system --> Data of As & Bs

```  var [const] A="BF+AF+B", B="AF-BF-A";  // Production rules
var [const] Axiom="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=7 --> 6,560 characters
```

}

fcn turtle(curve,order){ // Turtle with that can turn +-60*

```  const D=10.0, a60=60;
dir:=order.isOdd and a60 or 0;	   // start direction depends on order
img,color := PPM(1300,1200), 0x00ff00;  // green on black
x,y := 10, 10;
foreach c in (curve){  // A & B are no-op during drawing
switch(c){
```

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

```     }
}