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
V sqrt3_2 = sqrt(3) / 2
F sierpinski_arrowhead_next(points)
V result = [(0.0, 0.0)] * (3 * (points.len - 1) + 1)
V j = 0
L(i) 0 .< points.len - 1
V (x0, y0) = points[i]
V (x1, y1) = points[i + 1]
V dx = x1 - x0
result[j] = (x0, y0)
I y0 == y1
V d = abs(dx * :sqrt3_2 / 2)
result[j + 1] = (x0 + dx / 4, y0 - d)
result[j + 2] = (x1 - dx / 4, y0 - d)
E I y1 < y0
result[j + 1] = (x1, y0)
result[j + 2] = (x1 + dx / 2, (y0 + y1) / 2)
E
result[j + 1] = (x0 - dx / 2, (y0 + y1) / 2)
result[j + 2] = (x0, y1)
j += 3
result[j] = points.last
R result
V size = 600
V iterations = 8
V outfile = File(‘sierpinski_arrowhead.svg’, WRITE)
outfile.write(‘<svg xmlns='http://www.w3.org/2000/svg' width='’size‘' height='’size"'>\n")
outfile.write("<rect width='100%' height='100%' fill='white'/>\n")
outfile.write(‘<path stroke-width='1' stroke='black' fill='none' d='’)
V margin = 20.0
V side = size - 2 * margin
V x = margin
V y = 0.5 * size + 0.5 * sqrt3_2 * side
V points = [(x, y), (x + side, y)]
L 0 .< iterations
points = sierpinski_arrowhead_next(points)
L(point) points
outfile.write(I L.index == 0 {‘M’} E ‘L’)
outfile.write(point.x‘,’point.y"\n")
outfile.write("'/>\n</svg>\n")
- Output:
Output is similar to Nim and C++.
Ada
Even order curves are drawn pointing downwards.
with Ada.Command_Line;
with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Text_IO;
with PDF_Out;
procedure Arrowhead_Curve is
package Real_Math is
new Ada.Numerics.Generic_Elementary_Functions (PDF_Out.Real);
use Real_Math, PDF_Out, Ada.Command_Line, Ada.Text_IO;
subtype Angle_Deg is Real;
type Order_Type is range 0 .. 7;
Purple : constant Color_Type := (0.7, 0.0, 0.5);
Length : constant Real := 340.0;
Corner : constant Point := (120.0, 480.0);
Order : Order_Type;
Current : Point := (0.0, 0.0);
Direction : Angle_Deg := Angle_Deg'(0.0);
Doc : PDF_Out_File;
procedure Curve (Order : Order_Type; Length : Real; Angle : Angle_Deg) is
begin
if Order = 0 then
Current := Current + Length * Point'(Cos (Direction, 360.0),
Sin (Direction, 360.0));
Doc.Line (Corner + Current);
else
Curve (Order - 1, Length / 2.0, -Angle); Direction := Direction + Angle;
Curve (Order - 1, Length / 2.0, Angle); Direction := Direction + Angle;
Curve (Order - 1, Length / 2.0, -Angle);
end if;
end Curve;
begin
if Argument_Count /= 1 then
Put_Line ("arrowhead_curve <order>");
Put_Line (" <order> 0 .. 7");
Put_Line ("open sierpinski-arrowhead-curve.pdf to view ouput");
return;
end if;
Order := Order_Type'Value (Argument (1));
Doc.Create ("sierpinski-arrowhead-curve.pdf");
Doc.Page_Setup (A4_Portrait);
Doc.Margins (Margins_Type'(Left => Cm_2_5,
others => One_cm));
Doc.Stroking_Color (Purple);
Doc.Line_Width (2.0);
Doc.Move (Corner);
if Order mod 2 = 0 then
Direction := 0.0;
Curve (Order, Length, 60.0);
else
Direction := 60.0;
Curve (Order, Length, -60.0);
end if;
Doc.Finish_Path (Close_Path => False,
Rendering => Stroke,
Rule => Nonzero_Winding_Number);
Doc.Close;
end Arrowhead_Curve;
ALGOL 68
Generates an SVG file containing the curve using the L-System. Very similar to the Algol 68 Sierpinski square curve sample. Note the Algol 68 L-System library source code is on a separate page on Rosetta Code - follow the above link and then to the Talk page.
BEGIN # Sierpinski Arrowhead Curve in SVG #
# uses the RC Algol 68 L-System library for the L-System evaluation & #
# interpretation #
PR read "lsystem.incl.a68" PR # include L-System utilities #
PROC sierpinski arrowhead curve = ( STRING fname, INT size, length, order, init x, init y )VOID:
IF FILE svg file;
BOOL open error := IF open( svg file, fname, stand out channel ) = 0
THEN
# opened OK - file already exists and #
# will be overwritten #
FALSE
ELSE
# failed to open the file #
# - try creating a new file #
establish( svg file, fname, stand out channel ) /= 0
FI;
open error
THEN # failed to open the file #
print( ( "Unable to open ", fname, newline ) );
stop
ELSE # file opened OK #
REAL x := init x;
REAL y := init y;
INT angle := 0;
put( svg file, ( "<svg xmlns='http://www.w3.org/2000/svg' width='"
, whole( size, 0 ), "' height='", whole( size, 0 ), "'>"
, newline, "<rect width='100%' height='100%' fill='white'/>"
, newline, "<path stroke-width='1' stroke='black' fill='none' d='"
, newline, "M", whole( x, 0 ), ",", whole( y, 0 ), newline
)
);
LSYSTEM ssc = ( "XF"
, ( "X" -> "YF+XF+Y"
, "Y" -> "XF-YF-X"
)
);
STRING curve = ssc EVAL order;
curve INTERPRET ( ( CHAR c )VOID:
IF c = "F" THEN
x +:= length * cos( angle * pi / 180 );
y +:= length * sin( angle * pi / 180 );
put( svg file, ( " L", whole( x, 0 ), ",", whole( y, 0 ), newline ) )
ELIF c = "+" THEN
angle +:= 60 MODAB 360
ELIF c = "-" THEN
angle -:= 60 MODAB 360
FI
);
put( svg file, ( "'/>", newline, "</svg>", newline ) );
close( svg file )
FI # sierpinski square # ;
sierpinski arrowhead curve( "sierpinski_arrowhead.svg", 1200, 12, 6, 200, 700 )
END
ALGOL W
Produces an Ascii Art Sierpinski Arrowhead Curve using the algorithm from the Wikipedia page.
Note that the Wikipedia algotrithm draws even order curves with the base at the top.
begin % draw sierpinski arrowhead curves using ascii art %
integer CANVAS_WIDTH;
CANVAS_WIDTH := 200;
begin
% the ascii art canvas and related items %
string(1) array canvas ( 1 :: CANVAS_WIDTH, 1 :: CANVAS_WIDTH );
integer heading, asciiX, asciiY, width, maxX, maxY, minX, minY;
% draw a line using ascii art - the length is ignored and the heading determines the %
% character to use %
% the position is updated %
procedure drawLine( real value length ) ;
begin
% stores the min and max coordinates %
procedure updateCoordinateRange ;
begin
if asciiX > maxX then maxX := asciiX;
if asciiY > maxY then maxY := asciiY;
if asciiX < minX then minX := asciiX;
if asciiY < minY then minY := asciiY
end updateCoordinateRange ;
if heading = 0 then begin
updateCoordinateRange;
canvas( asciiX, asciiY ) := "_";
asciiX := asciiX + 1
end
else if heading = 60 then begin
updateCoordinateRange;
canvas( asciiX, asciiY ) := "/";
asciiY := asciiY - 1;
asciiX := asciiX + 1
end
else if heading = 120 then begin
asciiX := asciiX - 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "\";
asciiY := asciiY - 1
end
else if heading = 180 then begin
asciiX := asciiX - 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "_"
end
else if heading = 240 then begin
asciiX := asciiX - 1;
asciiY := asciiY + 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "/"
end
else if heading = 300 then begin
asciiY := asciiY + 1;
updateCoordinateRange;
canvas( asciiX, asciiY ) := "\";
asciiX := asciiX + 1
end if_various_headings
end drawLine ;
% changes the heading by the specified angle ( in degrees ) - angle must be +/- 60 %
procedure turn( integer value angle ) ;
if angle > 0
then heading := ( heading + angle ) rem 360
else begin
heading := heading + angle;
if heading < 0 then heading := heading + 360
end tuen ;
% initialises the ascii art canvas %
procedure initArt ;
begin
heading := 0;
asciiX := CANVAS_WIDTH div 2;
asciiY := asciiX;
maxX := asciiX;
maxY := asciiY;
minX := asciiX;
minY := asciiY;
for x := 1 until CANVAS_WIDTH do for y := 1 until CANVAS_WIDTH do canvas( x, y ) := " "
end initArt ;
% shows the used parts of the canvas %
procedure drawArt ;
begin
for y := minY until maxY do begin
write();
for x := minX until maxX do writeon( canvas( x, y ) )
end for_y ;
write()
end drawIArt ;
% draws a sierpinski arrowhead curve of the specified order and line length %
procedure sierpinskiArrowheadCurve( integer value order; real value length ) ;
begin
% recursively draws a segment of the sierpinski arrowhead curve %
procedure curve( integer value order; real value length; integer value angle ) ;
if 0 = order then drawline( length )
else begin
curve( order - 1, length / 2, - angle );
turn( angle );
curve( order - 1, length / 2, angle );
turn( angle );
curve( order - 1, length / 2, - angle )
end curve ;
if not odd( order ) then begin % order is even, we can just draw the curve. %
curve( order, length, +60 );
end
else begin % order is odd %
turn( +60 );
curve( order, length, -60 )
end if_not_odd_order__
end sierpinskiArrowheadCurve ;
% draw curves %
i_w := 1; s_w := 0; % set output formatting %
for order := 5 do begin
write( "Sierpinski arrowhead curve of order ", order );
write( "=====================================" );
write();
initArt;
sierpinskiArrowheadCurve( order, 1 );
drawArt
end for_order
end
end.
- Output:
Sierpinski arrowhead curve of order 5 ===================================== _ / \ \ / _/ \_ / \ \_ _/ _ \ / _ / \_/ \_/ \ \ / _/ \_ / _ _ \ \_/ \ / \_/ _ / \ _ / \ \_ _/ / \ \ / _ \ / _ \ / _/ \_/ \_/ \_/ \_/ \_ / \ \_ _/ _ \ / _ / \_/ \_/ \ \ _ _ / _/ / \ / \ \_ / _ \ / \ / _ \ \_/ \_/ \_ _/ \_/ \_/ _ \ / _ / \ _/ \_ / \ \ / / _ _ \ \ / _/ \_ \_/ \ / \_/ _/ \_ / \ _ / \ _ / \ \_ _/ / \ \_ _/ / \ \_ _/ _ \ / _ \ / _ \ / _ \ / _ \ / _ / \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \
AutoHotkey
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
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
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
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:
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 |
Button | Command |
---|---|
x | iterate L-system |
Forth
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
#define pi 4 * Atn(1)
#define yellow Rgb(255,255,0)
Dim Shared As Integer posX, posY
Dim Shared As Single direc
Sub Curva(orden As Byte, largo As Single, angulo As Integer)
If orden = 0 Then
posX += Fix(largo * Cos(direc))
posY -= Fix(largo * Sin(direc))
Line - (posX, posY), yellow
Else
Curva(orden-1, largo/2, -angulo)
direc += angulo
Curva(orden-1, largo/2, +angulo)
direc += angulo
Curva(orden-1, largo/2, -angulo)
End If
End Sub
Screenres 640, 480, 32
Dim As Integer x, y
Dim As Byte orden = 5
Dim As Integer tam = 2^orden
Dim As Single largo = 300
Dim As Single sixty = pi/3
direc = 0
posX = 640/4
posY = 3*480/4
Pset (posX, posY)
If (orden And 1) = 0 Then
Curva(orden, largo, +sixty)
Else
direc += sixty
Curva(orden, largo, -sixty)
End If
Windowtitle "Hit any key to end program"
Sleep
Fōrmulæ
Fōrmulæ programs are not textual, visualization/edition of programs is done showing/manipulating structures but not text. Moreover, there can be multiple visual representations of the same program. Even though it is possible to have textual representation —i.e. XML, JSON— they are intended for storage and transfer purposes more than visualization and edition.
Programs in Fōrmulæ are created/edited online in its website.
In this page you can see and run the program(s) related to this task and their results. You can also change either the programs or the parameters they are called with, for experimentation, but remember that these programs were created with the main purpose of showing a clear solution of the task, and they generally lack any kind of validation.
Solution
It can be done using an L-system. There are generic functions written in Fōrmulæ to compute an L-system in the page L-system.
The program that creates a Sierpiński arrowhead is:
Go
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 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
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
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
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)
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
/*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
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
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
- Programming Tasks
- Fractals
- 11l
- Ada
- APDF
- ALGOL 68
- ALGOL 68-l-system
- ALGOL W
- AutoHotkey
- C
- C++
- Delphi
- SysUtils,StdCtrls
- EasyLang
- Factor
- Forth
- FreeBASIC
- Fōrmulæ
- Go
- Go Graphics
- IS-BASIC
- Java
- Jq
- Julia
- Lambdatalk
- Mathematica
- Wolfram Language
- Nim
- Perl
- Phix
- Phix/pGUI
- Processing
- Processing Python mode
- Python
- Prolog
- Quackery
- Raku
- REXX
- Ruby
- RubyGems
- JRubyArt
- Rust
- Sidef
- Wren
- DOME
- Pages with broken file links
- XPL0
- Zkl