function solveLabelRotationLayout({
maxWidth: wm,
firstLabelWidth: l0,
labelHeight: h,
labelHeightMargin: m,
labelCount: n,
marginLeft: ml
}) {
const forwardDifferenceStep = 1e-6;
const iterMax = 20;
let theta = 45;
const thetaAll = Array(iterMax).fill(0);
thetaAll[0] = theta;
const f = theta => {
const st = sind(theta);
const l = Math.max(ml, l0 * cosd(theta));
return st * (wm - l - h * st) - (n - 1) * (h + m);
};
for (let i = 1; i <= iterMax; i++) {
const value = f(theta);
const value2 = f(theta + forwardDifferenceStep);
const derivativeApproximation = (value2 - value) / forwardDifferenceStep;
theta = theta - value / derivativeApproximation;
theta = clamp(theta, 0, 90);
thetaAll[i] = theta;
}
const convergenceThreshold = 1e-4;
const converged =
theta >= 0 && theta <= 90 && Math.abs(f(theta)) < convergenceThreshold;
mutable debugThetaAll = thetaAll;
const debugThetaCount = 100;
mutable debugThetaValues = Array.from({ length: debugThetaCount }, (_, i) => {
const k = i / (debugThetaCount - 1);
const theta = k * 90;
const value = f(theta);
const value2 = f(theta + forwardDifferenceStep);
const derivative = (value2 - value) / forwardDifferenceStep;
return {
theta,
value,
derivative
};
});
return {
converged,
theta,
labelSpacing: (h + m) / sind(theta),
marginLeft: Math.max(ml, l0 * cosd(theta) + (h / 2) * sind(theta)),
marginRight: (h * sind(theta)) / 2
};
}