Add logarithmic scale, custom labels, and fixed-point number formatting

- New Linear/Log mapping toggle; log mode uses geometric major-tick
  values with log-positioned minor ticks (validates min/max > 0)
- Optional comma-separated custom labels that override the calculated
  numbers and set the label count
- Format numbers in fixed-point at 4 significant figures (no more
  scientific notation like "1e+04"), snapping log-scale FP drift to ints
- Bump version to 0.75
This commit is contained in:
2026-07-13 00:40:17 +02:00
parent 1d5592e49e
commit 5ae79499cb
2 changed files with 105 additions and 23 deletions
+38 -8
View File
@@ -311,7 +311,16 @@
<div class="section">
<div class="section-eye">Scale range</div>
<div class="row row-2">
<div class="row row-3">
<div class="field">
<label>Mapping</label>
<div class="mode-toggle" role="group" aria-label="Scale mapping">
<button type="button" class="mode-btn active" data-scale="linear"
title="Values spaced evenly along the scale">Linear</button>
<button type="button" class="mode-btn" data-scale="log"
title="Values spaced logarithmically — requires min and max greater than 0">Log</button>
</div>
</div>
<div class="field">
<label>Min value</label>
<input name="min" type="number" step="any" value="0" required>
@@ -321,6 +330,7 @@
<input name="max" type="number" step="any" value="100" required>
</div>
</div>
<input type="hidden" name="scale_type" id="scale-type" value="linear">
</div>
<div class="section">
@@ -340,6 +350,13 @@
<span style="font-family:var(--f-mono);font-size:0.65rem;color:var(--muted)">(Labels minus one) should divide evenly into Major intervals for best appearance</span>
</div>
</div>
<div class="row" style="margin-top:1.25rem">
<div class="field">
<label>Custom labels (optional)</label>
<input name="labels" type="text" placeholder="e.g. Lo, 25, Mid, 75, Hi — leave blank to use calculated values">
<span style="font-family:var(--f-mono);font-size:0.65rem;color:var(--muted)">Comma-separated. When set, overrides the calculated numbers and its count sets the number of labels.</span>
</div>
</div>
</div>
<div class="actions">
@@ -400,13 +417,26 @@
text: 'Live text with embedded font — editable in SVG editors',
};
document.querySelectorAll('.mode-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
document.getElementById('output-mode').value = btn.dataset.mode;
document.getElementById('mode-desc').textContent = modeDesc[btn.dataset.mode];
doGenerate();
document.querySelectorAll('.mode-toggle').forEach(group => {
group.querySelectorAll('.mode-btn').forEach(btn => {
btn.addEventListener('click', () => {
group.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
if (btn.dataset.mode !== undefined) {
document.getElementById('output-mode').value = btn.dataset.mode;
document.getElementById('mode-desc').textContent = modeDesc[btn.dataset.mode];
}
if (btn.dataset.scale !== undefined) {
document.getElementById('scale-type').value = btn.dataset.scale;
if (btn.dataset.scale === 'log') {
const minInput = document.querySelector('input[name="min"]');
const maxInput = document.querySelector('input[name="max"]');
if (!(parseFloat(minInput.value) > 0)) minInput.value = '1';
if (!(parseFloat(maxInput.value) > 0)) maxInput.value = '100';
}
}
doGenerate();
});
});
});