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’, ‘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
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
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
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 W
- AutoHotkey
- C
- C++
- Delphi
- SysUtils,StdCtrls
- EasyLang
- Factor
- Forth
- FreeBASIC
- 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