| index 8cba621..95190bb 100644 |
| --- a/native/apple/Tests/CallsKitTests/CallGlyphTests.swift |
| +++ b/native/apple/Tests/CallsKitTests/CallGlyphTests.swift |
| @@ -96,29 +96,46 @@ import XCTest |
| return lines.joined(separator: "\n") |
| } |
| |
| - /// With CALLS_GLYPH_TUNE=1, the table that does best: for each symbol |
| - /// the correction, in thousandths of an em, whose worst size is |
| - /// nearest the centre line — the pixel grid makes that a search rather |
| - /// than a sum. |
| + /// With CALLS_GLYPH_TUNE=1, the table to use. The pixel grid makes |
| + /// each symbol's worst offset a step function of its correction, so |
| + /// the smallest correction that wins can sit on the edge of a step, |
| + /// where a thousandth of an em flips it. This takes the middle of the |
| + /// widest run of corrections (in 2/1000 em steps) whose worst size is |
| + /// within 0.3 pt of the centre line, or, failing that, within 0.15 pt |
| + /// of the best any correction does. |
| func testTuneTheTable() throws { |
| try XCTSkipUnless(ProcessInfo.processInfo.environment["CALLS_GLYPH_TUNE"] == "1") |
| var lines = ["tuned offsets (ems, negative up):"] |
| for symbol in CallGlyph.opticalOffsets.keys.sorted() { |
| - var best: (em: CGFloat, worst: CGFloat) = (0, .infinity) |
| - for step in -150...100 { |
| - let em = CGFloat(step) / 1000 |
| + let steps = Array(stride(from: -140, through: 100, by: 2)) |
| + let worsts: [CGFloat] = steps.map { step in |
| var worst: CGFloat = 0 |
| for (_, metrics) in Self.sizes { |
| var glyph = CallGlyph(symbol) |
| - glyph.trial = em |
| - guard let ink = Ink.measure(glyph.environment(\.callGlyphMetrics, metrics)) else { continue } |
| - worst = max(worst, abs(ink.centroidOffset)) |
| + glyph.trial = CGFloat(step) / 1000 |
| + if let ink = Ink.measure(glyph.environment(\.callGlyphMetrics, metrics)) { |
| + worst = max(worst, abs(ink.centroidOffset)) |
| + } else { |
| + worst = .infinity |
| + } |
| } |
| - if worst < best.worst - 0.01 || (abs(worst - best.worst) <= 0.01 && abs(em) < abs(best.em)) { |
| - best = (em, worst) |
| + return worst |
| + } |
| + let threshold = max(0.3, (worsts.min() ?? 0) + 0.15) |
| + var bestRun = (start: 0, length: 0), start = -1 |
| + for (i, worst) in worsts.enumerated() { |
| + if worst <= threshold { |
| + if start < 0 { start = i } |
| + if i - start + 1 > bestRun.length { bestRun = (start, i - start + 1) } |
| + } else { |
| + start = -1 |
| } |
| } |
| - lines.append(String(format: " \"%@\": %.3f, // worst %.2f pt", symbol, best.em, best.worst)) |
| + let middle = bestRun.start + bestRun.length / 2 |
| + lines.append(String( |
| + format: " \"%@\": %.3f, // worst %.2f pt; %.3f to %.3f all within %.2f", symbol, |
| + CGFloat(steps[middle]) / 1000, worsts[middle], CGFloat(steps[bestRun.start]) / 1000, |
| + CGFloat(steps[bestRun.start + bestRun.length - 1]) / 1000, threshold)) |
| } |
| print(lines.joined(separator: "\n")) |
| } |