Python Animation: Pinwheel Cogs In Action
Every new concept needs to have a programatic test.

While working fine in the woodshop, in as much as I decided to create a complete series on Python last year, I decided to update that training opportunity with a training module dedicated to Turtle Graphics.

Using the concepts being taught therein, please allow me to present the graphical proof that pinwheel gears are not only easier to cut, but that pinwheel cogs can indeed present a type of motion that allot more horologists - than we steam-punk 'tinkerers - will surely find gear-headily attractive:

`import turtlezSet = (turtle.Turtle(), turtle.Turtle())num_msec = 100def quit():    turtle.bye()def zoom():    zSet[0].tilt(2)    zSet[1].tilt(-2)    turtle.ontimer(zoom, num_msec)# Return False if the number of slices will not work (e.g 7, 14, 16, etc.)def draw_pin_wheel(zTurtle=turtle, zSlices=10, zRange=100, zBite=-1):    # Check for hgte 'pi problem'    if 360 % zSlices:       return False    angle = int(360 / zSlices)    locs = [[[None for z in range(1)] for y in range(2)] for x in range(zSlices + 1)]    if zBite <= 0:        zBite = int(zRange / 10)    zSize = len(locs)    zLine = 0    for ref in range(zSize):        zTurtle.left(angle)        zTurtle.forward(zRange)        locs[zLine][1] = zTurtle.pos()        zTurtle.back(zBite)        locs[zLine][0] = zTurtle.pos()        zTurtle.goto(0, 0)        print(locs[zLine])        zLine += 1    turtle.home()    # Demonstrative - let's keep it sane 4 others =)    for line in range(zSize):        listA = locs[line][0]        zTurtle.goto(listA[0], listA[1])  # Key Point        listB = locs[0][1]        if line < (zSize - 1):            listB = locs[line + 1][1]        # Cut-draw the outer 'cut'        zTurtle.goto(listB[0], listB[1])  # Cog-Angle        zTurtle.goto(listA[0], listA[1]) # Inner-Polygon    return Trueturtle.hideturtle()turtle.delay(0)turtle.penup()# Create cogturtle.penup()turtle.begin_poly()turtle.hideturtle()ok = draw_pin_wheel(zSlices=10, zRange=100, zBite=30)if not ok:    print("Bad Slice")    turtle.bye()turtle.end_poly()shape = turtle.Shape("compound")points = turtle.get_poly()shape.addcomponent(points, "black", "black")turtle.register_shape("MyPinwheel", shape)for ss in range(len(zSet)):    zTurtle = zSet[ss]    zTurtle.shape("MyPinwheel")    zTurtle.showturtle()    zTurtle.penup()turtle.goto(0, 200)turtle.color('green')turtle.write("PINWHEEL COGS IN-ACTION", font=("Arial", 24, "normal"), align='center')zSet[0].goto(90, 100)zSet[1].goto(-90, 97)# Schedule eventturtle.onkey(quit, "q")turtle.ontimer(zoom, num_msec)turtle.listen()turtle.mainloop()`

Once the above dual-gear demonstration is running on your machine, just press "q" to stop the animation.

If you don't want to read the code to see the animation in-action ... and if one can believe how overly obsessed we are with the web-gear topic ... then here is the movie.

If one has read this far, then here is a short video of a 2:1 x 3 multi-gear simulation that you may also enjoy. --The above might not look like it wood work (horrific pun - sorry!), but it do.

Sharing is caring!

-Rn

[ view entry ] ( 474 views )   |  permalink  |  related link
Web Gears - Part III: The Pinwheel Cog
It is no secret that males are obsessive. Proven by Readers' Digest many years ago, by tracking the infra-red pattern of blood flow when males think about a problem, the blood pretty much stays in our frontal lobe. No so for the females.

Why mention such a scientifically proven, yet completely politically incorrect fact? -Because "being born that way" is the only way that I might possibly explain my present & personal obsession with creating bewooden counters!

Submitted for your obsession therefore (or not =), here is the latest installment to what is proving to be far, far, too much fun:

`import turtle'''We need to be slightly more careful with the parameters here than in ourother demonstrations - the entire point of this rendition is to create a3-dimensional array of points that can be used for 'zBite' calculations.For things to work properly, we must therefore be sure that zStep <> zRange,as well as that zStep < zRange. Since this is an academic demo, in orderto make things a tad cleaner to read, we removed our in-code parameterchecks.The use of Cardinal slice-values is important!(see post at http://soft9000.com/blog9000/add.php?y= ... 122-020104)'''def draw_pinwheel(zSlices=10, zRange=100, zStep=10):    zt = turtle.Turtle(shape='turtle')    angle = int(360 / zSlices)    zSegs = int(zRange / zStep) + 1    locs = [[[-1 for z in range(1)] for y in range(zSegs)] for x in range(zSlices)]    zLine = 0    zt.hideturtle()    zt.color('red')    for ref in range(1, 360, angle):        zt.pendown()        zt.left(angle)        zLineSeg = 0        for zSeg in range(1, zRange + zStep, zStep):            zt.pendown()            zt.forward(zSeg)            zt.circle(1)            print(zLine, zLineSeg)            locs[zLine][zLineSeg] = [zt.pos()]            zt.penup()            zt.goto(0, 0)            zLineSeg += 1        zLine += 1        print(locs)        zt.goto(0, 0)    zt.color('black')    for zSub in range(1, zRange + zStep, zStep):        zt.right(90)        zt.forward(zSub)        zt.right(270)        zt.pendown()        zt.circle(zSub)        zt.penup()        zt.home()    zt.color('red')    for zLine in range(zSlices):        lineA = locs[zLine]        lineB = locs[0]        if zLine < (zSlices - 1):            lineB = locs[zLine + 1]        for zSeg in range(1, zSegs):            pointA = lineA[zSeg]            pointB = lineB[zSeg - 1]            zt.penup()            zt.goto(pointA[0][0], pointA[0][1])            zt.pendown()            zt.goto(pointB[0][0], pointB[0][1])    zt.penup()    zt.home()    zt.color('black')    zLoc = zRange + zStep    zt.goto(zLoc * -1, zLoc)    zt.write("draw_pinwheel(zSlices=" + str(zSlices) + ", zRange=" + str(zRange) + ", zStep=" + str(zStep) + ")")    zt.hideturtle()turtle.hideturtle()draw_pinwheel(zSlices=10, zRange=200, zStep=100)turtle.getscreen()._root.mainloop()`

We can cut along the red lines to make our pinwheel cog:

Of course, for those of us who feel the need to try various sizes on-the-fly, we retained that distinctive variable-sized 'Web-Gear flavoring:

When working with wood, squaring off a cog often makes for a less brittle latch point:

Next, note the use of a 3-dimensional array:

`locs =    [[[-1 for z in range(1)]      for y in range(zSegs)]        for x in range(zSlices)]...    for zLine in range(zSlices):        lineA = locs[zLine]        lineB = locs[0]        if zLine < (zSlices - 1):            lineB = locs[zLine + 1]        for zSeg in range(1, zSegs):            pointA = lineA[zSeg]            pointB = lineB[zSeg - 1]            zt.penup()            zt.goto(pointA[0][0], pointA[0][1])            zt.pendown()            zt.goto(pointB[0][0], pointB[0][1])`

For those whom might not be used to working with multi-dimensional array complexity, I added the print statements to the code for your 'edutational enjoyment.

From a re-use point of view (no pun intended,) note that one can use this array of points for drawing any type of shape (circles for classic cogs, triangles for gears, etc.)

## Cardinal Cog Locations

After a little experimentation, one will discover that not all slices are created equal!

When one is interested in precise pin locations (when aren't we?) then one will have to be sure that the number of slices requested are even whole-circle multiples:

`prime_slices = set()for slices in range(1, 100):    if 360 % slices is 0:        prime_slices.add(slices)        print("Prime slice", slices)print("There are", len(prime_slices), " proper slices")`

For steam-punkers there can some joy to be seen when non-cardinal slice values are used. For the purposes of this simulation however, please note that we should confine our pinwheel cog slice-requests to one of the primary / cardinal cog values.

For example, given the above primary-slice circle-test, note that out of 99 possible slice selections:

`Prime slice 1Prime slice 2 (impeller)Prime slice 3Prime slice 4Prime slice 5Prime slice 6Prime slice 8Prime slice 9Prime slice 10Prime slice 12 (hours)Prime slice 15Prime slice 18Prime slice 20Prime slice 24Prime slice 30Prime slice 36Prime slice 40Prime slice 45Prime slice 60Prime slice 72Prime slice 90There are 21  proper slices`

Discover also - within our present obsession (counters & clocks) - that the above set of cardinal-slice values under 100 are simply perfect for our needs.

Ultimately however, for those who feel no need to allow several cogs to be cut for a single pattern, here is the simplified version:

`import turtle'''We need to be slightly more careful with the parameters here than in ourother demonstrations - the entire point of this rendition is to create a3-dimensional array of points that can be used for 'zBite' calculations.The use of Cardinal slice-values is important!(see post at http://soft9000.com/blog9000/add.php?y= ... 122-020104)For things to work properly, we must therefore be sure that zStep <> zRange,as well as that zStep < zRange. Since this is an academic demo, in orderto make things a tad cleaner to read, we removed our in-code parameterchecks.'''def draw_pin_wheel(zSlices=10, zRange=100, zBite=-1, zSmile=False):    zt = turtle.Turtle(shape='turtle')    angle = int(360 / zSlices)    locs = [[[-1 for z in range(1)] for y in range(2)] for x in range(zSlices)]    if zBite <=0:        zBite = int(zRange / 10)    zLine = 0    zt.hideturtle()    for ref in range(1, 360, angle):        zt.left(angle)        zt.forward(zRange)        zt.circle(1)        locs[zLine][1] = zt.pos()        zt.back(zBite)        locs[zLine][0] = zt.pos()        zt.circle(1)        zt.goto(0, 0)        print(locs[zLine])        zLine += 1    if zSmile:        zt.home()        zt.begin_fill()        for line in range(len(locs)):            zt.penup()            listA = locs[line][0]            zt.goto(listA[0], listA[1])            zt.pendown()            listB = locs[0][0]            if line < zSlices - 1:                listB = locs[line + 1][0]            # Hex-draw the inner 'circle'            zt.goto(listB[0], listB[1])            zt.goto(listA[0], listA[1])        zt.end_fill()    zt.home()    zt.color('red')    for line in range(len(locs)):        zt.penup()        listA = locs[line][0]        zt.goto(listA[0], listA[1])        zt.pendown()        listB = locs[0][1]        if line < zSlices - 1:            listB = locs[line + 1][1]        # Cut-draw the outer 'cut'        zt.goto(listB[0], listB[1])        zt.goto(listA[0], listA[1])    zt.penup()    zt.color('black')    zLoc = zRange + zBite    zt.goto(zLoc * -1, zLoc)    zt.write("draw_pin_wheel(zSlices=" + str(zSlices) + ", zRange=" + str(zRange) + ", zBite=" + str(zBite) + ")")    zt.hideturtle()turtle.hideturtle()draw_pin_wheel(zSlices=10, zRange=100, zBite=30, zSmile=True)turtle.getscreen()._root.mainloop()`

Surely looking allot cleaner, the need to colorize the final pattern is suddenly not-so-much:

Note that 'zBite' is also back, as well as entirely optional.

[ view entry ] ( 505 views )   |  permalink  |  related link
Web Gears, Part II
Well the weekend is here. Time to hit the wood shop once again.

Last weekend we started creating our decimal counter. While the gears for the project are made entirely of wood, the assistive electronics (simple analog motor + a transistorized dump circuit) for the project are presently cooling in the garage.

Setting the sights on the next project, we decided to make the next gearing task out of cogs.

Updating last week's Web Gear generator, we came up with the following:

`import turtledef draw_cog(zSlices=10, zRange=100, zStep=10, zBite=120):    zt = turtle.Turtle(shape='turtle')    angle = int(360 / zSlices)    print(angle)    notpi = 2.85 # the smaller, the larger the cog-spacing. Keep it <= pi.    circumference = 2.0 * notpi * float(zRange)    overhill = circumference / float(zSlices)    zt.hideturtle()    for ref in range(1, 360, angle):        zt.pendown()        zt.left(angle)        zt.forward(zRange)        zt.pencolor('blue')        zt.circle(overhill / 2)        zt.pencolor('green')        zt.left(zBite)        zt.forward(overhill * 1.2)        zt.penup()        zt.right(zBite)        zt.forward(overhill * 1.2)        zt.pencolor('black')        zt.goto(0, 0)    for i in range(1, zRange + zStep, zStep):        zt.right(90)        zt.forward(i)        zt.right(270)        zt.pendown()        zt.circle(i)        zt.penup()        zt.home()    zLoc = zRange + zStep    zt.goto(zLoc * -1, zLoc)    zt.write("draw_cog(zSlices=" + str(zSlices) + ", zRange=" + str(zRange) + ", zStep=" + str(zStep) + ", zBite=" + str(zBite) + ")")    zt.hideturtle()turtle.hideturtle()draw_cog(zSlices=10, zRange=200, zStep=100, zBite=145)turtle.getscreen()._root.mainloop()`

Cutting along either the green "zByte" line, or the blue "notpi" circle will allow us to choose the type of cog (pinwheel or classic cog) that we want to cut:

Sharing is caring,

-Randall

P.S: Eliminating "zBite" so as to make the effective angle based upon our "zStep" (nice triangle to re-use there!) is presently on the # TODO: list! (i.e. it's been a long day =)

-Rn

[ view entry ] ( 451 views )   |  permalink  |  related link

<<First <Back | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Next> Last>>