I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

Sierpinski arrowhead curve

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

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

ALGOL W[edit]

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.

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 if_various_headings
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
heading := heading + angle;
if heading < 0 then heading := heading + 360
end tuen ;
 % initialises the ascii art canvas %
procedure initArt ;
begin
heading := 0;
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__
end sierpinskiArrowheadCurve ;
 % 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.
Output:
Sierpinski arrowhead curve of order 5
=====================================

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

AutoHotkey[edit]

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

C[edit]

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

// See https://en.wikipedia.org/wiki/Sierpi%C5%84ski_curve#Arrowhead_curve
#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) {
const char* filename = "sierpinski_arrowhead.svg";
if (argc == 2)
filename = argv[1];
FILE* out = fopen(filename, "w");
if (!out) {
perror(filename);
return EXIT_FAILURE;
}
write_sierpinski_arrowhead(out, 600, 8);
fclose(out);
return EXIT_SUCCESS;
}
Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

C++[edit]

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;
};
 
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)
points = sierpinski_arrowhead_next(points);
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;
}
write_sierpinski_arrowhead(out, 600, 8);
return EXIT_SUCCESS;
}
Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

Factor[edit]

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 ;
 
[ <L-system> arrowhead "Arrowhead" open-window ] with-ui


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

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


Forth[edit]

Works with: gforth version 0.7.3

ASCII[edit]

( 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
;
 
5 arrowhead
Output:
Sierpinski arrowhead curve of order 5 
=====================================
   _   _   _   _   _   _   _   _   _   _    ok
\_/ \ / \_/ \ / \_/ \ / \_/ \ / \_/ \ / \_/
   _/ \_    / \    _/ \_    / \    _/ \_
  /     \   \_/   /     \   \_/   /     \
  \_   _/      _  \     /  _      \_   _/
    \ /       / \_/     \_/ \       \ /
    / \       \_           _/       / \
    \_/         \         /         \_/
       _   _   _/         \_   _   _
      / \_/ \ /             \ / \_/ \
      \_    / \             / \    _/
        \   \_/             \_/   /
        /  _                   _  \
        \_/ \                 / \_/
           _/                 \_
          /                     \
          \_   _   _   _   _   _/
            \ / \_/ \ / \_/ \ /
            / \    _/ \_    / \
            \_/   /     \   \_/
               _  \     /  _
              / \_/     \_/ \
              \_           _/
                \         /
                /  _   _  \
                \_/ \ / \_/
                   _/ \_
                  /     \
                  \_   _/
                    \ /
                    / \
                    \_/


SVG file[edit]

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


Go[edit]

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)
rads := gg.Radians(float64(theta))
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
arrowhead(order, cx)
dc.SetRGB255(255, 0, 255) // magenta curve
dc.SetLineWidth(2)
dc.Stroke()
dc.SavePNG("sierpinski_arrowhead_curve.png")
}


Julia[edit]

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,
filename = "sierpinski_arrowhead_curve.png",
showpreview = true
)
 

Mathematica / Wolfram Language[edit]

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[edit]

Translation of: C++

Output is an SVG file.

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:
points = sierpinskiArrowheadNext(points)
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()
Output:

See output of C++ program.

Perl[edit]

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;

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

Phix[edit]

Library: Phix/pGUI

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

--
-- demo\rosetta\Sierpinski_arrowhead_curve.exw
-- ===========================================
--
--  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)
    cx += l*cos(theta*CD_DEG2RAD)
    cy += l*sin(theta*CD_DEG2RAD)
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*/, /*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)
        sierpinski_arrowhead_curve(order, cx)
        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[edit]

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;
}
The sketch can be run online :
here.

Processing Python mode[edit]

 
 
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
 

The sketch can be run online :
here.

Python[edit]

 
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[edit]

Works with: SWI Prolog
Translation of: C
main:-
write_sierpinski_arrowhead('sierpinski_arrowhead.svg', 600, 8).
 
write_sierpinski_arrowhead(File, Size, Order):-
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:

See: sierpinski_arrowhead.svg (offsite SVG image)

Quackery[edit]

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
witheach
[ switch
[ char L case [ -1 6 turn ]
char R case [ 1 6 turn ]
otherwise [ 4 1 walk ] ] ]
Output:

https://imgur.com/Qaz37ds

Raku[edit]

(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 { }
}
 
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> ],
],
);

See: Sierpinski-arrowhead-curve-perl6.svg (offsite SVG image)

REXX[edit]

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[edit]

Library: RubyGems
Library: JRubyArt

For grammar see Hilbert Curve

 
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
 
# SierpinskiArrowhead class
class SierpinskiArrowhead
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[edit]

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)
.add(rect);
let path = Path::new()
.set("fill", "none")
.set("stroke", "black")
.set("stroke-width", "1")
.set("d", data);
document = document.add(path);
svg::save(file, &document)
}
 
fn main() {
write_sierpinski_arrowhead("sierpinski_arrowhead.svg", 600, 8).unwrap();
}
Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

Sidef[edit]

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',
)
 
lsys.execute('xF', 7, "sierpiński_arrowhead.png", rules)

Output image: Sierpiński arrowhead

Wren[edit]

Translation of: Go
Library: DOME
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
arrowhead(order, __cx)
}
 
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)
}
}
}

zkl[edit]

Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#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*
}
}
img.writeJPGFile("sierpinskiArrowheadCurve.zkl.jpg");
}
Output:

Offsite image at Sierpinski arrowhead curve order 7