mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 10:17:02 -06:00
Engine-first Cargo workspace for DrawCraft (clean-room Illustrator): - geom: anchor-based path model, shapes, hit testing, snapping - color: colours, paints, gradients, swatches, blend modes, harmonies - doc: persistent Arc document tree, appearance stacks, text model, selection - render: vello_cpu renderer (appearance, gradients, clip groups, blend modes) - text: font DB, harfrust shaping, point/area/path layout - pathops: curve booleans (linesweeper), Pathfinder, offset, simplify - svg: import (usvg) and export - tools: selection/direct/pen/shape tools emitting commands - engine: Session, history with interactions, ~110 commands Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.3 KiB
Rust
47 lines
1.3 KiB
Rust
//! Snapping math shared by tools and smart guides.
|
|
|
|
use kurbo::Point;
|
|
|
|
/// Snap a value to a grid with the given spacing and origin.
|
|
pub fn snap_to_grid(v: f64, spacing: f64, origin: f64) -> f64 {
|
|
if spacing <= 0.0 {
|
|
return v;
|
|
}
|
|
((v - origin) / spacing).round() * spacing + origin
|
|
}
|
|
|
|
pub fn snap_point_to_grid(p: Point, spacing: f64) -> Point {
|
|
Point::new(snap_to_grid(p.x, spacing, 0.0), snap_to_grid(p.y, spacing, 0.0))
|
|
}
|
|
|
|
/// A candidate alignment line for smart guides.
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub enum Axis {
|
|
X,
|
|
Y,
|
|
}
|
|
|
|
/// Find the closest target within `tol` to `v`. Returns (target, delta).
|
|
pub fn closest(v: f64, targets: impl IntoIterator<Item = f64>, tol: f64) -> Option<(f64, f64)> {
|
|
targets.into_iter().map(|t| (t, t - v)).filter(|(_, d)| d.abs() <= tol).min_by(|a, b| a.1.abs().total_cmp(&b.1.abs()))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn grid() {
|
|
assert_eq!(snap_to_grid(13.0, 10.0, 0.0), 10.0);
|
|
assert_eq!(snap_to_grid(16.0, 10.0, 0.0), 20.0);
|
|
assert_eq!(snap_to_grid(16.0, 10.0, 3.0), 13.0);
|
|
assert_eq!(snap_to_grid(16.0, 0.0, 0.0), 16.0);
|
|
}
|
|
|
|
#[test]
|
|
fn closest_target() {
|
|
assert_eq!(closest(10.0, [0.0, 11.0, 9.5], 2.0), Some((9.5, -0.5)));
|
|
assert_eq!(closest(10.0, [0.0, 20.0], 2.0), None);
|
|
}
|
|
}
|