Polyspiral
You are encouraged to solve this task according to the task description, using any language you may know.
A Polyspiral is a spiral made of multiple line segments, whereby each segment is larger (or smaller) than the previous one by a given amount. Each segment also changes direction at a given angle.
Task
Animate a series of polyspirals, by drawing a complete spiral then incrementing the angle, and (after clearing the background) drawing the next, and so on. Every spiral will be a frame of the animation. The animation may stop as it goes full circle or continue indefinitely. The given input values may be varied.
If animation is not practical in your programming environment, you may show a single frame instead.
Pseudo code
set incr to 0.0
// animation loop
WHILE true
incr = (incr + 0.05) MOD 360
x = width / 2
y = height / 2
length = 5
angle = incr
// spiral loop
FOR 1 TO 150
drawline
change direction by angle
length = length + 3
angle = (angle + incr) MOD 360
ENDFOR
Straightforward implementation of the pseudocode, incr and angle are integers and incr is incremented by 5 instead of 0.05 as the % operation in C is not defined for non-integers. Requires the WinBGIm library.
<lang C>
/*Abhishek Ghosh, 23rd September 2017*/
include<graphics.h>
include<math.h>
define factor M_PI/180
define LAG 1000
void polySpiral(int windowWidth,int windowHeight){
int incr = 0, angle, i, length;
double x,y,x1,y1;
while(1){
incr = (incr + 5)%360;
x = windowWidth/2;
y = windowHeight/2;
length = 5;
angle = incr;
for(i=1;i<=150;i++){
x1 = x + length*cos(factor*angle);
y1 = y + length*sin(factor*angle);
line(x,y,x1,y1);
This Windows programm has no animation, it will simply save 100 bitmaps onto your harddrive
<lang cpp>
include <windows.h>
include <sstream>
include <ctime>
const float PI = 3.1415926536f, TWO_PI = 2.f * PI;
class vector2
{
public:
vector2( float a = 0, float b = 0 ) { set( a, b ); }
void set( float a, float b ) { x = a; y = b; }
void rotate( float r ) {
float _x = x, _y = y,
s = sinf( r ), c = cosf( r ),
a = _x * c - _y * s, b = _x * s + _y * c;
x = a; y = b;
}
vector2 add( const vector2& v ) {
x += v.x; y += v.y;
return *this;
}
float x, y;
Works with: gnuplot version 5.0 (patchlevel 3) and above
Plotting a polyspiral file-function for the load command
plotpoly.gp file for the load command is the only possible imitation of the fine function in the gnuplot.
<lang gnuplot>
plotpoly.gp 1/10/17 aev
Plotting a polyspiral and writing to the png-file.
Note: assign variables: rng, d, clr, filename and ttl (before using load command).
Direction d (-1 clockwise / 1 counter-clockwise)
reset
set terminal png font arial 12 size 640,640
ofn=filename.".png"
set output ofn
unset border; unset xtics; unset ytics; unset key;
set title ttl font "Arial:Bold,12"
set parametric
c=rng*pi; set xrange[-c:c]; set yrange[-c:c];
set dummy t
plot [0:c] t*cos(d*t), t*sin(d*t) lt rgb @clr
set output
</lang>
Note: assign variables: rng, d, clr, filename and ttl (before using load command).
Direction d (-1 clockwise / 1 counter-clockwise)
cd 'C:\gnupData'
PS0 smooth spiral (not a polyspiral)
reset
set terminal png font arial 12 size 640,640
set output "PS0gp.png"
set title "Smooth spiral #0 rng=10" font "Arial:Bold,12"
set parametric
c=10*pi; set trange [0:c]; set xrange[-c:c]; set yrange[-c:c];
set samples 1000
plot t*cos(t), t*sin(t) lt rgb "red"
set output
reset
set terminal png font arial 12 size 640,640
ofn=filename.".png"
set output ofn
unset border; unset xtics; unset ytics; unset key;
set parametric
c=rng*pi; set xrange[-c:c]; set yrange[-c:c];
set dummy t
plot [0:c] t*cos(d*t), t*sin(d*t) lt rgb @clr
set output
</lang>
Plotting many polyspiral and other pictures for animation.
Note: No generated pictures here on RC.
<lang gnuplot>
PSpirals4a.gp 1/19/17 aev
Plotting many polyspiral and other pictures for animation
reset
set terminal gif animate delay 100 loop 2 size 640,640
set output 'PolySpirsAnim.gif'
unset border; unset xtics; unset ytics; unset key;
unset autoscale
set xrange[0:640]
set yrange[0:640]
do for [i=0:6]{plot 'PS'.i.'.png' binary filetype=png with rgbimage}
set output
Animation for nice figures PS8 - PS14
reset
set terminal gif animate delay 100 loop 2 size 640,640
set output 'NiceFigsAnim.gif'
unset border; unset xtics; unset ytics; unset key;
unset autoscale
set xrange[0:640]
set yrange[0:640]
do for [i=8:14]{plot 'PS'.i.'.png' binary filetype=png with rgbimage}
set output
</lang>
Output:
2 created gif-files: PolySpirsAnim.gif and NiceFigsAnim.gif
Showing 2 animated gif-files.
Create 2 the following html-files and envoke them in browser.
<lang html>
<html><body>
This implementation compiles to javascript that runs in the browser using the ghcjs compiler . The reflex-dom library is used to help with svg rendering and animation.
-- The svg attributes needed to display a line segment.
lineAttrs :: Segment -> Map Text Text
lineAttrs ((x1,y1), (x2,y2)) =
fromList [ ( "x1", pack $ show x1)
, ( "y1", pack $ show y1)
, ( "x2", pack $ show x2)
, ( "y2", pack $ show y2)
, ( "style", "stroke:blue")
]
-- Use svg to display a line segment.
showLine :: MonadWidget t m => Int -> Dynamic t Segment -> m ()
showLine _ dSegment = elSvgns "line" (lineAttrs <$> dSegment) $ return ()
-- Given a point and distance/bearing , get the next point
advance :: Float -> (Point, Float, Float) -> (Point, Float, Float)
advance angle ((x,y), len, rot) =
let new_x = x + len * cos rot
new_y = y + len * sin rot
new_len = len + 3.0
new_rot = rot + angle
in ((new_x, new_y), new_len, new_rot)
-- Given an angle, generate a map of segments that form a spiral.
toSpiralMap :: Float -> Map Int ((Float,Float),(Float,Float))
toSpiralMap angle =
fromList -- changes list to map (for listWithKey)
$ zip [0..] -- annotates segments with index
$ (\pts -> zip pts $ tail pts) -- from points to line segments
$ take 80 -- limit the number of points
$ (\(pt,_,_) -> pt) -- cull out the (x,y) values
<$> iterate (advance angle) ((0, 0), 0, 0) -- compute the spiral
-- Display an element in svg namespace
elSvgns :: MonadWidget t m => Text -> Dynamic t (Map Text Text) -> m a -> m a
elSvgns t m ma = do
This Polyspiral Generator page alows user to enjoy hundreds of polyspirals in different colors.
This is inspired by a discovery made while using the gnuplot.
(See Discussion for Polyspiral task.) Note:
Some polyspirals would be degenerated to a single branch of it or even to a single line.
An image uploading is still blocked. But you have a browser!? So, copy/paste/save this page and double click it.
<lang html>
<html>
<head><title>Polyspiral Generator</title></head>
<script>
// Basic function for family of Polyspirals
// Where: rng - range (prime parameter), w2 - half of canvas width,
// d - direction (1 - clockwise, -1 - counter clockwise).
function ppsp(ctx, rng, w2, d) {
// Note: coefficients c, it, sc, sc2, sc3 are selected to fit canvas.
var c=Math.PI*rng, it=c/w2, sc=2, sc2=50, sc3=0.1, t, x, y;
console.log("Polyspiral PARs rng,w2,d:", rng, "/", w2, "/", d);
if (rng>1000) {sc=sc3}
ctx.beginPath();
for(var i=0; i<sc2*c; i++) {
t=it*i;
x = sc*t*Math.cos(d*t)+w2; y = sc*t*Math.sin(d*t)+w2;
ctx.lineTo(x, y);
}//fend i
ctx.stroke();
}
// ******************************************
// pspiral() - Generating and plotting Polyspirals
function pspiral() {
// Setting basic vars for canvas and inpu parameters
var cvs = document.getElementById('cvsId');
var ctx = cvs.getContext("2d");
var w = cvs.width, h = cvs.height;
var w2=w/2;
var clr = document.getElementById("color").value; // color
var d = document.getElementById("dir").value; // direction
var rng = document.getElementById("rng").value; // range
rng=Number(rng);
ctx.fillStyle="white"; ctx.fillRect(0,0,w,h);
ctx.strokeStyle=clr;
// Plotting spiral.
ppsp(ctx, rng, w2, d)
}//func end
</script></head>
<body style="font-family: arial, helvatica, sans-serif;">
Page with Polyspiral. Right-clicking on the canvas you can save spiral as a png-file, for example.
Try all ranges/colors! But particularly these ranges: 50, 70, 80, 90, 110, 130, 160, 210, 220, 240, 270, 280, 290, 300, 310, 330,
340, 350, 400, 430, 480, 510, all 1010-2000, a few 3000+, etc.
/* copied from stackoverflow */
function HSVtoRGB(h, s, v) {
var r, g, b, i, f, p, q, t;
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return "rgb("
+ Math.round(r * 255) + ","
+ Math.round(g * 255) + ","
+ Math.round(b * 255) + ")";
}
function toRadians(degrees) {
return degrees * (Math.PI / 180);
}
private fun drawSpiral(g: Graphics2D, length: Int, angleIncrement: Double) {
var x1 = width / 2.0
var y1 = height / 2.0
var len = length
var angle = angleIncrement
for (i in 0 until 150) {
g.setColor(Color.getHSBColor(i / 150f, 1.0f, 1.0f))
val x2 = x1 + Math.cos(angle) * len
val y2 = y1 - Math.sin(angle) * len
g.drawLine(x1.toInt(), y1.toInt(), x2.toInt(), y2.toInt())
x1 = x2
y1 = y2
len += 3
angle = (angle + angleIncrement) % (Math.PI * 2.0)
}
}
override protected fun paintComponent(gg: Graphics) {
super.paintComponent(gg)
val g = gg as Graphics2D
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
drawSpiral(g, 5, Math.toRadians(inc))
}
}
fun main(args: Array<String>) {
SwingUtilities.invokeLater {
val f = JFrame()
f.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
f.title = "PolySpiral"
f.setResizable(true)
f.add(PolySpiral(), BorderLayout.CENTER)
f.pack()
f.setLocationRelativeTo(null)
f.setVisible(true)
}
Both versions #1 and #2 are based on using my own small plotting helper functions.
You can find a few others on OEIS Wiki and here on RC Wiki.
<lang parigp>
\\ Plot the line from x1,y1 to x2,y2.
plotline(x1,y1,x2,y2,w=0)={plotmove(w, x1,y1);plotrline(w,x2-x1,y2-y1);}
\\ Convert degrees to radians.
rad2(degs)={return(degs*Pi/180.0)}
\\ Convert Polar coordinates to Cartesian.
cartes2(r,a,rndf=0)={my(v,x,y); x=r*cos(a); y=r*sin(a);
if(rndf==0, return([x,y]), return(round([x,y])))}
</lang>
Version #1. Polyspiral (a spiral made of multiple line segments).
In this version function plotpspiral() was translated from Java and J. Some tweaks and options were added to make it reusable and outputting differently looking polyspirals.
There are no animation features in PARI/GP.
\\ Polyspiral() - Where: ai is an angle increment (in radians), d is a distance/length,
\\ c is a direction 0/1 (clockwise/counter-clockwise); other parameters are self explanative.
\\ 4/15/16 aev Last updated: 4/18/16
polyspiral(size,lim,ai,d,di,c=0)={
plotinit(0);
plotcolor(0,3); \\blue
plotscale(0, -size,size, -size,size);
plotmove(0, 0,0);
plotpspiral(size,lim,ai,d,di,c);
plotdraw([0,size,size]);
}
Version #2. Multi-spiral figure translated from zkl.
This is definitely not a polyspiral, but a very nice "multi-spiral" figure similar to shown in zkl
and in a few other languages. Also, there is a very nice and impressive animation created in zkl,
but not possible in PARI/GP.
<lang parigp>
\\ plotpspiralz() Multi-spiral figure translated from zkl using my own ploting functions.
\\ 4/15/16 aev
plotpspiralz(size,lim,ai,di,lim2)={
my(x1,y1,u1,v1,air=rad2(ai),a,sai=Strprintf("%.3f",ai),sdi=Strprintf("%.3f",di),
\\ Spiralz() - Where: ai is an angle increment (in radians), di is a distance/length
\\ increment, other parameters are self explanative.
\\ 4/15/16 aev
Spiralz(size,lim,ai,di,lim2)={
plotinit(0); plotcolor(0,3); \\blue
plotscale(0, -size,size, -size,size);
\\plotscale(0, 0,size, 0,size);
plotmove(0, 0,0);
plotpspiralz(size,lim,ai,di,lim2);
plotdraw([0,size,size]);
}
Space toggles the timer, '+' increases speed (up to 100 FPS), '-' decreases speed.
'M' toggles "mod360", which inverts the angle every 360/2PI or so, since sin/cos
accept arguments in radians not degrees (and mod 2*PI changes nothing), producing
non-true polyspirals, but quite interesting nevertheless.
procedure Polyspiral(atom x1, y1)
atom angle = incr
integer len = 5
incr += 0.05
if mod360 then
incr = mod(incr,360)
end if
for i=1 to 150 do
atom x2 = x1 + cos(angle)*len
atom y2 = y1 + sin(angle)*len
cdCanvasSetForeground(cddbuffer, i*#200+i*#40+i*#10)
cdCanvasLine(cddbuffer, x1, y1, x2, y2)
{x1, y1} = {x2, y2}
len += 3
angle += incr
if mod360 then
angle = mod(angle,360)
end if
end for
end procedure
function redraw_cb(Ihandle /*ih*/, integer /*posx*/, integer /*posy*/)
if c=K_ESC then return IUP_CLOSE end if
if c=' ' then
IupSetInt(timer,"RUN",not IupGetInt(timer,"RUN"))
elsif find(c,"+-") then
-- ('+' increases speed, by decreasing TIME)
IupSetInt(timer,"TIME",max(10,IupGetInt(timer,"TIME")-(','-c)*10))
IupSetInt(timer,"RUN",0)
IupSetInt(timer,"RUN",1)
elsif upper(c)='M' then
mod360 = not mod360
end if
return IUP_CONTINUE
If you click on the image, it is animated.
Uses the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl
<lang zkl>w,h:=640,640;
bitmap:=PPM(w,h,0xFF|FF|FF); // White background
angleIncrement:=(3.0).toRad();
while(True){
r,angle:=0.0, 0.0;
ao,len,inc:=w/2, 2.5, angleIncrement+(130.0).toRad();
foreach c in (128){
s,a:=r + len, angle + inc;
x,y:=r.toRectangular(angle);
u,v:=r.toRectangular(a);
c=c.shiftLeft(21) + c.shiftLeft(10) + c*8; // convert c to a RGB
bitmap.line(ao+x,ao+y, ao+u,ao+v, c);
r,angle=s,a;
}
bitmap.writeJPGFile("polyspiral.zkl.jpg");
bitmap.fill(0xFF|FF|FF); // White background
angleIncrement=(angleIncrement + 0.05);
Atomic.sleep(3);
}</lang>