Harriss Spiral
You are encouraged to solve this task according to the task description, using any language you may know.
The discovery of the Harriss Spiral was first publicized about 2015. It is the brainchild of Edmund Orme Harriss, a British mathematician, writer and artist. Since 2010 he has been at the Fulbright College of Arts & Sciences at The University of Arkansas in Fayetteville, Arkansas, where he is an Assistant Professor of Arts & Sciences (ARSC) and Mathematical Sciences (MASC). He does research in the Geometry of Tilings and Patterns,a branch of Convex and Discrete Geometry.
In Harriss' own words, his namesake spiral, "…is constructed by taking a rectangle with height 1 and length the real root of x^3-x-1=0. With this rectangle you can cut off a similar rectangle, and then a square to get another similar rectangle. You can repeat this construction on the smaller similar rectangles, in each case getting a square and two more similar rectangles. Now simply adding an arc of a circle to each square (in the right way) gives the spiral, or more correctly the nest of spirals."
The Harriss Spiral is a variant of the decomposition for the golden spiral in which a rectangle is decomposed into three smaller units: a rectangle similar to the original rotated 90◦, a square, and a similar rectangle in the same orientation as the original rectangle. As in the golden-spiral decomposition, the individual non-square units can be decomposed further along these lines to create a cascading filling of the rectangle with ever-smaller squares. Unlike in the golden spiral, each square is incident on two smaller regions appearing in the same generation; if arcs are drawn between each square and the square which appeared in its previous generation The result is a branching structure, shown at right.
As with the golden spiral decomposition, the Harriss spiral requires a specific aspect ratio for the original rectangle. While the golden spiral requires an aspect ratio which is a solution to 𝜙2 = 𝜙 + 1 — the Golden Ratio of 1:1.618 — the Harriss spiral requires an aspect ratio 𝜌 satisfying 𝜌3 = 𝜌 + 1, whose real solution is known as the plastic ratio and equals 1:1.3247.
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Create and display a Harriss Spiral in your language.
ALGOL 68
Based on
Generates the curve as SVG which is written to standard output - redirect it to an HTML file and open it in a browser that supports SVG or an SVG viewer...
BEGIN # Harriss Spiral in SVG - translation of EasyLang/Processing samples #
PROC hex1 = ( INT d )STRING: IF d < 10 THEN REPR( ABS "0" + d ) ELSE REPR( ABS "a" + ( d - 10 ) ) FI;
PROC hex = ( INT v )STRING: hex1( v OVER 16 ) + hex1( v MOD 16 );
PROC rgb = ( INT r, g, b )STRING: "#" + hex( r ) + hex( g ) + hex( b );
OP SIND = ( REAL x )REAL: sin( x * pi / 180 );
OP COSD = ( REAL x )REAL: cos( x * pi / 180 );
PRIO FMT = 9;
OP FMT = ( REAL v, INT dp )STRING:
BEGIN
STRING result = IF ENTIER v = v
THEN whole( v, - dp * 16 )
ELSE fixed( v, - dp * 16, ABS dp )
FI;
INT v pos := LWB result;
WHILE result[ v pos ] = " " DO v pos +:= 1 OD;
result[ v pos : ]
END # FMT # ;
PROC point = ( REAL x, y )STRING: x FMT 2 + " " + y FMT 2;
PROC move to = ( REAL x, y )STRING: "M " + point( x, y );
PROC line to = ( REAL x, y )STRING: "L " + point( x, y );
PROC start path = ( INT r, g, b, REAL line width, x, y )STRING:
( " <path stroke-width='" + line width FMT 2
+ "' stroke='" + rgb( r, g, b ) + "' fill='none' d='" + move to( x, y ) + " "
);
PROC line = ( INT r, g, b, REAL line width, REAL x, y, x end, y end )VOID:
print( ( start path( r, g, b, line width, x, y ), " ", line to( x end, y end ), "'/>"
, newline
)
);
PROC arc = ( REAL x centre, y centre, ew, eh, start angle, end angle, INT r, g, b, REAL line width )VOID:
BEGIN
REAL rx = ew / 2, ry = ( eh / ew ) * eh / 2;
REAL x = x centre + rx * COSD start angle, y = y centre + ry * SIND start angle;
REAL x end = x centre + rx * COSD end angle, y end = y centre + ry * SIND end angle;
print( ( start path( r, g, b, line width, x, y )
, "A ", rx FMT 2, " ", ry FMT 2 , " 0 0 1 ", point( x end, y end )
, "'/>", newline
)
)
END # arc # ;
REAL h = 600; # scalable (may be any value) #
REAL hr = 1.325; # Harriss Ratio #
PROC spiral section = ( REAL x, y, angle, lngth
, INT iteration
, REAL line width
, BOOL show lines
) VOID:
IF iteration > 0 THEN
REAL start angle = angle + 45;
REAL end angle = start angle + 90;
# Calculate end point of lines #
REAL x end = x + lngth * COSD angle;
REAL y end = y + lngth * SIND angle;
REAL radius = 1.414 * lngth;
spiral section( x end, y end, angle + 90, lngth / hr / hr, iteration - 2, line width, show lines );
IF show lines THEN
line( 255, 255, 255, 1, x, y, x end, y end )
FI;
INT sn = 1, ew = 2, ns = 3, we = 4;
INT heading := sn;
IF ENTIER y end < ENTIER y THEN heading := sn FI;
IF ENTIER x end < ENTIER x THEN heading := ew FI;
IF ENTIER y end > ENTIER y THEN heading := ns FI;
IF ENTIER x end > ENTIER x THEN heading := we FI;
REAL l2 = lngth / 2;
IF heading = sn THEN
arc( x - l2, y - l2, radius, radius, start angle, end angle, 255, 255, 0, line width )
ELIF heading = ew THEN
arc( x - l2, y + l2, radius, radius, start angle, end angle, 255, 0, 0, line width )
ELIF heading = ns THEN
arc( x + l2, y + l2, radius, radius, start angle, end angle, 0, 0, 255, line width )
ELIF heading = we THEN
arc( x + l2, y - l2, radius, radius, start angle, end angle, 0, 0, 0, line width )
FI;
spiral section( x end, y end, angle - 90, lngth / hr, iteration - 1, line width, show lines )
FI # spiral section #;
PROC generate harriss spiral = ( INT c width, c height )VOID:
BEGIN
print( ( "<svg xmlns='http://www.w3.org/2000/svg' width='"
, whole( c width, 0 ), "' height='", whole( c height, 0 ), "'>", newline
, " <rect width='100%' height='100%' fill='#7f7f7f'/>", newline
)
);
REAL start x = c width / 2 + 50;
REAL start y = c height - 50;
REAL init len = h / hr / hr;
spiral section( start x, start y, -90, init len, 16, 2, FALSE );
print( ( "</svg>", newline ) )
END # generate harriss spiral #;
generate harriss spiral( 1000, 800 )
END
- Output:
The borders have been cropped in this image.
EasyLang
# Harris Spiral - translated from the Processing sample
#
# the EasyLang canvas is 100x100 with 0,0 at bottom left, 100,100 at top right
# the following maps the Processing canvas to the EasyLang one
cWidth = 100
cHeight = 100
func scaleX v s .
return (v * 100 / s)
.
func scaleY v s .
return 100 - (v * 100 / s)
.
proc scaledMove x y . .
move scaleX x cWidth scaleY y cHeight
.
proc scaledLine x y . .
line scaleX x cWidth scaleY y cHeight
.
#
# the following implements equivalents for some Processing functions and Boolean literals
# false = 0
true = 1
proc size w h . .
cWidth = w
cHeight = h
.
proc strokeWidth w . .
linewidth w / 18
.
proc stroke rb gb bb . .
color3 rb / 255 gb / 255 bb / 255
.
proc arc xCenter yCenter eWidth eHeight p1Angle p2Angle . .
rx = eWidth / 2
ry = (eHeight / eWidth) * eHeight / 2
arcAngle = p2Angle - p1Angle
angleStep = arcAngle / 720
angle = p1Angle
x = xCenter + rx * cos angle
y = yCenter + ry * sin angle
scaledMove x y
for s = 0 to 720
x = xCenter + rx * cos angle
y = yCenter + ry * sin angle
scaledLine x y
angle = angle + angleStep
.
.
#
# Harris Spiral - translation of the Processing sample
# EasyLang angles are in degrees, so conversion to radians is not needed
# some values have been tweaked
#
h = 600 ; # scalable (may be any value)
HR = 1.325 ; # Harriss Ratio
_wndW = 1000
_wndH = 1000
showLines = true ; # was false in the Processing sample
proc drawHarriss x y dAngle lngth iteration lineW . .
if iteration > 0
startAngle = dAngle + 45
endAngle = startAngle + 90
# Calculate end point of lines
xEnd = x + lngth * cos dAngle
yEnd = y + lngth * sin dAngle
if floor yEnd < floor y
heading$ = "SN"
.
if floor xEnd < floor x
heading$ = "EW"
.
if floor yEnd > floor y
heading$ = "NS"
.
if floor xEnd > floor x
heading$ = "WE"
.
if showLines = true
stroke 255 255 255
strokeWidth 3
scaledMove x y
scaledLine xEnd yEnd
.
radius = 1.414 * lngth
if heading$ = "SN"
cntrX = x - lngth / 2
cntrY = y - lngth / 2
stroke 255 255 0
strokeWidth lineW
.
if heading$ = "EW"
cntrX = x - lngth / 2
cntrY = y + lngth / 2
stroke 255 0 0
strokeWidth lineW
.
if heading$ = "NS"
cntrX = x + lngth / 2
cntrY = y + lngth / 2
stroke 0 0 255
strokeWidth lineW
.
if heading$ = "WE"
cntrX = x + lngth / 2
cntrY = y - lngth / 2
stroke 0 0 0
strokeWidth lineW
.
arc cntrX cntrY radius radius startAngle endAngle
drawHarriss xEnd yEnd dAngle - 90 lngth / HR iteration - 1 lineW
.
.
proc setup . .
size _wndW _wndH
background 555
clear
startX = _wndW / 2 + 50
startY = _wndH - 50
initLen = h / HR / HR
# Reverse Order Hides Joints
drawHarriss startX - (initLen / HR + initLen / HR / HR / HR) startY - initLen / HR / HR / HR / HR / HR / HR / HR / HR 180 initLen / HR / HR / HR / HR / HR / HR 2 6.0 ; # level 3
drawHarriss startX - (initLen / HR + initLen / HR / HR / HR) startY - (initLen + initLen / HR / HR) 270 initLen / HR / HR / HR / HR / HR 3 6.0 ; # level 3
drawHarriss startX + initLen / HR / HR / HR / HR startY - (initLen + initLen / HR / HR) 270 initLen / HR / HR / HR / HR / HR 3 6.0 ; # level 3
drawHarriss startX + initLen / HR startY - (initLen + initLen / HR / HR) 0 initLen / HR / HR / HR / HR 4 6.0 ; # level 3 rt-upper
drawHarriss startX - initLen / HR / HR / HR / HR startY - initLen / HR 0 initLen / HR / HR / HR / HR / HR 2 10.0 ; # level 2 mid-upper
drawHarriss startX - initLen / HR / HR / HR / HR startY - initLen / HR / HR / HR (-270) initLen / HR / HR / HR / HR 3 12.0 ; # level 2 mid-lower
drawHarriss startX - initLen / HR startY - initLen / HR / HR / HR 180 initLen / HR / HR / HR 4 12.0 ; # level 2 lt-lower
drawHarriss startX - initLen / HR startY - initLen 270 initLen / HR / HR 5 14.0 ; # level 2 lt-upper
drawHarriss startX startY - initLen 0 initLen / HR 6 14.0 ; # level 2 rt-upper
drawHarriss startX startY (-90) initLen 7 18.0 ; # level 1 base spiral
.
setup
- Output:
(the EasyLang canvas is square, the unused upper portion has been cropped)
FutureBasic
Special credit for this code goes to Steven Van Voorst who, in his own words, "spent countless hours on this project because it is so much fun." Steve developed a working prototype of the Harriss Spiral using the cross-platform Processing IDE. The Processing software is free and open source and runs on Mac OS, Windows, and Linux. Processing's Rossetta Code home page is: https://rosettacode.org/wiki/Category:Processing. The Processing home page is: https://processing.org/.
Steve programs in a variety of languages. He kindly shared his Processing source code which was translated into this FB task solution for macOS. Steve's contribution is greatly appreciated.
#define HR 1.3247 // Harriss ratio
#define LINES NO
_window = 1
begin enum 1
_harrissView
end enum
void local fn BuildWindow
NSInteger wndStyle =¬
NSWindowStyleMaskTitled +¬
NSWindowStyleMaskClosable +¬
NSWindowStyleMaskResizable +¬
NSWindowStyleMaskMiniaturizable
CGRect r = fn CGRectMake( 0, 0, 1000, 800 )
window _window, @"Harriss Spiral in FutureBasic for Macintosh", r, wndStyle
subclass view _harrissView, r, _window
ViewSetFlipped( _harrissView, YES )
ViewSetAutoresizingMask( _harrissView, NSViewWidthSizable + NSViewHeightSizable )
end fn
local fn DrawArcSegment( x as CGFloat, y as CGFloat, angle as CGFloat, length as CGFloat, iteration as int, arcColor as ColorRef, lineWidth as CGFloat, showLines as BOOL, showArcs as BOOL )
CFStringRef heading
CGFloat radius, cntrX, cntrY
if ( iteration > 0 )
float startAngle = angle + 45
float endAngle = startAngle + 90
float xEnd = x + length * cos( angle * (M_PI/180.0) )
float yEnd = y + length * sin( angle * (M_PI/180.0) )
if( fn floor(yEnd) < fn floor(y) ) then heading = @"SN" // 6
if( fn floor(xEnd) < fn floor(x) ) then heading = @"EW" // 5
if( fn floor(yEnd) > fn floor(y) ) then heading = @"NS" // 4
if( fn floor(xEnd) > fn floor(x) ) then heading = @"WE" // 3
if ( showLines ) then BezierPathStrokeLine( fn CGPointMake( x, y ), fn CGPointMake( xEnd, yEnd ), 1.0, fn ColorBlack )
// radius = 0.7 * length
radius = 0.70710678 * length // for higher precision miter and butt intersections
if( showArcs == YES )
if( fn StringIsEqual( heading, @"SN" ) == YES )
cntrX = x - length/2
cntrY = y - length/2
if rnd(2) == 1 then arcColor = fn ColorYellow else arcColor = fn ColorGreen
end if
if( fn StringIsEqual( heading, @"EW" ) == YES )
cntrX = x - length/2
cntrY = y + length/2
arcColor = fn ColorRed
end if
if( fn StringIsEqual( heading, @"NS" ) == YES )
cntrX = x + length/2
cntrY = y + length/2
arcColor = fn ColorBlue
end if
if( fn StringIsEqual( heading, @"WE" ) == YES )
cntrX = x + length/2
cntrY = y - length/2
if rnd(2) == 1 then arcColor = fn ColorOrange else arcColor = fn ColorBlack
end if
BezierPathRef path = fn BezierPathInit
BezierPathAppendPathWithArcWithCenter( path, fn CGPointMake( cntrX, cntrY ), radius, startAngle, endAngle, NO )
BezierPathSetLineJoinStyle( path, NSLineJoinStyleMiter )
BezierPathSetLineCapStyle( path, NSLineCapStyleRound )
BezierPathStrokeFill( path, lineWidth, arcColor, NULL )
end if
fn DrawArcSegment( xEnd, yEnd, angle - 90, length/HR, iteration - 1, arcColor, lineWidth, showLines, showArcs )
end if
end fn
local fn DrawHarrissSpiral
BezierPathFillRect( fn ViewFrame( _harrissView ), fn ColorGray )
CGRect r = fn WindowFrame( _window )
int h = 600
float startX = r.size.width / 2 + 80
float startY = r.size.height - 140
float initLen = h/HR/HR
fn DrawArcSegment( startX + initLen/HR, startY - (initLen + initLen/HR/HR), 0.0, initLen/HR/HR/HR/HR, 4, fn ColorGreen, 6.0, LINES, YES )
fn DrawArcSegment( startX + initLen/HR/HR/HR/HR, startY - (initLen + initLen/HR/HR), 270.0, initLen/HR/HR/HR/HR/HR, 3, fn ColorBlack, 6.0, LINES, YES )
fn DrawArcSegment( startX - (initLen/HR + initLen/HR/HR/HR), startY - (initLen + initLen/HR/HR), 270.0, initLen/HR/HR/HR/HR/HR, 3, fn ColorBlack, 6.0, LINES, YES )
fn DrawArcSegment( startX - (initLen/HR + initLen/HR/HR/HR), startY - initLen/HR/HR/HR/HR/HR/HR/HR/HR, 180.0, initLen/HR/HR/HR/HR/HR/HR, 2, fn ColorBlue, 6.0, LINES, YES )
fn DrawArcSegment( startX - initLen/HR/HR/HR/HR, startY - initLen/HR/HR/HR, -270.0, initLen/HR/HR/HR/HR, 3, fn ColorBlue, 8.0, LINES, YES )
fn DrawArcSegment( startX - initLen/HR/HR/HR/HR, startY - initLen/HR, 0.0, initLen/HR/HR/HR/HR/HR, 2, fn ColorGreen, 8.0, LINES, YES )
fn DrawArcSegment( startX - initLen/HR, startY - initLen, 270.0, initLen/HR/HR, 5, fn ColorRed, 12.0, LINES, YES )
fn DrawArcSegment( startX - initLen/HR, startY - initLen/HR/HR/HR, 180.0, initLen/HR/HR/HR, 4, fn ColorBlack, 12.0, LINES, YES )
fn DrawArcSegment( startX, startY - initLen, 0.0, initLen/HR, 6, fn ColorBlack, 16.0, LINES, YES )
fn DrawArcSegment( startX, startY, -90.0, initLen, 7, fn ColorOrange, 16.0, LINES, YES )
end fn
void local fn DoDialog( ev as long, tag as long, wnd as long )
select ( ev )
case _viewDrawRect
select ( tag )
case _harrissView : fn DrawHarrissSpiral
end select
case _windowWillClose : end
end select
end fn
on dialog fn DoDialog
fn BuildWindow
HandleEvents
Standard file output in FB below. Background color easily changed. At bottom, FB file showing construction lines.
- Output:
Java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JComponent;
import javax.swing.JFrame;
public final class HarrissSpiral extends JComponent {
public static void main(String[] args) {
EventQueue.invokeLater( () -> {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Harriss Spiral");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize( new Dimension(WIDTH, HEIGHT) );
frame.setResizable(false);
frame.add( new HarrissSpiral() );
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} );
}
@Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D graphics2D = (Graphics2D) graphics;
graphics2D.setColor(SHOW_LINES ? Color.WHITE : Color.DARK_GRAY);
graphics2D.fill(SCREEN);
drawHarrissSpiral(graphics2D);
}
private static void drawHarrissSpiral(Graphics2D graphics2D) {
final double HR2 = HR * HR;
final double HR3 = HR2 * HR;
final double HR4 = HR2 * HR2;
final double HR5 = HR4 * HR;
final double HR6 = HR4 * HR2;
final double HR8 = HR4 * HR4;
final double startX = WIDTH / 2.0 + 50.0;
final double startY = HEIGHT - 75.0;
final double initialLength = 600 / HR2;
drawArcSegment(startX + initialLength / HR, startY - ( initialLength + initialLength / HR2 ),
0.0, initialLength / HR4, 4, 6, graphics2D);
drawArcSegment(startX + initialLength / HR4, startY - ( initialLength + initialLength / HR2 ),
270.0, initialLength / HR5, 3, 6, graphics2D);
drawArcSegment(startX - ( initialLength / HR + initialLength / HR3 ),
startY - ( initialLength + initialLength / HR2 ), 270.0, initialLength / HR5, 3, 6, graphics2D);
drawArcSegment(startX - ( initialLength / HR + initialLength / HR3 ),
startY - initialLength / HR8, 180.0, initialLength / HR6, 2, 6, graphics2D);
drawArcSegment(startX - initialLength / HR4, startY - initialLength / HR3,
-270.0, initialLength / HR4, 3, 8, graphics2D);
drawArcSegment(startX - initialLength / HR4, startY - initialLength / HR,
0.0, initialLength / HR5, 2, 8, graphics2D);
drawArcSegment(startX - initialLength / HR, startY - initialLength,
270.0, initialLength / HR2, 5, 12, graphics2D);
drawArcSegment(startX - initialLength / HR, startY - initialLength / HR3,
180.0, initialLength / HR3, 4, 12, graphics2D);
drawArcSegment(startX, startY - initialLength, 0.0, initialLength / HR, 6, 16, graphics2D);
drawArcSegment(startX, startY, -90.0, initialLength, 7, 16, graphics2D);
}
private static void drawArcSegment(double x, double y, double angle, double length,
int iteration, int lineWidth, Graphics2D graphics2D) {
String heading = "";
Color arcColor = Color.WHITE;
if ( iteration > 0 ) {
final double xEnd = x + length * Math.cos(Math.toRadians(angle));
final double yEnd = y + length * Math.sin(Math.toRadians(angle));
if ( Math.floor(yEnd) < Math.floor(y) ) { heading = "RIGHT"; }
if ( Math.floor(xEnd) < Math.floor(x) ) { heading = "UPPER"; }
if ( Math.floor(yEnd) > Math.floor(y) ) { heading = "LEFT"; }
if ( Math.floor(xEnd) > Math.floor(x) ) { heading = "LOWER"; }
if ( SHOW_LINES ) {
graphics2D.setColor(Color.BLACK);
graphics2D.setStroke( new BasicStroke(1) );
graphics2D.drawLine((int) x, (int) y, (int) xEnd, (int) yEnd);
}
double centreX = 0.0, centreY = 0.0;
switch ( heading ) {
case "RIGHT" -> {
centreX = x - length / 2.0;
centreY = y - length / 2.0;
arcColor = ( RANDOM.nextInt(2) == 1 ) ? Color.YELLOW : Color.GREEN;
}
case "UPPER" -> {
centreX = x - length / 2.0;
centreY = y + length / 2.0;
angle += 180;
arcColor = Color.RED;
}
case "LEFT" -> {
centreX = x + length / 2.0;
centreY = y + length / 2.0;
arcColor = Color.BLUE;
}
case "LOWER" -> {
centreX = x + length / 2.0;
centreY = y - length / 2.0;
angle += 180;
arcColor = ( RANDOM.nextInt(2) == 1 ) ? Color.ORANGE : Color.BLACK;
}
}
final double radius = 0.7 * length;
graphics2D.setColor(arcColor);
graphics2D.setStroke( new BasicStroke(lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL) );
graphics2D.drawArc((int) ( centreX - radius ), (int) ( centreY - radius ),
(int) radius * 2, (int) radius * 2, (int) angle + 45, 90);
if ( heading.equals("UPPER") || heading.equals("LOWER") ) { angle -= 180; }
drawArcSegment(xEnd, yEnd, angle - 90, length / HR, iteration - 1, lineWidth, graphics2D);
}
}
private static final int WIDTH = 1_000;
private static final int HEIGHT = 750;
private static final double HR = 1.3247; // Harriss ratio
private static final boolean SHOW_LINES = false;
private static final Rectangle SCREEN = new Rectangle(0, 0, WIDTH, HEIGHT);
private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
}
Julia
""" https://rosettacode.org/wiki/Harriss_Spiral """
using Luxor
const HR = 1.324718 # The Harriss Ratio, aka Ian Stewart's "plastic number"
const SHOW_LINES = false
""" Recursively draw the Harriss spiral for `iteration` iterations, with radius sqrt(2) smaller each iteration. """
function harriss(x, y, angle, len, iteration, linew, radius = 0.0, cntrx = 0.0, cntry = 0.0)
iteration < 1 && return
startangle = angle + 45
endangle = startangle + 90
# Calculate end point of lines
xend = x + len * cospi(angle / 180)
yend = y + len * sinpi(angle / 180)
heading = yend < y ? "SN" : xend < x ? "EW" : yend > y ? "NS" : xend > x ? "WE" : "error"
if SHOW_LINES
sethue(0, 0, 0)
setline(1)
line(Point(x, y), Point(xend, yend), action = :stroke)
end
radius = len / sqrt(2)
if heading == "SN"
cntrx = x - len / 2
cntry = y - len / 2
sethue(255, 255, 0)
setline(linew)
elseif heading == "EW"
cntrx = x - len / 2
cntry = y + len / 2
sethue(255, 0, 0)
setline(linew)
elseif heading == "NS"
cntrx = x + len / 2
cntry = y + len / 2
sethue(0, 0, 255)
setline(linew)
elseif heading == "WE"
cntrx = x + len / 2
cntry = y - len / 2
sethue(0, 0, 0)
setline(linew)
end
arc(cntrx, cntry, radius, π * startangle / 180, π * endangle / 180, action = :stroke)
harriss(xend, yend, angle - 90, len / HR, iteration - 1, linew, radius, cntrx, cntry)
end
""" Draw the Harriss spiral several times in its fractal "nest of spirals" format """
function draw()
Drawing()
background(211/255, 211/255, 211/255) # light gray
origin()
startx = 50
starty = 280
init_len = 525 / HR / HR
# Reverse Order Hides Joints
harriss(startx - (init_len / HR + init_len / HR^3), starty - init_len / HR^7, 180, init_len / HR^6, 2, 6.0) # level 3
harriss(startx - (init_len / HR + init_len / HR^3), starty - (init_len + init_len / HR^2), 270, init_len / HR^5, 3, 6.0) # level 3
harriss(startx + init_len / HR^4, starty - (init_len + init_len / HR^2), 270, init_len / HR^5, 3, 6.0) # level 3
harriss(startx + init_len / HR, starty - (init_len + init_len / HR^2), 0, init_len / HR^4, 4, 6.0) # level 3 rt-upper
harriss(startx - init_len / HR^4, starty - init_len / HR, 0, init_len / HR^5, 2, 10.0) # level 2 mid-upper
harriss(startx - init_len / HR^4, starty - init_len / HR^3, -270, init_len / HR^4, 3, 12.0) # level 2 mid-lower
harriss(startx - init_len / HR, starty - init_len / HR^3, 180, init_len / HR^3, 4, 12.0) # level 2 lt-lower
harriss(startx - init_len / HR, starty - init_len, 270, init_len / HR^2, 5, 14.0) # level 2 lt-upper
harriss(startx, starty - init_len, 0, init_len / HR, 6, 14.0) # level 2 rt-upper
harriss(startx, starty, -90, init_len, 7, 18.0) # level 1 base spiral
finish()
preview()
end
draw()
Phix
Recursive approach, probably deserves a few more tweaks. No colours yet. I plan to add a run online link once finished, but since xpGUI.js is some way off being ready that might not be soon.
-- demo\rosetta\Harriss_Spiral.exw
with javascript_semantics
requires("1.0.6") -- (not yet shipped, this triggered a few bugfixes in gCanvasArc() for one)
include xpGUI.e
constant title = "Harriss Spiral",
help_text = """
Harriss Spiral demo.
Cycle construction line display with 'L',
or NSQR for None/Single/sQuare/Rect.
Toggle the initial line with 'I'.
Toggle termination style with 'T'.
Toggle diagnostic info with 'D'.
Increase or decrease depth with +/-.""",
HARRIS_RATIO = 1.3247,
IHR = 1/(1+HARRIS_RATIO),
HHR = HARRIS_RATIO*IHR
integer show_lines = 0, // 0:none, 1=line, 2=square, 3=rect. cycle with 'l'
max_depth = 1
bool initial = true,
termination = false, // true = match line tails of other rc entries
diagnostics = false // true = show side(level) in each rectangle
procedure draw_harris(gdx canvas, integer x,y,w,h, side=1, depth=max_depth)
-- x,y is the pre-rotated top left corner, with side 1..4:
-- 1: horizontal, upright, so x,y top left and square is lower right
-- 2: on left edge, so x,y btm left and the square is at the top right
-- 3: horizontal upside down edge, so x,y btm right and square top left
-- 4: on right edge, so x,y top right and the square is in the lower left
-- or graphically, with XX/XX representing the position of the square:
-- 3
-- x,y +----+--+ +--+--+ +--+--+ +--+--+ x,y
-- | | | | |XX| |XX| | | |
-- + +--+ 4/h | |XX| w |XX| | h +--+--+ w
-- 2 | |XX| or +--+--+ or +--+ + or |XX| |
-- | |XX| | | | | | |XX| |
-- +----+--+ x,y +--+--+ +--+--+ x,y +--+--+
-- 1/w 2/h 3/w 4/h
--
-- note that w,h are pre-rotated... max(w,h)/min(w,h) ~= HARRIS_RATIO.
--
-- first off calculate [sub]square side and left/mid/right top/mid/low:
integer ss = round(min(h,w)*HHR),
sss = ss-round(ss*IHR),
ss2 = floor(ss/2),
sss2 = floor(sss/2),
{lx,mx,rx} = {{x,x+w-ss,x+w-1},
{x-w,x-w+ss,x}}[floor((side+1)/2)],
{ty,my,ly} = {{y,y+h-ss,y+h},
{y-h,y-h+ss,y}}[odd(floor(side/2))+1]
if diagnostics then
integer {w2,h2} = sq_floor_div({w,h},2),
{sx,sy} = {{x+w2,y+h2},
{x+w2,y-h2},
{x-w2,y-h2},
{x+w2,y+h2}}[side]
gCanvasText(canvas,sx,sy,sprintf("%d(%d)",{side,depth}),XPG_C)
end if
if show_lines then
-- x1,y1 is the middle T point
-- x2,y2 is where the botton of the T hits the edge
-- x3,y3 is the opposite square corner to x1,y1
integer {x1,y1,x2,y2,x3,y3} = {{mx,my,rx,my,rx,ly},
{mx,my-1,mx,ty-1,rx,ty-1},
{lx,my,mx,my,mx,ty},
{mx,my,mx,ly-1,lx,ly-1}}[side]
if show_lines=2 then
gCanvasRect(canvas, x1,x3, y1,y3)
elsif show_lines=3 then
gCanvasLine(canvas, x1,y1, x2,y2)
{x1,y1,x2,y2} = {{mx,ty,mx,ly},
{lx,my,rx,my}}[even(side)+1]
gCanvasRect(canvas, x,x3, y,y3)
gCanvasLine(canvas, x1,y1, x2,y2) -- divider (top of T)
end if
end if
integer {cx,cy,a1,a2} = {{mx-ss2,my+ss2,315,45},
{mx+ss2,my+ss2,225,315},
{mx+ss2,my-ss2,135,225},
{mx-ss2,my-ss2,45,135}}[side]
atom r = sqrt(2)*ss
if initial or depth<max_depth then
if show_lines=1 then
integer {ex,ey} = {{mx,my+ss},
{mx+ss,my},
{mx,my-ss},
{mx-ss,my}}[side]
gCanvasLine(canvas, mx,my, ex,ey)
end if
gCanvasArc(canvas,cx,cy,r,r,a1,a2,width:=depth+1)
end if
if depth>1 or not termination then
if show_lines=1 then
integer {ex,ey} = {{mx+sss,my},
{mx,my-sss},
{mx-sss,my},
{mx,my+sss}}[side]
gCanvasLine(canvas, mx,my, ex,ey)
end if
{cx,cy,a1,a2} = {{mx+sss2,my-sss2,45,135},
{mx-sss2,my-sss2,315,45},
{mx-sss2,my+sss2,225,315},
{mx+sss2,my+sss2,135,225}}[side]
r = sqrt(2)*sss
gCanvasArc(canvas,cx,cy,r,r,a1,a2,width:=depth)
end if
if depth>1 then
switch side
case 1: draw_harris(canvas, mx,ty,ss,h-ss, 1, depth-1)
draw_harris(canvas, lx,ly,w-ss,h, 2, depth-1)
case 2: draw_harris(canvas, lx,my,w-ss,ss, 2, depth-1)
draw_harris(canvas, rx,ly,w,h-ss, 3, depth-1)
case 3: draw_harris(canvas, mx,ly,ss,h-ss, 3, depth-1)
draw_harris(canvas, rx,ty,w-ss,h, 4, depth-1)
case 4: draw_harris(canvas, rx,my,w-ss,ss, 4, depth-1)
draw_harris(canvas, lx,ty,w,h-ss, 1, depth-1)
end switch
end if
end procedure
procedure redraw(gdx canvas, integer w,h)
--- determine margins for a horizontal centred harris ratio rectangle:
integer {mw,mh} = iff(h>w/HARRIS_RATIO?{0,floor((h-w/HARRIS_RATIO)/2)}
:{floor((w-h*HARRIS_RATIO)/2),0})
gCanvasRect(canvas, mw, w-mw, mh, h-mh, true, colour:=XPG_PARCHMENT)
w -= mw*2
h -= mh*2
draw_harris(canvas, mw, mh, w, h)
gdx dlg = gGetParent(canvas)
gSetAttribute(dlg,"TITLE","%s (max_depth %d)",{title,max_depth})
end procedure
function show_help(gdx dlg)
gMsgBox(dlg,"Help",help_text)
return XPG_IGNORE -- (don't open browser help!)
end function
function key_handler(gdx dlg, integer c)
if c=VK_ESC then return XPG_CLOSE end if -- (standard practice for me)
if c=VK_F5 then return XPG_DEFAULT end if -- (let browser reload work)
if c=VK_F1 then return show_help(dlg) end if
switch upper(c)
case 'L': show_lines = rmdr(show_lines+1,4)
case 'N': show_lines = 0
case 'S': show_lines = 1
case 'Q': show_lines = 2
case 'R': show_lines = 3
case 'I': initial = not initial
case 'T': termination = not termination
case 'D': diagnostics = not diagnostics
case '+','=': max_depth = min(max_depth+1,7)
case '-','_': max_depth = max(max_depth-1,1)
end switch
gRedraw(dlg)
return XPG_DEFAULT
end function
gdx canvas = gCanvas(redraw)
gdx dialog = gDialog(canvas,title,"SIZE=510x540, MINSIZE=273x58")
gSetHandler(dialog, `KEY`, key_handler)
gShow(dialog)
gMainLoop()
Processing
Steve Van Voorst's original source code. Free open source cross-platform IDE can be downloaded here: https://processing.org/
// ======== start ========== //
int h = 600; // scalable (may be any value)
final float HR = 1.325; // Harriss Ratio
int _wndW = 1000;
int _wndH = 800;
float cntrX = 0;
float cntrY = 0;
float radius = 0;
String heading = "";
boolean showLines = false;
void drawHarriss(float x, float y, float angle, float len, int iteration, float lineW) {
if (iteration > 0) {
float startAngle = angle + 45;
float endAngle = startAngle + 90;
// Calculate end point of lines
float xEnd = x + len * cos(radians(angle));
float yEnd = y + len * sin(radians(angle));
if ((int)yEnd < (int)y) {
heading = "SN";
} //6
if ((int)xEnd < (int)x) {
heading = "EW" ;
} //5
if ((int)yEnd > (int)y) {
heading = "NS";
} //4
if ((int)xEnd > (int)x) {
heading = "WE";
} //3
if (showLines) {
stroke(0);
strokeWeight(1);
line(x, y, xEnd, yEnd);
}
radius = 1.414*len;
if (heading == "SN") {
cntrX = x - len/2;
cntrY = y - len/2;
stroke(255, 255, 0);
strokeWeight(lineW);
}
if (heading == "EW") {
cntrX = x - len/2;
cntrY = y + len/2;
stroke(255, 0, 0);
strokeWeight(lineW);
}
if (heading == "NS") {
cntrX = x + len/2;
cntrY = y + len/2;
stroke(0, 0, 255);
strokeWeight(lineW);
}
if (heading == "WE") {
cntrX = x + len/2;
cntrY = y - len/2;
stroke(0);
strokeWeight(lineW);
}
arc(cntrX, cntrY, radius, radius, radians(startAngle), radians(endAngle), OPEN);
drawHarriss( xEnd, yEnd, angle - 90, len/HR, iteration - 1, lineW);
}
}
void setup() {
size(_wndW, _wndH);
background(140);
noFill();
float startX = _wndW/2 + 50;
float startY = _wndH - 50;
float initLen = h/HR/HR;
// Reverse Order Hides Joints
drawHarriss(startX - (initLen/HR + initLen/HR/HR/HR), startY - initLen/HR/HR/HR/HR/HR/HR/HR/HR, 180, initLen/HR/HR/HR/HR/HR/HR, 2, 6.0); // level 3
drawHarriss(startX - (initLen/HR + initLen/HR/HR/HR), startY - (initLen+initLen/HR/HR), 270, initLen/HR/HR/HR/HR/HR, 3, 6.0); // level 3
drawHarriss(startX + initLen/HR/HR/HR/HR, startY - (initLen + initLen/HR/HR), 270, initLen/HR/HR/HR/HR/HR, 3, 6.0); // level 3
drawHarriss(startX + initLen/HR, startY - (initLen + initLen/HR/HR), 0, initLen/HR/HR/HR/HR, 4, 6.0); // level 3 rt-upper
drawHarriss(startX - initLen/HR/HR/HR/HR, startY - initLen/HR, 0, initLen/HR/HR/HR/HR/HR, 2, 10.0); // level 2 mid-upper
drawHarriss(startX - initLen/HR/HR/HR/HR, startY - initLen/HR/HR/HR, -270, initLen/HR/HR/HR/HR, 3, 12.0); // level 2 mid-lower
drawHarriss(startX - initLen/HR, startY - initLen/HR/HR/HR, 180, initLen/HR/HR/HR, 4, 12.0); // level 2 lt-lower
drawHarriss(startX - initLen/HR, startY - initLen, 270, initLen/HR/HR, 5, 14.0); // level 2 lt-upper
drawHarriss(startX, startY - initLen, 0, initLen/HR, 6, 14.0); // level 2 rt-upper
drawHarriss(startX, startY, -90, initLen, 7, 18.0); // level 1 base spiral
}
void draw() {
}
// ======== end =========== //
Python
Note: This code is compatible with Python versions 3.10 or later.
import math
import random
import pygame
WIDTH, HEIGHT = 1000, 750
HR = 1.3247
SHOW_LINES = False
def draw_arc_segment(
x: float,
y: float,
angle: float,
length: float,
iteration: int,
line_width: int,
surface: pygame.Surface,
):
if iteration <= 0:
return
heading = ""
arc_color = (255, 255, 255)
x_end = x + length * math.cos(math.radians(angle))
y_end = y + length * math.sin(math.radians(angle))
if math.floor(y_end) < math.floor(y):
heading = "RIGHT"
if math.floor(x_end) < math.floor(x):
heading = "UPPER"
if math.floor(y_end) > math.floor(y):
heading = "LEFT"
if math.floor(x_end) > math.floor(x):
heading = "LOWER"
if SHOW_LINES:
pygame.draw.line(surface, (0, 0, 0), (x, y), (x_end, y_end))
centre_x, centre_y = 0, 0
match heading:
case "RIGHT":
centre_x = x - length / 2
centre_y = y - length / 2
arc_color = random.choice(((255, 255, 0), (0, 255, 0)))
case "UPPER":
centre_x = x - length / 2
centre_y = y + length / 2
angle += 180
arc_color = (255, 0, 0)
case "LEFT":
centre_x = x + length / 2
centre_y = y + length / 2
arc_color = (0, 0, 255)
case "LOWER":
centre_x = x + length / 2
centre_y = y - length / 2
angle += 180
arc_color = random.choice(((255, 175, 0), (0, 0, 0)))
radius = 0.7 * length
pygame.draw.arc(
surface,
arc_color,
pygame.Rect(
int(centre_x - radius),
int(centre_y - radius),
int(radius * 2),
int(radius * 2),
),
math.radians(angle + 45),
math.radians(angle + 45 + 90),
)
if heading == "LOWER" or heading == "UPPER":
angle -= 180
draw_arc_segment(
x_end, y_end, angle - 90, length / HR, iteration - 1, line_width, surface
)
def draw_harriss_spiral(surface: pygame.Surface):
HR2 = HR * HR
HR3 = HR2 * HR
HR4 = HR2 * HR2
HR5 = HR4 * HR
HR6 = HR4 * HR2
HR8 = HR4 * HR4
start_x = WIDTH / 2.0 + 50.0
start_y = HEIGHT - 75.0
initial_length = 600 / HR2
draw_arc_segment(
start_x + initial_length / HR,
start_y - (initial_length + initial_length / HR2),
0,
initial_length / HR4,
4,
6,
surface,
)
draw_arc_segment(
start_x + initial_length / HR4,
start_y - (initial_length + initial_length / HR2),
270.0,
initial_length / HR5,
3,
6,
surface,
)
draw_arc_segment(
start_x - (initial_length / HR + initial_length / HR3),
start_y - (initial_length + initial_length / HR2),
270.0,
initial_length / HR5,
3,
6,
surface,
)
draw_arc_segment(
start_x - (initial_length / HR + initial_length / HR3),
start_y - initial_length / HR8,
180.0,
initial_length / HR6,
2,
6,
surface,
)
draw_arc_segment(
start_x - initial_length / HR4,
start_y - initial_length / HR3,
-270.0,
initial_length / HR4,
3,
8,
surface,
)
draw_arc_segment(
start_x - initial_length / HR4,
start_y - initial_length / HR,
0.0,
initial_length / HR5,
2,
8,
surface,
)
draw_arc_segment(
start_x - initial_length / HR,
start_y - initial_length,
270.0,
initial_length / HR2,
5,
12,
surface,
)
draw_arc_segment(
start_x - initial_length / HR,
start_y - initial_length / HR3,
180.0,
initial_length / HR3,
4,
12,
surface,
)
draw_arc_segment(
start_x, start_y - initial_length, 0.0, initial_length / HR, 6, 16, surface
)
draw_arc_segment(start_x, start_y, -90.0, initial_length, 7, 16, surface)
def main():
pygame.init()
pygame.display.set_caption("Harriss Spiral")
screen = pygame.display.set_mode((WIDTH, HEIGHT))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255, 255, 255) if not SHOW_LINES else (100, 100, 100))
draw_harriss_spiral(screen)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
- Output:
Wren
Note that some of the colors are random and consequently the output may not necessarily match the FutureBasic example. Also DOME uses its own color palette which differs from standard RGB colors.
import "dome" for Window
import "graphics" for Canvas, Color
import "math" for M
import "random" for Random
import "./ellipse" for Circle
var HR = 1.3247 // Harriss ratio
var HR2 = HR * HR
var HR3 = HR2 * HR
var HR4 = HR2 * HR2
var HR5 = HR4 * HR
var HR6 = HR4 * HR2
var HR8 = HR4 * HR4
var LINES = false // set to true to show construction lines
var Rand = Random.new()
class HarrissSpiral {
construct new(width, height) {
Window.title = "Harriss spiral"
Window.resize(width, height)
Canvas.resize(width, height)
if (LINES) Canvas.cls(Color.white) else Canvas.cls(Color.darkgray)
_w = width
_h = height
}
// We always show the arcs so no need for a parameter for that.
drawArcSegment(x, y, angle, length, iteration, arcColor, lineWidth, showLines) {
var heading
var radius
var cx
var cy
var circle
var adj
if (iteration > 0) {
var startAngle = angle + 45
var endAngle = startAngle + 90
var xEnd = x + length * M.cos(angle * Num.pi / 180)
var yEnd = y + length * M.sin(angle * Num.pi / 180)
if (M.floor(yEnd) < M.floor(y)) heading = "SN" // 6
if (M.floor(xEnd) < M.floor(x)) heading = "EW" // 5
if (M.floor(yEnd) > M.floor(y)) heading = "NS" // 4
if (M.floor(xEnd) > M.floor(x)) heading = "WE" // 3
if (showLines) Canvas.line(x, y, xEnd, yEnd, Color.black)
radius = 0.70710678 * length
if (heading == "SN") {
cx = x - length / 2
cy = y - length / 2
arcColor = (Rand.int(2) == 1) ? Color.yellow : Color.green
} else if (heading == "EW") {
cx = x - length / 2
cy = y + length / 2
arcColor = Color.red
} else if (heading == "NS") {
cx = x + length / 2
cy = y + length / 2
arcColor = Color.blue
} else if (heading == "WE") {
cx = x + length / 2
cy = y - length / 2
arcColor = (Rand.int(2) == 1) ? Color.orange : Color.black
}
for (i in -lineWidth/2...lineWidth/2) {
circle = Circle.new(cx, cy, radius + i)
adj = lineWidth/8 - (i < 0 ? -i/4 : (i+1)/4)
circle.drawArc(arcColor, startAngle - adj, endAngle + adj)
}
drawArcSegment(xEnd, yEnd, angle-90, length/HR, iteration-1, arcColor, lineWidth, showLines)
}
}
drawSpiral() {
var h = 600
var sx = (_w/2 + 80) // starting x
var sy = (_h - 140) // starting y
var il = h / HR2 // initial length
drawArcSegment(sx + il/HR, sy - (il + il/HR2), 0, il / HR4, 4, Color.green, 6, LINES)
drawArcSegment(sx + il/HR4, sy - (il + il/HR2), 270, il / HR5, 3, Color.black, 6, LINES)
drawArcSegment(sx - (il/HR + il/HR3), sy - (il + il/HR2), 270, il / HR5, 3, Color.black, 6, LINES)
drawArcSegment(sx - (il/HR + il/HR3), sy - il/HR8, 180, il / HR6, 2, Color.blue, 6, LINES)
drawArcSegment(sx - il/HR4, sy - il/HR3, -270, il / HR4, 3, Color.blue, 8, LINES)
drawArcSegment(sx - il/HR4, sy - il/HR, 0, il / HR5, 2, Color.green, 8, LINES)
drawArcSegment(sx - il/HR, sy - il, 270, il / HR2, 5, Color.red, 12, LINES)
drawArcSegment(sx - il/HR, sy - il/HR3, 180, il / HR3, 4, Color.black, 12, LINES)
drawArcSegment(sx, sy - il, 0, il / HR, 6, Color.black, 16, LINES)
drawArcSegment(sx, sy, -90, il, 7, Color.orange, 16, LINES)
}
init() { drawSpiral() }
update() {}
draw(dt) {}
}
var Game = HarrissSpiral.new(1000, 800)
- Output:
XPL0
include xpllib; \for InitDraw, DrawRectangle, DrawCircle, color names
def LINES = false; \set to 'true' to show construction lines
def ScrW=1024, ScrH=768;
def HR = 1.3247; \Harriss Ratio
def HR2 = HR * HR;
def HR3 = HR2 * HR;
def HR4 = HR2 * HR2;
def HR5 = HR4 * HR;
def HR6 = HR4 * HR2;
def HR8 = HR4 * HR4;
proc DrawArcSeg(X, Y, Angle, Length, Iter, ArcColor, LineW, ShowLines);
real X, Y, Angle, Length; int Iter, ArcColor, LineW, ShowLines;
real XEnd, YEnd, Radius, CX, CY;
int Heading;
def \Heading\ SN, EW, NS, WE;
[if Iter <= 0 then return;
XEnd:= X + Length * Cos(Angle*Pi/180.);
YEnd:= Y + Length * Sin(Angle*Pi/180.);
if Floor(YEnd) < Floor(Y) then Heading:= SN;
if Floor(XEnd) < Floor(X) then Heading:= EW;
if Floor(YEnd) > Floor(Y) then Heading:= NS;
if Floor(XEnd) > Floor(X) then Heading:= WE;
if ShowLines then [Move(fix(X), fix(Y)); Line(fix(XEnd), fix(YEnd), Black)];
Radius:= 0.7 * Length;
case Heading of
SN: [CX:= X - Length/2.;
CY:= Y - Length/2.;
ArcColor:= if Ran(2) = 1 then Yellow else Green;
];
EW: [CX:= X - Length/2.;
CY:= Y + Length/2.;
ArcColor:= Red;
];
NS: [CX:= X + Length/2.;
CY:= Y + Length/2.;
ArcColor:= Blue;
];
WE: [CX:= X + Length/2.;
CY:= Y - Length/2.;
ArcColor:= if Ran(2) = 1 then Brown else Black;
]
other [];
case rem( fix(Angle+3600.) / 360 ) / 90 of
0: ArcSegs:= %1111_1001;
1: ArcSegs:= %1110_0111;
2: ArcSegs:= %1001_1111;
3: ArcSegs:= %0111_1110
other ArcSegs:= %1010_1010;
LineWidth:= LineW/2;
DrawCircle(fix(CX), fix(CY), fix(Radius), ArcColor, false \fill\);
DrawArcSeg(XEnd, YEnd, Angle-90., Length/HR, Iter-1, ArcColor, LineW, ShowLines);
];
proc DrawHarrissSpiral;
def H = 600.;
def SX = float(ScrW/2 + 80); \Starting X
def SY = float(ScrH - 80); \Starting Y
def IL = H / HR2; \Initial Length
[
DrawArcSeg(SX+(IL/HR), SY-(IL+IL/HR2), 0., IL/HR4, 4, Green, 6, LINES);
DrawArcSeg(SX+(IL/HR4), SY-(IL+IL/HR2), 270., IL/HR5, 3, Black, 6, LINES);
DrawArcSeg(SX-(IL/HR+IL/HR3), SY-(IL+IL/HR2), 270., IL/HR5, 3, Black, 6, LINES);
DrawArcSeg(SX-(IL/HR+IL/HR3), SY-(IL/HR8), 180., IL/HR6, 2, Blue, 6, LINES);
DrawArcSeg(SX-(IL/HR4), SY-(IL/HR3), -270., IL/HR4, 3, Blue, 8, LINES);
DrawArcSeg(SX-(IL/HR4), SY-(IL/HR), 0., IL/HR5, 2, Green, 8, LINES);
DrawArcSeg(SX-(IL/HR), SY-(IL), 270., IL/HR2, 5, Red, 12, LINES);
DrawArcSeg(SX-(IL/HR), SY-(IL/HR3), 180., IL/HR3, 4, Black, 12, LINES);
DrawArcSeg(SX, SY-(IL), 0., IL/HR, 6, Black, 16, LINES);
DrawArcSeg(SX, SY, -90., IL, 7, Brown, 16, LINES);
];
[SetVid($105); \1024x768x8
InitDraw;
DrawRectangle(0, 0, ScrW, ScrH, if LINES then White else Gray, true \fill\);
Attrib($0E0E); \yellow
Cursor(1024/8/2-8, 0); Text(6, " Harriss Spiral ");
DrawHarrissSpiral;
]
- Output: