A reproduction circular slide rule from the mid-1960s may not be the cutting edge of consumer demand, but the pen version of a Tektronix Circuit Computer came out pretty well:

A Bash script compiles the GCMC code with eight different parameter combinations to produce pairs of G-Code files to draw (“engrave” being aspirational) and cut (“mill”, likewise) the three decks and the cursor.
The CNC 3018XL with a Pilot V5RT pen draws the deck scales on white paper:

Better paper definitely produces better results, so I must rummage through the Big Box o’ Paper to see what lies within. Laminating the decks improves their durability and matches the original Tek surface finish.
The MPCNC with a drag knife blade cuts through a laminated deck like butter:

Setting the XY origin to dead center on each deck requires carefully calibrating the USB video camera, with the end result accurate to maybe ±0.1 mm around the entire perimeter. Both machines move equal linear distances along both axes, which was definitely comforting.
Having made half a dozen cursors from various bits of acrylic, none of which look particularly good, demonstrates my engraving hand is too weak for a complete slide rule:

With logarithmic scales in hand, however, adapting the GCMC source code to produce general-purpose circular slide rules with only two decks and smaller diameters may be the way to improve my engraving-fu, as a full-scale Tektronix Circuit Computer would chew up three square-foot plastic sheets.
A general-purpose slide rule would need multi-color (well, at least bi-color) labels and digits for red “inverse” scales to remind you (well, me) they read backwards. Some slipsticks use left-slanting italics, left-pointing markers (“<2”), or other weirdness, but they’re all different.
An early small-scale version engraved on ABS came out OK, modulo poor ink fill:

Engraving the decks on hard drive platters doesn’t count:

All in all, it’s been an interesting exercise and, as you may have guessed, will become a Digital Machinist column.
The GCMC and Bash source code as a GitHub Gist:
// Tektronix Circuit Computer Reproduction | |
// Ed Nisley KE4ZNU - 2019-11 | |
//----- | |
// Library routines | |
include("tracepath.inc.gcmc"); | |
include("engrave.inc.gcmc"); | |
TekOD = to_mm(7.75in); // orginal Tek Circuit Computer diameter | |
FALSE = 0; | |
TRUE = 1; | |
//----- | |
// Command line parameters | |
// -D various useful tidbits | |
// add unit to speeds and depths: 2000mm / -3.00mm / etc | |
if (!isdefined("BaseOD")) { | |
BaseOD = TekOD; | |
} | |
comment("Base OD: ",BaseOD); | |
SizeRatio = BaseOD / TekOD; // overall scaling for different base diameters | |
comment(" scale factor: ",SizeRatio); | |
if (!isdefined("SelectPart")) { | |
SelectPart = "Bottom"; | |
} | |
comment("Part: ",SelectPart); | |
if (!isdefined("Operation")) { | |
Operation = "Engrave"; | |
} | |
comment("Operation: ",Operation); | |
if (!isdefined("ScaleSpeed")) { | |
ScaleSpeed = 2400mm; | |
} | |
if (!isdefined("TextSpeed")) { | |
TextSpeed = 2400mm; | |
} | |
// Engraving & drag knife force is proportional to depth, but you must know the coefficent! | |
if (!isdefined("EngraveZ")) { | |
EngraveZ = -1.0mm; | |
} | |
if (!isdefined("KnifeZ")) { | |
KnifeZ = -2.0mm; | |
} | |
if (!isdefined("KnifeSpeed")) { | |
KnifeSpeed = 1000mm; | |
} | |
//----- | |
// Define useful constants | |
SafeZ = 10.00mm; // above all obstructions | |
TravelZ = 1.00mm; // within engraving area | |
//----- | |
// Overall values | |
ScaleHeight = to_inch(3.0/8.0) * SizeRatio; // scale-to-scale distance | |
WindowHeight = ScaleHeight; // cutout window opening | |
DeckBottomOD = BaseOD; // deck sizes depend on scale height | |
DeckMiddleOD = DeckBottomOD - 2*ScaleHeight; | |
DeckTopOD = DeckMiddleOD - 2*(ScaleHeight + WindowHeight); | |
ScaleArc = 18deg; // angular length of one decade: +CCW | |
ScaleExdent = 0.20; // log spacing at end of scales to identifiers | |
Scale2Pi = log10(2*pi()) * ScaleArc; // angular offset for scales using 2*pi | |
ScaleRT = log10(2.197225) * ScaleArc; // angular offset for risetime | |
TauAngle = 150deg; // arbitrary offset to 1.0 on tau scales | |
TitleAngle = -50deg; // ... to Tek title, then +180deg to logo | |
INWARD = -1; // text and tick alignment (used as integers) | |
OUTWARD = 1; | |
TEXT_LEFT = -1; // text justification | |
TEXT_CENTERED = 0; | |
TEXT_RIGHT = 1; | |
TextFont = FONT_HSANS_1_RS; | |
TitleTextSize = 3.1 * SizeRatio * [1.0mm,1.0mm]; | |
LegendTextSize = 1.8 * SizeRatio * [1.0mm,1.0mm]; | |
ScaleTextSize = 1.4 * SizeRatio * [1.0mm,1.0mm]; | |
//---- | |
// Define tick layout for scales | |
// Numeric values = scale position, tick length | |
// These are not algorithmic! | |
TickMajor = 3.2mm * SizeRatio; // length of tick marks | |
TickMid = 1.9mm * SizeRatio; | |
TickMinor = 1.2mm * SizeRatio; | |
TickScaleNarrow = { | |
[1.0,TickMajor], | |
[1.1,TickMinor],[1.2,TickMinor],[1.3,TickMinor],[1.4,TickMinor], | |
[1.5,TickMid], | |
[1.6,TickMinor],[1.7,TickMinor],[1.8,TickMinor],[1.9,TickMinor], | |
[2.0,TickMajor], | |
[2.2,TickMinor],[2.4,TickMinor],[2.6,TickMinor],[2.8,TickMinor], | |
[3.0,TickMajor], | |
[3.2,TickMinor],[3.4,TickMinor],[3.6,TickMinor],[3.8,TickMinor], | |
[4.0,TickMajor], | |
[4.5,TickMinor], | |
[5.0,TickMajor], | |
[5.5,TickMinor], | |
[6.0,TickMajor], | |
[6.5,TickMinor], | |
[7.0,TickMajor], | |
[7.5,TickMinor], | |
[8.0,TickMajor], | |
[8.5,TickMinor], | |
[9.0,TickMajor], | |
[9.5,TickMinor] | |
}; | |
TickScaleWide = { | |
[1.0,TickMajor], | |
[1.1,TickMinor],[1.2,TickMinor],[1.3,TickMinor],[1.4,TickMinor], | |
[1.5,TickMid], | |
[1.6,TickMinor],[1.7,TickMinor],[1.8,TickMinor],[1.9,TickMinor], | |
[2.0,TickMajor], | |
[2.1,TickMinor],[2.2,TickMinor],[2.3,TickMinor],[2.4,TickMinor], | |
[2.5,TickMid], | |
[2.6,TickMinor],[2.7,TickMinor],[2.8,TickMinor],[2.9,TickMinor], | |
[3.0,TickMajor], | |
[3.2,TickMinor],[3.4,TickMinor],[3.6,TickMinor],[3.8,TickMinor], | |
[4.0,TickMajor], | |
[4.2,TickMinor],[4.4,TickMinor],[4.6,TickMinor],[4.8,TickMinor], | |
[5.0,TickMajor], | |
[5.5,TickMinor], | |
[6.0,TickMajor], | |
[6.5,TickMinor], | |
[7.0,TickMajor], | |
[7.5,TickMinor], | |
[8.0,TickMajor], | |
[8.5,TickMinor], | |
[9.0,TickMajor], | |
[9.5,TickMinor] | |
}; | |
TickLabels = [1,2,5]; // labels only these ticks, must be integers | |
TickGap = 0.50 * ScaleTextSize.y; // gap between text and ticks | |
PivotOD = 5.0mm; // center bolt OD | |
Legend1 = "Ed Nisley - KE4ZNU"; | |
Legend2 = "softsolder.com"; | |
//----------------------------------------------------------------------------- | |
// Text & Scale Engraving | |
//----- | |
// Write text on a radial line | |
function RadialText(TextPath,CenterPt,Radius,Angle,Justify,Orient) { | |
local pl = TextPath[-1].x; // path length | |
local ji = (Justify == TEXT_LEFT) ? 0mm : // justification, assume OUTWARD | |
(Justify == TEXT_CENTERED) ? -pl/2 : | |
(Justify == TEXT_RIGHT) ? -pl : | |
0mm; | |
if (Orient == INWARD) { | |
TextPath = rotate_xy(TextPath,180deg); | |
ji = -ji; | |
} | |
TextPath += [Radius + ji,0mm]; | |
return rotate_xy(TextPath,Angle) + CenterPt; | |
} | |
//----- | |
// Draw a radial legend | |
// Offset in units of char height: 0 = baseline on radius, +/- = above/below | |
function RadialLegend(Text,Center,Radius,Angle,Justify,Orient,Offset) { | |
local tp = scale(typeset(Text,TextFont),LegendTextSize) + [0mm,Offset * LegendTextSize.y]; | |
local tpr = RadialText(tp,Center,Radius,Angle,Justify,Orient); | |
feedrate(TextSpeed); | |
engrave(tpr,TravelZ,EngraveZ); | |
} | |
//----- | |
// Bend text around an arc | |
function ArcText(TextPath,CenterPt,Radius,BaseAngle,Justify,Orient) { | |
local pl = TextPath[-1].x; // path length | |
local c = 2*pi()*Radius; | |
local ta = to_deg(360 * pl / c); // subtended angle | |
local ja = (Justify == TEXT_LEFT ? 0deg : // assume OUTWARD | |
(Justify == TEXT_CENTERED) ? -ta / 2 : | |
(Justify == TEXT_RIGHT) ? -ta : | |
0deg); | |
ja = BaseAngle + Orient*ja; | |
local ArcPath = {}; | |
local pt,r,a; | |
foreach(TextPath; pt) { | |
if (!isundef(pt.x) && !isundef(pt.y) && isundef(pt.z)) { // XY motion, no Z | |
r = (Orient == OUTWARD) ? Radius - pt.y : Radius + pt.y; | |
a = Orient * 360deg * (pt.x / c) + ja; | |
ArcPath += {[r*cos(a) + CenterPt.x, r*sin(a) + CenterPt.y,-]}; | |
} | |
elif (isundef(pt.x) && isundef(pt.y) && !isundef(pt.z)) { // no XY, Z up/down | |
ArcPath += {pt}; | |
} | |
else { | |
error("ArcText - Point is not pure XY or pure Z: " + to_string(pt)); | |
} | |
} | |
return ArcPath; | |
} | |
//----- | |
// Draw scale legend | |
function ArcLegend(Text,Radius,Angle,Orient) { | |
local tp = scale(typeset(Text,TextFont),LegendTextSize); | |
local tpa = ArcText(tp,[0mm,0mm],Radius,Angle,TEXT_CENTERED,Orient); | |
feedrate(TextSpeed); | |
engrave(tpa,TravelZ,EngraveZ); | |
} | |
//----- | |
// Draw a decade of ticks & labels | |
// ArcLength > 0 = CCW, < 0 = CW | |
// UnitOnly forces just the unit tick, so as to allow creating the last tick of the scale | |
function DrawTicks(Radius,TickMarks,TickOrient,UnitAngle,ArcLength,Decade,LabelOrient,UnitOnly) { | |
feedrate(ScaleSpeed); | |
local a,r0,r1,p0,p1; | |
if (Decade == 1 || UnitOnly) { // draw unit marker | |
a = UnitAngle; | |
r0 = Radius + TickOrient * (TickMajor + 2*TickGap + ScaleTextSize.y); | |
p0 = r0 * [cos(a),sin(a)]; | |
r1 = Radius + TickOrient * (ScaleHeight - 2*TickGap); | |
p1 = r1 * [cos(a),sin(a)]; | |
goto(p0); | |
move([-,-,EngraveZ]); | |
move(p1); | |
goto([-,-,TravelZ]); | |
} | |
local ticklist = UnitOnly ? {TickMarks[0]} : TickMarks; | |
local tick; | |
foreach(ticklist; tick) { | |
a = UnitAngle + ArcLength * log10(tick[0]); | |
p0 = Radius * [cos(a), sin(a)]; | |
p1 = (Radius + TickOrient*tick[1]) * [cos(a), sin(a)]; | |
goto(p0); | |
move([-,-,EngraveZ]); | |
move(p1); | |
goto([-,-,TravelZ]); | |
} | |
feedrate(TextSpeed); // draw scale values | |
local lrad = Radius + TickOrient * (TickMajor + TickGap); | |
if (TickOrient == INWARD) { | |
if (LabelOrient == INWARD) { | |
lrad -= ScaleTextSize.y; // inward ticks + inward labels = offset inward | |
} | |
} | |
else { | |
if (LabelOrient == OUTWARD) { | |
lrad += ScaleTextSize.y; // outward ticks + outward labels = offset outward | |
} | |
} | |
ticklist = UnitOnly ? [TickLabels[0]] : TickLabels; | |
local ltext,lpath,tpa; | |
foreach(ticklist; tick) { | |
ltext = to_string(Decade * to_int(tick)); | |
lpath = scale(typeset(ltext,TextFont),ScaleTextSize); | |
a = UnitAngle + ArcLength * log10(tick); | |
tpa = ArcText(lpath,[0mm,0mm],lrad,a,TEXT_CENTERED,LabelOrient); | |
engrave(tpa,TravelZ,EngraveZ); | |
} | |
} | |
//----- | |
// Mark key locations | |
function MarkPivot() { | |
comment("Mark center point"); | |
feedrate(ScaleSpeed); | |
if (TRUE) { | |
goto([-,-,SafeZ]); | |
goto([PivotOD/2,0,-]); | |
move([-,-,EngraveZ]); | |
circle_cw([0,0]); // outline pivot | |
move([-PivotOD/2,0,-]); // draw X line | |
goto([-,-,TravelZ]); | |
goto([0,PivotOD/2,-]); | |
move([-,-,EngraveZ]); | |
move ([0,-PivotOD/2,-]); // draw Y line | |
goto([-,-,TravelZ]); | |
} | |
} | |
//----- | |
// Draw attribution | |
function DrawAttribution(AttribRad) { | |
comment("Attribution at: ",AttribRad); | |
feedrate(TextSpeed); | |
local tp,tpa; | |
if (Legend1) { | |
tp = scale(typeset(Legend1,TextFont),TitleTextSize); | |
tpa = ArcText(tp,[0mm,0mm],AttribRad,0deg,TEXT_CENTERED,OUTWARD); | |
feedrate(TextSpeed); | |
engrave(tpa,TravelZ,EngraveZ); | |
} | |
if (Legend2) { | |
tp = scale(typeset(Legend2,TextFont),TitleTextSize); | |
tpa = ArcText(tp,[0mm,0mm],AttribRad,180deg,TEXT_CENTERED,OUTWARD); | |
feedrate(TextSpeed); | |
engrave(tpa,TravelZ,EngraveZ); | |
} | |
if (FALSE) { // test code to verify ArcText | |
comment("ArcText test"); | |
ctr = [0mm,0mm]; | |
tp = scale(typeset("Right Inward",TextFont),ScaleTextSize); | |
tpa = ArcText(tp,ctr,30mm,45deg,TEXT_RIGHT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
tp = scale(typeset("Right Outward",TextFont),ScaleTextSize); | |
tpa = ArcText(tp,ctr,30mm,45deg,TEXT_RIGHT,OUTWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
tp = scale(typeset("Center Inward",TextFont),ScaleTextSize); | |
tpa = ArcText(tp,ctr,20mm,45deg,TEXT_CENTERED,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
tp = scale(typeset("Center Outward",TextFont),ScaleTextSize); | |
tpa = ArcText(tp,ctr,20mm,45deg,TEXT_CENTERED,OUTWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
tp = scale(typeset("Left Inward",TextFont),ScaleTextSize); | |
tpa = ArcText(tp,ctr,10mm,45deg,TEXT_LEFT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
tp = scale(typeset("Left Outward",TextFont),ScaleTextSize); | |
tpa = ArcText(tp,ctr,10mm,45deg,TEXT_LEFT,OUTWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
goto([0mm,0mm,-]); | |
move([40mm,40mm,-]); | |
} | |
if (FALSE) { // test code to verify RadialText | |
comment("RadialText test"); | |
ctr = [0mm,0mm]; | |
r = 20mm; | |
a = 0deg; | |
tp = scale(typeset("Left Inward",TextFont),LegendTextSize); | |
tpr = RadialText(tp,ctr,r,a,TEXT_LEFT,INWARD); | |
feedrate(TextSpeed); | |
engrave(tpr,TravelZ,EngraveZ); | |
tp = scale(typeset("Left Outward",TextFont),LegendTextSize); | |
tpr = RadialText(tp,ctr,r,a,TEXT_LEFT,OUTWARD); | |
feedrate(TextSpeed); | |
engrave(tpr,TravelZ,EngraveZ); | |
a = 90deg; | |
tp = scale(typeset("Right Inward",TextFont),LegendTextSize); | |
tpr = RadialText(tp,ctr,r,a,TEXT_RIGHT,INWARD); | |
feedrate(TextSpeed); | |
engrave(tpr,TravelZ,EngraveZ); | |
tp = scale(typeset("Right Outward",TextFont),LegendTextSize); | |
tpr = RadialText(tp,ctr,r,a,TEXT_RIGHT,OUTWARD); | |
feedrate(TextSpeed); | |
engrave(tpr,TravelZ,EngraveZ); | |
a = 180deg; | |
tp = scale(typeset("Center Inward",TextFont),LegendTextSize); | |
tpr = RadialText(tp,ctr,r,a,TEXT_CENTERED,INWARD); | |
feedrate(TextSpeed); | |
engrave(tpr,TravelZ,EngraveZ); | |
tp = scale(typeset("Center Outward",TextFont),LegendTextSize); | |
tpr = RadialText(tp,ctr,r,a,TEXT_CENTERED,OUTWARD); | |
feedrate(TextSpeed); | |
engrave(tpr,TravelZ,EngraveZ); | |
a = 270deg; | |
RadialLegend("Offset to radius",ctr,r,a,TEXT_CENTERED,INWARD,-0.5); | |
goto(ctr); | |
move([0,-2*r,EngraveZ]); | |
goto([r,0mm,-]); | |
circle_cw(ctr); | |
} | |
} | |
//----------------------------------------------------------------------------- | |
// Deck Engraving | |
//---------- | |
// Engrave bottom deck | |
function EngraveBottom() { | |
// Mark center pivot | |
MarkPivot(); | |
comment("Inductance scale"); | |
Radius = DeckRad - ScaleHeight; | |
MinLog = -9; | |
MaxLog = 6; | |
Arc = -ScaleArc; | |
dec = 1; | |
offset = 0deg; | |
for (logval = MinLog; logval < MaxLog; logval++) { | |
a = offset + logval * Arc; | |
DrawTicks(Radius,TickScaleNarrow,OUTWARD,a,Arc,dec,INWARD,FALSE); | |
dec = (dec == 100) ? 1 : 10 * dec; | |
} | |
a = offset + MaxLog * Arc; | |
DrawTicks(Radius,TickScaleNarrow,OUTWARD,a,Arc,1000,INWARD,TRUE); | |
r = Radius + TickMajor + 2*TickGap + LegendTextSize.y; | |
logval = MinLog + 1.5; | |
a = offset + logval * Arc; | |
ArcLegend("nH - nanohenry x10^-9",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("μH - microhenry x10^-6",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("mH - millihenry x10^-3",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("H - henry",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("kH - kilohenry x10^3",r,a,INWARD); | |
r = Radius + TickMajor + TickGap; | |
logval = MinLog - ScaleExdent; // scale identifiers | |
a = offset + logval * Arc; | |
tp = scale(typeset("L Scale →",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_RIGHT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
logval = MaxLog + ScaleExdent; | |
a = offset + logval * Arc; | |
tp = scale(typeset("← L Scale",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_LEFT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
comment("Inductive frequency scale"); | |
Radius = DeckRad - 2*ScaleHeight; | |
MinLog = 0; | |
MaxLog = 9; | |
Arc = 2*ScaleArc; // double-length scale for square roots | |
dec = 1; | |
offset = -(18 * ScaleArc - Scale2Pi); // using 18 degree arc length | |
for (logval = MinLog; logval < MaxLog; logval++) { | |
a = offset + logval * Arc; | |
DrawTicks(Radius,TickScaleWide,OUTWARD,a,Arc,dec,OUTWARD,FALSE); | |
dec = (dec == 100) ? 1 : 10 * dec; | |
} | |
a = offset + MaxLog * Arc; | |
DrawTicks(Radius,TickScaleWide,OUTWARD,a,Arc,1000,OUTWARD,TRUE); | |
feedrate(TextSpeed); // draw prefix legends | |
r = Radius + TickMajor + 2*TickGap + 2*LegendTextSize.y; | |
logval = MinLog + 0.5; | |
for (i = 0; i < 3; i++) { | |
a = offset + (i + logval) * Arc; | |
ArcLegend("Hz - hertz",r,a,OUTWARD); | |
} | |
for (i = 3; i < 6; i++) { | |
a = offset + (i + logval) * Arc; | |
ArcLegend("kHz - kilohertz x10^3",r,a,OUTWARD); | |
} | |
for (i = 6; i < 9; i++) { | |
a = offset + (i + logval) * Arc; | |
ArcLegend("MHz - megahertz x10^6",r,a,OUTWARD); | |
} | |
r = Radius + TickMajor + TickGap + LegendTextSize.y; | |
logval = MinLog - 0.5; // scale identifier | |
a = offset + logval * Arc; | |
ArcLegend("←------- FL Scale -------→",r,a,OUTWARD); | |
comment("Inductive TC / Risetime scale"); | |
Radius = DeckRad - 3*ScaleHeight; | |
MinLog = -12; | |
MaxLog = 3; | |
Arc = -ScaleArc; | |
dec = 1; | |
offset = -TauAngle; | |
for (logval = MinLog; logval < MaxLog; logval++) { | |
a = offset + logval * Arc; | |
DrawTicks(Radius,TickScaleNarrow,OUTWARD,a,Arc,dec,INWARD,FALSE); | |
dec = (dec == 100) ? 1 : 10 * dec; | |
} | |
a = offset + MaxLog * Arc; | |
DrawTicks(Radius,TickScaleNarrow,OUTWARD,a,Arc,1000,INWARD,TRUE); | |
feedrate(TextSpeed); // prefix legends | |
r = Radius + TickMajor + 2*TickGap + LegendTextSize.y; | |
logval = MinLog + 1.5; | |
a = offset + logval * Arc; | |
ArcLegend("ps - picosecond x10^-12",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("ns - nanosecond x10^-9",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("μs - microsecond x10^-6",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("ms - millisecond x10^-3",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("s - second",r,a,INWARD); | |
r = Radius + TickMajor + TickGap; | |
logval = MinLog - ScaleExdent; // scale identifiers | |
a = offset + logval * Arc; | |
tp = scale(typeset("τL Scale →",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_RIGHT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
logval = MaxLog + ScaleExdent; | |
a = offset + logval * Arc; | |
tp = scale(typeset("← τL Scale",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_LEFT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
//----- | |
// Add construction notes | |
comment("Attribution begins"); | |
r = DeckTopOD/2 - 2*ScaleHeight - WindowHeight; | |
DrawAttribution(r); | |
if (FALSE) { | |
t = "Disk OD: " + to_string(DeckBottomOD) + " " + | |
to_string(DeckMiddleOD) + " " + | |
to_string(DeckTopOD) + " mm"; | |
tp = scale(typeset(t,TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,90deg,TEXT_CENTERED,OUTWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
} | |
goto([-,-,SafeZ]); // done, so get out of the way | |
goto([0,0,-]); | |
comment("Bottom deck ends"); | |
} | |
//---------- | |
// Engrave middle deck | |
function EngraveMiddle() { | |
// Mark center pivot | |
MarkPivot(); | |
comment("Capacitance scale"); | |
Radius = DeckRad; | |
MinLog = -15; | |
MaxLog = 0; | |
Arc = ScaleArc; | |
dec = 1; | |
offset = 0deg; | |
for (logval = MinLog; logval < MaxLog; logval++) { | |
a = offset + logval * Arc; | |
DrawTicks(Radius,TickScaleNarrow,INWARD,a,Arc,dec,INWARD,FALSE); | |
dec = (dec == 100) ? 1 : 10 * dec; | |
} | |
a = offset + MaxLog * Arc; | |
DrawTicks(Radius,TickScaleNarrow,INWARD,a,Arc,1000,INWARD,TRUE); | |
r = Radius - ScaleHeight + TickGap; | |
logval = MinLog + 1.5; | |
a = offset + logval * Arc; | |
ArcLegend("fF - femtofarad x10^-15",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("pF - picofarad x10^-12",r,a,INWARD); | |
logval += 2.5; // offset for L/R window; | |
a = offset + logval * Arc; | |
ArcLegend("nF - nanofarad x10^-9",r,a,INWARD); | |
logval += 4; // … likewise | |
a = offset + logval * Arc; | |
ArcLegend("μF - microfarad x10^-6",r,a,INWARD); | |
logval += 2.5; // … restore normal spacing | |
a = offset + logval * Arc; | |
ArcLegend("mF - millifarad x10^-3",r,a,INWARD); | |
r = Radius - ScaleHeight - TickGap - LegendTextSize.y; // into blank space | |
logval = MinLog; // scale identifiers | |
a = offset + logval * Arc; | |
tp = scale(typeset("←--- C Scale",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_RIGHT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
logval = MinLog + 6; | |
a = offset + logval * Arc; | |
tp = scale(typeset("←--- C Scale ---→",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_CENTERED,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
logval = MaxLog; | |
a = offset + logval * Arc; | |
tp = scale(typeset("C Scale ---→",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_LEFT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
comment("Capacitive TC / risetime scale"); | |
Radius = DeckRad - 4*ScaleHeight; | |
MinLog = -12; | |
MaxLog = 3; | |
Arc = ScaleArc; | |
dec = 1; | |
offset = 3 * ScaleArc; | |
for (logval = MinLog; logval < MaxLog; logval++) { | |
a = offset + logval * Arc; | |
DrawTicks(Radius,TickScaleNarrow,OUTWARD,a,Arc,dec,INWARD,FALSE); | |
dec = (dec == 100) ? 1 : 10 * dec; | |
} | |
a = offset + MaxLog * Arc; | |
DrawTicks(Radius,TickScaleNarrow,OUTWARD,a,Arc,1000,INWARD,TRUE); | |
r = Radius + TickMajor + 2*TickGap + LegendTextSize.y; | |
logval = MinLog + 1.5; | |
a = offset + logval * Arc; | |
ArcLegend("ps - picosecond x10^-12",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("ns - nanosecond x10^-9",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("μs - microsecond x10^-6",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("ms - millisecond x10^-3",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("s - second",r,a,INWARD); | |
r = Radius + TickMajor + TickGap; | |
logval = MinLog - ScaleExdent; // scale identifiers | |
a = offset + logval * Arc; | |
tp = scale(typeset("← τC Scale",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_LEFT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
logval = MaxLog + ScaleExdent; | |
a = offset + logval * Arc; | |
tp = scale(typeset("τC Scale →",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_RIGHT,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
logval = MinLog - 2.5; | |
a = offset + logval * Arc; | |
tp = scale(typeset("←--- τC Scale ---→",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_CENTERED,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
comment("Inductive frequency scale legend"); | |
r = DeckRad - ScaleHeight - ScaleTextSize.y; | |
a = 58deg; // arbitrary text placement | |
ArcLegend("FL Scale",r,a,OUTWARD); | |
comment("Index for resonance calculations"); | |
Index = -(18*ScaleArc + Scale2Pi); // negative to read reciprocal of product | |
r = DeckRad - 1.5*ScaleHeight + 0.5*LegendTextSize.y; | |
ArcLegend("Frequency",r,Index,OUTWARD); | |
r = DeckRad - ScaleHeight - LegendTextSize.y; | |
ArcLegend("⇑",(r - TickGap),Index,INWARD); | |
r = DeckRad - 2*ScaleHeight + LegendTextSize.y; | |
ArcLegend("⇑",(r + TickGap),Index,OUTWARD); | |
r0 = DeckRad - ScaleHeight; | |
r1 = r0 - TickMajor; | |
goto(r0 * [cos(Index),sin(Index)]); | |
move([-,-,EngraveZ]); | |
move(r1 * [cos(Index),sin(Index)]); | |
goto([-,-,TravelZ]); | |
r0 = DeckRad - 2*ScaleHeight; | |
r1 = r0 + TickMajor; | |
goto(r0 * [cos(Index),sin(Index)]); | |
move([-,-,EngraveZ]); | |
move(r1 * [cos(Index),sin(Index)]); | |
goto([-,-,TravelZ]); | |
//----- | |
// Draw the attribution | |
comment("Attribution begins"); | |
r = DeckTopOD/2 - 2*ScaleHeight - WindowHeight; | |
DrawAttribution(r); | |
goto([-,-,SafeZ]); // done, so get out of the way | |
goto([0,0,-]); | |
comment("Middle deck ends"); | |
} | |
//---------- | |
// Engrave top deck | |
function EngraveTop() { | |
// Mark center pivot | |
MarkPivot(); | |
comment("Resistance scale"); | |
Radius = DeckRad; | |
MinLog = -1; | |
MaxLog = 8; | |
Arc = -ScaleArc; | |
dec = 100; | |
offset = 0deg; | |
for (logval = MinLog; logval < MaxLog; logval++) { | |
a = offset + logval * Arc; | |
DrawTicks(Radius,TickScaleNarrow,INWARD,a,Arc,dec,INWARD,FALSE); | |
dec = (dec == 100) ? 1 : 10 * dec; | |
} | |
a = offset + MaxLog * Arc; | |
DrawTicks(Radius,TickScaleNarrow,INWARD,a,Arc,100,INWARD,TRUE); | |
r = Radius - ScaleHeight + TickGap; | |
logval = MinLog + 0.5; | |
a = offset + logval * Arc; | |
ArcLegend("mΩ - milliohm",r,a,INWARD); | |
logval = MinLog + 2.5; | |
a = offset + logval * Arc; | |
ArcLegend("Ω - ohm",r,a,INWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("kΩ - kilohm x10^3",r,a,INWARD); | |
logval = MaxLog - 1; | |
a = offset + logval * Arc; | |
ArcLegend("MΩ - megohm x10^6",r,a,INWARD); | |
r = Radius - ScaleHeight - TickGap - LegendTextSize.y; | |
logval = MinLog + 4; | |
a = offset + logval * Arc; | |
ArcLegend("←--- R XC XL Scale ---→",r,a,INWARD); | |
comment("Capacitive frequency scale"); | |
Radius = DeckRad; | |
MinLog = 0; | |
MaxLog = 9; | |
Arc = ScaleArc; | |
dec = 1; | |
offset = 18 * -ScaleArc; | |
for (logval = MinLog; logval < MaxLog; logval++) { | |
a = offset + logval * Arc; | |
DrawTicks(Radius,TickScaleNarrow,INWARD,a,Arc,dec,OUTWARD,FALSE); | |
dec = (dec == 100) ? 1 : 10 * dec; | |
} | |
a = offset + MaxLog * Arc; | |
DrawTicks(Radius,TickScaleNarrow,INWARD,a,Arc,1000,OUTWARD,TRUE); | |
r = Radius - (TickMajor + 2*TickGap + LegendTextSize.y); | |
logval = MinLog + 1.5; | |
a = offset + logval * Arc; | |
ArcLegend("Hz - hertz",r,a,OUTWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("kHz - kilohertz x10^3",r,a,OUTWARD); | |
logval += 3; | |
a = offset + logval * Arc; | |
ArcLegend("MHz - megahertz x10^6",r,a,OUTWARD); | |
r = Radius - ScaleHeight - TickGap - LegendTextSize.y; | |
logval = MaxLog - 3; | |
a = offset + logval * Arc; | |
ArcLegend("←--- FC Scale ---→",r,a,OUTWARD); | |
comment("RC Circuit Pointers"); | |
local ctr = [0mm,0mm]; | |
r0 = DeckRad - 2*ScaleHeight; | |
r1 = r0 - ScaleHeight; | |
a = -(17 * ScaleArc); | |
goto(r0 * [cos(a),sin(a)]); | |
move([-,-,EngraveZ]); | |
move(r1 * [cos(a),sin(a)]); | |
goto([-,-,TravelZ]); | |
ArcLegend("⇓",(r0 - TickGap),a,OUTWARD); | |
RadialLegend(" Time Constant",ctr,r1,a,TEXT_LEFT,INWARD,-0.5); | |
a += ScaleRT; | |
goto(r0 * [cos(a),sin(a)]); | |
move([-,-,EngraveZ]); | |
move(r1 * [cos(a),sin(a)]); | |
goto([-,-,TravelZ]); | |
ArcLegend("⇓",(r0 - TickGap),a,OUTWARD); | |
RadialLegend(" Risetime",ctr,r1,a,TEXT_LEFT,INWARD,-0.5); | |
a -= ScaleRT/2; | |
RadialLegend(" RC",ctr,r0 - 2*ScaleTextSize.y,a,TEXT_LEFT,INWARD,-0.5); | |
comment("L/R Circuit Pointers"); | |
r0 = DeckRad; | |
r1 = r0 - ScaleHeight; | |
a = -TauAngle; | |
goto(r0 * [cos(a),sin(a)]); | |
move([-,-,EngraveZ]); | |
move(r1 * [cos(a),sin(a)]); | |
goto([-,-,TravelZ]); | |
ArcLegend("⇓",(r0 - TickGap),a,OUTWARD); | |
RadialLegend("Time Constant ",ctr,r1,a,TEXT_RIGHT,OUTWARD,-0.5); | |
a -= ScaleRT; | |
goto(r0 * [cos(a),sin(a)]); | |
move([-,-,EngraveZ]); | |
move(r1 * [cos(a),sin(a)]); | |
goto([-,-,TravelZ]); | |
ArcLegend("⇓",(r0 - TickGap),a,OUTWARD); | |
RadialLegend("Risetime ",ctr,r1,a,TEXT_RIGHT,OUTWARD,-0.5); | |
a += ScaleRT/2; | |
RadialLegend("L/R ",ctr,r0 - 2*ScaleTextSize.y,a,TEXT_RIGHT,OUTWARD,-0.5); | |
comment("Title and logo"); | |
feedrate(TextSpeed); | |
r = 0.65*DeckRad; | |
tp = scale(typeset("Homage",TextFont),TitleTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,TitleAngle,TEXT_CENTERED,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
r -= 1.5*TitleTextSize.y; | |
tp = scale(typeset("Tektronix",TextFont),TitleTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,TitleAngle,TEXT_CENTERED,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
r -= 1.5*TitleTextSize.y; | |
tp = scale(typeset("Circuit Computer",TextFont),TitleTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,TitleAngle,TEXT_CENTERED,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
r -= 1.5*TitleTextSize.y; | |
if (TRUE) { | |
tp = scale(typeset("TEK 003-023",TextFont),LegendTextSize); | |
} | |
else { | |
tp = scale(typeset("https://vintagetek.org/tektronix-circuit-computer/",TextFont),LegendTextSize); | |
} | |
tpa = ArcText(tp,[0mm,0mm],r,TitleAngle,TEXT_CENTERED,INWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
r = 0.3*DeckRad; | |
a = TitleAngle + 180deg; | |
tp = scale(typeset("Ed Nisley",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_CENTERED,OUTWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
r += 1.5*TitleTextSize.y; | |
tp = scale(typeset("KE4ZNU",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_CENTERED,OUTWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
r += 1.5*TitleTextSize.y; | |
tp = scale(typeset("softsolder.com",TextFont),LegendTextSize); | |
tpa = ArcText(tp,[0mm,0mm],r,a,TEXT_CENTERED,OUTWARD); | |
engrave(tpa,TravelZ,EngraveZ); | |
goto([-,-,SafeZ]); // done, so get out of the way | |
goto([0,0,-]); | |
comment("Top deck ends"); | |
} | |
//---------- | |
// Engrave cursor hairline | |
function EngraveCursor() { | |
// Mark center pivot | |
MarkPivot(); | |
comment("Cursor hairline"); | |
feedrate(ScaleSpeed); | |
goto([-,-,TravelZ]); | |
repeat(2) { | |
goto([DeckTopOD/2 - 2.25*ScaleHeight,0,-]); // slight overlap on arrows | |
move([-,-,EngraveZ]); | |
move([DeckBottomOD/2 + ScaleHeight,0,-]); | |
goto([-,-,TravelZ]); | |
} | |
goto([-,-,SafeZ]); // done, so get out of the way | |
goto([0,0,-]); | |
} | |
//----------------------------------------------------------------------------- | |
// Deck milling | |
// Assumes adhesive clamping to avoid protrusions above work area | |
//----- | |
// Bottom deck | |
function MillBottom() { | |
comment("Mill Bottom"); | |
feedrate(KnifeSpeed); | |
goto([-,-,TravelZ]); | |
local r = PivotOD/2; | |
goto([0,r,-]); // entry move to align knife | |
arc_cw([r,0,0],r); // blade enters surface | |
move([-,-,KnifeZ]); // apply cutting force | |
circle_cw([0,0]); | |
arc_cw([0,-r],r); // cut past entry point | |
goto([-,-,TravelZ]); | |
r = DeckRad; | |
local a = 5deg; | |
local p0 = r * [cos(a),sin(a),-]; // entry point | |
local p1 = r * [cos(-a),sin(-a),-]; // exit point | |
goto(p0); | |
arc_cw([r,0,0],r); // blade enters surface | |
move([-,-,KnifeZ]); // apply cutting force | |
circle_cw([0,0]); // cut circle | |
arc_cw(p1,r); // cut past entry point | |
goto([-,-,TravelZ]); | |
goto([0,0,-]); | |
goto([-,-,SafeZ]); | |
} | |
//----- | |
// Middle deck | |
function MillMiddle() { | |
FLNotchArc = 85deg; // width exposing FL scale | |
FLRampArc = 7deg; // … width of entry & exit ramps | |
FLNotchOffset = 2deg; // … start angle from 0° | |
comment("Mill Middle"); | |
feedrate(KnifeSpeed); | |
goto([-,-,TravelZ]); | |
local r = PivotOD/2; | |
goto([0,r,-]); // entry move to align knife | |
arc_cw([r,0,0],r); // blade enters surface | |
move([-,-,KnifeZ]); // apply cutting force | |
circle_cw([0,0]); | |
arc_cw([0,-r],r); // cut past entry point | |
goto([-,-,TravelZ]); | |
// FL scale notch | |
local r0 = DeckRad; | |
local a0 = FLNotchOffset; // end of notch ramp | |
local p0 = r0 * [cos(a0),sin(a0),-]; | |
local a1 = a0 + FLNotchArc; // start of notch ramp | |
local p1 = r0 * [cos(a1),sin(a1),-]; | |
goto(p0); | |
arc_cw([r0,0,0],r0); // blade enters surface | |
move([-,-,KnifeZ]); // apply cutting force | |
arc_cw(p1,-r0); // largest arc to start of notch | |
local r1 = r0 - ScaleHeight; | |
local a3 = a1 - FLRampArc; // start of notch base | |
local p3 = r1 * [cos(a3),sin(a3),-]; | |
local a4 = a0 + FLRampArc; // end of notch base | |
local p4 = r1 * [cos(a4),sin(a4),-]; | |
move(p3); | |
arc_cw(p4,r1); // smallest arc on notch base | |
move(p0); // end of notch ramp | |
arc_cw([r0,0,-],r0); // round off corner | |
local p5 = r0 * [cos(-a0),sin(-a0),-]; // small overtravel past entry point | |
arc_cw(p5,r0); | |
goto([-,-,TravelZ]); | |
// L/R τ and RT Scale window | |
local WindowArc = 39deg; | |
ac = -6 * ScaleArc; // center of window arc | |
r0 = DeckRad - ScaleHeight; // outer | |
r1 = DeckRad - 2 * ScaleHeight; // inner | |
aw = WindowArc - to_deg(atan(ScaleHeight,(r0 + r1)/2)); // window arc minus endcaps | |
p0 = r0 * [cos(ac + aw/2),sin(ac + aw/2),-]; // endcap entry & exit | |
p1 = r0 * [cos(ac - aw/2),sin(ac - aw/2),-]; | |
p2 = r1 * [cos(ac - aw/2),sin(ac - aw/2),-]; | |
p3 = r1 * [cos(ac + aw/2),sin(ac + aw/2),-]; | |
goto(p3); // cut entry point | |
arc_cw(p0 +| [-,-,0],ScaleHeight/2); // blade enters surface | |
move([-,-,KnifeZ]); // apply pressure | |
arc_cw(p1,r0); // smallest arc | |
arc_cw(p2,ScaleHeight/2); // half a circle | |
arc_ccw(p3,r1); | |
arc_cw(p0,ScaleHeight/2); | |
arc_cw(p1 +| [-,-,TravelZ],r0); // exit from cut | |
goto([0,0,-]); | |
goto([-,-,SafeZ]); | |
} | |
//----- | |
// Top deck | |
function MillTop() { | |
comment("Mill Top"); | |
feedrate(KnifeSpeed); | |
goto([-,-,TravelZ]); | |
local r = PivotOD/2; | |
goto([0,r,-]); // entry move to align knife | |
arc_cw([r,0,0],r); // blade enters surface | |
move([-,-,KnifeZ]); // apply cutting force | |
circle_cw([0,0]); | |
arc_cw([0,-r],r); // cut past entry point | |
goto([-,-,TravelZ]); | |
r = DeckRad; | |
local a = 5deg; | |
local p0 = r * [cos(a),sin(a),-]; // entry point | |
local p1 = r * [cos(-a),sin(-a),-]; // exit point | |
goto(p0); | |
arc_cw([r,0,0],r); // blade enters surface | |
move([-,-,KnifeZ]); // apply cutting force | |
circle_cw([0,0]); // cut circle | |
arc_cw(p1,r); // cut past entry point | |
goto([-,-,TravelZ]); | |
// RC τ and RT Scale window | |
local WindowArc = 54deg; | |
local ac = -17 * ScaleArc + ScaleRT/2; // center of window arc | |
local r0 = DeckRad - ScaleHeight; // outer | |
local r1 = DeckRad - 2 * ScaleHeight; // inner | |
local aw = WindowArc - to_deg(atan(ScaleHeight,(r0 + r1)/2)); // window arc minus endcaps | |
p0 = r0 * [cos(ac + aw/2),sin(ac + aw/2),-]; | |
p1 = r0 * [cos(ac - aw/2),sin(ac - aw/2),-]; | |
local p2 = r1 * [cos(ac - aw/2),sin(ac - aw/2),-]; | |
local p3 = r1 * [cos(ac + aw/2),sin(ac + aw/2),-]; | |
goto(p3); | |
arc_cw(p0 +| [-,-,0],ScaleHeight/2); // blade enters surface | |
move([-,-,KnifeZ]); // apply pressure | |
arc_cw(p1,r0); // smallest arc | |
arc_cw(p2,ScaleHeight/2); // half a circle | |
arc_ccw(p3,r1); | |
arc_cw(p0,ScaleHeight/2); | |
arc_cw(p1 +| [-,-,TravelZ],r0); // exit from cut | |
goto([0,0,-]); | |
goto([-,-,SafeZ]); | |
} | |
//---------- | |
// Cut cursor outline | |
CursorHubOD = 1.0in; | |
CursorTipWidth = to_inch(9.0/16.0); | |
CursorTipRadius = to_inch(1.0/16.0); | |
function MillCursor() { | |
// Mark center pivot | |
MarkPivot(); | |
comment("Cursor outline"); | |
local dr = DeckBottomOD/2; | |
local hr = CursorHubOD/2; | |
local a = atan(hr - CursorTipWidth/2,dr); // rough & ready approximation | |
local p0 = hr * [sin(a),cos(a),-]; // upper tangent point on hub | |
local c1 = [dr - CursorTipRadius,CursorTipWidth/2 - CursorTipRadius*cos(a),-]; | |
local p1 = c1 + [CursorTipRadius*sin(a),CursorTipRadius*cos(a),-]; | |
local p2 = c1 + [CursorTipRadius,0,-]; // around tip radius | |
feedrate(KnifeSpeed); | |
goto([-,-,TravelZ]); | |
goto([-hr,0,-]); | |
move([-,-,EngraveZ]); | |
repeat(3) { | |
arc_cw(p0,hr); | |
move(p1); | |
arc_cw(p2,CursorTipRadius); | |
move([p2.x,-p2.y,-]); | |
arc_cw([p1.x,-p1.y,-],CursorTipRadius); | |
move([p0.x,-p0.y,-]); | |
arc_cw([-hr,0,-],hr); | |
} | |
goto([-,-,SafeZ]); // done, so get out of the way | |
goto([0,0,-]); | |
} | |
//----------------------------------------------------------------------------- | |
// The actual machining sequences! | |
//----- | |
// Bottom Deck | |
if (SelectPart == "Bottom") { | |
DeckOD = DeckBottomOD; | |
DeckRad = DeckOD / 2; | |
comment(" OD: ",DeckOD); | |
if (Operation == "Engrave") { | |
EngraveBottom(); | |
} | |
elif (Operation == "Mill") { | |
MillBottom(); | |
} | |
else { | |
error("Invalid operation: ",Operation); | |
} | |
} | |
//------ | |
// Middle Deck | |
if (SelectPart == "Middle") { | |
DeckOD = DeckMiddleOD; | |
DeckRad = DeckOD / 2; | |
comment(" OD: ",DeckOD); | |
if (Operation == "Engrave") { | |
EngraveMiddle(); | |
} | |
elif (Operation == "Mill") { | |
MillMiddle(); | |
} | |
else { | |
error("Invalid operation: ",Operation); | |
} | |
} | |
//----- | |
// Top Deck | |
if (SelectPart == "Top") { | |
DeckOD = DeckTopOD; | |
DeckRad = DeckOD / 2; | |
comment(" OD: ",DeckOD); | |
if (Operation == "Engrave") { | |
EngraveTop(); | |
} | |
elif (Operation == "Mill") { | |
MillTop(); | |
} | |
else { | |
error("Invalid operation: ",Operation); | |
} | |
} | |
//----- | |
// Cursor | |
if (SelectPart == "Cursor") { | |
DeckOD = DeckBottomOD; | |
DeckRad = DeckOD / 2; | |
comment(" OD: ",DeckOD); | |
if (Operation == "Engrave") { | |
EngraveCursor(); | |
} | |
elif (Operation == "Mill") { | |
MillCursor(); | |
} | |
else { | |
error("Invalid operation: ",Operation); | |
} | |
} |
#!/bin/bash | |
# Tek Circuit Computer Engraving | |
# Ed Nisley KE4ZNU - 2019-11 | |
#OD='BaseOD=118mm' # CD = 120 | |
#OD='BaseOD=93mm' # hard drive = 95mm | |
#EZ='EngraveZ=-5mm' # Engraving Z | |
Flags='-P 3 --pedantic' # avoid leading hyphen gotcha | |
# Set these to match your file layout | |
ProjPath='/mnt/bulkdata/Project Files/Tektronix Circuit Computer/Firmware' | |
LibPath='/opt/gcmc/library' | |
Prolog='prolog.gcmc' | |
Epilog='epilog.gcmc' | |
ScriptPath=$ProjPath | |
Script='Tek Circuit Computer.gcmc' | |
#----- | |
# params: deck operation | |
function Runit { | |
fn=TekCC-${1}-${2}.ngc | |
echo "(File: "$fn")" > $fn | |
sel='SelectPart="'$1'"' | |
op='Operation="'$2'"' | |
echo Output: $fn | |
echo " "$sel | |
echo " "$op | |
if [ -e $fn ] | |
then rm -f $fn | |
fi | |
gcmc -D "$OD" -D "$EZ" \ | |
-D "$sel" -D "$op" $Flags \ | |
--include "$LibPath" --prologue "$Prolog" --epilogue "$Epilog" \ | |
"$ScriptPath"/"$Script" >> "$fn" | |
} | |
#----- | |
Runit Bottom Engrave | |
Runit Bottom Mill | |
Runit Middle Engrave | |
Runit Middle Mill | |
Runit Top Engrave | |
Runit Top Mill | |
Runit Cursor Engrave | |
Runit Cursor Mill |
My roommate in college (pre-HP35 era) used a circular general purpose slide “rule”, er, disk. Somehow, nobody ever wanted to borrow it.
In keeping with the climate requirements of the Midwest, I used an aluminum Pickett. Most Post(tm) sliderule owners had fun* adjusting them each season.
(*) For various definitions of the word.
The rotors on my circular Pickett were always too hard to turn. I should see if a dose of modern dry silicone lube would improve its disposition. Ya never know when you might need a slipstick, but not having used the thing in nigh onto half a century, it’s not urgent …