"""Dimensioned orthographic + exploded drawings -> multi-page PDF.""" import math import os import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib.backends.backend_pdf import PdfPages from matplotlib.patches import Rectangle, Circle, Polygon import numpy as np import rack_params as P HERE = os.path.dirname(os.path.abspath(__file__)) INK = "#111111" HATCH_FC = "#d9d9de" FACE_FC = "#f0f0f3" DIM = "#b3261e" # dimension lines HOLE = "#1a5fb4" # cable grommet holes VENT = "#2a7f4f" # ventilation holes CLAMP = "#d05a00" # clamps ACC_ = "#b3261e" # step numbers GHOST = "#9aa0a6" # hidden / reference plt.rcParams.update({ "font.family": "DejaVu Sans", "font.size": 7, "axes.linewidth": 0.0, "pdf.fonttype": 42, }) # --------------------------------------------------------------------------- # Sheet + axis helpers # --------------------------------------------------------------------------- def frame(fig, title, subtitle=""): fig.text(0.035, 0.955, title, fontsize=15, fontweight="bold", color=INK) if subtitle: fig.text(0.035, 0.925, subtitle, fontsize=8.5, color="#555") fig.text(0.035, 0.028, f"UNDER-DESK CLAMP-ON HOMELAB RACK | {fmt(W)} W x {fmt(D)} D x {fmt(H)} H" " | 3/4\" PureBond birch (0.703\" actual) | all dimensions in inches", fontsize=7, color="#666") fig.add_artist(plt.Line2D([0.035, 0.965], [0.050, 0.050], transform=fig.transFigure, color="#ccc", lw=0.8)) def fit(fig, left, top, width, xlim, ylim): """Add an equal-aspect axes sized exactly to the data, hung from `top`.""" xs, ys = xlim[1] - xlim[0], ylim[1] - ylim[0] h_frac = (width * fig.get_figwidth()) * (ys / xs) / fig.get_figheight() ax = fig.add_axes([left, top - h_frac, width, h_frac]) ax.set_aspect("equal") ax.axis("off") ax.set_xlim(*xlim) ax.set_ylim(*ylim) return ax def newax(fig, rect): ax = fig.add_axes(rect) ax.set_aspect("equal") ax.axis("off") return ax def sect(ax, x, y, w, h, fc=HATCH_FC, hatch="////", lw=1.0, ec=INK, z=3): """A panel cut by the viewing/section plane -> hatched.""" ax.add_patch(Rectangle((x, y), w, h, facecolor=fc, edgecolor=ec, hatch=hatch, lw=lw, zorder=z)) def face(ax, x, y, w, h, fc=FACE_FC, lw=0.9, ec=INK, z=2, ls="-"): """A panel seen face-on (not cut) -> plain fill.""" ax.add_patch(Rectangle((x, y), w, h, facecolor=fc, edgecolor=ec, lw=lw, zorder=z, linestyle=ls)) def hidden(ax, x, y, w, h, z=1): ax.add_patch(Rectangle((x, y), w, h, facecolor="none", edgecolor=GHOST, lw=0.7, linestyle=(0, (4, 3)), zorder=z)) def fmt(v): """Format inches to the nearest 1/16, the way a tape measure reads.""" neg = v < 0 v = abs(v) whole = int(math.floor(v + 1e-9)) n = int(round((v - whole) * 16)) if n == 16: whole, n = whole + 1, 0 if n == 0: out = f'{whole}"' else: d = 16 while n % 2 == 0: n //= 2 d //= 2 out = f'{whole}-{n}/{d}"' if whole else f'{n}/{d}"' return ("-" if neg else "") + out def cut(v): """Round DOWN to the nearest 1/16. Used for any piece that must drop in between two others — erring long means it will not fit.""" whole = int(math.floor(v + 1e-9)) n = int(math.floor((v - whole) * 16 + 1e-9)) if n == 0: return f'{whole}"' d = 16 while n % 2 == 0: n //= 2 d //= 2 return f'{whole}-{n}/{d}"' def dimh(ax, x0, x1, y, text=None, ext_from=None, color=DIM, fs=7, below=False): if ext_from is not None: for xx in (x0, x1): ax.plot([xx, xx], [ext_from, y + (0.3 if y > ext_from else -0.3)], color=color, lw=0.45, zorder=9) ax.annotate("", xy=(x1, y), xytext=(x0, y), zorder=9, arrowprops=dict(arrowstyle="<|-|>", color=color, lw=0.7, mutation_scale=7, shrinkA=0, shrinkB=0)) ax.text((x0 + x1) / 2, y + (-0.18 if below else 0.18), text if text is not None else fmt(x1 - x0), ha="center", va="top" if below else "bottom", color=color, fontsize=fs, zorder=10, bbox=dict(boxstyle="square,pad=0.15", fc="white", ec="none")) def dimv(ax, y0, y1, x, text=None, ext_from=None, color=DIM, fs=7, side="right"): if ext_from is not None: for yy in (y0, y1): ax.plot([ext_from, x + (0.3 if x > ext_from else -0.3)], [yy, yy], color=color, lw=0.45, zorder=9) ax.annotate("", xy=(x, y1), xytext=(x, y0), zorder=9, arrowprops=dict(arrowstyle="<|-|>", color=color, lw=0.7, mutation_scale=7, shrinkA=0, shrinkB=0)) ax.text(x, (y0 + y1) / 2, text if text is not None else fmt(y1 - y0), ha="center", va="center", rotation=90, color=color, fontsize=fs, zorder=10, bbox=dict(boxstyle="square,pad=0.15", fc="white", ec="none")) def leader(ax, xy, xytext, text, color=INK, fs=6.8, ha="left"): ax.annotate(text, xy=xy, xytext=xytext, fontsize=fs, color=color, ha=ha, va="center", zorder=12, bbox=dict(boxstyle="square,pad=0.2", fc="white", ec="none", alpha=0.9), arrowprops=dict(arrowstyle="-", color=color, lw=0.6, connectionstyle="arc3,rad=0.0", shrinkB=1)) def note(ax, x, y, text, fs=7, color=INK, weight="normal", ha="left"): ax.text(x, y, text, fontsize=fs, color=color, fontweight=weight, ha=ha, va="top", zorder=11) W, D, H, T = P.W, P.D, P.H, P.T XI0, XI1 = P.X_IN_0, P.X_IN_1 def clamp_chain(ax, y, color=CLAMP, ext_from=None): xs = [0] + list(P.CLAMP_X) + [W] for a, b in zip(xs, xs[1:]): dimh(ax, a, b, y, color=color, fs=6.6, ext_from=ext_from) # =========================================================================== # PAGE 1 — FRONT ELEVATION # =========================================================================== def page_front(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "1 · FRONT ELEVATION", "As seen from the chair. The equipment bay is the only opening you look into — " "all cabling is hidden behind the front wall above it.") ax = fit(fig, 0.055, 0.885, 0.90, (-6.5, W + 7.5), (-4.4, H + 5.2)) ax.add_patch(Rectangle((-5.0, H), W + 10, 1.0, facecolor="#eceff4", edgecolor=GHOST, lw=0.8, zorder=1)) ax.text(W / 2, H + 0.5, "EXISTING 48\" DESKTOP — rack top bears directly on the underside", ha="center", va="center", fontsize=7, color="#667") sect(ax, 0, 0, W, T) sect(ax, 0, H - T, W, T) sect(ax, 0, T, T, H - 2 * T) sect(ax, W - T, T, T, H - 2 * T) sect(ax, XI0, P.Z_MID_0, XI1 - XI0, T) face(ax, XI0, P.Z_MID_1, XI1 - XI0, P.CLEAR, fc="#e4e4ea") hidden(ax, XI0, P.Z_TOP_0 - T, XI1 - XI0, T) ax.text(W / 2, P.Z_MID_1 + P.CLEAR / 2 + 0.35, "FRONT WALL", ha="center", va="center", fontsize=9, fontweight="bold", color="#444") ax.text(W / 2, P.Z_MID_1 + P.CLEAR / 2 - 0.75, "closes the cable bay — you never see wiring from here", ha="center", va="center", fontsize=7, color="#666") for name, cx, w, d, h in P.DEVICES: ax.add_patch(Rectangle((cx - w / 2, P.Z_BOT_1), w, h, facecolor="#dce6f5", edgecolor="#4a6fa5", lw=0.7, zorder=4)) ax.text(cx, P.Z_BOT_1 + h / 2, "PC" if "mini PC" in name else ("SW" if "switch" in name else "HDD"), ha="center", va="center", fontsize=6.5, color="#2a4a7a", zorder=5) ax.text(W / 2, P.Z_MID_0 - 0.6, f"EQUIPMENT BAY · {fmt(P.CLEAR_W)} x {fmt(P.CLEAR)} x {fmt(P.CLEAR_D)} clear", ha="center", va="center", fontsize=7.5, color="#2a4a7a") for cx in P.CLAMP_X: ax.add_patch(Rectangle((cx - 0.9, H - T - 0.15), 1.8, 1.35, facecolor="none", edgecolor=CLAMP, lw=0.9, linestyle=(0, (3, 2)), zorder=6)) ax.text(W / 2, H + 3.3, "4 CLAMPS, HIDDEN BEHIND THE DESK AT THE WALL EDGE", ha="center", fontsize=7.5, color=CLAMP, fontweight="bold") clamp_chain(ax, H + 2.2) dimh(ax, 0, W, -3.2, ext_from=0) dimh(ax, XI0, XI1, -1.9, ext_from=T) dimv(ax, 0, H, W + 4.6, text=fmt(H) + ' OVERALL', ext_from=W) dimv(ax, P.Z_BOT_1, P.Z_MID_0, W + 2.4, text=fmt(P.CLEAR) + ' clear', ext_from=W) dimv(ax, P.Z_MID_1, P.Z_TOP_0, W + 2.4, text=fmt(P.CLEAR) + ' clear', ext_from=W) dimv(ax, 0, T, -2.4, text='3/4"', ext_from=0, side="left") dimv(ax, P.Z_MID_0, P.Z_MID_1, -2.4, text='3/4"', ext_from=0, side="left") fig.text(0.055, 0.175, "Device positions are recommended, not structural — slide them to suit your kit. " "Airflow gaps as drawn are 0.70\"–0.95\" between units;\neach mini PC also sits directly over two 2\" " "vent holes in the bottom panel, and its cables exit upward through the shelf rather than sideways.", fontsize=7.5, color="#555", va="top") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 2 — REAR ELEVATION # =========================================================================== def page_rear(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "2 · REAR ELEVATION", "As seen from the wall. This is the working side: the cable bay opens here and the " "clamps are fitted and released from here.") ax = fit(fig, 0.055, 0.885, 0.90, (-6.5, W + 7.5), (-4.4, H + 5.2)) ax.add_patch(Rectangle((-5.0, H), W + 10, 1.0, facecolor="#eceff4", edgecolor=GHOST, lw=0.8, zorder=1)) ax.text(18, H + 0.5, "DESKTOP", ha="center", va="center", fontsize=7, color="#667") sect(ax, 0, 0, W, T) sect(ax, 0, H - T, W, T) sect(ax, 0, T, T, H - 2 * T) sect(ax, W - T, T, T, H - 2 * T) sect(ax, XI0, P.Z_MID_0, XI1 - XI0, T) face(ax, XI0, P.Z_BOT_1, XI1 - XI0, P.CLEAR, fc="#e4e4ea") face(ax, XI0, P.Z_TOP_0 - T, XI1 - XI0, T, fc="#dfe3ea") for x in P.REAR_HOLE_X: ax.add_patch(Circle((x, P.REAR_HOLE_Z), P.REAR_HOLE_DIA / 2, facecolor="white", edgecolor=HOLE, lw=1.3, zorder=5)) ax.text(W / 2, P.Z_BOT_1 + 0.35, "REAR WALL — closes the equipment bay", ha="center", va="bottom", fontsize=8, fontweight="bold", color="#444") xs = list(P.REAR_HOLE_X) dimh(ax, XI0, xs[0], P.Z_MID_0 + 0.5, color=HOLE, fs=6.4) for a, b in zip(xs, xs[1:]): dimh(ax, a, b, P.Z_MID_0 + 0.5, color=HOLE, fs=6.4) dimh(ax, xs[-1], XI1, P.Z_MID_0 + 0.5, color=HOLE, fs=6.4) leader(ax, (xs[0], P.REAR_HOLE_Z), (-6.2, P.Z_BOT_1 - 1.4), "6 x 2\" cable pass-throughs\nequally spaced at " + fmt(P.REAR_HOLE_PITCH), color=HOLE) ax.text(W / 2, P.Z_MID_1 + 2.1, "CABLE BAY — OPEN THIS SIDE", ha="center", va="center", fontsize=9, fontweight="bold", color="#2a4a7a") ax.text(W / 2, P.Z_MID_1 + 1.1, "power strips · adapters · patch leads · excess slack", ha="center", va="center", fontsize=7, color="#2a4a7a") for x in P.GROMMET_X: ax.add_patch(Rectangle((x - P.GROMMET_DIA / 2, P.Z_MID_1 - 0.06), P.GROMMET_DIA, 0.2, facecolor=HOLE, edgecolor="none", zorder=6)) leader(ax, (P.GROMMET_X[0], P.Z_MID_1), (-6.2, P.Z_MID_1 + 2.4), f"{len(P.GROMMET_X)} x 2\" grommets\n(one per device)", color=HOLE) for cx in P.CLAMP_X: ax.add_patch(Rectangle((cx - 0.9, H - T), 1.8, 1.75, facecolor="#fff3e6", edgecolor=CLAMP, lw=1.0, zorder=7)) ax.plot([cx, cx], [H - T, H + 0.75], color=CLAMP, lw=1.2, zorder=8) leader(ax, (P.CLAMP_X[3] + 0.9, H + 0.4), (W + 3.0, H + 3.0), "heavy-duty C-clamp,\npads on both jaws", color=CLAMP) dimh(ax, 0, W, -3.2, ext_from=0) dimh(ax, XI0, XI1, -1.9, ext_from=T) dimv(ax, 0, H, W + 4.6, text=fmt(H) + ' OVERALL', ext_from=W) dimv(ax, P.Z_TOP_0 - T, P.Z_TOP_0, W + 2.4, text='3/4" doubler', ext_from=W) dimv(ax, P.Z_BOT_1, P.Z_MID_0, -2.4, text=fmt(P.CLEAR) + ' clear', ext_from=0, side="left") dimv(ax, P.Z_BOT_1, P.REAR_HOLE_Z, W + 1.0, text=fmt(P.CLEAR / 2) + " to CL", color=HOLE, ext_from=W - T) clamp_chain(ax, H + 2.4) ax.text(W / 2, H + 3.5, "CLAMP CENTRES — even 9\" pitch", ha="center", fontsize=7.5, color=CLAMP, fontweight="bold") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 3 — SIDE CROSS-SECTION # =========================================================================== def page_section(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "3 · SIDE CROSS-SECTION (section A-A, cut at mid-width)", "The two opposite-facing U shapes. The upper U opens to the wall, the lower U opens " "to you, and they share the middle shelf.") ax = fit(fig, 0.032, 0.885, 0.65, (-8.0, D + 11.0), (-6.2, H + 5.4)) ax.add_patch(Rectangle((-6.5, H), D + 8.0, 1.0, facecolor="#eceff4", edgecolor=INK, lw=1.0, zorder=2)) ax.text(-6.1, H + 0.5, "DESKTOP (1\" typical)", fontsize=7, color="#556", va="center") sect(ax, 0, 0, D, T) sect(ax, 0, P.Z_MID_0, D, T) sect(ax, 0, P.Z_TOP_0, D, T) sect(ax, P.Y_FRONT_WALL_0, P.Z_MID_1, T, P.CLEAR) sect(ax, P.Y_REAR_WALL_0, P.Z_BOT_1, T, P.CLEAR) sect(ax, P.Y_DOUBLER_0, P.Z_TOP_0 - T, 2.75, T, fc="#c9c9d2", hatch="\\\\\\\\") ax.text(D / 2 + 0.5, P.Z_MID_1 + P.CLEAR / 2 + 0.45, "REAR U — CABLE BAY", ha="center", va="center", fontsize=8.5, color="#2a4a7a", fontweight="bold") ax.text(D / 2 + 0.5, P.Z_MID_1 + P.CLEAR / 2 - 0.65, fmt(P.CLEAR) + " clear", ha="center", va="center", fontsize=7.5, color="#2a4a7a") ax.text(4.6, P.Z_BOT_1 + P.CLEAR / 2 + 1.15, "FRONT U — EQUIPMENT BAY", ha="center", va="center", fontsize=8, color="#2a4a7a", fontweight="bold") ax.annotate("", xy=(D + 3.4, P.Z_MID_1 + P.CLEAR / 2), xytext=(D + 0.2, P.Z_MID_1 + P.CLEAR / 2), arrowprops=dict(arrowstyle="-|>", color="#2a4a7a", lw=1.5, mutation_scale=12)) ax.text(D + 3.7, P.Z_MID_1 + P.CLEAR / 2, "opens\nto wall", fontsize=7.5, color="#2a4a7a", va="center") ax.annotate("", xy=(-3.4, P.Z_BOT_1 + P.CLEAR / 2), xytext=(-0.2, P.Z_BOT_1 + P.CLEAR / 2), arrowprops=dict(arrowstyle="-|>", color="#2a4a7a", lw=1.5, mutation_scale=12)) ax.text(-3.7, P.Z_BOT_1 + P.CLEAR / 2, "opens\nto you", fontsize=7.5, color="#2a4a7a", va="center", ha="right") pcw, pch = 5.1, 2.0 ax.add_patch(Rectangle((0.5, P.Z_BOT_1), pcw, pch, facecolor="#dce6f5", edgecolor="#4a6fa5", lw=0.8, zorder=4)) ax.text(0.5 + pcw / 2, P.Z_BOT_1 + pch / 2, "mini PC", ha="center", va="center", fontsize=6.5, color="#2a4a7a", zorder=5) gx = P.GROMMET_Y ax.add_patch(Rectangle((gx - P.GROMMET_DIA / 2, P.Z_MID_0), P.GROMMET_DIA, T, facecolor="white", edgecolor=HOLE, lw=1.3, zorder=5)) t = np.linspace(0, 1, 60) ax.plot(0.5 + pcw + (gx - 0.5 - pcw) * t + 0.3 * np.sin(t * math.pi), P.Z_BOT_1 + pch * 0.5 + (P.Z_MID_1 + 1.5 - P.Z_BOT_1 - pch * 0.5) * t ** 2.1, color=HOLE, lw=1.5, zorder=6) leader(ax, (gx + 0.75, P.Z_MID_0 + T / 2), (D + 3.4, P.Z_MID_0 - 0.9), f"2\" grommet\n({len(P.GROMMET_X)} across the width)", color=HOLE) # clamp assembly cy = D - 2.0 ax.add_patch(Polygon([(cy, H - T - 0.10), (D + 1.7, H - T - 0.10), (D + 1.7, H + 1.60), (cy, H + 1.60), (cy, H + 1.18), (D + 1.2, H + 1.18), (D + 1.2, H + 0.32), (cy, H + 0.32)], closed=True, facecolor="#fff0e0", edgecolor=CLAMP, lw=1.1, zorder=8)) for yy in (H + 0.27, H - T - 0.15): ax.plot([cy + 0.2, D + 1.1], [yy, yy], color="#7a3d00", lw=2.2, zorder=9, solid_capstyle="butt") leader(ax, (D + 1.7, H + 0.9), (D + 3.4, H + 3.2), "heavy-duty C-clamp, 4\" min. opening\nrubber pad on each jaw", color=CLAMP) # dimensions — everything above or below the section, nothing on top of it dimh(ax, 0, gx, -1.3, text=fmt(P.GROMMET_Y) + ' to grommet centre', color=HOLE, ext_from=0) dimh(ax, 0, P.Y_REAR_WALL_0, -2.7, text=fmt(P.Y_REAR_WALL_0) + ' equipment bay', ext_from=0) dimh(ax, 0, D, -4.1, text=fmt(D) + ' OVERALL DEPTH', ext_from=0) dimh(ax, P.Y_FRONT_WALL_1, D, H + 2.5, text=fmt(D - P.T) + ' cable bay') dimh(ax, P.Y_DOUBLER_0, D, H + 3.8, text='2-3/4" doubler') dimv(ax, 0, H, D + 10.6, text=fmt(H) + ' OVERALL', ext_from=D) dimv(ax, P.Z_BOT_1, P.Z_MID_0, D + 8.2, text=fmt(P.CLEAR) + ' clear', ext_from=D) dimv(ax, P.Z_MID_1, P.Z_TOP_0, D + 8.2, text=fmt(P.CLEAR) + ' clear', ext_from=D) ax.text(-4.0, -5.6, "◀ FRONT (you)", fontsize=9, color="#333", fontweight="bold", ha="center", va="center") ax.text(D + 4.5, -5.6, "WALL ▶", fontsize=9, color="#333", fontweight="bold", ha="center", va="center") axn = fig.add_axes([0.700, 0.115, 0.28, 0.74]); axn.axis("off") axn.set_xlim(0, 1); axn.set_ylim(0, 1) note(axn, 0, 0.99, "WHY THIS SECTION WORKS", fs=9, weight="bold") note(axn, 0, 0.945, ( f"· Each bay is {fmt(P.CLEAR)} clear. Three 0.703\" panels\n" f" plus two {fmt(P.CLEAR)} bays = {P.H:.3f}\" overall.\n" " That is the geometric minimum for this\n" " spec — nothing is wasted on structure.\n\n" "· The front wall and the rear wall sit on\n" " opposite faces, so the section reads as\n" " two U's back to back: exactly your sketch.\n\n" "· Because those two walls are staggered, the\n" " cross-section is a closed loop. That is what\n" f" stops the box racking over a {fmt(W)} span with\n" " no back panel and no front panel. A design\n" " with both walls on the same face would\n" " fold up like a parallelogram.\n\n" "· The doubler under the top panel gives the\n" " clamp jaw 1.41\" of plywood to bite on, so\n" " it cannot crush or split the top ply.\n\n" "· Cable route: mini PC → up through the\n" " grommet → cable bay → out of the open rear\n" " to the power strip. Nothing ever crosses\n" " the equipment bay where you can see it.\n\n" "· Airflow: the equipment bay is open at the\n" " front, vented through its floor, and vented\n" " again through each grommet. There is no\n" " sealed pocket anywhere in the assembly."), fs=7.2, color="#333") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 4 — TOP VIEW # =========================================================================== def page_top(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "4 · TOP VIEW", "Looking straight down with the desk removed. Solid outline = top panel. " "Dashed = everything below it.") ax = fit(fig, 0.055, 0.865, 0.90, (-7.0, W + 9.0), (-7.6, D + 4.0)) face(ax, 0, 0, W, D, fc="#f5f5f8", lw=1.4) ax.add_patch(Rectangle((T, P.Y_DOUBLER_0), W - 2 * T, 2.75, facecolor="#e8eaf0", edgecolor="none", zorder=1)) hidden(ax, T, 0, W - 2 * T, T) hidden(ax, T, D - T, W - 2 * T, T) hidden(ax, T, P.Y_DOUBLER_0, W - 2 * T, 2.75) for x in P.GROMMET_X: ax.add_patch(Circle((x, P.GROMMET_Y), P.GROMMET_DIA / 2, facecolor="none", edgecolor=HOLE, lw=1.1, linestyle=(0, (3, 2)), zorder=5)) for x in P.VENT_X: for y in P.VENT_Y: ax.add_patch(Circle((x, y), P.VENT_DIA / 2, facecolor="none", edgecolor=VENT, lw=0.9, linestyle=(0, (2, 2)), zorder=4)) for cx in P.CLAMP_X: ax.add_patch(Rectangle((cx - 1.0, D - 2.5), 2.0, 2.5, facecolor="#fff0e0", edgecolor=CLAMP, lw=1.0, zorder=6)) ax.plot(cx, D - 1.25, marker="+", color=CLAMP, ms=8, mew=1.2, zorder=7) ax.text(W / 2, D + 1.0, "WALL SIDE — clamps here, cable bay opens here", ha="center", fontsize=8, color="#444", fontweight="bold") ax.text(W / 2, -1.5, "FRONT — you sit here", ha="center", va="center", fontsize=8, color="#444", fontweight="bold") leader(ax, (18.0, P.Y_DOUBLER_0 + 1.4), (W + 1.2, D + 2.4), f"clamp doubler below\n{fmt(P.INNER_LEN)} x 2-3/4\"", color="#556") leader(ax, (P.GROMMET_X[1], P.GROMMET_Y), (-6.8, D + 2.4), f"{len(P.GROMMET_X)} x 2\" grommet holes\nin the MIDDLE SHELF", color=HOLE) leader(ax, (P.VENT_X[1], P.VENT_Y[0]), (-6.8, -4.6), "12 x 1-1/2\" vent holes\nin the BOTTOM PANEL", color=VENT) dimh(ax, 0, W, -6.4, ext_from=0) clamp_chain(ax, -3.4, ext_from=None) for cx in P.CLAMP_X: ax.plot([cx, cx], [D - 2.5, -3.1], color=CLAMP, lw=0.4, linestyle=(0, (2, 3)), zorder=2) dimv(ax, 0, D, W + 2.2, ext_from=W) dimv(ax, D - 2.5, D, W + 0.9, text='2-1/2"', ext_from=W) dimv(ax, 0, P.GROMMET_Y, -2.2, text=fmt(P.GROMMET_Y), color=HOLE, ext_from=0) dimh(ax, 0, P.GROMMET_X[0], P.GROMMET_Y + 1.9, text='4"', color=HOLE) fig.text(0.055, 0.145, f"Clamp centres at X = " + ", ".join(fmt(x) for x in P.CLAMP_X) + f" — an even {fmt(P.W / 4)} pitch, " "no unsupported span over 9\", and every clamp lands on the doubler.\n" "The clamp zone is the rearmost 2-1/2\" of the top panel, so a clamp with a " "2\" throat depth reaches it comfortably from the desk's rear edge.", fontsize=7.5, color="#555", va="top") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 5 — HOLE SETTING-OUT # =========================================================================== def page_holes(pdf): """Three panels get holes. Every X mark is shared, so you measure once.""" fig = plt.figure(figsize=(11, 8.5)) frame(fig, f"5 · HOLE SETTING-OUT — {sum(len(q[chr(104)+chr(111)+chr(108)+chr(101)+chr(115)]) for q in P.PANELS if q[chr(113)+chr(116)+chr(121)])} holes in 3 panels", "Mark from the LEFT end and the FRONT edge of each panel. The shelf and the floor " "share one set of six X marks.") mw = P.INNER_LEN # --- 1. middle shelf -------------------------------------------------- ax = fit(fig, 0.055, 0.868, 0.52, (-4.0, mw + 5.0), (-4.6, D + 3.0)) face(ax, 0, 0, mw, D, fc="#f5f5f8", lw=1.3) ax.text(mw / 2, D + 2.0, f"MIDDLE SHELF · {len(P.GROMMET_X)} holes @ 2\" dia", ha="center", fontsize=9, fontweight="bold") ax.plot([0, mw], [P.GROMMET_Y] * 2, color=HOLE, lw=0.6, linestyle=(0, (6, 3)), zorder=2) for x in P.GROMMET_X: xl = x - XI0 ax.add_patch(Circle((xl, P.GROMMET_Y), P.GROMMET_DIA / 2, facecolor="white", edgecolor=HOLE, lw=1.3, zorder=5)) ax.plot([xl, xl], [P.GROMMET_Y - 1.3, P.GROMMET_Y + 1.3], color=HOLE, lw=0.5, zorder=6) ax.text(xl, P.GROMMET_Y - 1.7, fmt(xl), ha="center", va="top", fontsize=6.6, color=HOLE, fontweight="bold") dimh(ax, 0, mw, -2.9, ext_from=0, fs=6.6) dimv(ax, 0, P.GROMMET_Y, -2.2, text='8"', color=HOLE, ext_from=0) dimv(ax, 0, D, mw + 2.0, ext_from=mw, fs=6.6) # --- 2. bottom panel -------------------------------------------------- ax2 = fit(fig, 0.055, 0.508, 0.52, (-4.0, W + 5.0), (-5.4, D + 3.0)) face(ax2, 0, 0, W, D, fc="#f5f5f8", lw=1.3) ax2.text(W / 2, D + 2.0, "BOTTOM PANEL · 12 holes @ 1-1/2\" dia", ha="center", fontsize=9, fontweight="bold") for y in P.VENT_Y: ax2.plot([0, W], [y] * 2, color=VENT, lw=0.6, linestyle=(0, (6, 3)), zorder=2) for x in P.VENT_X: for y in P.VENT_Y: ax2.add_patch(Circle((x, y), P.VENT_DIA / 2, facecolor="white", edgecolor=VENT, lw=1.3, zorder=5)) ax2.plot([x, x], [-1.1, D], color=VENT, lw=0.4, linestyle=(0, (2, 3)), zorder=3) ax2.text(x, -1.4, fmt(x), ha="center", va="top", fontsize=6.6, color=VENT, fontweight="bold") dimh(ax2, 0, W, -3.6, ext_from=0, fs=6.6) dimv(ax2, 0, P.VENT_Y[0], -2.2, text=fmt(P.VENT_Y[0]), color=VENT, ext_from=0) dimv(ax2, 0, P.VENT_Y[1], W + 2.0, text=fmt(P.VENT_Y[1]), color=VENT, ext_from=W) # --- 3. rear wall ----------------------------------------------------- ax3 = fit(fig, 0.055, 0.198, 0.52, (-4.0, mw + 5.0), (-4.6, P.CLEAR + 3.0)) face(ax3, 0, 0, mw, P.CLEAR, fc="#f5f5f8", lw=1.3) ax3.text(mw / 2, P.CLEAR + 2.0, "REAR WALL · 6 holes @ 2\" dia, equally spaced", ha="center", fontsize=9, fontweight="bold") ax3.plot([0, mw], [P.CLEAR / 2] * 2, color=HOLE, lw=0.6, linestyle=(0, (6, 3)), zorder=2) for xl in P.REAR_HOLE_X_LOCAL: ax3.add_patch(Circle((xl, P.CLEAR / 2), P.REAR_HOLE_DIA / 2, facecolor="white", edgecolor=HOLE, lw=1.3, zorder=5)) ax3.text(xl, -0.5, fmt(xl), ha="center", va="top", fontsize=6.6, color=HOLE, fontweight="bold") dimh(ax3, 0, mw, -2.9, ext_from=0, fs=6.6) dimv(ax3, 0, P.CLEAR / 2, -2.2, text='2"', color=HOLE, ext_from=0) dimv(ax3, 0, P.CLEAR, mw + 2.0, ext_from=mw, fs=6.6) # --- notes column ----------------------------------------------------- axn = fig.add_axes([0.60, 0.10, 0.37, 0.77]); axn.axis("off") axn.set_xlim(0, 1); axn.set_ylim(0, 1) note(axn, 0, 0.99, "THE SIX X MARKS", fs=9.5, weight="bold") note(axn, 0, 0.955, "The shelf holes and the floor holes line up vertically once\n" "the rack is together, but the shelf sits BETWEEN the sides,\n" "so its own left end starts 11/16\" further in. Its marks are\n" "therefore 11/16\" smaller than the bottom panel's.\n\n" "Always measure from the LEFT END OF THE PIECE IN FRONT OF\n" "YOU, using the row for that panel:", fs=7.6, color="#333") rows = [["Panel", "X marks from the left end", "Y / Z", "Dia"], ["Middle shelf", " · ".join(fmt(x - XI0) for x in P.GROMMET_X[:3]) + "\n" + " · ".join(fmt(x - XI0) for x in P.GROMMET_X[3:]), '8" from\nthe front', '2"'], ["Bottom panel", " · ".join(fmt(x) for x in P.VENT_X[:3]) + "\n" + " · ".join(fmt(x) for x in P.VENT_X[3:]), fmt(P.VENT_Y[0]) + ' and\n' + fmt(P.VENT_Y[1]), '1-1/2"'], ["Rear wall", " · ".join(fmt(x) for x in P.REAR_HOLE_X_LOCAL[:3]) + "\n" + " · ".join(fmt(x) for x in P.REAR_HOLE_X_LOCAL[3:]), '2" up\n(mid-height)', '2"']] y = 0.845 for i, r in enumerate(rows): w = "bold" if i == 0 else "normal" c = INK if i == 0 else "#333" note(axn, 0.0, y, r[0], fs=7.4, weight=w, color=c) note(axn, 0.235, y, r[1], fs=7.0, weight=w, color=c) note(axn, 0.715, y, r[2], fs=7.0, weight=w, color=c) note(axn, 0.905, y, r[3], fs=7.0, weight=w, color=c) axn.plot([0, 1], [y - 0.048, y - 0.048], color="#ddd", lw=0.6) y -= 0.075 note(axn, 0, y - 0.01, "WHAT EACH SET IS FOR", fs=9.5, weight="bold") note(axn, 0, y - 0.05, "· SHELF (blue, 2\") — each machine's cables go\n" " straight up into the hidden cable bay. Rubber\n" " grommets go in these six.\n\n" "· FLOOR (green, 1-1/2\") — cool air in. Two under\n" " every device, and both rows sit inside each\n" " device's own footprint.\n\n" "· REAR WALL (blue, 2\") — cables that need to leave\n" " sideways or straight back to the wall without\n" " going up through the shelf.\n\n" "Equal spacing on the rear wall puts every hole within\n" "1-1/8\" of a device centre anyway, so each one still\n" "lands fully behind its machine.", fs=7.4, color="#333") note(axn, 0, y - 0.40, "DRILLING", fs=9.5, weight="bold") note(axn, 0, y - 0.44, "Two hole saws: 1-1/2\" and 2\". Drill everything BEFORE\n" "assembly, with the panel clamped over a sacrificial\n" "scrap so the saw cannot blow out the back veneer —\n" "PureBond's face veneer is only about 1/42\" thick and\n" "tears easily. Score the circle with a knife first if\n" "you want a really clean edge.\n\n" "The top panel, both sides, the front wall and the\n" "doubler have NO holes.", fs=7.4, color="#333") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 6 — EXPLODED ASSEMBLY # =========================================================================== ISO_C, ISO_S = math.cos(math.radians(30)), math.sin(math.radians(30)) def iso(x, y, z): return ((x - y) * ISO_C, (x + y) * ISO_S + z) def iso_box(ax, box, color, off=(0, 0, 0), lw=0.7, holes=None, axis="z"): x0, y0, z0, x1, y1, z1 = box x0, x1 = x0 + off[0], x1 + off[0] y0, y1 = y0 + off[1], y1 + off[1] z0, z1 = z0 + off[2], z1 + off[2] v = {k: iso(*p) for k, p in { "a": (x0, y0, z0), "b": (x1, y0, z0), "c": (x1, y1, z0), "d": (x0, y1, z0), "e": (x0, y0, z1), "f": (x1, y0, z1), "g": (x1, y1, z1), "h": (x0, y1, z1)}.items()} for keys, shade in [(["e", "f", "g", "h"], 1.00), (["a", "b", "f", "e"], 0.80), (["b", "c", "g", "f"], 0.62)]: c = tuple(min(1, ch * shade + (1 - shade) * 0.10) for ch in matplotlib.colors.to_rgb(color)) ax.add_patch(Polygon([v[k] for k in keys], closed=True, facecolor=c, edgecolor="#0d0d10", lw=lw, zorder=5)) if holes: th = np.linspace(0, 2 * math.pi, 40) for cx, cy, dia in holes: r = dia / 2 if axis == "z": # flat panel -> holes read on its top face pts = [iso(cx + off[0] + r * math.cos(a), cy + off[1] + r * math.sin(a), z1) for a in th] else: # upright panel -> holes read on its front face pts = [iso(cx + off[0] + r * math.cos(a), y0, cy + off[2] + r * math.sin(a)) for a in th] ax.add_patch(Polygon(pts, closed=True, facecolor="#0d0d10", edgecolor="#0d0d10", lw=0.4, zorder=6)) def page_exploded(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "6 · EXPLODED ASSEMBLY", f"Eight pieces, seven unique cuts, {sum(len(q[chr(104)+chr(111)+chr(108)+chr(101)+chr(115)]) for q in P.PANELS if q[chr(113)+chr(116)+chr(121)])} holes. Arrows show the direction each part drops into place.") ax = newax(fig, [0.01, 0.075, 0.68, 0.80]) E = 7.0 order = [ ("bottom", (0, 0, -E * 1.7), "#4b4b55"), ("rear_wall", (0, E * 0.9, -E * 0.5), "#5c5c68"), ("side_l", (-E * 1.1, 0, 0), "#565661"), ("side_r", (E * 1.1, 0, 0), "#565661"), ("middle", (0, 0, 0), "#63636f"), ("front_wall", (0, -E * 0.9, E * 0.55), "#5c5c68"), ("doubler", (0, E * 0.7, E * 1.15), "#8a8a9a"), ("top", (0, 0, E * 1.9), "#4b4b55"), ] for key, off, col in sorted(order, key=lambda o: -(o[1][0] - o[1][1] + o[1][2] * 0.01)): p = P.PANELS_BY_KEY[key] iso_box(ax, p["box"], col, off=off, holes=p["holes"], axis=p["axis"]) # Numbered balloons keyed to the parts list — avoids overlapping text labels. balloon = {"top": "1", "doubler": "2", "front_wall": "3", "middle": "4", "side_l": "5", "side_r": "5", "rear_wall": "6", "bottom": "7"} for key, off, _ in order: p = P.PANELS_BY_KEY[key] cx = (p["box"][0] + p["box"][3]) / 2 cy = (p["box"][1] + p["box"][4]) / 2 cz = (p["box"][2] + p["box"][5]) / 2 a = iso(cx + off[0], cy + off[1], cz + off[2]) if off != (0, 0, 0): b = iso(cx, cy, cz) ax.annotate("", xy=(b[0] + (a[0] - b[0]) * 0.25, b[1] + (a[1] - b[1]) * 0.25), xytext=a, zorder=9, arrowprops=dict(arrowstyle="-|>", color="#b3261e", lw=0.9, mutation_scale=9, linestyle=(0, (3, 2)))) ax.text(a[0], a[1], balloon[key], fontsize=8.5, color="white", fontweight="bold", ha="center", va="center", zorder=13, bbox=dict(boxstyle="circle,pad=0.32", fc="#b3261e", ec="white", lw=1.0)) ax.relim(); ax.autoscale() axl = fig.add_axes([0.685, 0.075, 0.30, 0.80]); axl.axis("off") axl.set_xlim(0, 1); axl.set_ylim(0, 1) note(axl, 0, 0.99, "PARTS", fs=9.5, weight="bold") parts = [] for key in ["top", "doubler", "front_wall", "middle", "side_l", "rear_wall", "bottom"]: q = P.PANELS_BY_KEY[key] parts.append((str(q["part"]), q["name"].upper(), f'{cut(q["size"][0])} x {cut(q["size"][1])}', "2" if key == "side_l" else "1")) y = 0.945 for n, nm, size, qty in parts: axl.text(0.018, y, n, fontsize=7.5, color="white", fontweight="bold", ha="center", va="top", bbox=dict(boxstyle="circle,pad=0.28", fc="#b3261e", ec="none")) note(axl, 0.075, y, f"{nm}", fs=7.6, weight="bold") note(axl, 0.075, y - 0.025, f"{size} qty {qty}", fs=7.2, color="#555") y -= 0.055 note(axl, 0, y - 0.015, "ASSEMBLY ORDER", fs=9.5, weight="bold") steps = [ "a Glue and screw part 2 to the underside of part 1,\n flush with the wall edge. Set aside.", "b Screw the two part 5 sides to part 7, driving up\n through the bottom into the side edges.", "c Drop part 6 in between the sides, sitting on the\n bottom panel, flush with the wall edge.", "d Drop part 4 on top of part 6; screw through the\n sides into its edges.", "e Drop part 3 in between the sides on top of the\n shelf, flush with the front edge.", "f Cap with the part 1 sub-assembly; screw down\n through it into the sides and both wall edges.", "g Sand, prime, two coats matte black. Fit the\n grommets last, once the paint is properly hard.", ] y -= 0.058 for s in steps: note(axl, 0, y, s, fs=7.4, color="#333") y -= 0.052 note(axl, 0, y - 0.012, "FASTENERS AT EVERY JOINT", fs=9.5, weight="bold") note(axl, 0, y - 0.052, "#8 x 1-1/2\" wood screws at 6\" centres, always into\n" "pre-drilled and countersunk holes (9/64\" clearance\n" "through the outer panel, 3/32\" pilot into the edge)\n" "plus a bead of Titebond II. Plywood edge grain splits\n" "very easily — do not skip the pilot holes. Keep every\n" "screw at least 3/8\" back from a panel end. About 56\n" "screws in total.", fs=7.4, color="#333") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 7 — PANEL DETAIL SHEET # =========================================================================== def page_panels(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "7 · PANEL DETAIL SHEET", "Every unique piece drawn flat at the same scale. These are FINISHED sizes for " "0.703\" PureBond — cut to them exactly.") order = ["top", "bottom", "middle", "side_l", "front_wall", "rear_wall", "doubler"] positions = [(0.055, 0.855), (0.055, 0.655), (0.055, 0.455), (0.055, 0.255), (0.545, 0.855), (0.545, 0.655), (0.545, 0.455)] for key, (px, py) in zip(order, positions): p = P.PANELS_BY_KEY[key] pw, ph = p["size"] ax = fit(fig, px, py, 0.38, (-2.5, 39.0), (-3.2, 13.0)) face(ax, 0, 0, pw, ph, fc="#f2f2f6", lw=1.2) # holes, converted into this panel's own local frame x0, y0, z0, x1, y1, z1 = p["box"] ou = x0 if p["axis"] in ("z", "y") else y0 ov_ = y0 if p["axis"] == "z" else z0 for hx, hy, hd in p["holes"]: col = VENT if abs(hd - P.VENT_DIA) < 1e-6 else HOLE ax.add_patch(Circle((hx - ou, hy - ov_), hd / 2, facecolor="white", edgecolor=col, lw=1.0, zorder=5)) qty = 2 if key == "side_l" else 1 ax.text(0, ph + 1.5, f"{p['part']}. {p['name'].upper()} x{qty}", fontsize=9, fontweight="bold", va="bottom") ax.text(0, ph + 0.45, f'{cut(pw)} x {cut(ph)} x 3/4" nom. — {p["note"] or "no holes"}', fontsize=7.2, color="#555", va="bottom") dimh(ax, 0, pw, -2.0, ext_from=0, fs=6.8) dimv(ax, 0, ph, pw + 1.4, ext_from=pw, fs=6.8) fig.text(0.545, 0.240, f"TOTAL: 8 pieces from 7 unique cuts, {sum(len(q[chr(104)+chr(111)+chr(108)+chr(101)+chr(115)]) for q in P.PANELS if q[chr(113)+chr(116)+chr(121)])} holes.\n\n" "Every cut is a straight rectangle. No rabbets, dados, mitres,\n" "dowels or joinery of any kind anywhere in this design.\n\n" "THE TWO NUMBERS THAT DEPEND ON THICKNESS:\n" f" inner panel length = {fmt(W)} - 2 x thickness = {cut(P.INNER_LEN)}\n" f" side panel height = 2 x bay + thickness = {cut(P.SIDE_HT)}\n\n" "Those assume 0.703\". Measure your actual sheet with calipers\n" "before cutting and recompute if it differs — everything else\n" "on this page is independent of thickness.", fontsize=7.6, color="#333", va="top") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 8 — CUT DIAGRAM # =========================================================================== def page_cutdiagram(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "8 · CUT DIAGRAM — one 4' x 8' sheet of PureBond birch", "Three rip cuts (R1-R3) down the length first, then crosscut the pieces off each " "strip. Grain runs the 96\" length.") ax = fit(fig, 0.065, 0.855, 0.87, (-11, 104), (-10, 54)) face(ax, 0, 0, 96, 48, fc="#fbfbfd", lw=1.6) ax.text(48, 49.4, "4' x 8' SHEET · 96\" x 48\" · ~70 lb", ha="center", fontsize=9.5, fontweight="bold") ax.annotate("", xy=(90, 45), xytext=(70, 45), arrowprops=dict(arrowstyle="-|>", color="#8a7", lw=1.0, mutation_scale=8)) ax.text(80, 45.9, "face grain", ha="center", fontsize=6.8, color="#6a8a6a") for y0, hgt, tag in P.RIPS: ax.plot([0, 96], [y0 + hgt] * 2, color=DIM, lw=1.5) ax.text(-1.8, y0 + hgt / 2, tag, ha="right", va="center", fontsize=8.5, color=DIM, fontweight="bold") LABEL_POS = { # (height fraction, font size, in|out) "top": (0.50, 6.6, "in"), "bottom": (0.79, 6.6, "in"), # above both vent rows "middle": (0.30, 6.6, "in"), # below the grommet row "side_l": (0.50, 5.6, "in"), "side_r": (0.50, 5.6, "in"), "front_wall": (0.50, 6.4, "in"), "rear_wall": (0.00, 6.4, "out"), # 6 holes fill the whole panel "doubler": (0.50, 5.8, "in"), } tone = {"top": "#dfe6f2", "bottom": "#dfe6f2", "middle": "#dfe6f2", "side_l": "#e6f0e6", "side_r": "#e6f0e6", "front_wall": "#f6e9dc", "rear_wall": "#f6e9dc", "doubler": "#f2e2f0"} for key, x0, y0, w, h, rip in P.NEST: p = P.PANELS_BY_KEY[key] ax.add_patch(Rectangle((x0, y0), w, h, facecolor=tone[key], edgecolor=INK, lw=0.9, zorder=3)) # place the caption clear of that piece's own holes frac, fs2, mode = LABEL_POS.get(key, (0.5, 6.4, "in")) nm = "SIDE PANEL" if key.startswith("side") else p["name"].upper() if mode == "out": ax.plot([x0 + w, x0 + w + 1.6], [y0 + h / 2] * 2, color="#888", lw=0.5, zorder=4) ax.text(x0 + w + 2.0, y0 + h / 2, f"{p['part']}. {nm}\n{cut(w)} x {cut(h)}", ha="left", va="center", fontsize=fs2, fontweight="bold", zorder=6, bbox=dict(boxstyle="square,pad=0.2", fc="white", ec="none", alpha=0.9)) else: yy = y0 + h * frac ax.text(x0 + w / 2, yy, f"{p['part']}. {nm}", ha="center", va="bottom", fontsize=fs2, fontweight="bold", zorder=6, bbox=dict(boxstyle="square,pad=0.1", fc="white", ec="none", alpha=0.85)) ax.text(x0 + w / 2, yy - 0.25, f"{cut(w)} x {cut(h)}", ha="center", va="top", fontsize=fs2 - 0.4, color="#444", zorder=6, bbox=dict(boxstyle="square,pad=0.1", fc="white", ec="none", alpha=0.85)) # holes shown in place so you can drill before breaking the strips down ou = p["box"][0] if p["axis"] in ("z", "y") else p["box"][1] ov_ = p["box"][1] if p["axis"] == "z" else p["box"][2] for hx, hy, hd in p["holes"]: col = VENT if abs(hd - P.VENT_DIA) < 1e-6 else HOLE ax.add_patch(Circle((x0 + hx - ou, y0 + hy - ov_), hd / 2, facecolor="white", edgecolor=col, lw=0.7, zorder=5)) # waste, derived from the nest rather than hard-coded used_top = sum(r[1] for r in P.RIPS) waste = [(0.0, used_top, 96.0, 48.0 - used_top)] rowsy = {} for key, x0, y0, w, h, rip in P.NEST: rowsy.setdefault((rip, y0), []).append((x0 + w, h)) for (rip, y0), lst in rowsy.items(): mx = max(v[0] for v in lst) hh = max(v[1] for v in lst) if 96.0 - mx > 0.5: waste.append((mx, y0, 96.0 - mx, hh)) for x0, y0, w, h in waste: ax.add_patch(Rectangle((x0, y0), w, h, facecolor="#f0f0f0", edgecolor=GHOST, lw=0.7, hatch="...", zorder=1)) ax.text(48, used_top + (48 - used_top) / 2, f'OFFCUT — 96" x {fmt(48 - used_top)} left over\n' "(keep it for a monitor riser, shelf stiffeners, or a second rack)", ha="center", va="center", fontsize=8.5, color="#888") dimh(ax, 0, 96, -5.0, ext_from=0) dimv(ax, 0, 48, 100.5, ext_from=96) for y0, hgt, tag in P.RIPS: dimv(ax, y0, y0 + hgt, -6.0, text=fmt(hgt) + " rip", ext_from=0, fs=6.6) fig.text(0.065, 0.185, f"Allow 1/8\" for each saw kerf — the layout leaves {fmt(48 - sum(r[1] for r in P.RIPS))} " "of spare width, so kerf loss never affects a finished dimension.\n" "Cut the three rips first: every panel that has to match another comes off the same " "strip, so their common dimension is identical by construction.\n" f"Drill all {sum(len(q[chr(104)+chr(111)+chr(108)+chr(101)+chr(115)]) for q in P.PANELS if q[chr(113)+chr(116)+chr(121)])} holes while the pieces are still large and easy to clamp, then make " "the final crosscuts.", fontsize=7.8, color="#555", va="top") pdf.savefig(fig); plt.close(fig) # =========================================================================== # PAGE 9 — WHAT TO ASK HOME DEPOT FOR # =========================================================================== def page_homedepot(pdf): fig = plt.figure(figsize=(11, 8.5)) frame(fig, "9 · WHAT TO ASK HOME DEPOT FOR", "Hand this sheet to the associate at the panel saw. They can do the straight cuts. " "They cannot drill the holes.") axl = fig.add_axes([0.055, 0.10, 0.42, 0.78]); axl.axis("off") axl.set_xlim(0, 1); axl.set_ylim(0, 1) note(axl, 0, 0.99, "CUT REQUEST", fs=11, weight="bold") note(axl, 0, 0.955, "Sheet: 3/4 in. x 4 ft. x 8 ft. PureBond Birch Plywood\n" "Internet #100077837 · Model #165921", fs=7.8, color="#555") ct = [fmt(r[1]) for r in P.RIPS] rows = [["", "Cut", "Result"], ["1", f"Rip {ct[0]} off the 48\" width", f"strip R1, {ct[0]} x 96\""], ["2", f"Rip another {ct[1]}", f"strip R2, {ct[1]} x 96\""], ["3", f"Rip {ct[2]}", f"strip R3, {ct[2]} x 96\""], ["4", f"Crosscut R1 at {fmt(W)} and again at {fmt(W)}", "TOP and BOTTOM panels"], ["5", "Crosscut R2 at " + cut(P.INNER_LEN), "MIDDLE SHELF"], ["6", f"Crosscut R2 at {fmt(D)}, twice", "two SIDE blanks"], ["7", f"Rip R3 lengthwise at {fmt(P.CLEAR)}", f"a {fmt(P.CLEAR)} strip and a 2-3/4\" strip"], ["8", f"Crosscut the {fmt(P.CLEAR)} strip at " + cut(P.INNER_LEN) + ", twice", "FRONT WALL and REAR WALL"], ["9", "Crosscut the 2-3/4\" strip at " + cut(P.INNER_LEN), "CLAMP DOUBLER"]] y = 0.885 for i, r in enumerate(rows): w = "bold" if i == 0 else "normal" note(axl, 0.0, y, r[0], fs=7.6, weight="bold", color=ACC_ if i else INK) note(axl, 0.07, y, r[1], fs=7.6, weight=w) note(axl, 0.60, y, r[2], fs=7.6, weight=w, color="#555" if i else INK) axl.plot([0, 1], [y - 0.028, y - 0.028], color="#e2e2e6", lw=0.6) y -= 0.055 note(axl, 0, y - 0.02, "FINISH THE SIDE PANELS AT HOME", fs=10, weight="bold") note(axl, 0, y - 0.062, f"Leave the two side blanks at {fmt(D)} x {fmt(D)} and trim\n" f"them to {cut(P.SIDE_HT)} yourself once you have measured your\n" f"actual sheet thickness. Side height = 2 x bay + thickness.\n" f"At the listed 0.703\" that is {cut(P.SIDE_HT)}.", fs=7.8, color="#333") axr = fig.add_axes([0.53, 0.10, 0.42, 0.78]); axr.axis("off") axr.set_xlim(0, 1); axr.set_ylim(0, 1) note(axr, 0, 0.99, "WHAT THEY WILL AND WON'T DO", fs=11, weight="bold") note(axr, 0, 0.945, "CUTS — yes. Every store has a panel saw and will break\n" "down a sheet for you. Policy varies: many stores give you\n" "the first one or two cuts free and charge roughly a dollar\n" "a cut after that, and some cap the total number. Call your\n" "store first and ask, because nine cuts is on the high side.\n\n" "TOLERANCE — treat the panel saw as plus or minus 1/16\",\n" "not a cabinet shop. That is why the rips come first: any\n" "two panels that must match are cut from the same strip, so\n" "they end up identical whatever the saw actually did.\n\n" "SMALL PIECES — they will usually refuse anything under\n" f"about 12\". Nothing in step 1-9 is smaller than {fmt(D)},\n" "and if they baulk at the side blanks just take the whole\n" "strip home and crosscut it yourself.\n\n" "HOLES — no. Home Depot does not drill, bore or cut circles\n" "as a service, at any store. All 24 holes are yours to do\n" "with a drill and two hole saws (1-1/2\" and 2\"). It is about\n" "twenty minutes of work.\n\n" "SQUARE — do not assume the factory edges are square or\n" "that the sheet is exactly 48\" x 96\". Ask them to trim a\n" "thin strip off one long edge first if it looks rough, then\n" "measure the rips from that clean reference edge.", fs=7.8, color="#333") note(axr, 0, 0.30, "IF YOU WOULD RATHER NOT HAUL A FULL SHEET", fs=10, weight="bold") note(axr, 0, 0.258, "A 4x8 sheet of 3/4\" birch is about 70 lb and will not fit\n" "in most cars. Options: have the store do the three rips so\n" "you leave with three manageable strips, use Home Depot's\n" "truck rental, or buy two 2 ft x 4 ft project panels instead\n" "— though you will need a third for the long pieces and\n" "it works out more expensive.", fs=7.8, color="#333") pdf.savefig(fig); plt.close(fig) def main(): out = os.path.join(HERE, "underdesk-rack-drawings.pdf") with PdfPages(out) as pdf: page_front(pdf) page_rear(pdf) page_section(pdf) page_top(pdf) page_holes(pdf) page_exploded(pdf) page_panels(pdf) page_cutdiagram(pdf) page_homedepot(pdf) pdf.infodict()["Title"] = "Under-desk clamp-on homelab rack — construction drawings" print("wrote", out) if __name__ == "__main__": main()