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.

11l

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

F sierpinski_arrowhead_next(points)
   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
V outfile = File(‘sierpinski_arrowhead.svg’, WRITE)

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
   points = sierpinski_arrowhead_next(points)
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++.

Ada

Library: APDF

Even order curves are drawn pointing downwards.

with Ada.Command_Line;
with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Text_IO;
with PDF_Out;

procedure Arrowhead_Curve is

   package Real_Math is
     new Ada.Numerics.Generic_Elementary_Functions (PDF_Out.Real);
   use Real_Math, PDF_Out, Ada.Command_Line, Ada.Text_IO;

   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 ("arrowhead_curve <order>");
      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.Create ("sierpinski-arrowhead-curve.pdf");
   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;
end Arrowhead_Curve;

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

    sierpinski arrowhead curve( "sierpinski_arrowhead.svg", 1200, 12, 6, 200, 700 )

END

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.

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

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

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:

Media:Sierpinski_arrowhead_c.svg

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

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:

Media:Sierpinski_arrowhead_cpp.svg

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;
P2.X:= Pos.X + Len*Cos(DegToRad(CurAngle));
P2.Y:= Pos.Y - Len*Sin(DegToRad(CurAngle));
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;


procedure ShowSierpinskiArrowhead(Image: TImage);
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;
SierpinskiArrowheadCurve(Image,4, Size);
Image.Canvas.Pen.Color:=clBlue;
Image.Canvas.Pen.Width:=2;
SierpinskiArrowheadCurve(Image,6, Size);
Image.Canvas.Pen.Color:=clRed;
Image.Canvas.Pen.Width:=1;
SierpinskiArrowheadCurve(Image,8, Size);
end;
Output:
Elapsed Time: 50.555 ms.

EasyLang

Run it

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 ;

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

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
;

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

5 arrowhead

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

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 final class SierpinskiArrowhead {

	public static void main(String[] aArgs) throws IOException {
		List<Point> points = initialStraightLine();
    	for ( int i = 1; i < 8; i++ ) {
    		points = nextIteration(points);
    	}    	
    	
    	String text = sierpinskiArrowheadText(points, IMAGE_SIZE);    
    	Files.write(Paths.get("sierpinkskiArrowhead.svg"), text.getBytes());
	}
	
	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;
	        result.add( new Point(x0, y0) );
	        
	        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, y0));
	        	result.add( new Point(x1 + dx / 2, ( y0 + y1 ) / 2) );
	        } else {
	        	result.add( new Point(x0 - dx / 2, ( y0 + y1 ) / 2) );
	        	result.add( new Point(x0, y1) );
	        }
	    }
	    
	    result.add(aPoints.get(aPoints.size() - 1)); 
	    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:

Media:SierpinskiArrowheadJava.svg

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,
    filename = "sierpinski_arrowhead_curve.png",
    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]


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

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

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

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

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

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

Media:Sierpinski_arrowhead_prolog.svg

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

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

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

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

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:

Media:Sierpinski_arrowhead_rust.svg

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

lsys.execute('xF', 7, "sierpiński_arrowhead.png", rules)

Output image: Sierpiński arrowhead

Wren

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

File:Wren-Sierpinski arrowhead curve.png

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