You are encouraged to solve this task according to the task description, using any language you may know.

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

## 11l

Translation of: Nim
V sqrt3_2 = sqrt(3) / 2

V result = [(0.0, 0.0)] * (3 * (points.len - 1) + 1)
V j = 0
L(i) 0 .< points.len - 1
V (x0, y0) = points[i]
V (x1, y1) = points[i + 1]
V dx = x1 - x0
result[j] = (x0, y0)
I y0 == y1
V d = abs(dx * :sqrt3_2 / 2)
result[j + 1] = (x0 + dx / 4, y0 - d)
result[j + 2] = (x1 - dx / 4, y0 - d)
E I y1 < y0
result[j + 1] = (x1, y0)
result[j + 2] = (x1 + dx / 2, (y0 + y1) / 2)
E
result[j + 1] = (x0 - dx / 2, (y0 + y1) / 2)
result[j + 2] = (x0, y1)
j += 3
result[j] = points.last
R result

V size = 600
V iterations = 8

outfile.write(‘<svg xmlns='http://www.w3.org/2000/svg' width='’size‘' height='’size"'>\n")
outfile.write("<rect width='100%' height='100%' fill='white'/>\n")
outfile.write(‘<path stroke-width='1' stroke='black' fill='none' d='’)
V margin = 20.0
V side = size - 2 * margin
V x = margin
V y = 0.5 * size + 0.5 * sqrt3_2 * side
V points = [(x, y), (x + side, y)]
L 0 .< iterations
L(point) points
outfile.write(I L.index == 0 {‘M’} E ‘L’)
outfile.write(point.x‘,’point.y"\n")
outfile.write("'/>\n</svg>\n")
Output:

Output is similar to Nim and C++.

Library: APDF

Even order curves are drawn pointing downwards.

with PDF_Out;

package Real_Math is

subtype Angle_Deg  is Real;
type    Order_Type is range 0 .. 7;

Purple : constant Color_Type := (0.7, 0.0, 0.5);
Length : constant Real       := 340.0;
Corner : constant Point      := (120.0, 480.0);

Order     : Order_Type;
Current   : Point      := (0.0, 0.0);
Direction : Angle_Deg  := Angle_Deg'(0.0);
Doc       : PDF_Out_File;

procedure Curve (Order : Order_Type; Length : Real; Angle : Angle_Deg) is
begin
if Order = 0 then
Current := Current + Length * Point'(Cos (Direction, 360.0),
Sin (Direction, 360.0));
Doc.Line (Corner + Current);
else
Curve (Order - 1, Length / 2.0, -Angle);  Direction := Direction + Angle;
Curve (Order - 1, Length / 2.0,  Angle);  Direction := Direction + Angle;
Curve (Order - 1, Length / 2.0, -Angle);
end if;
end Curve;

begin
if Argument_Count /= 1 then
Put_Line ("  <order>   0 .. 7");
Put_Line ("open sierpinski-arrowhead-curve.pdf to view ouput");
return;
end if;
Order := Order_Type'Value (Argument (1));

Doc.Page_Setup (A4_Portrait);
Doc.Margins (Margins_Type'(Left   => Cm_2_5,
others => One_cm));
Doc.Stroking_Color (Purple);
Doc.Line_Width (2.0);
Doc.Move (Corner);
if Order mod 2 = 0 then
Direction := 0.0;
Curve (Order, Length, 60.0);
else
Direction := 60.0;
Curve (Order, Length, -60.0);
end if;
Doc.Finish_Path (Close_Path => False,
Rendering  => Stroke,
Rule       => Nonzero_Winding_Number);
Doc.Close;

## ALGOL 68

Generates an SVG file containing the curve using the L-System. Very similar to the Algol 68 Sierpinski square curve sample. Note the Algol 68 L-System library source code is on a separate page on Rosetta Code - follow the above link and then to the Talk page.

BEGIN # Sierpinski Arrowhead Curve in SVG                                    #
# uses the RC Algol 68 L-System library for the L-System evaluation &  #
# interpretation                                                       #

PR read "lsystem.incl.a68" PR               # include L-System utilities #

PROC sierpinski arrowhead curve = ( STRING fname, INT size, length, order, init x, init y )VOID:
IF FILE svg file;
BOOL open error := IF open( svg file, fname, stand out channel ) = 0
THEN
# opened OK - file already exists and     #
#             will be overwritten         #
FALSE
ELSE
# failed to open the file                 #
# - try creating a new file               #
establish( svg file, fname, stand out channel ) /= 0
FI;
open error
THEN                                      # failed to open the file #
print( ( "Unable to open ", fname, newline ) );
stop
ELSE                                               # file opened OK #

REAL x     := init x;
REAL y     := init y;
INT  angle := 0;
put( svg file, ( "<svg xmlns='http://www.w3.org/2000/svg' width='"
, whole( size, 0 ), "' height='", whole( size, 0 ), "'>"
, newline, "<rect width='100%' height='100%' fill='white'/>"
, newline, "<path stroke-width='1' stroke='black' fill='none' d='"
, newline, "M", whole( x, 0 ), ",", whole( y, 0 ), newline
)
);

LSYSTEM ssc = ( "XF"
, ( "X" -> "YF+XF+Y"
, "Y" -> "XF-YF-X"
)
);
STRING curve = ssc EVAL order;
curve INTERPRET ( ( CHAR c )VOID:
IF   c = "F" THEN
x +:= length * cos( angle * pi / 180 );
y +:= length * sin( angle * pi / 180 );
put( svg file, ( " L", whole( x, 0 ), ",", whole( y, 0 ), newline ) )
ELIF c = "+" THEN
angle +:= 60 MODAB 360
ELIF c = "-" THEN
angle -:= 60 MODAB 360
FI
);

put( svg file, ( "'/>", newline, "</svg>", newline ) );
close( svg file )
FI # sierpinski square # ;

END

## ALGOL W

Note that the Wikipedia algotrithm draws even order curves with the base at the top.

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
else begin
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;
drawArt
end for_order
end
end.
Output:
Sierpinski arrowhead curve of order 5
=====================================

_
/ \
\ /
_/ \_
/     \
\_   _/
_  \ /  _
/ \_/ \_/ \
\         /
_/         \_
/  _       _  \
\_/ \     / \_/
_    /     \    _
/ \   \_   _/   / \
\ /  _  \ /  _  \ /
_/ \_/ \_/ \_/ \_/ \_
/                     \
\_                   _/
_  \                 /  _
/ \_/                 \_/ \
\    _               _    /
_/   / \             / \   \_
/  _  \ /             \ /  _  \
\_/ \_/ \_           _/ \_/ \_/
_          \         /          _
/ \        _/         \_        / \
\ /       /  _       _  \       \ /
_/ \_      \_/ \     / \_/      _/ \_
/     \    _    /     \    _    /     \
\_   _/   / \   \_   _/   / \   \_   _/
_  \ /  _  \ /  _  \ /  _  \ /  _  \ /  _
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \

## AutoHotkey

Translation of: Go

Requires Gdip Library

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

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

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

## C

This code is based on the Phix and Go solutions, but produces a file in SVG format.

#include <math.h>
#include <stdio.h>
#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) {
if (argc == 2)
filename = argv[1];
FILE* out = fopen(filename, "w");
if (!out) {
perror(filename);
return EXIT_FAILURE;
}
fclose(out);
return EXIT_SUCCESS;
}
Output:

## C++

The output of this program is an SVG file.

#include <fstream>
#include <iostream>
#include <vector>

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

struct point {
double x;
double y;
};

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() {
if (!out) {
std::cerr << "Cannot open output file\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Output:

## Delphi

Works with: Delphi version 6.0
type T2DVector=packed record
X,Y: double;
end;

var Pos: T2DVector;
var CurAngle: double;

procedure Turn(Angle: double);
{Turn current angle by specified degrees}
begin
CurAngle:=CurAngle+Angle;
end;

procedure DrawLine(Canvas: TCanvas; Len: double);
{Draw current line, rotated by the current angle}
var P2: T2DVector;
begin
Canvas.Pen.Mode:=pmCopy;
Canvas.Pen.Style:=psSolid;
Canvas.MoveTo(Round(Pos.X),Round(Pos.Y));
Canvas.LineTo(Round(P2.X),Round(P2.Y));
Pos:=P2;
end;

procedure Curve(Canvas: TCanvas; Order: integer; Length: double; Angle: integer);
{Recursively draw curve of specified order and based specified Angle}
begin
if 0 = Order then DrawLine(Canvas,Length)
else
begin
Curve(Canvas,Order - 1, Length / 2, -Angle);
Turn(angle);
curve(Canvas,Order - 1, Length / 2, Angle);
Turn(angle);
Curve(Canvas,Order - 1, Length / 2, -Angle);
end;
end;

procedure SierpinskiArrowheadCurve(Image: TImage; Order: integer; Length: double);
{Draw arrowhead curve of specified order.}
{Length controls the width of one side of the arrowhead }
begin
Pos.X:=10; Pos.Y:=10;
if (Order and 1)=0 then Curve(Image.Canvas, Order, Length, +60)
else
begin
{Order is odd}
Turn(+60);
Curve(Image.Canvas, Order, Length, -60);
end;
end;

var Size: double;
begin
if Image.Width>Image.Height then Size:=Image.Height/0.9
else Size:=Image.Width;
{Super impose three different orders, colors and line-widths}
Image.Canvas.Pen.Color:=clBlack;
Image.Canvas.Pen.Width:=3;
Image.Canvas.Pen.Color:=clBlue;
Image.Canvas.Pen.Width:=2;
Image.Canvas.Pen.Color:=clRed;
Image.Canvas.Pen.Width:=1;
end;
Output:
Elapsed Time: 50.555 ms.

## EasyLang

x = 5
y = 10
ang = 60
linewidth 0.5
#
proc curv o l a . .
if o = 0
x += cos ang * l
y += sin ang * l
line x y
else
o -= 1
l /= 2
curv o l (-a)
ang += a
curv o l a
ang += a
curv o l (-a)
.
.
move x y
curv 7 90 -60

## Factor

Works with: Factor version 0.99 2020-08-14
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 ;

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

( 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

( 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
;

## FreeBASIC

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

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

Sub Curva(orden As Byte, largo As Single, angulo As Integer)
If orden = 0 Then
posX += Fix(largo * Cos(direc))
posY -= Fix(largo * Sin(direc))
Line - (posX, posY), yellow
Else
Curva(orden-1, largo/2, -angulo)
direc += angulo
Curva(orden-1, largo/2, +angulo)
direc += angulo
Curva(orden-1, largo/2, -angulo)
End If
End Sub

Screenres 640, 480, 32

Dim As Integer x, y
Dim As Byte orden = 5
Dim As Integer tam = 2^orden
Dim As Single largo = 300
Dim As Single sixty = pi/3

direc = 0
posX = 640/4
posY = 3*480/4
Pset (posX, posY)

If (orden And 1) = 0 Then
Curva(orden, largo, +sixty)
Else
direc += sixty
Curva(orden, largo, -sixty)
End If

Windowtitle "Hit any key to end program"
Sleep

## Fōrmulæ

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

Programs in Fōrmulæ are created/edited online in its website.

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

Solution

It can be done using an L-system. There are generic functions written in Fōrmulæ to compute an L-system in the page L-system.

The program that creates a Sierpiński arrowhead is:

## 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.

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

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()
}

## IS-BASIC

100 PROGRAM "Sierpin2.bas"
110 LET LEVEL=5
120 OPTION ANGLE DEGREES
130 SET VIDEO MODE 1:SET VIDEO COLOUR 0:SET VIDEO X 40:SET VIDEO Y 27
140 OPEN #101:"video:"
150 SET PALETTE BLACK,YELLOW
160 DISPLAY #101:AT 1 FROM 1 TO 27
170 PLOT 1180,20,ANGLE 180;
180 CALL SIERP(560,LEVEL)
190 DEF CURVE(D,A,LEV)
200   IF LEV=0 THEN
210     PLOT FORWARD D;
220   ELSE
230     CALL CURVE(D/2,-A,LEV-1)
240     PLOT RIGHT A;
250     CALL CURVE(D/2,A,LEV-1)
260     PLOT RIGHT A;
270     CALL CURVE(D/2,-A,LEV-1)
280   END IF
290 END DEF
300 DEF SIERP(D,LEV)
310   CALL CURVE(D,60,LEV)
320   PLOT LEFT 60;
330   CALL CURVE(D,-60,LEV)
340   PLOT LEFT 60;
350   CALL CURVE(D,60,LEV)
360 END DEF

## Java

import java.awt.Point;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public static void main(String[] aArgs) throws IOException {
List<Point> points = initialStraightLine();
for ( int i = 1; i < 8; i++ ) {
points = nextIteration(points);
}

}

private static List<Point> initialStraightLine() {
final int margin = 50;
final int boxSize = IMAGE_SIZE - 2 * margin;
final int x = margin;
final int y = Math.round(( IMAGE_SIZE + SQRT3_2 * boxSize ) / 2.0F);

List<Point> points = Arrays.asList( new Point(x, y), new Point(x + boxSize, y) );
return points;
}

private static List<Point> nextIteration(List<Point> aPoints) {
List<Point> result = new ArrayList<Point>();

for ( int i = 0; i < aPoints.size() - 1; i++ ) {
final int x0 = aPoints.get(i).x;
final int y0 = aPoints.get(i).y;
final int x1 = aPoints.get(i + 1).x;
final int y1 = aPoints.get(i + 1).y;
final int dx = x1 - x0;

if ( y0 == y1 ) {
final float d = Math.abs(dx) * SQRT3_2 / 2;
result.add( new Point(x0 + dx / 4, Math.round(y0 - d)) );
result.add( new Point(x1 - dx / 4, Math.round(y0 - d)) );
} else if ( y1 < y0 ) {
result.add( new Point(x1 + dx / 2, ( y0 + y1 ) / 2) );
} else {
result.add( new Point(x0 - dx / 2, ( y0 + y1 ) / 2) );
}
}

return result;
}

private static String sierpinskiArrowheadText(List<Point> aPoints, int aSize) {
StringBuilder text = new StringBuilder();
text.append("<svg xmlns='http://www.w3.org/2000/svg'");
text.append(" width='" + aSize + "' height='" + aSize + "'>\n");
text.append("<rect width='100%' height='100%' fill='white'/>\n");
text.append("<path stroke-width='1' stroke='black' fill='white' d='");
for ( int i = 0; i < aPoints.size(); i++ ) {
text.append(( i == 0 ? "M" : "L" ) + aPoints.get(i).x + ", " + aPoints.get(i).y + " ");
}
text.append("'/>\n</svg>\n");

return text.toString();
}

private static final float SQRT3_2 = (float) Math.sqrt(3.0F) / 2.0F;
private static final int IMAGE_SIZE = 700;

}
Output:

## 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`.

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

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

# Compute the curve using a Lindenmayer system of rules
def rules:
{X: "Yf+Xf+Y", Y: "Xf-Yf-X", "": "X"};

def sierpinski(\$count):
rules as \$rules
| def repeat(\$count):
if \$count == 0 then .
else ascii_downcase | gsub("x"; \$rules["X"]) | gsub("y"; \$rules["Y"])
| repeat(\$count-1)
end;
\$rules[""] | repeat(\$count) ;

def interpret(\$x):
if   \$x == "+" then turtleRotate(60)
elif \$x == "-" then turtleRotate(-60)
elif \$x == "f" then turtleForward(3)
else .
end;

def sierpinski_curve(\$n):
sierpinski(\$n)
| split("")
| reduce .[] as \$action (
turtle([0,-350]) | turtleDown | turtleRotate(60);
interpret(\$action) ) ;

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

sierpinski_curve(8)
| svg

## 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
)

## Lambdatalk

{def sierp
{def sierp.r
{lambda {:order :length :angle}
{if {= :order 0}
then M:length
else {sierp.r {- :order 1} {/ :length 2} {- :angle}}
T:angle
{sierp.r {- :order 1} {/ :length 2} :angle}
T:angle
{sierp.r {- :order 1} {/ :length 2} {- :angle}}
}}}
{lambda {:order :length}
{if {= {% :order 2} 0}
then {sierp.r :order :length 60}
else T60
{sierp.r :order :length -60}
}}}

the output can be seen in http://lambdaway.free.fr/lambdawalks/?view=sierpinsky

## Mathematica / Wolfram Language

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]

## Nim

Translation of: C++

Output is an SVG file.

import math

const Sqrt3_2 = sqrt(3.0) / 2.0

type Point = tuple[x, y: float]

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"

outfile.close()
Output:

See output of C++ program.

## 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)");

print \$fh  \$svg->xmlify(-namespace=>'svg');
close \$fh;

## Phix

Library: Phix/pGUI

You can run this online here, and experiment with [shift] +/- as noted below.

--
-- ===========================================
--
--  Draws curves lo to hi (simultaneously), initially {6,6}, max {10,10}, min {1,1}
--  Press +/- to change hi, shift +/- to change lo.
--  ("=_" are also mapped to "+-", for the non-numpad +/-)
--
with javascript_semantics
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

-- 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*/, /*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_DEFAULT
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)
if platform()!=JS then
IupMainLoop()
IupClose()
end if
end procedure

main()

## Processing

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,
return s;
}

The sketch can be run online :
here.

### Processing Python mode

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]
line(x, y, s[X], s[Y])

return s

The sketch can be run online :
here.

## Python

import matplotlib.pyplot as plt
import math

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

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

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

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

if __name__ == '__main__':
# Sierpinski Arrowhead Curve L-System Definition
s_axiom = "XF"
s_rules = {"X": "YF+XF+Y",
"Y": "XF-YF-X"}
s_angle = 60

draw_lsystem(s_axiom, s_rules, s_angle, 7)

## Prolog

Works with: SWI Prolog
Translation of: C
main:-

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

## Quackery

Using an L-system.

[ \$ "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          ( \$ --> \$ )

[ \$ "BLALB" ]                  is A          ( \$ --> \$ )

[ \$ "ARBRA" ]                  is B          ( \$ --> \$ )

\$ "A"

6 times expand

turtle
10 frames
witheach
[ switch
[ char L case [ -1 6 turn ]
char R case [  1 6 turn ]
otherwise   [  4 1 walk ] ] ]
1 frames
Output:

## Raku

(formerly Perl 6)

Works with: Rakudo version 2020.02
use SVG;

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

my \$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 { }
}

\$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> ],
],
);

## REXX

Translation of: Algol W
/*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. */
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

def setup
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

## Rust

Output is a file in SVG format. Another variation on the same theme as the C, Go and Phix solutions.

// [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() {
}
Output:

## Sidef

Uses the LSystem() class from Hilbert curve.

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',
)

## Wren

Translation of: Go
Library: DOME
import "graphics" for Canvas, Color, Point
import "dome" for Window

class Game {
static init() {
__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) {}

// 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
__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)
}
}
}
Output:

## XPL0

int  PosX, PosY;
real Dir;

proc Curve(Order, Length, Angle);
int  Order; real Length, Angle;
[if Order = 0 then
[PosX:= PosX + fix(Length*Cos(Dir));
PosY:= PosY - fix(Length*Sin(Dir));
Line(PosX, PosY, \$E \yellow\);
]
else    [Curve(Order-1, Length/2.0, -Angle);
Dir:= Dir + Angle;
Curve(Order-1, Length/2.0, +Angle);
Dir:= Dir + Angle;
Curve(Order-1, Length/2.0, -Angle);
];
];

def Order=5, Length=300.0, Pi=3.141592654, Sixty=Pi/3.0;
[SetVid(\$12);   \VGA graphics: 640x480x8
PosX:= 640/4;  PosY:= 3*480/4;
Move(PosX, PosY);
Dir:= 0.0;
if (Order&1) = 0 then
Curve(Order, Length, +Sixty)
else    [Dir:= Dir + Sixty;
Curve(Order, Length, -Sixty);
];
]
Output:
http://www.xpl0.org/rcarrow.gif

## zkl

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

order:=7;

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