-
GCMC Text on Arcs: Improved Version
The Tektronix Circuit Computer scale annotations read both inward (from the center) and outward (from the rim):

Text on Arcs – orientation It’s surprisingly difficult (for me, anyhow) to see the middle FL Scale as reading upside-down, rather than mirror-image backwards.
This turned into a rewrite of the the read-outward annotation code I used for the vacuum tube reflectors. Eventually the justification and orientation options came out right:

Text-on-arcs example The text baseline sits at the specified radius from the center point, regardless of its orientation, so you must offset the text path by half its height in the proper direction before handing it to the
ArcTextfunction.The testcase shows the invocation ritual:
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,-]);A utility function to draw scale legends stuffs some of that complexity into a bottle:
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); }Which means most of the text uses a simpler invocation:
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);Arcdetermines the angular span of each decade, with positive values going counterclockwise.MinLogis the logarithm of the scale endpoint, so adding 1.5 puts the text angle one-and-a-half decades fromMinLogand multiplying byArcmoves it in the right direction. Theoffsetangle rotates the entire scale with respect to the 0° reference sticking out the X axis over on the right. The top picture has its 0° reference pointing north-northeast.The GCMC source code as a GitHub Gist:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersINWARD = -1; // text and tick alignment (used as integers) OUTWARD = 1; TEXT_LEFT = -1; // text justification TEXT_CENTERED = 0; TEXT_RIGHT = 1; //—– // 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; } GCMC treats variables defined inside a function as local, unless they’re already defined in an enclosing scope, whereupon you overwrite the outer variables. This wasn’t a problem in my earlier programs, but I fired the footgun with nested functions using the same local / temporary variables. Now, I ruthlessly declare truly local variables as
local, except when I don’t, for what seem good reasons at the time.