My Mac 40th anniversary draft

I assume that most of you have already listened—at least in part—to the latest episode of Upgrade, the 40th Anniversary of the Mac Draft. Any podcast with both John Gruber and John Siracusa is going be longer than your average Upgrade; I’m impressed they brought it in at 2½ hours. The whole show is good, but the Hall of Shame section is just hilarious.

I don’t have the sort of encyclopedic Mac knowledge the draft participants—Shelly Brisbin, Stephen Hackett, Dan Moren, Jason Snell, and the aforementioned Johns—have, but I do want to mention what my picks would be for two of the categories: first Mac and best/favorite Mac software.

My first Mac slips in between Siracusa’s original 128k Mac and Shelly’s Mac Plus.1 It was the 512k Mac, popularly known as the Fat Mac because it had so much memory. I bought it in early 1985, when I was was in grad school, and wrote my thesis on it. I took advantage of a discount through the University of Illinois, and added on an external diskette drive, an ImageWriter printer, and the cool canvas carrying bag, in which I hauled the Mac between my apartment and office—just like in Apple’s marketing literature of the time.

I tend to think of the Fat Mac as the first truly useable Mac, mainly because it had enough RAM for apps to have some breathing room. Even better, Switcher, which allowed you to run multiple apps at once before MultiFinder, gave the Fat Mac a giant advantage over the original. I was teaching a course in the spring semester of ’85 and made up my tests on the Mac, making the illustrations in MacPaint and then switching over instantly to paste them into a MacWrite document.

As for my best/favorite Mac software, I’m going to do the traditional podcast draft thing and talk about how I was torn between two apps, thus giving me effectively two picks. But before that, I need to point out that the choices made by those on the podcast are all objectively better than mine. I’m deliberately choosing apps that are weird but were very useful to me in the early years. They also showed off the Mac’s user interface to great advantage.

The app I nearly chose was Macintosh Pascal, written by THINK Technologies (who went on to publish Lightspeed Pascal and Lightspeed C, which had the greatest programmer slogan ever: “Make mistakes faster”) and distributed by Apple.

Macintosh Pascal

This was an interpreted Pascal, so the code ran relatively slowly, but the lack of a compilation step made the edit/debug/edit/debug cycle go quickly. On the editing side, Macintosh Pascal was my introduction to syntax highlighting, which you can see (sort of) in the screenshot above. This was the Mac’s font styles used to great effect. Debugging was also done visually by dragging a stop sign to the line of code that needed a breakpoint. These are things we take for granted now, but they were a revelation to me in 1985.

So with my not-quite-choice out of the way, here’s my fave: Claris CAD.

Claris CAD

Am I kidding? No. This was a fantastic program for the kind of drawing I was doing back in the early ’90s and am still doing today. It wasn’t drafting, per se, but it did involve the kinds of construction typically done on a drafting table: lines tangent to circles, circles tangent to lines, lines perpendicular to other lines, and so on. The cursor would snap to features of the drawing and show a preview of how the next item would be drawn. It nearly always did exactly what I wanted.

And this is not just hazy nostalgia; I thought Claris CAD was an amazing program at the time. It was simply more in tune with the drawing of manufactured objects than were (and are) apps more obviously based on Bezier curves.

These apps were not only great, they are perfect for a draft. No one would snipe them.


  1. I also had a Plus, but it was my second Mac and was bought for me by my employer. 


A Typinator snippet for plotting

My last post ended with this graph:

Friction comparison graph

It was made, as most of my graphs are, using Python and Matplotlib. Here’s the code that did it:

python:
 1:  #!/usr/bin/env python
 2:  
 3:  import matplotlib.pyplot as plt
 4:  from matplotlib.ticker import MultipleLocator, AutoMinorLocator
 5:  import numpy as np
 6:  import sys
 7:  
 8:  # Array of angles in degrees
 9:  theta = np.arange(0.5, 90.0, 0.5)
10:  
11:  # Arrays of friction coefficients
12:  muFloor = 1/(2*np.tan(theta*np.pi/180))
13:  muBoth = np.sqrt(1 + np.tan(theta*np.pi/180)**2) - np.tan(theta*np.pi/180)
14:  
15:  # Create the plot with a given size in inches
16:  fig, ax = plt.subplots(figsize=(6, 4))
17:  
18:  # Add the lines
19:  ax.plot(theta, muFloor, '-', color='#1b9e77', lw=2, label="Floor only")
20:  ax.plot(theta, muBoth, '-', color='#d95f02', lw=2, label="Wall and floor")
21:  
22:  # Set the limits
23:  plt.xlim(xmin=0, xmax=90)
24:  plt.ylim(ymin=0, ymax=2)
25:  
26:  # Set the major and minor ticks and add a grid
27:  ax.xaxis.set_major_locator(MultipleLocator(15))
28:  ax.xaxis.set_minor_locator(AutoMinorLocator(3))
29:  ax.yaxis.set_major_locator(MultipleLocator(.5))
30:  ax.yaxis.set_minor_locator(AutoMinorLocator(2))
31:  # ax.grid(linewidth=.5, axis='x', which='major', color='#dddddd', linestyle='-')
32:  # ax.grid(linewidth=.5, axis='y', which='major', color='#dddddd', linestyle='-')
33:  
34:  # Title and axis labels
35:  plt.title('Leaning ladder problem')
36:  plt.xlabel('Ladder angle (degrees)')
37:  plt.ylabel('Friction coefficient')
38:  
39:  # Make the border and tick marks 0.5 points wide
40:  [ i.set_linewidth(0.5) for i in ax.spines.values() ]
41:  ax.tick_params(which='both', width=.5)
42:  
43:  # Add the legend
44:  ax.legend(loc=(.58, .62), frameon=False)
45:  
46:  # Save as SVG
47:  plt.savefig('20240110-Friction comparison graph.svg', format='svg')

In broad outline, this is how nearly all of my Matplotlib graphs are made, because I have a Typinator snippet that inserts generic plot-making code that I modify to set the limits, tick marks, legend, etc. that are appropriate for the graph.

By the way, I don’t think I’ve mentioned here that I’ve switched to Typinator. Ergonis was having a sale at the tail end of 2022, and I decided to give it a go. I had been using Keyboard Maestro for my snippets for several years, but KM isn’t truly meant for text expansion, and I wanted to move to something more built-for-purpose. TextExpander would, I suppose, be the obvious choice, but I didn’t want another subscription. Typinator looked to have all the features I needed, and I haven’t regretted the choice in the year I’ve been using it.

Anyway, here’s the Typinator definition of my plotting snippet:

Typinator definition of plot snippet

As you can see, the abbreviation is ;plot. As you can’t see, at least not fully, the expansion is this:

import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, AutoMinorLocator

# Create the plot with a given size in inches
fig, ax = plt.subplots(figsize=(6, 4))

# Add a line
ax.plot(x, y, '-', color='blue', lw=2, label='Item one')

# Set the limits
# plt.xlim(xmin=0, xmax=100)
# plt.ylim(ymin=0, ymax=50)

# Set the major and minor ticks and add a grid
# ax.xaxis.set_major_locator(MultipleLocator(20))
# ax.xaxis.set_minor_locator(AutoMinorLocator(2))
# ax.yaxis.set_major_locator(MultipleLocator(10))
# ax.yaxis.set_minor_locator(AutoMinorLocator(5))
# ax.grid(linewidth=.5, axis='x', which='both', color='#dddddd', linestyle='-')
# ax.grid(linewidth=.5, axis='y', which='major', color='#dddddd', linestyle='-')

# Title and axis labels
plt.title('{{?Plot title}}')
plt.xlabel('{{?X label}}')
plt.ylabel('{{?Y label}}')

# Make the border and tick marks 0.5 points wide
[ i.set_linewidth(0.5) for i in ax.spines.values() ]
ax.tick_params(which='both', width=.5)

# Add the legend
# ax.legend()

# Save as PDF
plt.savefig('{{?File name}}.pdf', format='pdf')

Typinator uses special syntax for placeholder text. In this case, the placeholders are

I don’t have to remember this syntax. The {…} popup menu has a bunch of placeholder options and will insert the correct code when you define the placeholder.

Text field definition

When I type ;plot, this dialog box appears to enter the text for each placeholder:

Dialog box for entering plot info

Typinator remembers the last text I entered for each placeholder, which makes things go faster if I’m doing a series of similar graphs.

If you go through the snippet, you’ll see that many of the lines of Matplotlib code are commented out, in particular those that set the formatting of the graph. That’s because I usually like to see what Matplotlib comes up with by default. If I don’t like the defaults, I uncomment the appropriate lines and start tinkering. You’ll also notice that this produces PDFs by default; that’s because the plots I make for work have always been PDFs. As I expect to be writing very few work reports from now on, I’ll probably change the file format in the last line from PDF to SVG or PNG.


Ladders and friction

Rhett Allain, who’s the physics columnist for Wired and a professor of physics at Southeast Louisiana State University, solved a funny problem a couple of weeks ago involving a ladder leaning against a wall. You can see his solution on YouTube or Medium. I think of it as an oddball problem because it’s very different from the “ladder leaning against a wall” problems I’ve been seeing for over 40 years.

Ladder leaning against wall

It started as a calculus problem in which you are to assume that Point A moves down the wall with a constant speed and then determine the speed of Point B. Prof. Allain thought (rightly) that Point A of a real ladder wouldn’t fall at a constant speed, so he decided to turn it into a physics problem with gravity acting to accelerate the ladder’s downward movement. His goal in this altered problem was to determine the point at which Point A loses contact with the wall. A key feature in Allain’s problem was zero friction between the wall and the ladder and the floor and the ladder. This certainly makes the solution easier.

Ladder problems in engineering

Both of these problems are different from the ladder/wall problems that are commonly given to engineers in their introductory mechanics courses. Ladders and walls are typically found in the friction section of the study of statics. In these problems, the idea is to determine either

The key word in these problems is doesn’t. These are statics problems, in which things don’t move, and we look for the conditions under which the ladder stays in place. Here are some images from example problems in textbooks I have:

From Seely & Ensign’s Analytical Mechanics for Engineers (Wiley, 1941):

Ladder problem from Seely and Ensign

From Den Hartog’s Mechanics (McGraw-Hill, 1948, but I have the Dover reprint):

Ladder problem from Den Hartog

From Synge & Griffith’s Principles of Mechanics (McGraw-Hill, 1949):

Ladder problem from Synge and Griffith

From McGill & King’s Engineering Mechanics: Statics (PWS-Kent, 1989):

Ladder problems from McGill and King

They use different loading and different friction conditions, but basic idea is the same in all of them.

Static friction

In addition to the laws of statics—Newton’s Second Law with no acceleration—these problems make use of Coulomb’s1 static friction relationship:

fμN

where f is the friction force, which runs parallel to the contact surface in the direction opposite that of impending motion; N is the normal force; and μ is the friction coefficient. Let’s talk about each of these.

In common speech, “impending” means “about to happen,” but that isn’t quite what the authors of engineering textbooks mean when they use it in this context. These are, after all, statics problems, so there is no movement about to happen. The meaning here has been stretched to “what would happen if there were no friction.”

The “normal” in “normal force” means “perpendicular.” The normal force is perpendicular to the contact surface.

The friction coefficient, μ, is a marvel of engineering simplification. The magnitude of the friction force depends on many things: which two materials are in contact, their surface roughness, any lubrication that might be present, even the temperature. But to get practical solutions, we simplify these many conditions down to a single number. Well, maybe not a single number, because when you look up the friction coefficient for a particular set of materials, you’ll often find a range of values. Still, boiling friction down to a number, even if we don’t know the number exactly, is a great way to think about friction and get reasonable answers.

The simplest ladder problem

In the simplest version of the ladder problem, we assume there is friction between the ladder and the floor (Point B), but no friction between the ladder and the wall (Point A). If you look carefully, you’ll see that this is the problem Synge & Griffith were exploring. Here’s a free-body diagram of the ladder and all the forces acting on it:

FBD of ladder with friction at floor only

We’ve taken the mass center of the ladder to be at its geometric center.

The three equations of statics are

∑F x=N Af B=0 ∑F y=N Bmg=0 ∑M B=mg(L2cosθ)N A(Lsinθ)=0

Here, the x and y directions are horizontal and vertical as usual, and the positive direction for moments about Point B is counterclockwise.

Because we were smart in choosing Point B to take moments about, the second and third equations have only one unknown variable each and can be solved directly:

N B=mg N A=mg2cotθ

We substitute the second of these solutions into the first statics equation to get

f B=mg2cotθ

Here’s where the friction coefficient comes in. We know that f BμN B, so substituting in the solutions from statics, this inequality turns into

f B=mg2cotθμN B=μmg

or, after rearranging,

μ12cotθ

So this gives us the minimum friction coefficient for the ladder to stay up at a given angle. A little algebra tells us the lowest at which the ladder will stay up for a given coefficient of friction:

θtan 112μ

If you’re worried about trig functions changing signs and messing up these inequalities, recall that the ladder angle is always between 0° and 90°, so there are no sign changes for tangent or cotangent.

A slightly more complicated ladder problem

Now let’s add friction between the ladder and the wall. To keep the number of variables to a minimum, we’ll assume the ladder/wall friction coefficient is the same as the ladder/floor friction coefficient.

Here’s the free-body diagram:

FBD of ladder with friction at wall and floor

The addition of f A to the system means we no longer have a statically determinant system. Now we have four unknowns to go with the three equations of statics:

∑F y=f A+N Bmg=0 ∑F x=N Af B=0 ∑M B=mg(L2cosθ)N A(Lsinθ)f A(Lcosθ)=0

And we have two inequalities of Coulomb friction:

f AμN A,f BμN B

We can combine these five relationships, and if we’re very careful in keeping the inequalities pointed in the right directions, we’ll come up with an inequality that relates the friction coefficient to the angle of the ladder. But most engineers wouldn’t solve the problem that way. Instead, they’d use the following physical reasoning to simplify the algebra:

  1. When two surfaces are on the verge of slipping, the inequality in Coulomb’s friction relationship turns into an equality. This makes the algebraic manipulations easier, because we don’t have to worry about the inequality signs flipping on us.
  2. The ladder can’t slip at just one of its ends. If it slips at Point A, it must also slip at Point B, and vice versa. And the same is true when the two points are on the verge of slipping.
  3. The larger the friction coefficient, the lower the angle at which the ladder will be on the verge of slipping. So when we solve for the ladder angle or friction coefficient on the verge of slipping, it will be the minimum angle or minimum friction coefficient.

Using these three bits of reasoning, we can say

f A=μN A,f B=μN B

and combine these equations with the statics equations as follows:

The second equation of statics tells us that

f B=N A

so

N A=μN B

The first equation of statics tells us

f A=mgN B

so

mgN B=μN A=μ 2N B

Therefore,

N B=mg1+μ 2 N A=μmg1+μ 2

and

f A=μ 2mg1+μ 2

Plugging the expressions for N A and f A into the third equation of statics gives

mg(L2cosθ)μmg1+μ 2(Lsinθ)μ 2mg1+μ 2(Lcosθ)=0

which can be rearranged to

(12μ 21+μ 2)cosθ=μ1+μ 2sinθ

This can be simplified in the following steps,

1+μ 22μ 22(1+μ 2)cosθ=μ1+μ 2sinθ 1μ 22cosθ=μsinθ

to

tanθ=1μ 22μ

So if we’re given μ, the minimum angle for which the ladder won’t slip is

θ=tan 1(1μ 22μ)

If μ>1, this gives us a negative θ, which is outside the range of ladder angles (0° to 90°) for which the statics equations were written. So a friction coefficient greater than one means the ladder will stay up at any angle.

If we’re given the ladder angle, the minumum friction coefficient to prevent slipping is

μ=1+tan 2θtanθ

This one of the two solutions to a quadratic equation. The other gives a negative friction coefficient, which we don’t care about. As the ladder angle approaches 0°, the minimum friction coefficient approaches one. This is consistent with the finding above that a friction coefficient above one means the ladder will stay up at any angle.

Note that our claims that these are the minimum angle and friction coefficient don’t come out of the algebra, they come out of Item 3 of our physical reasoning.

By the way, just because we went through this solution using physical reasoning to simplify the math, that doesn’t mean we couldn’t get the same result using the inequalities and carrying out the algebra. I have a few pages in my notebook in which I did that (including a couple of mistakes along the way) just to prove to myself that I could.

Comparison

One last thing we can do is plot the results of the two problems and see how they compare. Here are the miniumum friction coefficients necessary to keep the ladder from slipping over the full range of angles.

Friction comparison graph

There’s not much difference between the two problems when the ladder angle is high. That’s because the ladder isn’t pressing hard against the wall at those angles, so most of the work of keeping the ladder up in the second problem is being done by the floor/ladder friction. On the other hand, at low ladder angles, the difference between the two problems is huge; the wall/ladder friction in the second problem is doing a lot of work.

Simple problems like this aren’t especially important in the everyday working life of an engineer (unless you work for Werner), but the principles they teach are applicable across many disciplines. That’s why they appear in so many textbooks.


  1. This is Charles-Augustin Coloumb, the guy who’s most famous for his law of electrical charges. In accordance with Stiger’s Law of Eponymy, the friction relationship that bears his name was written about by others before him


Again with man pages and BBEdit

Julia Evans has been posting on Mastodon recently about the GNU Project’s insistence on documenting its commands through info pages instead of man pages and (the following may be biased by my own thoughts) how absolutely awful that is. The posts reminded me that although I wrote about how I open and read man pages in BBEdit last year, I never showed how I use references to related man pages as links. Time to change that.

So you don’t have to go through the terrible burden of reading an earlier blog post, here’s the source code of the command I use from the Terminal (or iTerm) to open man pages in a new BBEdit window:

bash:
 1:  #!/bin/bash
 2:  
 3:  # Interpret the arguments as command name and section. As with `man`,
 4:  # the section is optional and comes first if present.
 5:  if [[ $# -lt 2 ]]; then
 6:    cmd=${1}
 7:    sec=''
 8:  else
 9:    cmd=${2}
10:    sec=${1}
11:  fi
12:  
13:  # Get the formatted man page, filter out backspaces and convert tabs
14:  # to spaces, and open the text in a new BBEdit document. Set the title
15:  # of the window and scroll to the top.
16:  man $sec $cmd | col -bx | bbedit --view-top --clean -t $cmd

This is slightly different from the code I originally posted. It incorporates options suggested by readers to the bbedit command in the pipeline of Line 16:

  1. --view-top puts the cursor and the scroll position at the top of the document.
  2. --clean sets the state of the document to unmodified so you can close it without getting the “do you want to save this?” warning.
  3. -t $cmd sets the title of the document to the command name.

I’ve also changed the name of the command from bman to bbman to better fit the naming pattern set by bbedit, bbfind, and bbdiff. So if I type

bbman ls

at the command line, a new BBEdit window will open1 with the text of the ls man page. The bold characters that I’d see if I ran

man ls

don’t appear in the BBEdit window, but I’ve never gotten any value out of that limited sort of text formatting, so I don’t miss it.

Although most of what I do in a man page is search and read, sometimes I like to use the hints within the text to open a new, related man page. So I wrote an AppleScript (BBEdit has great AppleScript support) that uses the cursor postion or the selected text to open a new page. Here’s how it works:

Say I’m in SEE ALSO section of the ls man page, and I want to open the chmod man page that’s referred to there. I can either double-click to select chmod or just single-click to put the cursor within the word.

Man page in BBEdit

I then select Man Page from the Scripts menu—or, more likely, use the ⌃⇧M shortcut I’ve assigned to it—and up will pop a new window with the chmod man page.

Here’s the Man Page AppleScript:

 1:  use AppleScript version "2.4" -- Yosemite (10.10) or later
 2:  use scripting additions
 3:  
 4:  -- This script is expected to be run either with the command name selected (as if
 5:  -- by double-clicking) or with the cursor within the command name. The section
 6:  -- (in parentheses) may be immediately after the command name.
 7:  
 8:  -- Function for getting the man page section from between parenthesis.
 9:  -- Input is the character position of the opening parenthesis (if present).
10:  -- Returns the section or an empty string.
11:  on getSection(parenPos)
12:    tell application "BBEdit"
13:      if (character parenPos of front document as text) is "(" then
14:        set secStart to parenPos + 1
15:        set secEnd to find ")" searching in front document
16:        set secEnd to (characterOffset of secEnd's found object) - 1
17:        return characters secStart through secEnd of front document as text
18:      else
19:        return ""
20:      end if
21:    end tell
22:  end getSection
23:  
24:  -- Start by selecting the word the cursor is in (via ⌥←, ⌥⇧→) if there isn't already a selection.
25:  tell application "BBEdit"
26:    if length of selection is 0 then
27:      tell application "System Events"
28:        key code 123 using option down
29:        delay 0.125
30:        key code 124 using {option down, shift down}
31:        delay 0.125
32:      end tell
33:    end if
34:  end tell
35:  
36:  -- Set the command name according to the selection and the section according to
37:  -- whatever may be in parentheses immediately after the command name. This is
38:  -- in a new tell block to ensure that the selection has been updated by the
39:  -- previous tell block.
40:  tell application "BBEdit"
41:    set cmdName to selection as text
42:    set parenPos to (characterOffset of selection) + (length of selection)
43:    set manSection to my getSection(parenPos)
44:  end tell
45:  
46:  -- Get the man page and pipe it through col to delete backspaces and expand tabs. Then
47:  -- pipe that to bbedit with appropriate options. The --clean option means the new
48:  -- document is treated as unmodified so it can be closed without a confirmation dialog.
49:  set manCmd to "man " & manSection & " " & cmdName & " | col -bx "
50:  set manCmd to manCmd & "| /usr/local/bin/bbedit --view-top --clean -t " & cmdName
51:  do shell script manCmd

I think it’s pretty well commented. You can see that Lines 48–51 invoke the same shell pipeline used in the bbman script. The main features that make this different from the shell script are:

  1. Handling the case in which no text is selected but the cursor is within the name of the command. Lines 25–34 check for this condition and select the enclosing word by simulating ⌥← followed by ⌥⇧→.
  2. Figuring out the man page section by looking inside the parentheses that may follow the name of the command. That’s handled by the getSection function in Lines 11–22, which uses the character position of the end of the command name to start its search.

Over the years, I’ve seen lots of ways to turn man pages into HTML, which would make the linking more natural. But they’ve all seemed more trouble than they’re worth. The trick of passing the man output through col -bx to turn it into plain text is something I’ve always come back to, whether my preferred text editor has been BBEdit, TextMate, or (on Linux) NEdit.


  1. On my computer, BBEdit is always running.