Tags

, , , ,

Like many people who cook, often the result is a pot full of something that needs to be transferred to one or more containers and then stored. Which leads to the obvious questions: How many containers of what sizes do we need?

This might seem off-topic for an engineering company, but there is a role for software to play in making this question easier to answer, and I think I’ve found a clever solution that might help others.

Some time ago I got frustrated by this, and made a simple chart of volume by depth of liquid in each frequently used pot in the kitchen. Usage was simple enough, just use anything as a dip stick to get a record of the liquid level, measure the liquid on the stick, and look it up on the chart. To make that easy to do, I stored the chart with the pans, and a tape measure in a drawer.

But the tape measure kept getting sticky, the chart accumulated stains, and I never did work out what volume units were most effective to use on the chart.

So I decided it was time to approach the problem a different way.

The Pan Ruler

A Pan Ruler is not a kind of tyrannical bread. Rather it is a dip stick with markings that directly read off the volume of a specific pot or pan.

The most direct way to make one would be to add a liquid to a pan, and make marks on a stick for each level. This has the advantage of being simple and direct, if slightly tedious. If your favorite cauldron has an odd shape, it might also be the only practical approach.

Instead, I will create a paper template and transfer the marks to a stick. A little bit of elementary algebra will allow computing the spacing of the marks.

Which leads to the next question: what marks?

Most of the time, we are drawing from a stock of disposable reusable containers. These come in several standard volumes and shapes. If we ignore the shapes (like the shallow square that isn’t quite right for either a sandwich or a slice of pie) that are not really suited to liquids, there are about five sizes we keep on hand, and about three that we use frequently.

So marking the stick in “containers” would make sense.

Our common containers (these are a store-brand generic, but similar styles are available from major brands as well) are:

large rectangle   8 cup   1 gal
salad bowl        6 cup   3/4 gal
medium rectangle  3 cup   3/8 gal
small rectangle   9 oz    9/64 gal
monkey dish       4 oz    1/32 gal

Given the pan’s diameter in inches, the depth in inches of a gallon is easily computed:

r = diameter / 2
A = pi * r * r
depth = Vgal / A

From the depth of a gallon, conversion to depth for each container size is just a simple matter of scaling.

Implementation

The plan is to use Lua, along with the CD library, to write a PDF file containing a template page for each interesting pot or pan. I could use a drawing tool to draw the template, but it will be much easier to write a few lines of code that compute the locations of marks along the ruler, and reuse that for each size of pot and container.

I use Lua for simple programming because it is powerful enough to express what I want while simple enough to make its development environment easy to keep on hand. For development, all you really need is Lua itself and a text editor. An editor that is aware of Lua such as SciTE is handy for syntax coloring. A full Lua IDE such as ZeroBrane Studio is nice to have but not strictly necessary.

The CD (Canvas Draw) library provides an abstract canvas on which to draw, and drivers to store the drawing in one of several file formats (including PDF) as well as interfaces to its siblings IUP for interactive user interfaces and IM for image processing.

A CD canvas has several coordinate systems we could use. The simplest is to use raw canvas “pixels” which get scaled to real world units with a parameter when the PDF file driver is loaded. We’ll scale the canvas so that there are 10 pixels per millimeter, which is close to 300 pixels per inch, and is way more precision than we need for to draw the rulers.

Code

As a one-off script, I haven’t bothered with lots of (read, “any”) command line parameters or other options. I think of the script as the source text of the PDF file it writes. So name it panruler.lua and it will write panruler.pdf when executed with Lua.

require"cdlua"
require"cdluapdf"

-- Generate a formatter that labels every
-- nth item.
function nths(n)
    return function (i)
        if i%n ~= 0 then return nil end
        return ('%.0f'):format(i/n)
    end
end

-- Make all the marks of a Pan Ruler template on a
-- canvas c, starting at x0,y0, with width w and height h,
-- and with specified tick spacing, label format and title.
function marks(c, x0,y0, w,h, spacing, label, title)
    label = label or nths(2)
    title = title or tostring(spacing)
    c:Foreground(cd.BLACK)
    c:Font("Courier", cd.PLAIN, 12)
    c:TextAlignment(cd.BASE_LEFT)
    c:Text(x0+5,y0, title)

    c:LineWidth(1)
    c:LineStyle(cd.CONTINUOUS)
    c:Line(x0,y0, x0,y0+h)
    local x,y = x0,y0+50
    local i = 0
    repeat
        local s = label(i)
        local tw = s and w or 0.9*w
        c:Line(x,y, x+tw,y)
        if s then c:Text(x+w+5,y, s) end
        i = i + 1
        y = y0 + 50 + i*spacing
    until y > y0+h
end

-- Draw all the elements of the Pan Ruler templates on a single
-- page. Include inch and cm rulers to make it easy to verify
-- print scale.
--
-- Assumes the pan is effectively a circular cylinder, and computes
-- the depth of one (liquid) gallon in that pan, then scales that
-- by the volumes of various practical containers to get mark spacing
-- suitable for each size of container.
--
-- The page coordinates are scaled to 10 pixels per mm, but the pan
-- dimensions are given in inches. This leads to lots of use of the
-- constant 254, 24.5 mm/inch. The other magic number is 231 cubic
-- inches per gallon.
function PanRuler(c, pan)
    local halfs, thirds, fourths, tenths = nths(2), nths(3), nths(4), nths(10)

    -- calculations for this pan
    local r = pan.diameter / 2
    local A = math.pi * r * r * 254 * 254
    local Vgal = 231 * 254  * 254  * 254
    local Dgal = Vgal / A
    local depth = 2540

    -- add a vertical ruler to the page, step X position
    local xr = 100
    local function addruler(step, fmt, label)
        marks(c,  xr,100, 100,depth, step, fmt, label)
        xr = xr + 200
    end

    -- reference inch and cm rulers for checking printer scale
    depth = 2540
    addruler(254/2, halfs, 'inch')
    addruler(50, halfs, 'cm')

    -- set of rulers for this pan and each container
    depth = math.min(2540, 1.25*pan.depth * 254 + 75)

    -- rulers in gallons and litres
    addruler(Dgal/4, fourths, 'gal')
    addruler(Dgal/37.8541, tenths, 'L')

    -- common containers
    addruler(Dgal/4, halfs, 'big')
    addruler(Dgal/8, thirds, 'bowl')
    addruler(Dgal/16, thirds, 'salad')
    addruler(9*Dgal/256, halfs, 'small')

    -- Add a text line, step Y position down, Starting
    -- from the upper right corner, allowing a 1cm margin
    local rm,tm = 2160-100, 2790-100
    local cr = tm - 100
    local function statline(s,n)
        cr=cr-50
        c:Text(rm, cr, ("%s: %5.2f in"):format(s,n))
    end

    -- Label the page with the pan name
    c:Font("Courier", cd.BOLD, 22)
    c:TextAlignment(cd.BASE_RIGHT)
    c:Text(rm, cr, pan.name)
    -- include some details about the pan
    c:Font("Courier", cd.PLAIN, 14)
    statline('Dia', pan.diameter)
    statline('Depth', pan.depth)
    statline('Gal', Dgal/254.)
    statline('Cup', Dgal/254./16)
    c:TextAlignment(cd.BASE_LEFT)
end

-- data verifier for a pan list entry
function Pan(t)
    return {
        diameter = t[1],
        depth = t[2] or 4,
        name = t[3] or '',
    }
end

-- list of pans that need rulers, with specified
-- diameter, depth, and name for each.
local pans = {
    Pan{5.5, 3, '1 qt Saucepan'},
    Pan{8,   5, '4 qt Saucepan'},
    Pan{7,   6, 'Tall Crockpot'},
    Pan{10,  6, '8 qt Soup Pot'},
}

-- PDF Canvas with page size set to US Letter scaled to 10 pixels per mm,
-- in a file named after this script.
local fname = arg[0]:gsub(".lua$",".pdf")
print("Writing "..fname)
canvas = assert(cd.CreateCanvas(cd.PDF, fname .. " -w216 -h279 -s254"))
for i=1,#pans do
    if i ~= 1 then canvas:Flush() end
    PanRuler(canvas, pans[i])
end
canvas:Kill()

The above was developed and tested with Lua 5.1 because I haven’t had a strong incentive to update to the latest version of the language. I don’t expect that porting to Lua 5.2 or Lua 5.3 would present any significant issues as I haven’t intentionally used any features that would cause a problem. A big source of inertia keeping me at Lua 5.1 on my Windows machines is that I haven’t yet sought the ideal replacement for the convenience of the now rather elderly Lua for Windows distribution.

Results

Running the script as shown produced panrulers.pdf which can be printed on US Letter paper, providing sets of ruler templates for two sizes of saucepan, a common crockpot, and a soup pot.

Note that the odds are pretty good that the pots and pans in your kitchen have different dimensions, but pots that are 7 to 10 inches in diameter seem to be fairly common.

Final Thoughts

The template pages can be used to transfer marks by hand to any food-safe stick, or could be laminated and used with any handy stir stick or spoon that is already in the bot.

Using a laser engraver to mark a set of sticks permanently is left as an exercise, but is an obvious extension of the idea.


If you have a project involving embedded systems, micro-controllers, electronics design, audio, video, or more we can help. Check out our main site and call or email us with your needs. No project is too small!

+1 626 303-1602
Cheshire Engineering Corp.
710 S Myrtle Ave #315
Monrovia, CA 91016

(Written with StackEdit.)