Sierpinski arrowhead curve: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Forth entry)
No edit summary
Line 685: Line 685:
</lang>
</lang>


=={{header|Mathematica}} / {{header|Wolfram Language}}==
<lang Mathematica>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]</lang>


=={{header|Perl}}==
=={{header|Perl}}==

Revision as of 15:31, 5 June 2021

Sierpinski arrowhead curve is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

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

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. <lang algolw>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.</lang>

Output:
Sierpinski arrowhead curve of order 5
=====================================

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

AutoHotkey

Translation of: Go

Requires Gdip Library <lang AutoHotkey>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</lang>

C

This code is based on the Phix and Go solutions, but produces a file in SVG format. <lang c>// See https://en.wikipedia.org/wiki/Sierpi%C5%84ski_curve#Arrowhead_curve

  1. include <math.h>
  2. include <stdio.h>
  3. 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;

}</lang>

Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

C++

The output of this program is an SVG file. <lang cpp>#include <fstream>

  1. include <iostream>
  2. 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;

}</lang>

Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

Factor

Works with: Factor version 0.99 2020-08-14

<lang factor>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</lang>


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

<lang forth>( 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</lang>

Output:
Sierpinski arrowhead curve of order 5 
=====================================
   _   _   _   _   _   _   _   _   _   _    ok
\_/ \ / \_/ \ / \_/ \ / \_/ \ / \_/ \ / \_/
   _/ \_    / \    _/ \_    / \    _/ \_
  /     \   \_/   /     \   \_/   /     \
  \_   _/      _  \     /  _      \_   _/
    \ /       / \_/     \_/ \       \ /
    / \       \_           _/       / \
    \_/         \         /         \_/
       _   _   _/         \_   _   _
      / \_/ \ /             \ / \_/ \
      \_    / \             / \    _/
        \   \_/             \_/   /
        /  _                   _  \
        \_/ \                 / \_/
           _/                 \_
          /                     \
          \_   _   _   _   _   _/
            \ / \_/ \ / \_/ \ /
            / \    _/ \_    / \
            \_/   /     \   \_/
               _  \     /  _
              / \_/     \_/ \
              \_           _/
                \         /
                /  _   _  \
                \_/ \ / \_/
                   _/ \_
                  /     \
                  \_   _/
                    \ /
                    / \
                    \_/


SVG file

<lang forth>( 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</lang>


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. <lang go>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")

}</lang>


Julia

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

) </lang>

Mathematica / Wolfram Language

<lang Mathematica>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]</lang>

Perl

<lang 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;</lang> See: sierpinski-arrowhead-curve.svg (offsite SVG image)

Phix

Library: Phix/pGUI

<lang Phix>-- demo\rosetta\Sierpinski_arrowhead_curve.exw -- -- Draws curves lo to hi (simultaneously), initially {6,6}, max {10,10} -- Press +/- to change hi, shift +/- to change lo. -- ("=_" are also mapped to "+-", for the non-numpad +/-) -- 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*/, integer /*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_CONTINUE

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

end procedure

main()</lang>


Processing

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

}</lang>The sketch can be run online :
here.

Processing Python mode

<lang python>

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

</lang>

The sketch can be run online :
here.

Prolog

Works with: SWI Prolog
Translation of: C

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

See: sierpinski_arrowhead.svg (offsite SVG image)

Raku

(formerly Perl 6)

Works with: Rakudo version 2020.02

<lang perl6>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> ],
   ],

);</lang> See: Sierpinski-arrowhead-curve-perl6.svg (offsite SVG image)

REXX

Translation of: Algol W

<lang rexx>/*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. */</lang>
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 <lang ruby> 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

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

</lang>

Rust

Output is a file in SVG format. Another variation on the same theme as the C, Go and Phix solutions. <lang rust>// [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();

}</lang>

Output:

See: sierpinski_arrowhead.svg (offsite SVG image)

Sidef

Uses the LSystem() class from Hilbert curve. <lang ruby>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)</lang> Output image: Sierpiński arrowhead

Wren

Translation of: Go
Library: DOME

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

}</lang>

zkl

Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl <lang 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");

}</lang>

Output:

Offsite image at Sierpinski arrowhead curve order 7