It’s always the same. “I’ll just hack out this quick program,” you think. And you get it to the point where it works and does the job you intended it to do. But then: “Maybe it would be nice if it also did this…”
And even when you’ve added all the functionality the program will bear, and your wife is yelling up the stairs, “It’s time to make dinner” or the dog is nodding towards his lead while tapping on his watch or it’s suddenly 3am when only a moment ago it was tea-time, you find yourself rationalising, “I’ll stop now, but first I’ll just move that bit of text one character to the right because it will look so much better”. But, of course, we all know that’s not the end of it.
Coding is addictive. You get wired-in, locked into the world of your code and there’s always one more little adjustment to make. You swear it’s the last, but it never is.
It was like that from the very beginning, from the moment I got my first Sinclair Spectrum and then, even more so when I upgraded to a BBC Micro with a 5.25in floppy disk drive. (The lengthy and tedious process of saving programs to cassette tape would sometimes be enough to overcome my obsession and make me crawl off to bed.)
And it’s still that way now. It’s just that it’s been a long time since I experienced that kind of flow where BBC Basic was involved.
So, having posted my blogs about recreating the first-ever program I wrote in BBC Basic, and how I’m using a DataCentre device so I can hack away at programs on either my BBC Master or the BeebEm emulator on my iMac, it won’t surprise you to learn that work on the depth of field calculator program didn’t stop there. Oh no.
I knew that this new version of the program wasn’t the same as the incarnation I’d originally coded on the Sinclair Spectrum and then the BBC Micro. I had a vague recollection of there being a much bigger table.
It occurred to me that I’d used this program – at least, the Spectrum version of it – to produce data for the publication I was working on at the time.
This was my first job in journalism, as a section editor for a partwork called The Photo, published by Marshall Cavendish. Back in the 1970s and early 80s partworks were very popular. They were weekly publications, resembling magazines, that you would collect and put in binders so that they built up into a very expensive encyclopaedia. And one of the characteristics of a partwork is that it has a finite run – if memory serves, The Photo ran for 96 issues.
Another characteristic of the partwork is that you can cover a specific subject only once. In the technical section that I ran, for example, once you’d published an article on, say, depth of field, you couldn’t revisit it later unless you could find a completely new angle and all new information. So by the end of the publication, we were running out of ideas. Then some genius had the brainwave that we would run ‘Data’ articles consisting of handy tables, hints and tips, glossaries and data sheets.
So my very first ‘proper’ program actually turned out to be useful. I could fill some space with depth of field tables.
I remember lugging my Spectrum, cassette recorder, tapes and associated cables into work and then going on a long search for a TV to plug into. Because these were the days when there wasn’t a screen to be seen anywhere, even in a publishing company’s offices. We hacked out our articles on manual typewriters. (Two or three years later I would work at Marshall Cavendish again; by this time I’d upgraded from Spectrum to BBC Micro Model B to a BBC Master Turbo and the office had a computer – a sole Sirius PC used by the secretary. ‘Desktop publishing’ was in the air but was yet to have significant uptake in periodical publishing.)
Finally, I tracked down a TV in the company board room – actually in another building – ran my program and dutifully noted down the output with pencil and paper. Yup, no printer.
It just so happens I still have a complete set of The Photo and so I dug into the last volume and, sure enough, there were the tables. But they weren’t as I remember.
We ran two tables: one shows depth of field figures but for close-up photography, with the axes being magnification ratios and apertures (below); and the other is a hyperfocal distance table with axes of focal lengths and apertures (above).
(If these terms are unknown to you, or you think you might enjoy the phrase “enter circle of confusion”, then see my original post.)
I have absolutely no memory of the close-up calculations. But the hyperfocal distance table makes sense. So I went back to my new program and started hacking.
Here’s the how the main screen looks now, with a little tarting up and an extra menu item:
And I’ve added that hyperfocal distance table:
REM >DOF2 REM Depth of Field Calculator REM Version 2 with hyperfocal distance table : MODE 128 REM --- MAIN GLOBALS --- flen% = 50: REM focal length, mm fstop = 8.0 : REM aperture circ = 0.03 : REM circle of confusion, mm dist = 5.0: REM focus point in meters DoFnear = 0.0 DoFfar = 0.0 : REM --- Set up arrays for tables --- distances% = 18 : REM max index for dists array DIM dists(distances%) FOR I% = 0 TO distances% READ dists(I%) NEXT stops% = 9 : REM max index for fstop array DIM stops(stops%) FOR I% = 0 TO stops% READ stops(I%) NEXT flengths% = 9 DIM focals%(flengths%) FOR I% = 0 TO flengths% READ focals%(I%) NEXT : REM --- Main Loop --- done% = FALSE REPEAT CLS PROCdisplay sel$ = CHR$(FNmenu) IF sel$ = "Q" THEN done% = TRUE IF sel$ = "A" THEN fstop = FNsetparam(sel$) IF sel$ = "C" THEN circ = FNsetparam(sel$) IF sel$ = "D" THEN dist = FNsetparam(sel$) IF sel$ = "F" THEN flen% = FNsetparam(sel$) IF sel$ = "H" THEN PROChypertable UNTIL done% = TRUE CLS END : REM distances for table DATA 0.5,0.7,1,1.5,2,3,4,6,8,12,15,25,30,50,75,100,150,200,500 REM f-stops for table DATA 1.4,2,2.8,4,5.6,8,11,16,22,32 REM focal lengths for hyperfocal distance table DATA 20,24,28,35,50,85,105,135,200,300 : DEF FNsetparam(ch$) CLS IF ch$="A" THEN prompt$ = "aperture":min = 1:max = 130:default=fstop IF ch$="F" THEN prompt$="focal length":min=8:max=1000:default=flen% IF ch$="C" THEN prompt$="circle of confusion":min=0.025:max=0.05:default=circ IF ch$="D" THEN prompt$="subject distance":min=0.1:max=1000:default=dist prompt$ = "Enter " + prompt$ PRINT TAB(5,6);prompt$ INPUT TAB(LEN(prompt$) + 6,6);">"param result = param IF param < min THEN result = min IF param > max THEN result = max IF param = 0 THEN result = default =result : DEF PROCprinthead(label$) pad% = (80 - LEN(label$)) / 2 extra% = LEN(label$) MOD 2 COLOUR 0 : COLOUR 129 PRINTTAB(0,0);SPC(80) PRINTTAB(0,1);SPC(pad%);label$;SPC(pad% + extra%) PRINTTAB(0,2);SPC(80) COLOUR 1 : COLOUR 128 ENDPROC : DEF PROCdisplay y% = 4 : REM Y position for text PROCprinthead("DEPTH OF FIELD CALCULATOR") PROCcalc(dist) @% = &01000006 PRINT TAB(1,y%);"Focal Length: "; flen%; "mm" @% = &01000004 PRINT TAB(23,y%);"Aperture: f/"; fstop @% = &01020105 PRINT TAB(41,y%);"Subj Dist: "; dist; "m" @% = &01020305 PRINT TAB(67,y%);"CoC: "; circ; "mm" y% = y% + 1 @% = &01020106 PRINT TAB(1,y%); "DoF Near: " ; DoFnear ; "m" PRINT TAB(23,y%); "DoF Far: " ; FNfarstr(DoFfar) PRINT TAB(41,y%); "Hyperfocal distance: "; FNhyper(flen%,fstop); "m" y% = y% + 2 x% = 19 PRINT TAB(x%,y%); "dist"; TAB(x%+17,y%); "near"; TAB(x%+36,y%); "far" PRINT TAB(x%,y%+1); "----"; TAB(x%+17,y%+1); "----"; TAB(x%+32,y%+1); "-------" y% = y% + 2 FOR I% = 0 TO distances% PROCcalc(dists(I%)) @% = &0100003 PRINTTAB(x%,y%+I%), dists(I%); "m" @% = &01020108 PRINTTAB(x%+12,y%+I%)DoFnear;"m" fardist$ = FNfarstr(DoFfar) PRINTTAB(x%+31,y%+I%); SPC(8-LEN(fardist$)); fardist$ NEXT ENDPROC : DEF FNfarstr(far) IF far > 0 THEN far$ = STR$(far)+"m" ELSE far$ = "-inf-" =far$ : DEF PROCcalc(dst) fdist = dst * 1000 : REM work in mm fact = fdist * circ * fstop DoFnear = fdist / (1 + (fact / flen% ^ 2)) DoFnear = DoFnear / 1000 DoFfar = fdist / (1 - (fact / flen% ^ 2)) DoFfar = DoFfar / 1000 ENDPROC : DEF FNhyper(focl%,ap) = ((focl% ^ 2 / (circ * ap)) + focl%) / 1000 : DEF PROChypertable CLS y% = 4 : REM Y position for text PROCprinthead("HYPERFOCAL DISTANCES") PRINTTAB(5,y%);"f/" FOR I% = 0 TO stops% @% = &01000006 PRINTTAB(7+(I%*7),y%),stops(I%) NEXT y% = y% + 1 PRINTTAB(3,y%);"mm" FOR I% = 0 TO flengths% @% = &01000004 y% = y% + 1 PRINT TAB(1,y%),focals%(I%) @% = &01020106 FOR J% = 0 TO stops% PRINT TAB(7+(J%*7),y%),FNhyper(focals%(I%), stops(J%)) NEXT NEXT PRINTTAB(2,y%+1);STRING$(74,"-") @% = &01000004 PRINTTAB(1,y%+2),flen% @% = &01020106 FOR I% = 0 TO stops% PRINTTAB(7+(I%*7),y%+2),FNhyper(flen%, stops(I%)) NEXT @% = &01020305 PRINTTAB(2,y%+5);"Circle of confusion: ";circ;"mm : Distances in metres" PRINTTAB(2,y%+7);"Press <RETURN> for main screen" REPEAT UNTIL INKEY -74 ENDPROC : DEF FNmenu PRINT TAB(1,29);"(F)ocal length : (A)perture : (D)istance : (C)ircle of Confusion" PRINT TAB(1,30);"(H)yperfocal distance table : (Q)uit" opt% = FALSE REPEAT key% = INKEY(0) REM convert to uppercase if necessary key% = key% AND &DF IF key%=65 OR key%=67 OR key%=68 OR key%=70 OR key%=81 OR key%=72 THEN opt%=TRUE UNTIL opt% = TRUE =key%