mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 03:06:57 -06:00
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:
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user