J. Peterson's Writing About Electronics, Reviews, 3D Modeling, etc.

How to Make Apple’s Mac Pro Holes

Apple’s recently introduced Mac Pro features a distinctive pattern of holes on the front grill. I’m not likely to own one anytime soon (prices for a well configured machine approach a new car), but that pattern is very appealing, and re-creating it is a fun exercise.

The best clue about the pattern comes from this page pitching the product. About halfway down, by the heading “More air than metal” is a short video clip showing how the hemispherical holes are milled to create the pattern.

Let’s start with a screen grab of the holes from the front. The holes are laid out with their centers equally spaced apart and the tops of the lower circles fall on the same line as the bottom of the circles above them. So the circles are spaced 2r apart vertically, where r is the radius of the circle.

The horizontal spacing is a bit more work. The angles of the equilateral triangle formed by the centers are 180°/3 = 60° (or π/3 as they say in the ‘hood). If you draw a vertical line from the center of the top circle to the line connecting the centers of the bottom circles, that line (as you see above) is 2r long. With a bit of trig, you can find half the horizontal spacing x by using the right triangle formed by that line, x and the side of the equilateral triangle. The angle from the vertical center line to the equilateral triangle edge is half of π/3, π/6. So,

\[x=2r\tan \frac{\pi }{6}\]

and 2x is the horizontal spacing of the circles.

The hemispherical holes are milled into both sides of the plate, but the holes on the other side are offset so the hole centers on one side fall exactly in the middle of the triangle formed by the hole centers on the other side:

You already know the horizontal offset for the centers from one side to the other is x, but how far up do you go to hit the center of that triangle? Let’s call that h.

You’ll use the same tan(π/6) trick we used above, this time using the triangle formed by x and h. Like the triangle used to find x, the angle here is also π/6. So:

\[h=x\tan \frac{\pi }{6}\]

Let’s clean this up a bit:

\[h=x\tan \frac{\pi }{6}=2r\tan \frac{\pi }{6}\tan \frac{\pi }{6}\]
\[\tan \frac{\pi }{6}=\frac{1}{\sqrt{3}}\] so…
\[h=2r\frac{1}{\sqrt{3}}\frac{1}{\sqrt{3}}=\frac{2r}{3}\]

There’s still the issue of how thick the plate is, relative to the size of the holes. I took screen grabs of the film clip and compared them by counting pixels:

Examining the images, the thickness was about 101, with the diameter (2r) of the holes coming in at 176. Now, these numbers aren’t at all precise, because of the perspective introduced by whatever animation software was used. But I can’t help but notice the following coincidence:

\[ \frac{101}{176}=0.573\approx \tan \frac{\pi }{6}=\frac{1}{\sqrt{3}}=0.577\]

Yeah. The ratio of the plate thickness to the hole diameter is just like the ratio of the hole horizontal spacing to the hole diameter. So let’s turn this around, and summarize by saying for a plate of any thickness t, use:

\[r=\frac{t\sqrt{3}}{2}\]
\[x=2r\tan \frac{\pi }{6}=\frac{2r}{\sqrt{3}}=t\]
\[h=\frac{2r}{3}\]

Where r is the radius (half the diameter) of the spheres and 2x is the horizontal spacing of the sphere centers on a given row. For the next row, the centers are offset by x horizontally from the centers of the previous row. The rows are spaced 2r apart vertically, from sphere center to center. The same grid of spheres carved into the back side is displaced by x horizontally, and h vertically from the spheres in the front. The centers of the front spheres are on the front surface of the plate, the back spheres on the back.

So to CAD this up, all you need to do is start with a rectangular block of thickness t, and use the formulas above to place the centers of the spheres (with diameter 2r) on the front and back of the block.

If you just want to quickly print or look at the result in 3D, I’ve posted some sample STL files on Thingiverse.

6 Comments

  1. Dear Mr. Peterson.

    While looking at your article “Tit Testing Block, I found this work about the Apple panel model.
    As a new OpenSCAD user I was inspired to give it ta try.

    The code follows…

    Eric.

    Btw… I noted the “Baboon” image used in one of your early writings called Utah Raster Kit. Back in the 80’s I used the same image at IBM while demonstrating some graphics hardware. (The PGA.) It’s amazing how paths cross.

    // Thanks to J. Peterson for inspiration on the math.

    // Enclose this code in a “module” using these variables, and embed if you wish.
    // Mind the module nesting.

    // Top colors are arbitrarily grey.
    // Bottom holes are red and pink.
    // Color only shows up in preview.

    // Change this to make your spheres smoother.
    // $fn=100 takes a half hour to render on my computer.
    $fn=40;

    // Change this stuff to draw your panel!
    // Limit THICKNESS to about 20% of width.
    // (It still works, but the panel is essentially useless…)
    width = 100 ;
    height = 200 ;
    thickness = 8;
    // End parameter entry.

    // BEGIN MATH (comments precede the item…
    total_margin = thickness * 2;
    sphere_radius = ((thickness*sqrt(3)) / 2) ;
    hor_spacing = thickness * 2;
    vert_spacing = (2 * sphere_radius);

    // Distance much to move the holes on the bottom to the right.
    bottom_holes_offset = ((2 * sphere_radius) / 3) + (thickness /2);

    number_of_horizontal_holes = (floor ((width – total_margin) / (hor_spacing )) ) ;
    number_of_rows_vertically = (floor ((height – total_margin) / (vert_spacing)) ) ;

    // Distance to move the ALL the holes to match the cube.
    vert_translater = (height – (vert_spacing * number_of_rows_vertically) + vert_spacing) / 2;

    // Distance to move the ALL the holes to match the cube.
    hor_translater = (width – (hor_spacing * number_of_horizontal_holes) + hor_spacing) / 2;
    // END MATH

    // Debug
    echo (“thickness =” , thickness);
    echo (“total_margin =”, total_margin);
    echo (“sphere_radius = “, sphere_radius);
    echo (“hor_spacing = “, hor_spacing);
    echo (“vert_spacing = “, vert_spacing);
    echo (“bottom_holes_offset = “, bottom_holes_offset);
    echo (“numholes =”, number_of_horizontal_holes );
    echo (“number_of_rows_vertically = “, number_of_rows_vertically);
    echo (“vert_translater =”, vert_translater);
    echo (“hor_translater =”, hor_translater);
    // End Debug

    // Two modules to make holes follow, the second set — “other_holes” differs
    // only in that one fewer “number_of_rows_vertically_is drawn” for the odd rows.
    // (OpenSCAD make it hard to re-assign a value within an “if”.)

    module holes (){
    for (row = [ 0 : 1 : (number_of_horizontal_holes) – 1])
    for (column = [0 : 2 : (number_of_rows_vertically) -1])
    translate ( [ row * hor_spacing , (column) * vert_spacing ,0] )
    color (“lightgrey”)
    sphere (r=sphere_radius);

    for (row = [0: 1 : (number_of_horizontal_holes) – 2])
    for (column = [1 : 2 : (number_of_rows_vertically ) -1])
    translate ([(row * hor_spacing) + thickness , (column) * vert_spacing ,0])
    color(“grey”)
    sphere (r=sphere_radius);
    }

    module other_holes (){
    for (row = [ 0 : 1 : (number_of_horizontal_holes) – 1])
    for (column = [0 : 2 : (number_of_rows_vertically) -1])
    translate ( [ row * hor_spacing , (column) * vert_spacing ,0] )
    color (“red”)
    sphere (r=sphere_radius);

    for (row = [0: 1 : (number_of_horizontal_holes) – 2])
    for (column = [1 : 2 : (number_of_rows_vertically ) – 2])
    translate ([(row * hor_spacing) + thickness , (column) * vert_spacing ,0])
    color(“pink”)
    sphere (r=sphere_radius);
    }

    difference (){
    translate ([0,0,(- thickness / 2)])
    color (“grey”)
    cube ([width,height,thickness]);

    // Move the holes to inside of the panel (cube).
    translate ([hor_translater, vert_translater, sphere_radius / 2 ])
    union (){
    holes ();
    translate ([0,bottom_holes_offset,-thickness])
    other_holes();
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *