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’, ‘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++.

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

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