Rulers: the left ruler leaves out a label that would run into the one above

A label on the left ruler is spelled one character per line, so a long one is
tall: "-0.0625" is seven characters and reaches 60.5 pt down the ruler, while a
sixteenth-inch step leaves 54 pt at zoom 12. Binary steps made such labels
reachable -- the decimal ladder's "-0.1" was four characters in an 86 pt gap --
so the ruler could draw one label over another.

A label that would start inside the one above it is left out. Every tick still
draws, as do the labels that fit, and `vertical_label_height` gives the reach
of a label so the decision is one comparison.
This commit is contained in:
Hamza
2026-10-19 23:47:04 +07:00
committed by bflatastic
parent 39b59b5730
commit c58c642f07
2 changed files with 43 additions and 2 deletions
+22 -2
View File
@@ -1557,6 +1557,16 @@ fn grid(p: &egui::Painter, xf: &Xf, spacing: f64, subdiv: u32, look: LineLook) {
/// A ruler label for `v` (in the ruler's unit) on a ruler whose labels are `step` apart: whole
/// numbers, or the fewest decimals that write `step` exactly, so a binary step keeps its value
/// (an inch ruler's 1/16 reads `0.0625`, not the `0.06` that rounding the step's magnitude gave).
/// How far down the left ruler a label of `chars` characters reaches, counting from its top: it
/// is spelled one character per line, [`RULER_CHAR`] apart, and the last one is a line tall.
pub(crate) fn vertical_label_height(chars: usize) -> f32 {
chars.saturating_sub(1) as f32 * RULER_CHAR + RULER_LINE
}
/// A ruler label's line spacing and line height on the left ruler.
const RULER_CHAR: f32 = 8.5;
const RULER_LINE: f32 = 9.5;
pub(crate) fn ruler_label(v: f64, step: f64) -> String {
let exact = |d: u32| (step * 10f64.powi(d as i32)).fract().abs() < 1e-9;
let decimals = (0..=7).find(|d| exact(*d)).unwrap_or(7) as usize;
@@ -1710,14 +1720,24 @@ fn rulers(ui: &Ui, full: egui::Rect, xf: &Xf, hover: Option<Point>, unit: Unit,
}
});
let clip_left = p.with_clip_rect(left);
// A label spelled down the ruler is tall, and a long one can reach the next label's place:
// "-0.0625" is seven characters, 60 pt of ruler, where a sixteenth-inch step leaves 54 pt at
// zoom 12. The ticks all stay; a label that would run into the one above it is left out.
let mut filled_to = f32::NEG_INFINITY;
ticks(left.left_top(), left.left_bottom(), vectorcraft_geom::Vec2::new(sn, cs), &mut |d, y| {
let sy = left.top() + d;
clip_left.line_segment([pos2(left.right() - tick(y), sy), pos2(left.right(), sy)], Stroke::new(1.0, t.ruler_tick));
if near(y, step) {
// Vertical labels read top-to-bottom, one digit per line like Illustrator.
for (k, ch) in ruler_label(y, step).chars().enumerate() {
let text = ruler_label(y, step);
let top = sy + 2.0;
if top < filled_to {
return;
}
filled_to = top + vertical_label_height(text.chars().count());
for (k, ch) in text.chars().enumerate() {
clip_left.text(
pos2(left.left() + 4.0, sy + 2.0 + k as f32 * 8.5),
pos2(left.left() + 4.0, top + k as f32 * RULER_CHAR),
egui::Align2::LEFT_TOP,
ch.to_string(),
font.clone(),
+21
View File
@@ -117,6 +117,27 @@ fn new_documents_start_in_the_general_preference() {
assert_eq!((d.units, d.artboards[0].rect.width()), (Unit::Millimeters, 612.0));
}
/// A label spelled down the left ruler is tall, and a sixteenth-inch step does not always leave
/// room for a long one: at zoom 12 the labels are 54 pt apart while "-0.0625" reaches 60.5 pt, so
/// the ruler leaves that label out rather than drawing it over its neighbour. Every tick stays.
#[test]
fn a_long_vertical_label_does_not_fit_a_sixteenth_inch_step() {
use crate::canvas::vertical_label_height;
use vectorcraft_doc::Unit;
// One character is one line; each further character adds a line.
assert_eq!(vertical_label_height(1), 9.5);
assert_eq!(vertical_label_height(2), 18.0);
assert_eq!(vertical_label_height(7), 60.5);
// The gap a label has to fit into, at the zoom where a sixteenth is first chosen and beyond.
let gap = |zoom: f64| Unit::Inches.ruler_step(zoom) * Unit::Inches.points() * zoom;
assert!((gap(12.0) - 54.0).abs() < 1e-9, "{}", gap(12.0));
assert!(vertical_label_height(7) > gap(12.0) as f32, "a seven-character label has to be left out");
// The labels this ruler actually writes below an inch are six characters, which do fit.
assert!(vertical_label_height("0.0625".chars().count()) < gap(12.0) as f32);
// Zoomed further in the gap grows and even a long label fits.
assert!(vertical_label_height(7) < gap(20.0) as f32, "{}", gap(20.0));
}
#[test]
fn ruler_labels_have_the_steps_decimals() {
use crate::canvas::ruler_label;