Sierpinski arrowhead curve

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

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

11lEdit

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’, ‘w’)

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

AdaEdit

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 WEdit

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

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

AutoHotkeyEdit

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

CEdit

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

Media:Sierpinski_arrowhead_cpp.svg

FactorEdit

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


ForthEdit

Works with: gforth version 0.7.3

ASCIIEdit

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

( 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


GoEdit

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

jqEdit

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

JuliaEdit

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
)

LambdatalkEdit

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

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]

NimEdit

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.

PerlEdit

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)

PhixEdit

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

ProcessingEdit

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 modeEdit

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.

PythonEdit

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)

PrologEdit

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

QuackeryEdit

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:

 

RakuEdit

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

REXXEdit

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
═════════════════════════════════════════
                     _
                    / \
                    \ /
                   _/ \_
                  /     \
                  \_   _/
                 _  \ /  _
                / \_/ \_/ \
                \         /
               _/         \_
              /  _       _  \
              \_/ \     / \_/
             _    /     \    _
            / \   \_   _/   / \
            \ /  _  \ /  _  \ /
           _/ \_/ \_/ \_/ \_/ \_
          /                     \
          \_                   _/
         _  \                 /  _
        / \_/                 \_/ \
        \    _               _    /
       _/   / \             / \   \_
      /  _  \ /             \ /  _  \
      \_/ \_/ \_           _/ \_/ \_/
     _          \         /          _
    / \        _/         \_        / \
    \ /       /  _       _  \       \ /
   _/ \_      \_/ \     / \_/      _/ \_
  /     \    _    /     \    _    /     \
  \_   _/   / \   \_   _/   / \   \_   _/
 _  \ /  _  \ /  _  \ /  _  \ /  _  \ /  _
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \

RubyEdit

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

RustEdit

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

SidefEdit

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

WrenEdit

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

XPL0Edit

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

zklEdit

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