|
| 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