diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index 7d6d9489..cf09ef8a 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -96,6 +96,9 @@ pub struct RenderOptions { /// Pattern editing mode's tile edge and swatch bounds colour (Object → Pattern → Tile Edge /// Color), RGB. pub tile_edge: [u8; 3], + /// Colour and style of the paths made into guides (Preferences › Guides & Grid, as the ruler + /// guides the UI draws): RGB, and whether they are dotted. + pub guide: ([u8; 3], bool), /// View Opacity Mask (Alt-click the mask thumbnail): instead of the artwork, show this object's /// opacity mask alone as its coverage in greyscale (white = opaque, black = transparent). pub mask_view: Option, @@ -201,6 +204,7 @@ impl Default for RenderOptions { trim: false, skip_templates: false, tile_edge: vectorcraft_doc::LAYER_COLORS[0].1, + guide: ([0x4a, 0xd8, 0xff], false), mask_view: None, highlight_substitutions: false, anti_alias: AntiAlias::Art, @@ -1158,7 +1162,17 @@ impl Renderer { NodeKind::Path { path, rule, guide, .. } => { let bp = path.to_bezpath(); if *guide { - self.hairline(ctx, f, &bp, [0x4a, 0xd8, 0xff, 255]); + let ([r, g, b], dots) = f.opts.guide; + if dots { + let mut screen = bp.clone(); + screen.apply_affine(f.view); + ctx.set_transform(Affine::IDENTITY); + ctx.set_stroke(kurbo::Stroke::new(1.5).with_dashes(0.0, [1.5, 2.5])); + ctx.set_paint(f.ink.fixed([r, g, b, 255])); + ctx.stroke_path(&screen); + } else { + self.hairline(ctx, f, &bp, [r, g, b, 255]); + } } else { self.draw_shape(ctx, f, n, &bp, *rule); } diff --git a/crates/render/src/tests_tileedge.rs b/crates/render/src/tests_tileedge.rs index 3657f0b3..2c487ff8 100644 --- a/crates/render/src/tests_tileedge.rs +++ b/crates/render/src/tests_tileedge.rs @@ -67,3 +67,31 @@ fn swatch_bounds_outline_the_period_dashed() { let off = render(&editing(tt, false, false), Some([0, 200, 0])); assert!((51..70).all(|x| is_white(off.pixel(x, 90)))); } + +/// #1534: a path made into a guide is drawn in the guide colour and style (Preferences › Guides & +/// Grid), as ruler guides are: a line, or dots with gaps between them. +#[test] +fn guide_paths_take_the_guide_color_and_style() { + let mut d = Document::new(200.0, 200.0); + let (layer, id) = (d.layers[0].id, d.alloc_id()); + let mut n = Node::path(id, shapes::rectangle(Rect::new(0.0, 0.0, 60.0, 60.0)), Appearance::basic(Paint::None, Paint::None, 0.0)); + if let NodeKind::Path { guide, .. } = &mut n.kind { + *guide = true; + } + d.insert(Some(layer), 0, n).unwrap(); + let draw = |guide| { + let opts = RenderOptions { background: Some([255; 4]), guide, ..Default::default() }; + Renderer::new().render(&d, 120, 120, Affine::translate((50.5, 50.5)), &opts) + }; + // Along the top edge of the square (y = 50), between its corners: the pixels the red covers. + let inked = |r: &Rendered| (55..105).filter(|x| r.pixel(*x, 50)[1] < 64).count(); + let line = draw(([200, 0, 0], false)); + let p = line.pixel(80, 50); + assert!(p[0] > 150 && p[1] < 100 && p[2] < 100, "{p:?}"); + assert_eq!(inked(&line), 50); + let dotted = draw(([200, 0, 0], true)); + let dots = inked(&dotted); + assert!((10..40).contains(&dots), "{dots}: {:?}", (45..70).map(|x| dotted.pixel(x, 50)[1]).collect::>()); + let default = draw(RenderOptions::default().guide); + assert!((55..105).all(|x| !is_white(default.pixel(x, 50))), "a line by default"); +} diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 373e6939..8d3ac45f 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -234,7 +234,10 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { // File Handling › Display Bitmaps as Anti-aliased Images in Pixel Preview: off, images show // their pixels as they rasterize, hard-edged. let smooth_images = pixel.is_none() || app.session.prefs.anti_aliased_bitmaps; + // Paths made into guides are drawn as the ruler guides are (Preferences › Guides & Grid). + let guide_look = LineLook::guides(&app.session.prefs, &t); let key = CacheKey { + guide: ([guide_look.color.r(), guide_look.color.g(), guide_look.color.b()], guide_look.dots), doc: st.uid as usize, revision: st.revision, interacting: st.interaction.is_some(), @@ -297,6 +300,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { let [r, g, b, _] = c.to_rgba8(1.0); [r, g, b] }), + guide: key.guide, mask_view, highlight_substitutions: true, // General › Anti-aliased Artwork: off, edges are hard on screen (raster effects and diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 1648279f..1f165d59 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -321,6 +321,8 @@ pub struct CacheKey { /// Appearance of Black › On Screen › Display All Blacks as Rich Black /// ([`vectorcraft_render::RenderOptions::rich_black`]). pub rich_black: bool, + /// Colour and style of guide paths ([`vectorcraft_render::RenderOptions::guide`]). + pub guide: ([u8; 3], bool), } #[derive(Clone, Copy, Debug, Default)]