Skip to content

Commit 682b0c0

Browse files
committed
Create spiro.r
Draw spirograph - like graphics. Whoopee.
1 parent 93a6253 commit 682b0c0

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

spiro.r

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# because why not
2+
# Arguments: (for now, require base and roll to be integers)
3+
# base: static circle diameter
4+
# roll: moving circle diameter
5+
# draw: radius of roll circle where pen is
6+
# pencol: vector of color designators
7+
# inout: "in" or "out" for placement of draw circle on roll circle
8+
# length: how many times around base circle to draw. If I did it right, for
9+
# comensurable circles this will close the figure.
10+
# maxlen: cut off the pattern when wiseasses load radii 37, 3457.
11+
#
12+
# TODO
13+
14+
# --BUG fix frame size -- for an outside case with pen > roll, current algorithm
15+
# fails.
16+
# -- Add option for an angle offset of starting position. This allows overlay of a
17+
# pattern, or alignment of inside vs outside (since pen starts at non-mirrored
18+
# position wrt direction of rotation).
19+
# -- Animate: redraw with a moving roll circle, a red dot at the pen point, etc.
20+
#
21+
# if e.g.
22+
# roll == base/2.5, then it does 2.5 rotations per cycle, implying 2 cycles required.
23+
# but '2.5' means 5 rotations = 2 base circumfs, or 5:2 radii. could be (2,5) or
24+
# say (6,15) , and in either case roll/gcd(b,r) is roll/gcd .
25+
spiro <- function(base=6, roll=5, draw=1, inside=TRUE, maxlen=100, plotit=TRUE, pencol='red', offang = 0, ... ){
26+
# and the rest is just sines and cosines :-)
27+
if (length(base) > 1 ) {
28+
warning("length(base) > 1; only first value used")
29+
base <- base[1]
30+
}
31+
# locate pen start at 0,0 radians (base,draw) and initial radius as follows from inputs.
32+
incr <- pi/250 #may change this stepsize
33+
for (j in 1: length(roll) ) runlen <- roll/gcd(base,roll[j])
34+
#change to pmin to get vector of reps
35+
reps <- 2*pi/incr * pmin(runlen,maxlen)
36+
# This version of graph limits appears compatible with all draw vs. roll sizes.
37+
lims <- max(base, base+draw + roll*(-1)^inside)
38+
# base circle
39+
par(mar=c(.1,.1,.1,.1))
40+
plot(base*sin(seq(0,2*pi,by=.01)),base*cos(seq(0,2*pi,by=.01)),t='l',xlab='',ylab='',axes=FALSE,xlim=c(-lims,lims),ylim=c(-lims,lims),asp=1 )
41+
# Now, here, I think I'll calculate angb and angr inside the per-pattern loop.
42+
# Must be a cleaner way to set up all these lengths!
43+
rp <- max(length(draw), length(roll), length(inside), length(offang) )
44+
draw <- rep(draw, length=rp)
45+
roll <- rep(roll, length=rp)
46+
pencol <- rep(pencol,length=rp)
47+
reps <- rep(reps, length=rp)
48+
inside <- rep(inside, length=rp)
49+
offang <- rep(offang, length=rp)
50+
# now just stick 'jk' index all over the place in the loop.
51+
for (jk in 1:length(roll) ){
52+
angb <- seq(0,by=incr,length=reps[jk]) #may need to lengthen by one element
53+
# when outside, flip sign; I think this is where offang comes into play
54+
angr <- angb * (1 +(-1)^inside[jk] * base/roll[jk]) +offang[jk]
55+
#angr <- angb * (1 - base/roll[jk])
56+
57+
# need a switch or something to deal with inside==FALSE . Do later.
58+
# get location of rollcircle in cartesian
59+
# here, do want to subtract since rollcenter is 'roll' inwards from base circle
60+
# (at least so long as inout=='in'
61+
# same change in sign for outside
62+
rollx <- (base + (-1)^inside[jk] * roll[jk])*cos(angb)
63+
rolly <- (base + (-1)^inside[jk] * roll[jk])*sin(angb)
64+
# find pen location relative to center of rollcircle, in "local" cartesian
65+
dx <- (draw[jk])*cos(angr)
66+
dy <- (draw[jk])*sin(angr)
67+
# add cartesian vectors and attach to array of coords
68+
newx <- dx + rollx
69+
newy <- dy + rolly
70+
pen <- cbind(newx,newy)
71+
#points(newx,newy,pch='.') # or lines below
72+
for (j in 2:reps[jk]) {
73+
lines(pen[(j-1):j,1], pen[(j-1):j,2],col=pencol[jk], ...)
74+
Sys.sleep(.005) #to watch the pattern grow
75+
# if want to plot roll as well, then need to redraw the entire plot:
76+
# if(rollit) {
77+
# #same initialization
78+
# #draw roll centered at angb
79+
# # draw all of pen
80+
# }
81+
}
82+
}
83+
return(invisible(pen))
84+
# restore graphics par
85+
par( mar=c(5,4,4,2)+0.1 ) #default settings
86+
}
87+

0 commit comments

Comments
 (0)