Wednesday, December 25, 2019

New Life For An Old Radio

Back before I got into 3D printing, I used to collect and restore old vacuum tube radios and phonographs.  I was particularly fond of wood cabinet table-top radios from the 1930s because they often used very beautiful, artistically bent plywood for the cabinets, sometimes with exotic wood veneers. The circuits were not too hard to repair once you learned which type of parts failed the most.

Believe it or not, before TV became popular, and for a little while after, there used to be shops where people actually earned a living by selling and repairing radios.  Those shops used to subscribe to manual publishers that provided schematics and tuneup instructions for new radios as they were released by the manufacturers.  The two main service manual companies, Rider's and Beitman's, produced multiple volumes of radio repair information, all arranged by manufacturer, model, and date.  I have a couple volumes of the old paper manuals in a box somewhere.  You can easily find them on the web- here's a link and here's another, and you can download a full set of the Beitman's manuals via the internet archive, here (finally, something you can download legally with bittorrent!).


One of my favorite restorations was a Zenith 5S-127 radio made in 1937.  It has a large round dial of a type that is very popular with old radio collectors.  IRIC, I paid about $35 for it at an auction about 25 years ago.






When I restored old radios, I would restore the electronics first, then the cabinets.  Repairing the electronics was sometimes a little difficult and tedious, so I used the cabinet restoration as a motivator to keep going on the electronics.


Some of the service info for the radio from the Rider's manual.

A schematic diagram from the Beitman's manual.

A schematic from the Rider's manual.


My restorations weren't museum quality- I didn't try too hard to preserve the look of the original components and chassis.  I was more concerned with restoring the function of the electronics, and then getting the cabinets looking nice again.



Failures


Old radios had all sorts of parts that were prone to failure.  Electrolytic capacitors in power supplies often dried up and shorted or just stopped working as capacitors.  There were also a lot of paper and metal foil capacitors, usually dipped in beeswax, that would fail shorted because the paper would decompose.  They used a lot of carbon composition resistors that would drift upward in resistance over time.  That would cause the bias voltages on tubes to drift out of range and the radio would quit working properly.  Tubes would fail by becoming "gassy", developing shorts, or just burn out.  Even wire would fail because it had cotton insulation that would get chewed on by insects or rodents, or rubber that would harden and crack.  Speakers were often destroyed when something poked through the cloth cover and tore the paper cone.  Water damage often wrecked speakers and cabinets if the radio was stored in a barn or basement.


It's pretty easy to find modern replacements for all those parts that will last a lot longer than the originals did.  Modern electrolytic capacitors are made better, but will still probably always be the most likely parts to fail.  Old radios from the 20's sometimes used electrolytics caps that were just a couple uF.  Those and the paper and foil type caps can be replaced with non electrolytic mylar film caps that will probably last hundreds of years. Carbon composition resistors can be replaced with modern thin film or metal film resistors. Wire can be replaced with modern stuff that uses PVC insulation, but I often use PTFE (Teflon) insulated wire that I expect to last a lot longer.


Transformers can be hard to replace, and sometimes speakers are also tricky, especially those that had electromagnets instead of permanent magnets.  The magnet coil in the speaker was often used as a choke in the power supply, so if you replace the speaker with a modern, permanent magnet type, you have to keep the old speaker's magnet wired in to keep the power supply working properly.



Tubes


Tubes are still relatively easy to replace - they were made by the millions, but you need a tester to check them.  Back when I was doing radio restorations, I found a military surplus TV-7 tube tester at a swap meet for $50.  Shortly after I bought it, tube mania hit the audio world and suddenly tube testers were in big demand by tube audio fanatics. Those people have deep pockets, and they drove the prices up, so now my TV-7 is worth about $800.


You can still find reasonably priced tube testers that can test commonly used radio tubes, and you can still find reasonably priced tubes to test. 



Dials


One of the ways that radio manufacturers tried to distinguish their radios from the competition was to use fancy dials that sometimes used complex arrangements of pulleys and cords (and even motors) to move the dial pointer when you turned a knob or pushed a button.  Zenith radios are very popular with collectors because they really went all-in on the dial design. 


Here's a video of one of Zenith's "shutter dial" console radios that can sell to collectors for thousands of dollars:




And here's the high point of Zenith radio design, a 1935 Stratosphere console with 16 tubes!






Dial Belt Replacement


My Zenith radio used a cloth belt that was somehow rubberized, and of course, was broken when I got the radio.  I replaced it using the common technique at that time (20 years ago, not 1937)- I cut an o-ring to the appropriate length and glued the ends together.  I think it lasted a couple months.


The radio has been sitting on a shelf in my office for years, gathering dust, staring at me, and daring me to try to repair the dial again.  Today, I could resist no longer, so I took the radio apart and measured the o-ring, then drew a replacement in CAD:



CAD rendering of the belt.  Do you think you could handle something like this?  3mm wide, and 1.2 mm thick, 71mm inside diameter.
... then I sliced it:
Yeah, a pretty complicated slice, too.  Not for beginners!
...and printed it using TPU filament.  It took three attempts to get the size just right.  Once I had the belt fitting properly, I dusted off the chassis and reassembled the radio.  TPU is good for this application because it is flexible and will stretch a little under tension.  It is super strong and will not break, ever.



Printed TPU belt (orange) mounted on the radio.  The radio is geared so that turning the tuning knob turns the tuning capacitor slowly and moves two dial pointers at the same time.  Pay no attention to the dust on the chassis.
When I powered it up I found that it was working quite well and the dial calibration was even pretty accurate.  The whole process probably took about an hour.

3D printing can be used to replace all sorts of hard or impossible to find mechanical parts in old radios.  Knobs, pushbuttons, dial parts, etc., can all be replaced with printed replicas. 


Maybe it's time for me to start restoring old radios again.  The purists who go for museum quality restorations will never approve, but us regular folks, who want the radio to look and work like new, won't mind if there are some replacement parts inside.

Thursday, December 12, 2019

Generating Sand Table Patterns Using Sandify

Update:  Sandify now minimizes edge and corner motion.  There's no need to use trimify2x.pl to clean up the gccode!  There are two levels of optimization.  The first, which is automatically applied, preserves the order of the pattern generation as Trimify2x.pl did. The second level reduces edge and corner motion further by reordering the sequence of the lines in the drawing, which can have an impact on the way the final drawing looks. That level of optimization has to be activated by checking the "try to minimize perimeter moves" box on the "machine" definition tab in Sandify.

At the end of this post there's a link to the Trimify2x optimized pattern files presented here. I suggest you just grab the parameters from the patterns you're interested in and regenerate the gcode. Sandify's optimization is better than trimify2x.


Now back to the original post:


I recently posted about a program I wrote to optimize sand table patterns generated by Sandify.  If you're one of the 20 or so people on the planet who has built a sand table and uses Sandify to generate patterns for it, you may wonder why I wrote that program.  Why is there a need to "optimize" the pattern files?

If you have a square or round table, maybe you use sandify to generate patterns for it that look a lot like spirograph drawings, etc.  Maybe you limit the size of the patterns to something close to the size of your table.  In that case, there won't be a lot of excess motion around the edges of the table, and not much need for an optimizer that cuts out that type of motion.



I happened to build a table that is rectangular, with a drawing area of 710 x 1600 mm.  If I restricted patterns to fit in the center of the table, say 710 mm in diameter or so, it would leave a lot of empty space on the table.  So, from the start I have been making patterns that will fill up the entire table surface which means that they inevitably have a lot of motion at the edges of the table, because of the way Sandify works.

Mishegoss3.gcode

Shortly after I built the table, I generated patterns that were 30-40 cycles long and would take 10-20 minutes to finish and put together sequences of those patterns that would run for hours.  When you run patterns that way, you can usually see residue of the previous pattern in the new pattern drawn on the table.  Sometimes it doesn't look very nice.  I threw in erase patterns after every few patterns in the sequence, but it still wasn't satisfactory.

I started experimenting with patterns that ran for hundreds of cycles and often took over an hour to draw.  Those patterns often drew over parts of the pattern they drew earlier, but in this situation, it usually looks good because the new part of the drawing is similar to the old part and the transition is smooth.  If the pattern is going to take an hour to finish and it keeps drawing over itself, it looks really different every 10 minutes.  The problem with using high cycle count patterns was the amount of time the ball was spending at the edges of the table.  It's pretty boring to watch that type of motion because it isn't really contributing to the drawing.

That's what got me thinking about optimizing the patterns by stripping out all the unnecessary edge and corner to corner motion.  Now that the optimizer is written and working, I can generate patterns with very high cycle counts that extend well beyond the boundaries of the table knowing that the optimizer will take out all the "trash".

There are a few things that I do for every pattern.  I always home the machine before each pattern starts.  That leaves the ball at the lower left corner in all the images below.  I usually start the patterns along the bottom or left edge in the patterns below.  Sometimes that works out OK, but sometimes I have to save the pattern in reverse to get it to do that.  Once in a while, if a pattern wants to start along the top or right edge I will manually edit the file and add a couple G01 commands to move the ball to the starting point along those edges without crossing the table and leaving a track in the sand.

When you play with all the settings and let the patterns grow beyond the boundaries of the table, you can find some really interesting stuff:


110219_3.gcode
One technique I use a lot is to start with a very large basic shape, then set the grow step to a negative value.  That will cause the pattern to get smaller with each cycle.  If you use a high enough cycle count, the pattern will shrink to zero and then start expanding outward again, but flipped in orientation.

110219_4.gcode

A lot of the zig-zag lines come from playing with the track length and size.

110219_5.gcode

Another source of interesting effects is to use a non integer number of sides or lobes for the different base shapes that are available.  Try a polygon with 4.3 sides or a star with 5.6 points.

110219_6.gcode

Very often the Sandify preview looks quite different from the finished pattern on the table. In the example below, the messy looking stuff on the right side of the Sandify preview turns into the vertically oriented lines in the drawing.

The green ball along the top edge is the starting point of the pattern, and the red ball along the bottom edge is the end point.  The green squiggly line is the "track".


110219_7.gcode  Notice the ball is at the top edge- I must have saved this pattern in reverse so that the start would be along the bottom edge.
Another example- a lot of the messy looking stuff at the upper right corner is some of the first stuff drawn.  If this drawing were done in reverse, the messy stuff would be overwriting the "neater" later stuff.



The wandering back and forth in the pattern below is caused by using switchbacks.

heart3_.gcode
110219_8.gcode



circles3.gcode



jazz1.gcode



loopy_2.gcode

Sometimes you get exactly what you'd expect from the Sandify preview:



mishegoss4.gcode
The green ball on the left edge, below, indicates the starting point of the pattern.  The red ball near the center is the end point.  The green thing near the center is the "track" that is responsible for the waviness of the lines.  This pattern was saved as a reverse pattern so the start and end would be this way instead of the opposite.  If it started near the center, the ball would have left a track in the sand going from the edge to the center to start the pattern and that track would have shown up in the pattern.


heartfuzz1.gcode


mishegoss5.gcode




mishegoss6.gcode



yikes.gcode

The left side of the next two patterns look the same in the Sandify previews, but one pattern was saved in the forward direction and the other in reverse.



rolo1.gcode




rolo2.gcode


rolo3.gcode

mishegoss1.gcode (not quite finished)

Some of these patterns look really good, even 3 dimensional, from an angle, too...


If you're interested in the settings I used to generate these patterns, you can DL a zip file, here, of the "clean" gcode (optimized by trimify2x.pl) files.  Each file contains a header with the parameters used to generate the pattern.  You can enter those parameters into the appropriate boxes at Sandify.org and regenerate the same patterns (but use the dimensions of your own table).  If you regenerate the patterns, the file size will be much larger than those files that have been optimized by trimify2x.pl.

Wednesday, December 11, 2019

Still More Sand Table Updates

Another Problem


The Spice Must Flow's new, quieter mechanism was great, but I kept having a problem with it.  It would run OK for a while, then the mechanism would bind and the pattern would shift while making a terrible noise.

Then I saw a link to something interesting while looking at the Duet forums:

https://www.machinedesign.com/motion-control/linear-bearings-understanding-21-ratio-and-how-overcome-stick-slip-phenomenon

When you read an article like this, it can be difficult to apply to a real world situation where you don't know any of the forces or the coefficients of friction.  But even without numbers, the article provides useful concepts.  For example, keeping friction as low as possible is best (duh!), and a "longer" bearing is better than a shorter bearing.  Minimizing slop in the bearing will keep the carriage from tilting and binding.

In The Spice Must Flow, the Y axis worked fine, and can even run an erase pattern consisting of mostly Y parallel lines at 1000 mm/sec without any issues.  But the X axis was still problematic.  On certain patterns, the machine would run OK for a little while and then suddenly the ball would abruptly change path accompanied by a horrible noise.  I took the sand box off the table and watched the mechanism run for a while to try to see what was happening.  I noticed that the magnet carriage would wobble as it moved and then tilt a bit right before the noise and path change.

I redesigned the magnet carriage yet again, going back to a printable design that uses eight PTFE inserts as bearings so the printed plastic never contacts the X axis guide tube.  Four of those bearing inserts are fixed and the other four have screws behind them that allow adjustment of contact pressure or play against the X axis tube.  The bearings are spread apart to increase the stability of the whole carriage on the tube.

The new carriage uses 3 mm diameter steel pins to anchor the belts, spaced to match the pulley spacing on the ends of the X axis to ensure the belts remain parallel to the guide tube.

The new magnet carriage design uses 8x PTFE inserts acting as bearings, and uses the same spring and magnet as all the previous designs.  Screws that push on the PTFE bearings allow adjustment of play


I milled a 15 x 8 mm teflon strip, then cut it into 7 mm thick pieces using a Japanese pull saw with very sharp teeth and narrow kerf.  If you've never used a Japanese pull saw, I urge you to try one.  You'll never go back.  I milled the individual blocks to final 5x8x15 mm dimensions.

The new magnet carriage printed in PETG.  Screws hold the cover down, and 

The two halves of the new magnet carriage design.  There are slots for eight 5x8x15 mm PTFE bearings, four of which have screws to adjust their contact pressure or play on the X axis tube.  3mm steel pins to anchor the belts fit into the round holes next to the bearings.

I installed the new magnet carriage and it almost fixed the problem.  The mechanism still occasionally stalled on certain patterns.  I concluded that the mechanism is operating right at the torque limit of the motors.  The 1:5 speed step-up made things run quietly but gave up a little too much torque.  I decided that I needed to get some of the torque back by reducing the speed step-up from 1:5 to 1:4.

The 1:5 speed step-up was accomplished by putting 80 tooth pulleys on the motors driving 16 tooth pulleys on a shaft that spins the 40 tooth drive pulleys.  There are two easy ways to convert a 80/16 1:5 ratio to a 1:4 ratio.  I could either use 80/20 pulleys or 64/16 pulleys.  The 80 tooth pulleys I used originally had 5mm bore and I did a poor job of boring them out to 6.35 mm and they wobbled a bit as they rotated so I decided to replace them with 64 tooth pulleys.  I ordered and waited in vain for them to arrive from China, so I replaced the 16 tooth pulleys with 20 tooth pulleys.  I updated the config file on the controller to reflect the new drive ratio by changing steps/mm from 128 to 160.  That got back enough of the motor torque that the mechanism now works reliably on every pattern I've thrown at it.  The noise level isn't much different than it was at 1:5. 

Another Noise


Once the mechanism was working reliably I went back to attacking noise and one I hadn't previously noticed became apparent.  The machine would make a clunking sound when motion along the X axis reversed direction near the center of the table. 

A little investigation found that the belt tension was deforming the frame members and causing the Y axis rails to bow outward.  The result was excessive slop in the fit of the X axis between the Y rails near the center of the table.  The clunking sound was caused by the entire X axis shifting between the two Y axis rails when the X axis reversed direction.  I cut a strip of plywood the width of the table, drilled and installed a couple screws and t-nuts then mounted it under the table between the two Y axis rails.  Offsetting the ends slightly pulled the Y axis rails back into parallel and allowed me to adjust the X axis bearings so there's no more clunking when the X axis reverses.

More Changes to Come


A future redesign of the X axis may include a spring on one of the bearings to keep the X axis stable, and larger pulleys using flanged ball bearings to get quieter, lower friction operation.

Saturday, November 9, 2019

Attempting to Automatically Clean a 3D Printer Nozzle Using a Wire Brush

I want to print more lamp shades using UMMD with clear ABS or PETG filament.  The only problem with these very large, long prints is that eventually, charred crud that builds up on the extruder nozzle deposits itself in the print.  I had a small, dense wire brush for nozzle cleaning from an old Stratasys printer, so I ran a couple experiments to try to use it to clean UMMD's nozzle.

After a couple quick measurements on the printer, I printed a bracket to hold the brush:

Printed bracket to hold the brush near the front edge of the bed.  The screw allows the height of the brush to be adjusted.


I heated up the nozzle and manually moved it back and forth over the brush.  The brush height is adjustable so I played with it a little to see if there was any particular setting that was better than any other.  It didn't seem to make much difference if I really buried the nozzle in the brush or just gently touched it.

Then I went into PrusaSlicer and modified the printer settings by adding some custom gcode that would run on every layer change.

The "After layer change G-code" tells the nozzle where the wire brush is located.

I ran multiple test prints (all ABS) to see if I could get the brush to clean the nozzle but found it just left blobs on the nozzle that eventually found their way into the print.  I played with it quite a bit and couldn't find any combo of zig-zag path and brush height that would get the nozzle clean every time.  Sometimes it did manage to dislodge a blob of molten plastic, but the nozzle picked it back up again on its next pass over/through the brush.

Here's some slo-mo video of the nozzle going through the brush.  You can see blobs of plastic on the brush and nozzle before it goes into the brush, and one gets picked up by the nozzle as it leaves the brush.


Better video of 3D printer nozzle encounter with a wire brush. from Mark Rehorst on Vimeo.
Nozzle brush in slow motion from Mark Rehorst on Vimeo.

A little of the plastic sticks to the brush, but that blob may get picked up by the nozzle on a future pass through the brush, and end up on the print.

Not my idea of a clean nozzle...


I don't know how or if Stratasys actually makes this work, but I can't seem to get it to work.  Maybe the shape of the nozzle is the problem- the brush came from a stratasys printer that had very shallow, rounded nozzles, not the sharp, pointy type that fit the E3D hot-ends.

Maybe a motorized spinning brush will work if it spins fast enough to fling the plastic it scrapes off the nozzle away so that it doesn't just redeposit on the nozzle and eventually the print.

Maybe I'll come back to this later, until then, score one for entropy...

If anyone has any success with this sort of thing, I'd like to hear how you make it work.

Monday, November 4, 2019

Trimify2x: a Post-Processing Program to Improve Sandify Sand Table Patterns

Update 5/3/20:


Sandify now contains code that will minimize sand table perimeter travel, so my Perl program, described below, is now obsolete.


Sandify now has edge motion minimization built in, and automatically applied.  If you use the switch in the green rectangle, above, additional minimization that ignores the pattern sequence will be applied. I don't use that for the sand table, but it could be useful for a machine that uses an airbrush to paint patterns on walls, floors, etc.


Original content of this post:


The Spice Must Flow sand table is one of my favorite projects.  It uses a magnetically driven steel ball to draw patterns in sand.  The patterns that drive the table are stored as gcode files generated by a great program called Sandify.


A pattern created using Sandify, drawn on The Spice Must Flow.


I like to run the table fast, typically with maximum speed set to 500 mm/sec, so that it's interesting to watch as it draws patterns.  Here's a short video of it erasing at 1m/sec:


https://drive.google.com/open?id=1DZnheylNLAJRt0Zm-cQdBcHqBilZBN_8


Many people who have seen it say that they could stand and watch it all day, and I have to agree.  It's mesmerizing.  I've been working on making the mechanism run quietly at 500 mm/sec and largely succeeded, but there was one remaining problem.


Something I noticed almost as soon as the table was working over a year ago was that when the pattern being drawn exceeds the size of the table, the ball zips around on the edges of the table, sometimes back and forth along one or two edges, for quite a while before it gets back to drawing lines on the table. It's a problem because it takes a lot of extra time, increases wear on the mechanism, and it's not nearly as interesting to watch as lines being drawn on the table.


Here's a video that shows some of the wasted back-and-forth motion along the edges (watch from about 0:50 to 1:30):


The Spice Must Flow from Mark Rehorst on Vimeo.


As in a 3D printer, the ball's motion on the sand table is subject to acceleration and deceleration.  That means that short line segments never reach the maximum firmware-allowed speed.  All that crawling around on the edges of the table can really slow things down because it is very often short back and forth motion before the ball finally goes back out on the table and starts drawing in the sand.


I decided to write a program that will read the Sandify pattern file and strip out the lines that have the ball running back and forth along the edges.  The edited version of the pattern file will generate the exact same pattern with minimal time wasted along the edges of the table.




Sandify Pattern File Structure


The Sandify pattern files are just text files that start with a header that includes the file name, the parameters that are used by the Sandify program to generate the patterns including the dimensions of the pattern.  The rest of the file is the actual pattern in the form of a long series of G01 commands (basic gcode) with X,Y coordinates.


Here's a typical, but shortened, Sandify pattern file:

; Created by Sandify

;   https://jeffeb3.github.io/sandify/

;   Sandify Version: 0.1.3

;   Machine Type: Rectangular
;     MinX: 5 MaxX: 710 MinY: 5 MaxY: 1570
;   Content Type: Shapes
;     Starting Size: 800
;     Offset: X: 382 Y: 261
;     Selected Shape: Polygon
;       Polygon Sides: 4
;     Number of Loops: 165
;     Spin: true
;       Spin Value: -4
;       Spin Switchbacks: 0
;     Grow: true
;       Grow Value: -2
;     Track: true
;       Track Count: -37
;       Track Size: 0.8
;       Track Grow: false
;   Path Reversed: false

; filename: 'square1.gcode'

; BEGIN PRE

G28 Y
G28 X
G01 F30000; END PRE
G01 X710.000 Y1570.000
G01 X154.163 Y1570.000
G01 X153.366 Y1570.000
G01 X132.599 Y489.079
G01 X710.000 Y460.418
There are about 1100 of the G1 command lines (this is a very small pattern file) and then the file comes to an end:


G01 X5.000 Y587.353
G01 X5.000 Y36.396
G01 X5.000 Y5.000
G01 X294.080 Y5.000
G01 X710.000 Y5.000
G01 X710.000 Y1570.000
G01 X5.000 Y1570.000
G01 X5.000 Y705.184

; BEGIN POST

G04 S60
; square1.gcode; END POST

The line highlighted in yellow lists the dimensions of the pattern and will be useful later...
The patterns that move the ball are always a series of G01 commands which makes it easy to recognize those lines.  Each line such as "G01 X738.000 Y973.789" consists of a command (G01 - equivalent of a go-to statement) and coordinates of the point to which the ball should move (738.000, 973.789) from wherever the previous command left it.  If MinX=5, G01 commands that move the ball to/along the left edge will always contain X5.000, likewise the other axes.


After months spent thinking about how to process the files in a single pass, nothing was done. It seemed to require detecting all the possible transitions from one corner or edge to another, etc., and was a bit overwhelming. I was familiar with this uncomfortable state from my engineering days- it's the dreaded "analysis paralysis" in which one has too much information and too large a task to do anything.  



Stage 1

One day, after generating a particularly nice pattern for The Spice Must Flow (see photo above), I became really annoyed with the amount of time the ball spent moving around at the edges of the table.  I decided to try to manually edit the pattern file to cut that stuff out.

I looked for sequences of statements where the ball just went back and forth along an edge of the table.  What I realized was that those sequences are very easy to spot visually, so editing them out was also very easy.  I manually edited a couple pattern files (about an hour each!), stripping out the excess edge movement, first on one edge, then the next, etc., then ran the edited files on the table to see if they really improved performance.  Oh yeah!  The ratio of productive drawing time to useless edge movement was increased dramatically.


Since the files consist mainly of G01 commands, all I had to do was look for sequences like the one highlighted in blue and yellow, where X5.000 represents the left edge of the pattern:


G01 X15.000 Y23.000

G01 X5.000 Y100.000
G01 X5.000 Y230.000
G01 X5.000 Y110.000
G01 X5.000 Y321.000
G01 X5.000 Y16.000
G01 X15.000 Y21.000

and transform it to this by chopping out the stuff in yellow:


G01 X15.000 Y23.000

G01 X5.000 Y100.000
G01 X5.000 Y16.000
G01 X15.000 Y21.000

Some of those sequences are 100 statements long, and there may be hundreds or even thousands of those sequences in any given pattern file.  NotePad++ made it particularly easy to find the sequences by using the Search > Mark > Mark All function.


Sometimes the way out of analysis paralysis is to just start working on a piece of the problem and see where it takes you.  Buoyed by my new-found confidence in my ability to make an improvement, I decided to try to automate the edits I had done manually. A little research pointed me at Perl, a programming language specifically developed to process text files. I didn't know anything about programming in Perl, so I got a couple books and used some on-line searches.  As I studied the books and ran small test files, I kept thinking about how to scan the Sandify pattern files, what needed to be kept, and what could be thrown away.  It took about 4 days to get the first stage of the program working.


The first thing the program does is locate the statement in the header that contains the maximum and minimum values for each axis in the pattern. Next, it starts looking for and trimming sequences along pattern edges.


It's actually pretty easy (in hindsight, isn't everything?) to find those sequences that contain multiple statements along each edge- just examine three lines at a time and write the second line to the output file unless all three lines contain the edge value being checked.  That ensures that single and double instances of an edge statement pass through to the output file, but if there are three or more in sequence, only the first and last are written to the output file.  For more details, download the program and take a look at it.  I was very liberal with comments so it should be easy to see what's going on.


There is probably a way to have the program clean up all four edges in a single pass using an array, but I'm not much of a Perl programmer, so I wrote separate passes for each edge value, just as I had manually edited the files, creating intermediate files along the way and then deleting them just before the end of the program.


Processing the pattern files this way leads to a reduction in the file size (not that it matters much), a significant speed up in pattern completion, and much less boring edge motion.



Stage 2


The patterns I generate for the table frequently start with a very large base shape offset beyond the table's boundaries, and shrink and rotate it as the pattern cycles, letting it get to zero size and then allowing it to grow after that.  As the pattern grows it overwrites the stuff that was drawn as the pattern was shrinking.  That process leads to beautiful and interesting patterns that look different every few minutes throughout the drawing process but often contain a lot of unproductive, boring, edge and corner to corner motion.

Once all the excess edge motion was trimmed out, the pattern files now contained sequences of statements with corner coordinates.  Those sequences contain the remaining unproductive motion. The trimming program didn't add those corner statements - they were in the file the whole time, but buried among the useless motion along the edges. Now it was time to minimize unnecessary motion through the corners.  


If the ball leaves the table on one edge and reenters the table on the opposite edge, there are two paths it can take along the edges, both going through two corners of the table/pattern.  One way will usually be shorter than the other.



Two-corner path with the ball exiting the table on the left edge and reentering along the right edge.  Blue square is the edges of the pattern/table. Green is the ball.  Orange is the time wasting long path, black is the more desirable short path. 

Likewise, if the ball leaves the table along one edge and comes back on the table along an adjacent edge, there are two paths it can take.  One path goes the long way through three corners, and the other, shorter path, goes through only one corner.



A 3-corner path.  The path through the 4th corner (black) is always shorter than the path through 3 corners.


If the ball leaves the table on one edge and returns to the table along the same edge, sometimes it takes the long way between the two points and travels through all four corners of the table.  That's a silly waste of time!



A 4-corner path orange), a big time waster.  Simply deleting the 4-corner sequence forces the shorter (black) path.



Of course, sometimes the ball has to pass through a single corner and that's OK (the alternative is to take the longer, 3-corner path around the table- nope!).  There's no wasted motion there.


The second part of the optimizer program searches for the sequences of corner statements illustrated above, leaving single corners alone, checks two corner sequences and replaces the longer path with the shorter path through the other two corners, replaces three corner sequences with the shorter path through the fourth corner, and deletes sequences of four corners because they start and end on the same edge.


In the case of multiple loops around the table, as in the 7-corner path (orange) illustrated below, the first four corners will be deleted, and the remaining 3-corner path will be converted to the 1-corner path (black), greatly reducing the time wasted zipping around the edges of the table.



A 7-corner path (orange) that wastes a lot of time gets converted to the 1 corner path by first deleting the first four corners and then converting the remaining 3-corner path to the much faster 1-corner path (black).

I've run many test files and pattern and the trimming program seems to work reliably.  There's a minimal amount of wasted time zipping around the edges of the table now, which makes it much more interesting to watch as the patterns are drawn.


You can get the trimify.pl post-processing program here...


(((note- there's an updated version linked near the bottom of this post)))))


You'll need Perl to run the program.  I used Strawberry Perl for Windows, but the program should work with any recent Perl 5 on any OS.  To run the program, just open the Perl command line app, point it at the directory where you keep the target Sandify pattern file, and type "perl trimify.pl" Type in the input file name when it asks, hit "enter", and you'll find the trimmed pattern in a file called clean.gcode.  That's it.



A Sample of the Results


The picture at the top of this post is a pattern file that starts with a heart shape that is larger than the table area.  With each cycle of the pattern, the heart shrinks a bit bringing it onto the table, first along only one edge.  With each cycle, the ball travels around the perimeter of the table including a little back-and-forth motion along one edge.  Here's video of the start of the pattern, as generated by Sandify:


Untrimmed pattern running on sand table from Mark Rehorst on Vimeo.



That looping around the table goes on for quite a while before it starts drawing anything at the opposite end of the table.


The next video is the start of the same pattern after it has been optimized using the trimify.pl.  There are essentially two optimizations seen here.  First, the back-and-forth motion along one edge is eliminated.  The second, and much more important optimization in this case, is that looping around the edges of the table is eliminated, greatly speeding up pattern generation and improving watchability of the table.


The same pattern with wasted edge motion trimmed out from Mark Rehorst on Vimeo.



This is just one example of the improvement that comes from trimming the Sandify pattern files using trimify.pl.  The original file, heartfuzz1.gcode was 359 kB long.  The trimmed file, heartfuzz1_clean.gcode is just 193 kB long.  The difference represents wasted motion along the edges of the table.  But how much wasted motion?  On my table, with acceleration set to 1000 mm/sec^2 and speed set to 500 mm/sec, the untrimmed file takes 59:20 to complete.  The trimmed file requires only 42:03 to complete the same pattern.  That's a difference of more than 17 minutes of the ball moving around the edges of the table, producing the exact same pattern.


I'm going to try to make a couple time-lapse videos of a pattern, one untrimmed and the other trimmed, and combine them into a single side by side video.  If and when I get it done, I'll post it here.




Making Sequences of Patterns


The RepRap Firmware that runs on the Duet controller boards has enabled the M98 gcode command.  That command allows you to create playlists of patterns to run on a sand table.  You put your pattern files (already cleaned by Trimify) on the uSD card on the controller board in the default /gcodes directory.  Then you create a sequence file and put it into the same directory.

The sequence file should look something like this:


M98 P"0:/gcodes/wipe0.gcode"

M98 P"0:/gcodes/heartfuzz1_clean.gcode"
G04 S90
M98 P"0:/gcodes/wipe0.gcode"
M98 P"0:/gcodes/mishegoss1_clean.gcode"
G04 S90
M98 P"0:/gcodes/wipe0.gcode"
M98 P"0:/gcodes/mishegoss2_clean.gcode"
G04 S90

This sequence will erase the table using the wipe0.gcode pattern, then run the heartfuzz1 pattern, then wait 90 seconds, then erase, then run the mishegoss1 pattern, etc.  You can make the sequence as long as you like.  You can even create files that call different sequences.  Unfortunately, there doesn't seem to be any way to automatically randomize the pattern sequence, so you'll have to do that yourself.


Name the sequence file something like "sequence_01.gcode".  You can then select that file to run using the web interface on the controller. 



UPDATE!  11/25/19


I have discovered that the "clean" pattern files occasionally still contain some useless motion along the corners/edges.  It seems to occur when the useless stuff in the pattern bends around a single corner.  Running Trimify a second time using the "clean" output file as input has fixed it in every case (which isn't many) I've seen.  I have run Trimify multiple times on some test files, and it has never damaged the files, so I will be modifying Trimify to automatically perform two passes on the input file and will post a link to the new version here as soon as it is done.



UPDATE!  11/30/19

The residue problem is now solved (I think), and I've made a couple additional tweaks to the program which is now called trimify2x.pl

The first change is that the output file is now named from whatever the input file is.  If you enter "mystars.gcode" for the input file name, the output file name will be "mystars_clean.gcode".  The second change is that program now makes two full passes over the input file and I believe completely eliminates any "residue" left from the first pass.


The compare plugin in notepad++ was extremely useful for checking operation of the program.  It compares and highlights differences between two files.  Here's a comparison of a sandify pattern called jazz2.gcode and the output of trimify called "clean.gcode" (click on the image and open in a new tab to make the text readable):



This is the notepad++ comparison of the original jazz2.gcode file (right) with the trimify output file clean.gcode (left).  All the stuff that's highlighted in green on the input file is gone in the output file.  Notice that line numbers 1570 and 1574 both contain G01 X710.000 Y5.000 and both end up in sequence in the output file.  
This is a comparison of the output from the original trimify - clean.gcode (left) and the output from trimify2x - jazz2_clean.gcode (right).  The useless motion that sends the ball to the corner of the table at X710.000 Y5.000 is now eliminated.  In this particular input file, that was the only change needed.  I have seen other pattern files with more of this sort of thing.
 In this particular example, the second pass of the maxx trimming is what takes out the duplicate and useless G01 X710.000 Y5.000 lines.  Trimify2x runs the whole process twice including all the edge and corner trimming and substitution.  I suspect that the corner trimming and substitution may never do anything on a second pass, but it was easy to include it so I did.

Trimify2x.pl is every bit as crude as trimify.pl.  I was going to try to set it up to loop but found it much easier to just append a copy of the original stuff to the end of the program, then make a couple minor tweaks to the input and output file names.


I am not planning on making any more changes to the program unless someone points out a bug.  Trimify2x.pl is located here.



Here's a better example of before and after trimify2x.  First, the original pattern straight out of Sandify:


Sandify pattern running on The Spice Must Flow sand table. from Mark Rehorst on Vimeo.


And here is the result of running trimify2x on that pattern file:

Sandify pattern file cleaned by Trimify2x from Mark Rehorst on Vimeo.

This particular pattern went from taking 2 hours down to taking only 57 minutes.  Over an hour was wasted at the edges of the table in the original pattern file.


This is what that entire pattern looks like when it's finished:





PS


Trimify (and trimify2x) may give unexpected results if you string together a bunch of pattern files and then try to optimize them in one go.  The program only looks for the MinX, etc., declaration statement once.  If you change the dimensions from one pattern to another, the optimizer will probably screw things up.  It's best to optimize the patterns individually, before stringing them together.


Trimify (and trimify2x) is every bit as crudely coded as you would expect a first-time Perl programmer's code to be.  There's no error checking, range limiting, etc.  If you give it bad input it will try to process it.  I have found that occasionally I get compiler warnings about a variable getting assigned a string instead of an expected numeric value.  I have looked at the files that cause that warning to be generated and can't see any reason for the warning.  The resulting trimmed output files look and work fine on the table, so I'm not  really motivated to try to track down the source.


Now that the excess edge and corner motion has been eliminated, the edges of the pattern are sometimes left looking a little ragged once the pattern is complete.  That can easily be fixed by simply sending the ball on a single trip around the edges of the table.