# Archive for category Software

### HP 7475A Plotter: SuperFormula With Paper Size Sensing

Posted by Ed in Photography & Images, Software on 2015-09-02

Well, it’s actually not “sensing”, but the demo code now sizes the graph to the paper size reported by the plotter, so you can plot on cheap and readily available A-size paper. The Vulcan Nerve Pinch that switches paper size on the fly is `Enter+Size`

; I leave the DIP switches set for B-size sheets, because they’re more impressive and take longer to plot.

A collection of A-size plots:

The perspective foreshortening makes the sheets look square and the plots seem circular; they’re not.

The plots lie in rough time sequence from lower left to upper right, showing that I tweaked the `n1`

parameter to avoid the sort of tiny middle that gnawed a hole right through the center-bottom sheet. I also removed higher `m`

parameter values, because more than 50-ish points doesn’t work well on smaller sheets.

I figured out how to use the Python ternary “operator” and tweaked the print formatting, but basically it’s a hack job through & through.

The Python source code, including the hacked Chiplotle routines that produce the SuperFormula:

from chiplotle import * from math import * from datetime import * import random def superformula_polar(a, b, m, n1, n2, n3, phi): ''' Computes the position of the point on a superformula curve. Superformula has first been proposed by Johan Gielis and is a generalization of superellipse. see: http://en.wikipedia.org/wiki/Superformula Tweaked to return polar coordinates ''' t1 = cos(m * phi / 4.0) / a t1 = abs(t1) t1 = pow(t1, n2) t2 = sin(m * phi / 4.0) / b t2 = abs(t2) t2 = pow(t2, n3) t3 = -1 / float(n1) r = pow(t1 + t2, t3) if abs(r) == 0: return (0,0) else: # return (r * cos(phi), r * sin(phi)) return (r,phi) def supershape(width, height, m, n1, n2, n3, point_count=10*1000, percentage=1.0, a=1.0, b=1.0, travel=None): '''Supershape, generated using the superformula first proposed by Johan Gielis. - `points_count` is the total number of points to compute. - `travel` is the length of the outline drawn in radians. 3.1416 * 2 is a complete cycle. ''' travel = travel or (10*2*pi) ## compute points... phis = [i * travel / point_count for i in range(1 + int(point_count * percentage))] points = [superformula_polar(a, b, m, n1, n2, n3, x) for x in phis] ## scale and transpose... path = [ ] for r, a in points: x = width * r * cos(a) y = height * r * sin(a) path.append(Coordinate(x, y)) return Path(path) ## RUN DEMO CODE if __name__ == '__main__': plt=instantiate_plotters()[0] # plt.write('IN;') if plt.margins.soft.width < 11000: # A=10365 B=16640 maxplotx = (plt.margins.soft.width / 2) - 100 maxploty = (plt.margins.soft.height / 2) - 150 legendx = maxplotx - 2600 legendy = -(maxploty - 650) tscale = 0.45 numpens = 4 m_list = [n/10.0 for n in [11, 13, 17, 19, 23]]; # prime/10 = number of spikes n1_list = [n/100.0 for n in range(55,75,1) + range(80,120,5) + range(120,200,10)] # ring-ness 0.1 to 2.0, higher is larger else: maxplotx = plt.margins.soft.width / 2 maxploty = plt.margins.soft.height / 2 legendx = maxplotx - 3000 legendy = -(maxploty - 700) tscale = 0.45 numpens = 6 m_list = [n/10.0 for n in [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]]; # prime/10 = number of spikes n1_list = [n/100.0 for n in range(15,75,1) + range(80,120,5) + range(120,200,10)] # ring-ness 0.1 to 2.0, higher is larger print "Max: ({},{})".format(maxplotx,maxploty) n2_list = [n/100.0 for n in range(10,50,1) + range(55,100,5) + range(110,200,10)] # spike-ness 0.1 to 2.0, lower is spiky plt.write(chr(27) + '.H200:') # set hardware handshake block size plt.set_origin_center() plt.write(hpgl.SI(tscale*0.285,tscale*0.375)) # scale based on B size characters plt.write(hpgl.VS(10)) # slow speed for those abrupt spikes plt.select_pen(1) # standard loadout has pen 1 = black plt.write(hpgl.PA([(legendx,legendy)])) plt.write(hpgl.LB("Started " + str(datetime.today()))) m = random.choice(m_list) pen = 1 for n1, n2 in zip(random.sample(n1_list,numpens),random.sample(n2_list,numpens)): n3 = n2 print "{0} - m: {1:.1f}, n1: {2:.2f}, n2=n3: {3:.2f}".format(pen,m,n1,n2) plt.select_pen(pen) plt.write(hpgl.PA([(legendx, legendy - 100*pen)])) plt.write(hpgl.LB("Pen {0}: m={1:.1f} n1={2:.2f} n2=n3={3:.2f}".format(pen,m,n1,n2))) e = supershape(maxplotx, maxploty, m, n1, n2, n3) plt.write(e) pen = pen + 1 if (pen % numpens) else 1 plt.select_pen(1) plt.write(hpgl.PA([(legendx, legendy - 100*(numpens + 1))])) plt.write(hpgl.LB("Ended " + str(datetime.today()))) plt.select_pen(0)

### HP 7475A Plotter: SuperFormula Demo Madness!

Posted by Ed in Machine Shop, Photography & Images, Software on 2015-08-27

A gallery of SuperFormula plots, resized / contrast stretched / ruthlessly compressed (clicky for more dots):

The gray one at the middle-bottom suffered from that specular reflection; the automagic contrast stretch couldn’t boost the paper with those burned pixels in the way.

Those sheets all have similar plots on the back, some plots used refilled pens that occasionally bled through the paper, others have *obviously* bad / dry pens, and you’ll spot abrupt color changes where I swapped out a defunct pen on the fly, but they should give you an idea of the variations.

The more recent plots have a legend in the right bottom corner with coefficients and timestamps:

Limiting the pen speed to 10 cm/s (down from the default 38.1 cm/s = 15.00 inch/s) affects only the outermost segments of the spikes; down near the dense center, the 9600 b/s serial data rate limits the plotting speed. Plotting slowly helps old pens with low flow rates draw reasonably dense lines.

Each plot takes an hour, which should suffice for most dog-and-pony events.

I fill a trio of Python lists with useful coefficient values, then choose random elements for each plot: a single value of `m`

determines the number of points for all six traces, then six pairs of values set `n1`

and `n2=n3`

. The lists are *heavily* weighted to produce spiky traces, rather than smooth ovals, so the “random” list selections aren’t uniformly distributed across the full numeric range of the values.

Because the coefficient lists contain fixed values, the program can produce only a finite number of different plots, but I’m not expecting to see any duplicates. You can work out the possibilities by yourself.

The modified Chiplotle demo code bears little resemblance to the original:

from chiplotle import * from math import * from datetime import * import random def superformula_polar(a, b, m, n1, n2, n3, phi): ''' Computes the position of the point on a superformula curve. Superformula has first been proposed by Johan Gielis and is a generalization of superellipse. see: http://en.wikipedia.org/wiki/Superformula Tweaked to return polar coordinates ''' t1 = cos(m * phi / 4.0) / a t1 = abs(t1) t1 = pow(t1, n2) t2 = sin(m * phi / 4.0) / b t2 = abs(t2) t2 = pow(t2, n3) t3 = -1 / float(n1) r = pow(t1 + t2, t3) if abs(r) == 0: return (0,0) else: # return (r * cos(phi), r * sin(phi)) return (r,phi) def supershape(width, height, m, n1, n2, n3, point_count=10*1000, percentage=1.0, a=1.0, b=1.0, travel=None): '''Supershape, generated using the superformula first proposed by Johan Gielis. - `points_count` is the total number of points to compute. - `travel` is the length of the outline drawn in radians. 3.1416 * 2 is a complete cycle. ''' travel = travel or (10*2*pi) ## compute points... phis = [i * travel / point_count for i in range(1 + int(point_count * percentage))] points = [superformula_polar(a, b, m, n1, n2, n3, x) for x in phis] ## scale and transpose... path = [ ] for r, a in points: x = width * r * cos(a) y = height * r * sin(a) path.append(Coordinate(x, y)) return Path(path) ## RUN DEMO CODE if __name__ == '__main__': paperx = 8000 papery = 5000 tscale = 0.45 numpens = 6 m_list = [n/10.0 for n in [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]]; # prime/10 = number of spikes n1_list = [n/100.0 for n in range(15,75,1) + range(80,120,5) + range(120,200,10)] # ring-ness 0.1 to 2.0, higher is larger diameter n2_list = [n/100.0 for n in range(10,50,1) + range(55,100,5) + range(110,200,10)] # spike-ness 0.1 to 2.0, lower means spiky points paramlist = [[n1,n2] for n1 in random.sample(n1_list,numpens) for n2 in random.sample(n2_list,numpens)] if not False: plt=instantiate_plotters()[0] plt.write('IN;') # plt.write(chr(27) + '.H200:') # set hardware handshake block size plt.set_origin_center() plt.write(hpgl.SI(tscale*0.285,tscale*0.375)) # scale based on B size characters plt.write(hpgl.VS(10)) # slow speed for those abrupt spikes pen = 1 plt.select_pen(pen) plt.write(hpgl.PA([(paperx - 3000,-(papery - 600))])) plt.write(hpgl.LB("Started " + str(datetime.today()))) m = random.choice(m_list) for n1, n2 in zip(random.sample(n1_list,numpens),random.sample(n2_list,numpens)): n3 = n2 print "m: ", m, " n1: ", n1, " n2=n3: ", n2 plt.write(hpgl.PA([(paperx - 3000,-(papery - 500 + 100*(pen - 1)))])) plt.select_pen(pen) plt.write(hpgl.LB("Pen " + str(pen) + ": m=" + str(m) + " n1=" + str(n1) + " n2=n3=" + str(n2))) e = supershape(paperx, papery, m, n1, n2, n3) plt.write(e) if pen < numpens: pen += 1 else: pen = 1 pen = 1 plt.select_pen(pen) plt.write(hpgl.PA([(paperx - 3000,-(papery - 500 + 100*numpens))])) plt.write(hpgl.LB("Ended " + str(datetime.today()))) plt.select_pen(0) else: e = supershape(paperx, papery, 1.9, 0.8, 3, 3) io.view(e)

### HP 7475A Plotter: Pen Refilling Station

Posted by Ed in Machine Shop, Software on 2015-08-21

A place to store your vials of blended inkjet juice, plus a workstation for the plotter pen you’re refilling *and* that ink vial up front:

The two pen holders accommodate ordinary fiber-tip pens and ceramic-tip pens. The slot along the front lets you keep track of the ink level, not that there’s much danger of running dry at 0.05 ml per refill from a vial holding 1 ml of blended ink. The big flange makes it harder for me to knock the damn thing over; avoiding an ink spill, even when you have a towel underneath, is a Good Thing.

The Slic3r tool path preview shows off the Hilbert Curve top & bottom infill:

The OpenSCAD source code:

// HP7475A Plotter Pen Refill Station // Ed Nisley KE4ZNU - August 2015 //- Extrusion parameters - must match reality! ThreadThick = 0.25; ThreadWidth = 0.40; function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); Protrusion = 0.1; HoleWindage = 0.2; module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); FixDia = Dia / cos(180/Sides); cylinder(r=(FixDia + HoleWindage)/2, h=Height, $fn=Sides); } //------ // Dimensions WallThick = 6*ThreadWidth; BaseThick = IntegerMultiple(1.0,ThreadThick); VialOD = 8.0; VialOC = VialOD + WallThick; VialArray = [4,4]; // number of vials in each direction PenOD = [14.7,11.7]; // regular fiber pen body, ceramic *cap* dia NumPens = len(PenOD); // really works for just two pens... PenLength = 38; FlangeOD = 18; echo(str("Max pen OD: ",max(PenOD))); echo(str("Number of pens: ",len(PenOD))); Holder = [(VialOC*VialArray[0] + WallThick),(VialOC*VialArray[1] + 2*FlangeOD + WallThick),(3*VialOD + BaseThick)]; HolderRound = 5.0; //- Build it difference() { union() { hull() { for (i=[-1,1], j=[-1,1]) { translate([i*(Holder[0]/2 - HolderRound),j*(Holder[1]/2 - HolderRound),0]) cylinder(r=HolderRound,h=Holder[2],$fn=8*4); } } hull() { for (i=[-1,1], j=[-1,1]) { translate([i*Holder[1]/2,j*(Holder[1]/2 - HolderRound),0]) cylinder(r=HolderRound,h=BaseThick,$fn=8*4); } } for (i=[0:len(PenOD) - 1]) translate([(i*Holder[0]/2 - Holder[0]/4),-Holder[1]/4,BaseThick]) { // spacing is a total hack rotate(180/12) cylinder(d=FlangeOD,h=PenLength,$fn=3*4); } } for (i=[0:VialArray[0] - 1] , j=[0:VialArray[1] - 1]) { vx = i*VialOC - (VialOC*(VialArray[0] - 1)/2); vy = j*VialOC - (VialOC*(VialArray[1] - 1)/2) + FlangeOD; translate([vx,vy,BaseThick]) rotate(180/8) PolyCyl(VialOD,Holder[2],8); } translate([0,(VialOD/2 - Holder[1]/2),BaseThick]) rotate(180/8) PolyCyl(VialOD,Holder[2],8); // edges along open side => snug fit for (i=[0:len(PenOD) - 1]) translate([(i*Holder[0]/2 - Holder[0]/4),-Holder[1]/4,BaseThick]) { // spacing is a total hack rotate(180/12) PolyCyl(PenOD[i],(PenLength + Protrusion),3*4); } }

Mostly because I can…

### Clover MCI-900 Mini Iron Holder

Posted by Ed in Home Ec, Machine Shop, Software on 2015-08-17

Mary flattens seam allowances and prepares appliqué pieces with a Clover MCI-900 Mini Iron. The stand resembles the wire gadgets that came with soldering irons, back in the day:

That stand may be suitable on a workbench, but it’s perilously unstable on an ironing board. After fiddling around for a while and becoming increasingly frustrated with it, she asked for a secure holder that wouldn’t fall over and perhaps had a heat shield around the hot end.

I ran off a quick prototype to verify my measurements and provide a basis for further discussion:

I proposed screwing that holder to a rectangle of leftover countertop extending under the hot end, with a U-shaped heat shield extending upward to keep fingers and fabric away from the blade. She decided the countertop might be entirely too heavy and the heat shield might be too confining, so she suggested just angling the iron upward and adding a flat platform to stabilize it.

Her wish being my command:

I’m still not convinced that having the hot end up in the air is a Good Thing, but she thinks it’s worth trying as-is. A pair of 10-32 screw holes under each end will let it mount to a base board, should that becomes necessary.

I’ll stick a foam sheet under the platform so it doesn’t slide around. The cord normally dangles downward off the side of the ironing board or work table, so the iron won’t get up and walk away, but it might pull the whole affair toward the edge.

Because OpenSCAD now includes a text() function, engraving her name in the platform turned out to be no big deal:

I should fill the letters with JB Weld epoxy darkened with laser printer toner (who knew?) to make them stand out. They’re more conspicuous in person than in the picture, so maybe it doesn’t matter.

The slots holding the iron have a semicircular bottom and straight-wall sides, created by extruding hulled 2D shapes, arranging them along the iron’s central axis, and tilting the “iron” at the appropriate angle:

That’s a 10° tilt, chosen because it looked right. The model recomputes itself around the key dimensions, so we can raise / lower the iron, change the angle, and so forth and so on, as needed.

Assuming that a hot end sticking out in mid-air isn’t too awful, this one looks like a keeper.

The OpenSCAD source code:

// Clover MCI-900 Mini Iron holder // Ed Nisley KE4ZNU - August 2015 Layout = "Holder"; // Iron Holder //- Extrusion parameters - must match reality! ThreadThick = 0.25; ThreadWidth = 0.40; function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); Protrusion = 0.1; HoleWindage = 0.2; inch = 25.4; Tap10_32 = 0.159 * inch; Clear10_32 = 0.190 * inch; Head10_32 = 0.373 * inch; Head10_32Thick = 0.110 * inch; Nut10_32Dia = 0.433 * inch; Nut10_32Thick = 0.130 * inch; Washer10_32OD = 0.381 * inch; Washer10_32ID = 0.204 * inch; //------ // Dimensions CornerRadius = 4.0; CenterHeight = 25; // center at cord inlet on body BodyLength = 110; // cord inlet to body curve at front flange Incline = 10; // central angle slope FrontOD = 29; FrontBlock = [20,1.5*FrontOD + 2*CornerRadius,FrontOD/2 + CenterHeight + BodyLength*sin(Incline)]; CordOD = 10; CordLen = 10; RearOD = 22; RearBlock = [15 + CordLen,1.5*RearOD + 2*CornerRadius,RearOD/2 + CenterHeight]; PlateWidth = 2*FrontBlock[1]; TextDepth = 3*ThreadThick; ScrewOC = BodyLength - FrontBlock[0]/2; ScrewDepth = CenterHeight - FrontOD/2 - 5; echo(str("Screw OC: ",ScrewOC)); BuildSize = [200,250,200]; // largest possible thing module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); FixDia = Dia / cos(180/Sides); cylinder(r=(FixDia + HoleWindage)/2, h=Height, $fn=Sides); } // Trim bottom from child object module TrimBottom(BlockSize=BuildSize,Slice=CornerRadius) { intersection() { translate([0,0,BlockSize[2]/2]) cube(BlockSize,center=true); translate([0,0,-Slice]) children(); } } // Build a rounded block-like thing module RoundBlock(Size=[20,25,30],Radius=CornerRadius,Center=false) { HS = Size/2 - [Radius,Radius,Radius]; translate([0,0,Center ? 0 : (HS[2] + Radius)]) hull() { for (i=[-1,1], j=[-1,1], k=[-1,1]) { translate([i*HS[0],j*HS[1],k*HS[2]]) sphere(r=Radius,$fn=4*4); } } } // Create a channel to hold something // This will eventually be subtracted from a block // The offsets are specialized for this application... module Channel(Dia,Length) { rotate([0,90,0]) linear_extrude(height=Length) rotate(90) hull() { for (i=[-1,1]) translate([i*Dia,2*Dia]) circle(d=Dia/8); circle(d=Dia,$fn=8*4); } } // Iron-shaped series of channels to be removed from blocks module IronCutout() { union() { translate([-2*CordLen,0,0]) Channel(CordOD,2*CordLen + Protrusion); Channel(RearOD,RearBlock[0] + Protrusion); translate([BodyLength - FrontBlock[0]/2 - FrontBlock[0],0,0]) Channel(FrontOD,2*FrontBlock[0]); } } //- Build it if (Layout == "Iron") IronCutout(); if (Layout == "Holder") difference() { union() { translate([(BodyLength + CordLen)/2 - CordLen,0,0]) TrimBottom() RoundBlock(Size=[(CordLen + BodyLength),PlateWidth,CornerRadius]); translate([(RearBlock[0]/2 - CordLen),0,0]) TrimBottom() RoundBlock(Size=RearBlock); translate([BodyLength - FrontBlock[0]/2,0,0]) { TrimBottom() RoundBlock(Size=FrontBlock); } } translate([0,0,CenterHeight]) rotate([0,-Incline,0]) IronCutout(); translate([0,0,-Protrusion]) PolyCyl(Tap10_32,ScrewDepth + Protrusion,6); translate([ScrewOC,0,-Protrusion]) PolyCyl(Tap10_32,ScrewDepth + Protrusion,6); translate([(RearBlock[0] - CordLen) + BodyLength/2 - FrontBlock[0],0,CornerRadius - TextDepth]) { translate([0,10,0]) linear_extrude(height=TextDepth + Protrusion,convexity=1) // rendering glitches for convexity > 1 text("Mary",font="Ubuntu:style=Bold Italic",halign="center",valign="center"); translate([0,-10,0]) linear_extrude(height=TextDepth + Protrusion,convexity=1) // rendering glitches for convexity > 1 text("Nisley",font="Ubuntu:style=Bold Italic",halign="center",valign="center"); } }

The M2 buzzed away for four hours on that puppy, with the first 2½ hours devoted to building the platform. That’s the downside of applying Hilbert Curve infill to two big flat surfaces, but the texture looks *really good*.

### Testing USB Memory Devices

Posted by Ed in PC Tweakage, Photography & Images, Software on 2015-08-12

Tantris recommended the `f3`

set of programs to verify USB memory devices, which certainly seemed as though it would be faster and much less labor-intensive than my low-tech manual method.

Compiling it from source required installing two dependencies, which I discovered by the simple expedient of iteratively smashing into “fatal error: parted/parted.h: No such file or directory” messages:

`libudev-dev`

`libparted0-dev`

With those in place, unleashing `f3probe`

on the most recent replacement Sony 64 GB MicroSD card went swimmingly:

sudo ./f3probe --time-ops /dev/sdb F3 probe 5.0 Copyright (C) 2010 Digirati Internet LTDA. This is free software; see the source for copying conditions. Please unplug and plug back the USB drive. Waiting... Thanks Please unplug and plug back the USB drive. Waiting... Thanks Please unplug and plug back the USB drive. Waiting... Thanks Please unplug and plug back the USB drive. Waiting... Thanks Please unplug and plug back the USB drive. Waiting... Thanks Please unplug and plug back the USB drive. Waiting... Thanks CAUTION CAUTION CAUTION No more resets are needed, so do not unplug the drive Probe finished, recovering blocks... Done Good news: The device `/dev/sdb' is the real thing Device geometry: *Real* size: 60.37 GB (126613504 blocks) Announced size: 60.37 GB (126613504 blocks) Module: 64.00 GB (2^36 Bytes) Physical block size: 512.00 Byte (2^9 Bytes) Probe time: 61.19 seconds Probe read op: count=775, total time=4.00s, avg op time=5.16ms Probe write op: count=753, total time=3.77s, avg op time=5.00ms Probe reset op: count=6, total time=53.42s, avg op time=8903.21ms

As predicted, most of the time passed while I fiddled with the SD Card adapter in the slot on the side of the U2711 monitor: push to release, push to insert, repeat as prompted.

Despite the `f3fix`

program’s ability to “repair” counterfeit USB memory by resetting the partition to the actual capacity, I think that’s a Bad Idea. Based on my admittedly limited experience, counterfeit junk generally doesn’t come from the middle of the quality-control bell curve, so expecting that crap to actually *work* over the long term seems, shall we say, overconfident.

The `f3`

doc also told me about `lsblk`

, which may come in handy every now & again:

lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 111.8G 0 disk ├─sda1 8:1 0 56.8G 0 part / └─sda2 8:2 0 9.3G 0 part [SWAP] sdb 8:16 1 60.4G 0 disk └─sdb1 8:17 1 60.4G 0 part /media/ed/9C33-6BBD sr0 11:0 1 1024M 0 rom

Now I have a reminder of how to do this for The Next Time…

### HP 7475A Plotter: Superformula Demo

Posted by Ed in Machine Shop, Software on 2015-08-05

Setting `n2=n3=1.5`

generates smoothly rounded shapes, rather than the spiky ones produced by `n2=n3=1.0`

, so I combined the two into a single demo routine:

A closer look shows all the curves meet at the points, of which there are 37:

The spikes suffer from limited resolution: each curve has 10 k points, but if the extreme end of a spike lies between two points, then it gets blunted on the page. Doubling the number of points would help, although I think this has already gone well beyond the, ah, point of diminishing returns.

I used the three remaining “disposable” liquid ink pens for the spiked curves; the black pen was beyond repair. They produce gorgeous lines, although the magenta ink seems a bit thinned out by the water I used to rinse the remains of the last refill out of the spiral vent channel.

I modified the Chiplotle `supershape()`

function to default to my choices for `point_count`

and `travel`

, then copied the `superformula()`

function and changed it to return polar coordinates, because I’ll eventually try scaling the linear value as a function of the total angle, which is *much* easier in polar coordinates.

The demo code produces the patterns in the picture by iterating over interesting values of `n1`

and `n2=n3`

, stepping through the pen carousel for each pattern. As before, `m`

should be `prime/10`

to produce a `prime`

number of spikes / bumps. You could add more iteration values, but six of ’em seem entirely sufficient.

A real demo should include a large collection of known-good parameter sets, from which it can pick six sets to make a plot. A legend documenting the parameters for each pattern, plus the date & time, would bolster the geek cred.

The Python source code with the modified Chiplotle routines:

from chiplotle import * from math import * def superformula_polar(a, b, m, n1, n2, n3, phi): ''' Computes the position of the point on a superformula curve. Superformula has first been proposed by Johan Gielis and is a generalization of superellipse. see: http://en.wikipedia.org/wiki/Superformula Tweaked to return polar coordinates ''' t1 = cos(m * phi / 4.0) / a t1 = abs(t1) t1 = pow(t1, n2) t2 = sin(m * phi / 4.0) / b t2 = abs(t2) t2 = pow(t2, n3) t3 = -1 / float(n1) r = pow(t1 + t2, t3) if abs(r) == 0: return (0,0) else: # return (r * cos(phi), r * sin(phi)) return (r,phi) def supershape(width, height, m, n1, n2, n3, point_count=10*1000, percentage=1.0, a=1.0, b=1.0, travel=None): '''Supershape, generated using the superformula first proposed by Johan Gielis. - `points_count` is the total number of points to compute. - `travel` is the length of the outline drawn in radians. 3.1416 * 2 is a complete cycle. ''' travel = travel or (10*2*pi) ## compute points... phis = [i * travel / point_count for i in range(1 + int(point_count * percentage))] points = [superformula_polar(a, b, m, n1, n2, n3, x) for x in phis] ## scale and transpose... path = [ ] for r, a in points: x = width * r * cos(a) y = height * r * sin(a) path.append(Coordinate(x, y)) return Path(path) ## RUN DEMO CODE if __name__ == '__main__': paperx = 8000 papery = 5000 if not False: plt=instantiate_plotters()[0] plt.set_origin_center() plt.write(hpgl.VS(10)) pen = 1 for m in [3.7]: for n1 in [0.20, 0.60, 0.8]: for n2 in [1.0, 1.5]: n3 = n2 e = supershape(paperx, papery, m, n1, n2, n3) plt.select_pen(pen) if pen < 6: pen += 1 else: pen = 1 plt.write(e) plt.select_pen(0) else: e = supershape(paperx, papery, 1.9, 0.8, 3, 3) io.view(e)

### HP 7475A Plotter: Exploring SuperFormula Parameters

Posted by Ed in Machine Shop, Software on 2015-08-03

Although the Superformula can produce a bewildering variety of patterns, I wanted to build an automated demo that plotted interesting sets of similar results. Herewith, some notes after an evening of fiddling around with the machinery.

Starting with the original Chiplotle formula, tweaked for B size paper:

`ss=geometry.shapes.supershape(8000,5000,5.3,0.4,1,1,point_count=1+10*1000,travel=10.001*2*math.pi)`

The first two parameters set the more-or-less maximum X and Y values in plotter units; the plot is centered at zero and will extend that far in both the positive and negative directions. For US paper:

- A = Letter = 11 x 8½ inch → 4900 x 3900
- B = 17 x 11 inch → 8000 x 5000

The `point_count`

parameter defines the number of points to be plotted for the entire figure. They’re uniformly distributed in *angle*, not in *distance*, so some parts of the figure will plot very densely and others sparsely, but the plotter will connect all of the points with straight lines and it’ll look reasonably OK. For the figures below, 10*1000 works well.

The `travel`

value defines the number of full cycles that the figure will make, in units of 2π. Ten cycles seems about right.

The four parameters in between those are the `m, n1, n2,`

and `n3`

values plugged directly into the basic Superformula. The latter two are exponents of the trig terms; 1.0 seems less bizarre than anything else.

Sooo, that leaves only two knobs…

With `travel`

set for 10 full cycles, `m`

works best when set to a value that’s equal to `prime`

/10, which produces `prime`

points around the figure. Thus, if you want 11 points, use `m=1.1`

and for 51 points, use `m=5.1`

. Some non-prime numbers produce useful patterns (as below), others collapse into just a few points.

The `n1`

parameter is an overall exponent for the whole formula in the form `-1/n1`

. Increasing values, from 0.1 to about 2.0, expand the innermost points of the pattern outward and turn the figure into more of a ring and less of a lattice.

So, for example, with `m=3.1`

, setting `n1= 0.15, 0.30, 0.60`

produces this pattern with 31 points:

Varying both at once, thusly:

`(m,n1) = (1.9,0.20)=green (3.7,0.30)=red (4.9,0.40)=blue`

produces this pattern:

Yeah, 49 isn’t a prime number. It’s interesting, though.

Note that `n1`

doesn’t set the absolute location of the innermost points; you must see how it interacts with `m`

before leaping to any conclusions.

It’s still not much in the way of Art, but it does keep the plotter chugging along for quite a while and that’s the whole point. I must yank the functions out of the Chiplotle library, set my default values, add one point to automagically close the last vertex, and maybe convert them to polar coordinates to adjust the magnitude as a function of angle.

Yes, that poor green ceramic-tip pen is running out of ink after all these decades.

## Blowback