Desktop: Snap overlays to physical instead of logical pixels (#3493)

WIP
This commit is contained in:
Timon 2026-02-04 08:24:16 +01:00 committed by GitHub
parent c7e3d01e2b
commit 9c56c852ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 38 additions and 20 deletions

View File

@ -491,11 +491,13 @@ impl OverlayContextInternal {
let mut path = BezPath::new();
if let Some(first) = polygon.last() {
path.move_to(kurbo::Point::new(first.x.round() - 0.5, first.y.round() - 0.5));
let p = self.snap_to_physical_pixel_center(*first);
path.move_to(kurbo::Point::new(p.x, p.y));
}
for point in polygon {
path.line_to(kurbo::Point::new(point.x.round() - 0.5, point.y.round() - 0.5));
let p = self.snap_to_physical_pixel_center(*point);
path.line_to(kurbo::Point::new(p.x, p.y));
}
path.close_path();
@ -522,8 +524,8 @@ impl OverlayContextInternal {
fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option<f64>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
let transform = self.get_transform();
let start = start.round() - DVec2::splat(0.5);
let end = end.round() - DVec2::splat(0.5);
let start = self.snap_to_physical_pixel_center(start);
let end = self.snap_to_physical_pixel_center(end);
let mut path = BezPath::new();
path.move_to(kurbo::Point::new(start.x, start.y));
@ -541,7 +543,7 @@ impl OverlayContextInternal {
fn manipulator_handle(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
let transform = self.get_transform();
let position = position.round() - DVec2::splat(0.5);
let position = self.snap_to_physical_pixel_center(position);
let circle = kurbo::Circle::new((position.x, position.y), MANIPULATOR_GROUP_MARKER_SIZE / 2.);
@ -554,8 +556,7 @@ impl OverlayContextInternal {
fn hover_manipulator_handle(&mut self, position: DVec2, selected: bool) {
let transform = self.get_transform();
let position = position.round() - DVec2::splat(0.5);
let position = self.snap_to_physical_pixel_center(position);
let circle = kurbo::Circle::new((position.x, position.y), (MANIPULATOR_GROUP_MARKER_SIZE + 2.) / 2.);
@ -605,7 +606,7 @@ impl OverlayContextInternal {
let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
let position = position.round() - DVec2::splat(0.5);
let position = self.snap_to_physical_pixel_center(position);
let corner = position - DVec2::splat(size) / 2.;
let transform = self.get_transform();
@ -620,7 +621,7 @@ impl OverlayContextInternal {
let size = 1.;
let color_fill = color.unwrap_or(COLOR_OVERLAY_WHITE);
let position = position.round() - DVec2::splat(0.5);
let position = self.snap_to_physical_pixel_center(position);
let corner = position - DVec2::splat(size) / 2.;
let transform = self.get_transform();
@ -632,7 +633,7 @@ impl OverlayContextInternal {
fn circle(&mut self, position: DVec2, radius: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
let position = position.round();
let position = self.snap_to_physical_pixel(position);
let transform = self.get_transform();
let circle = kurbo::Circle::new((position.x, position.y), radius);
@ -708,7 +709,7 @@ impl OverlayContextInternal {
self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle);
}
fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
let sign = scale.signum();
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
fill_color.insert(0, '#');
@ -739,7 +740,7 @@ impl OverlayContextInternal {
let Some(show_hover_ring) = show_compass_with_hover_ring else { return };
let transform = self.get_transform();
let center = compass_center.round() - DVec2::splat(0.5);
let center = self.snap_to_physical_pixel_center(compass_center);
// Hover ring
if show_hover_ring {
@ -784,7 +785,7 @@ impl OverlayContextInternal {
fn pivot(&mut self, position: DVec2, angle: f64) {
let uv = DVec2::from_angle(angle);
let (x, y) = (position.round() - DVec2::splat(0.5)).into();
let (x, y) = self.snap_to_physical_pixel_center(position).into();
let transform = self.get_transform();
@ -814,7 +815,7 @@ impl OverlayContextInternal {
}
fn dowel_pin(&mut self, position: DVec2, angle: f64, color: Option<&str>) {
let (x, y) = (position.round() - DVec2::splat(0.5)).into();
let (x, y) = self.snap_to_physical_pixel_center(position).into();
let color = color.unwrap_or(COLOR_OVERLAY_YELLOW_DULL);
let transform = self.get_transform();
@ -921,29 +922,30 @@ impl OverlayContextInternal {
};
let start_point = transform.transform_point2(point_to_dvec2(first.start()));
let start_point = self.snap_to_physical_pixel(start_point);
path.move_to(kurbo::Point::new(start_point.x, start_point.y));
for curve in curves {
match curve {
PathSeg::Line(line) => {
let a = transform.transform_point2(point_to_dvec2(line.p1));
let a = a.round() - DVec2::splat(0.5);
let a = self.snap_to_physical_pixel_center(a);
path.line_to(kurbo::Point::new(a.x, a.y));
}
PathSeg::Quad(quad_bez) => {
let a = transform.transform_point2(point_to_dvec2(quad_bez.p1));
let b = transform.transform_point2(point_to_dvec2(quad_bez.p2));
let a = a.round() - DVec2::splat(0.5);
let b = b.round() - DVec2::splat(0.5);
let a = self.snap_to_physical_pixel_center(a);
let b = self.snap_to_physical_pixel_center(b);
path.quad_to(kurbo::Point::new(a.x, a.y), kurbo::Point::new(b.x, b.y));
}
PathSeg::Cubic(cubic_bez) => {
let a = transform.transform_point2(point_to_dvec2(cubic_bez.p1));
let b = transform.transform_point2(point_to_dvec2(cubic_bez.p2));
let c = transform.transform_point2(point_to_dvec2(cubic_bez.p3));
let a = a.round() - DVec2::splat(0.5);
let b = b.round() - DVec2::splat(0.5);
let c = c.round() - DVec2::splat(0.5);
let a = self.snap_to_physical_pixel_center(a);
let b = self.snap_to_physical_pixel_center(b);
let c = self.snap_to_physical_pixel_center(c);
path.curve_to(kurbo::Point::new(a.x, a.y), kurbo::Point::new(b.x, b.y), kurbo::Point::new(c.x, c.y));
}
}
@ -1145,4 +1147,20 @@ impl OverlayContextInternal {
self.line(quad.bottom_left(), quad.bottom_right(), None, None);
}
}
fn snap_to_physical_pixel(&self, p: DVec2) -> DVec2 {
let s = self.viewport.scale();
if !s.is_finite() || s <= 0.0 {
return p.round();
}
(p * s).round() / s
}
fn snap_to_physical_pixel_center(&self, p: DVec2) -> DVec2 {
let s = self.viewport.scale();
if !s.is_finite() || s <= 0.0 {
return p.round() - DVec2::splat(0.5);
}
self.snap_to_physical_pixel(p) - DVec2::splat(0.5 / s)
}
}